First version with variables that actually work
This commit is contained in:
parent
5116b82e77
commit
e65e8b2097
11 changed files with 250 additions and 50 deletions
|
@ -7,9 +7,14 @@ import { PositionNotifier } from "./PositionNotifier";
|
||||||
import { Movable } from "_Model/Movable";
|
import { Movable } from "_Model/Movable";
|
||||||
import { extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous } from "./RoomIdentifier";
|
import { extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous } from "./RoomIdentifier";
|
||||||
import { arrayIntersect } from "../Services/ArrayHelper";
|
import { arrayIntersect } from "../Services/ArrayHelper";
|
||||||
import { EmoteEventMessage, JoinRoomMessage } from "../Messages/generated/messages_pb";
|
import {
|
||||||
|
BatchToPusherMessage,
|
||||||
|
BatchToPusherRoomMessage,
|
||||||
|
EmoteEventMessage,
|
||||||
|
JoinRoomMessage, SubToPusherRoomMessage, VariableMessage
|
||||||
|
} from "../Messages/generated/messages_pb";
|
||||||
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
||||||
import { ZoneSocket } from "src/RoomManager";
|
import {RoomSocket, ZoneSocket} from "src/RoomManager";
|
||||||
import { Admin } from "../Model/Admin";
|
import { Admin } from "../Model/Admin";
|
||||||
|
|
||||||
export type ConnectCallback = (user: User, group: Group) => void;
|
export type ConnectCallback = (user: User, group: Group) => void;
|
||||||
|
@ -35,7 +40,7 @@ export class GameRoom {
|
||||||
private readonly disconnectCallback: DisconnectCallback;
|
private readonly disconnectCallback: DisconnectCallback;
|
||||||
|
|
||||||
private itemsState = new Map<number, unknown>();
|
private itemsState = new Map<number, unknown>();
|
||||||
private variables = new Map<string, string>();
|
public readonly variables = new Map<string, string>();
|
||||||
|
|
||||||
private readonly positionNotifier: PositionNotifier;
|
private readonly positionNotifier: PositionNotifier;
|
||||||
public readonly roomId: string;
|
public readonly roomId: string;
|
||||||
|
@ -45,6 +50,8 @@ export class GameRoom {
|
||||||
private versionNumber: number = 1;
|
private versionNumber: number = 1;
|
||||||
private nextUserId: number = 1;
|
private nextUserId: number = 1;
|
||||||
|
|
||||||
|
private roomListeners: Set<RoomSocket> = new Set<RoomSocket>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
connectCallback: ConnectCallback,
|
connectCallback: ConnectCallback,
|
||||||
|
@ -312,6 +319,22 @@ export class GameRoom {
|
||||||
|
|
||||||
public setVariable(name: string, value: string): void {
|
public setVariable(name: string, value: string): void {
|
||||||
this.variables.set(name, value);
|
this.variables.set(name, value);
|
||||||
|
|
||||||
|
// TODO: should we batch those every 100ms?
|
||||||
|
const variableMessage = new VariableMessage();
|
||||||
|
variableMessage.setName(name);
|
||||||
|
variableMessage.setValue(value);
|
||||||
|
|
||||||
|
const subMessage = new SubToPusherRoomMessage();
|
||||||
|
subMessage.setVariablemessage(variableMessage);
|
||||||
|
|
||||||
|
const batchMessage = new BatchToPusherRoomMessage();
|
||||||
|
batchMessage.addPayload(subMessage);
|
||||||
|
|
||||||
|
// Dispatch the message on the room listeners
|
||||||
|
for (const socket of this.roomListeners) {
|
||||||
|
socket.write(batchMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public addZoneListener(call: ZoneSocket, x: number, y: number): Set<Movable> {
|
public addZoneListener(call: ZoneSocket, x: number, y: number): Set<Movable> {
|
||||||
|
@ -343,4 +366,12 @@ export class GameRoom {
|
||||||
public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) {
|
public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) {
|
||||||
this.positionNotifier.emitEmoteEvent(user, emoteEventMessage);
|
this.positionNotifier.emitEmoteEvent(user, emoteEventMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addRoomListener(socket: RoomSocket) {
|
||||||
|
this.roomListeners.add(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeRoomListener(socket: RoomSocket) {
|
||||||
|
this.roomListeners.delete(socket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
AdminMessage,
|
AdminMessage,
|
||||||
AdminPusherToBackMessage,
|
AdminPusherToBackMessage,
|
||||||
AdminRoomMessage,
|
AdminRoomMessage,
|
||||||
BanMessage,
|
BanMessage, BatchToPusherRoomMessage,
|
||||||
EmotePromptMessage,
|
EmotePromptMessage,
|
||||||
EmptyMessage,
|
EmptyMessage,
|
||||||
ItemEventMessage,
|
ItemEventMessage,
|
||||||
|
@ -12,7 +12,7 @@ import {
|
||||||
PlayGlobalMessage,
|
PlayGlobalMessage,
|
||||||
PusherToBackMessage,
|
PusherToBackMessage,
|
||||||
QueryJitsiJwtMessage,
|
QueryJitsiJwtMessage,
|
||||||
RefreshRoomPromptMessage,
|
RefreshRoomPromptMessage, RoomMessage,
|
||||||
ServerToAdminClientMessage,
|
ServerToAdminClientMessage,
|
||||||
ServerToClientMessage,
|
ServerToClientMessage,
|
||||||
SilentMessage,
|
SilentMessage,
|
||||||
|
@ -33,6 +33,7 @@ const debug = Debug("roommanager");
|
||||||
|
|
||||||
export type AdminSocket = ServerDuplexStream<AdminPusherToBackMessage, ServerToAdminClientMessage>;
|
export type AdminSocket = ServerDuplexStream<AdminPusherToBackMessage, ServerToAdminClientMessage>;
|
||||||
export type ZoneSocket = ServerWritableStream<ZoneMessage, ServerToClientMessage>;
|
export type ZoneSocket = ServerWritableStream<ZoneMessage, ServerToClientMessage>;
|
||||||
|
export type RoomSocket = ServerWritableStream<RoomMessage, BatchToPusherRoomMessage>;
|
||||||
|
|
||||||
const roomManager: IRoomManagerServer = {
|
const roomManager: IRoomManagerServer = {
|
||||||
joinRoom: (call: UserSocket): void => {
|
joinRoom: (call: UserSocket): void => {
|
||||||
|
@ -156,6 +157,29 @@ const roomManager: IRoomManagerServer = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listenRoom(call: RoomSocket): void {
|
||||||
|
debug("listenRoom called");
|
||||||
|
const roomMessage = call.request;
|
||||||
|
|
||||||
|
socketManager.addRoomListener(call, roomMessage.getRoomid());
|
||||||
|
|
||||||
|
call.on("cancelled", () => {
|
||||||
|
debug("listenRoom cancelled");
|
||||||
|
socketManager.removeRoomListener(call, roomMessage.getRoomid());
|
||||||
|
call.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
call.on("close", () => {
|
||||||
|
debug("listenRoom connection closed");
|
||||||
|
socketManager.removeRoomListener(call, roomMessage.getRoomid());
|
||||||
|
}).on("error", (e) => {
|
||||||
|
console.error("An error occurred in listenRoom stream:", e);
|
||||||
|
socketManager.removeRoomListener(call, roomMessage.getRoomid());
|
||||||
|
call.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
adminRoom(call: AdminSocket): void {
|
adminRoom(call: AdminSocket): void {
|
||||||
console.log("adminRoom called");
|
console.log("adminRoom called");
|
||||||
|
|
||||||
|
@ -230,7 +254,7 @@ const roomManager: IRoomManagerServer = {
|
||||||
): void {
|
): void {
|
||||||
socketManager.dispatchRoomRefresh(call.request.getRoomid());
|
socketManager.dispatchRoomRefresh(call.request.getRoomid());
|
||||||
callback(null, new EmptyMessage());
|
callback(null, new EmptyMessage());
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { roomManager };
|
export { roomManager };
|
||||||
|
|
|
@ -30,7 +30,7 @@ import {
|
||||||
BanUserMessage,
|
BanUserMessage,
|
||||||
RefreshRoomMessage,
|
RefreshRoomMessage,
|
||||||
EmotePromptMessage,
|
EmotePromptMessage,
|
||||||
VariableMessage,
|
VariableMessage, BatchToPusherRoomMessage, SubToPusherRoomMessage,
|
||||||
} from "../Messages/generated/messages_pb";
|
} from "../Messages/generated/messages_pb";
|
||||||
import { User, UserSocket } from "../Model/User";
|
import { User, UserSocket } from "../Model/User";
|
||||||
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
||||||
|
@ -49,7 +49,7 @@ import Jwt from "jsonwebtoken";
|
||||||
import { JITSI_URL } from "../Enum/EnvironmentVariable";
|
import { JITSI_URL } from "../Enum/EnvironmentVariable";
|
||||||
import { clientEventsEmitter } from "./ClientEventsEmitter";
|
import { clientEventsEmitter } from "./ClientEventsEmitter";
|
||||||
import { gaugeManager } from "./GaugeManager";
|
import { gaugeManager } from "./GaugeManager";
|
||||||
import { ZoneSocket } from "../RoomManager";
|
import {RoomSocket, ZoneSocket} from "../RoomManager";
|
||||||
import { Zone } from "_Model/Zone";
|
import { Zone } from "_Model/Zone";
|
||||||
import Debug from "debug";
|
import Debug from "debug";
|
||||||
import { Admin } from "_Model/Admin";
|
import { Admin } from "_Model/Admin";
|
||||||
|
@ -66,7 +66,9 @@ function emitZoneMessage(subMessage: SubToPusherMessage, socket: ZoneSocket): vo
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SocketManager {
|
export class SocketManager {
|
||||||
private rooms: Map<string, GameRoom> = new Map<string, GameRoom>();
|
private rooms = new Map<string, GameRoom>();
|
||||||
|
// List of rooms in process of loading.
|
||||||
|
private roomsPromises = new Map<string, PromiseLike<GameRoom>>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
|
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
|
||||||
|
@ -102,6 +104,14 @@ export class SocketManager {
|
||||||
roomJoinedMessage.addItem(itemStateMessage);
|
roomJoinedMessage.addItem(itemStateMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const [name, value] of room.variables.entries()) {
|
||||||
|
const variableMessage = new VariableMessage();
|
||||||
|
variableMessage.setName(name);
|
||||||
|
variableMessage.setValue(value);
|
||||||
|
|
||||||
|
roomJoinedMessage.addVariable(variableMessage);
|
||||||
|
}
|
||||||
|
|
||||||
roomJoinedMessage.setCurrentuserid(user.id);
|
roomJoinedMessage.setCurrentuserid(user.id);
|
||||||
|
|
||||||
const serverToClientMessage = new ServerToClientMessage();
|
const serverToClientMessage = new ServerToClientMessage();
|
||||||
|
@ -186,23 +196,10 @@ export class SocketManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVariableEvent(room: GameRoom, user: User, variableMessage: VariableMessage) {
|
handleVariableEvent(room: GameRoom, user: User, variableMessage: VariableMessage) {
|
||||||
const itemEvent = ProtobufUtils.toItemEvent(itemEventMessage);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: DISPATCH ON NEW ROOM CHANNEL
|
|
||||||
|
|
||||||
const subMessage = new SubMessage();
|
|
||||||
subMessage.setItemeventmessage(itemEventMessage);
|
|
||||||
|
|
||||||
// Let's send the event without using the SocketIO room.
|
|
||||||
// TODO: move this in the GameRoom class.
|
|
||||||
for (const user of room.getUsers().values()) {
|
|
||||||
user.emitInBatch(subMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
room.setVariable(variableMessage.getName(), variableMessage.getValue());
|
room.setVariable(variableMessage.getName(), variableMessage.getValue());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('An error occurred on "item_event"');
|
console.error('An error occurred on "handleVariableEvent"');
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,10 +281,18 @@ export class SocketManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrCreateRoom(roomId: string): Promise<GameRoom> {
|
async getOrCreateRoom(roomId: string): Promise<GameRoom> {
|
||||||
//check and create new world for a room
|
//check and create new room
|
||||||
let world = this.rooms.get(roomId);
|
let room = this.rooms.get(roomId);
|
||||||
if (world === undefined) {
|
if (room === undefined) {
|
||||||
world = new GameRoom(
|
let roomPromise = this.roomsPromises.get(roomId);
|
||||||
|
if (roomPromise) {
|
||||||
|
return roomPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: for now, the promise is useless (because this is synchronous, but soon, we will need to
|
||||||
|
// load the map server side.
|
||||||
|
|
||||||
|
room = new GameRoom(
|
||||||
roomId,
|
roomId,
|
||||||
(user: User, group: Group) => this.joinWebRtcRoom(user, group),
|
(user: User, group: Group) => this.joinWebRtcRoom(user, group),
|
||||||
(user: User, group: Group) => this.disConnectedUser(user, group),
|
(user: User, group: Group) => this.disConnectedUser(user, group),
|
||||||
|
@ -303,9 +308,12 @@ export class SocketManager {
|
||||||
this.onEmote(emoteEventMessage, listener)
|
this.onEmote(emoteEventMessage, listener)
|
||||||
);
|
);
|
||||||
gaugeManager.incNbRoomGauge();
|
gaugeManager.incNbRoomGauge();
|
||||||
this.rooms.set(roomId, world);
|
this.rooms.set(roomId, room);
|
||||||
|
|
||||||
|
// TODO: change this the to new Promise()... when the method becomes actually asynchronous
|
||||||
|
roomPromise = Promise.resolve(room);
|
||||||
}
|
}
|
||||||
return Promise.resolve(world);
|
return Promise.resolve(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async joinRoom(
|
private async joinRoom(
|
||||||
|
@ -676,6 +684,42 @@ export class SocketManager {
|
||||||
room.removeZoneListener(call, x, y);
|
room.removeZoneListener(call, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addRoomListener(call: RoomSocket, roomId: string) {
|
||||||
|
const room = await this.getOrCreateRoom(roomId);
|
||||||
|
if (!room) {
|
||||||
|
console.error("In addRoomListener, could not find room with id '" + roomId + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
room.addRoomListener(call);
|
||||||
|
//const things = room.addZoneListener(call, x, y);
|
||||||
|
|
||||||
|
const batchMessage = new BatchToPusherRoomMessage();
|
||||||
|
|
||||||
|
for (const [name, value] of room.variables.entries()) {
|
||||||
|
const variableMessage = new VariableMessage();
|
||||||
|
variableMessage.setName(name);
|
||||||
|
variableMessage.setValue(value);
|
||||||
|
|
||||||
|
const subMessage = new SubToPusherRoomMessage();
|
||||||
|
subMessage.setVariablemessage(variableMessage);
|
||||||
|
|
||||||
|
batchMessage.addPayload(subMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
call.write(batchMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRoomListener(call: RoomSocket, roomId: string) {
|
||||||
|
const room = this.rooms.get(roomId);
|
||||||
|
if (!room) {
|
||||||
|
console.error("In removeRoomListener, could not find room with id '" + roomId + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
room.removeRoomListener(call);
|
||||||
|
}
|
||||||
|
|
||||||
public async handleJoinAdminRoom(admin: Admin, roomId: string): Promise<GameRoom> {
|
public async handleJoinAdminRoom(admin: Admin, roomId: string): Promise<GameRoom> {
|
||||||
const room = await socketManager.getOrCreateRoom(roomId);
|
const room = await socketManager.getOrCreateRoom(roomId);
|
||||||
|
|
||||||
|
@ -831,6 +875,7 @@ export class SocketManager {
|
||||||
emoteEventMessage.setActoruserid(user.id);
|
emoteEventMessage.setActoruserid(user.id);
|
||||||
room.emitEmoteEvent(user, emoteEventMessage);
|
room.emitEmoteEvent(user, emoteEventMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const socketManager = new SocketManager();
|
export const socketManager = new SocketManager();
|
||||||
|
|
|
@ -34,6 +34,8 @@ import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "
|
||||||
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
|
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
|
||||||
import { isSetVariableIframeEvent, SetVariableEvent } from "./Events/SetVariableEvent";
|
import { isSetVariableIframeEvent, SetVariableEvent } from "./Events/SetVariableEvent";
|
||||||
|
|
||||||
|
type AnswererCallback<T extends keyof IframeQueryMap> = (query: IframeQueryMap[T]['query']) => IframeQueryMap[T]['answer']|PromiseLike<IframeQueryMap[T]['answer']>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens to messages from iframes and turn those messages into easy to use observables.
|
* Listens to messages from iframes and turn those messages into easy to use observables.
|
||||||
* Also allows to send messages to those iframes.
|
* Also allows to send messages to those iframes.
|
||||||
|
@ -111,12 +113,10 @@ class IframeListener {
|
||||||
private sendPlayerMove: boolean = false;
|
private sendPlayerMove: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
// Note: we are forced to type this in "any" because of https://github.com/microsoft/TypeScript/issues/31904
|
// Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
private answerers: {
|
||||||
private answerers: any = {};
|
[str in keyof IframeQueryMap]?: unknown
|
||||||
/*private answerers: {
|
} = {};
|
||||||
[key in keyof IframeQueryMap]?: (query: IframeQueryMap[key]['query']) => IframeQueryMap[key]['answer']|PromiseLike<IframeQueryMap[key]['answer']>
|
|
||||||
} = {};*/
|
|
||||||
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -156,7 +156,7 @@ class IframeListener {
|
||||||
const queryId = payload.id;
|
const queryId = payload.id;
|
||||||
const query = payload.query;
|
const query = payload.query;
|
||||||
|
|
||||||
const answerer = this.answerers[query.type];
|
const answerer = this.answerers[query.type] as AnswererCallback<keyof IframeQueryMap> | undefined;
|
||||||
if (answerer === undefined) {
|
if (answerer === undefined) {
|
||||||
const errorMsg = 'The iFrame sent a message of type "'+query.type+'" but there is no service configured to answer these messages.';
|
const errorMsg = 'The iFrame sent a message of type "'+query.type+'" but there is no service configured to answer these messages.';
|
||||||
console.error(errorMsg);
|
console.error(errorMsg);
|
||||||
|
@ -432,7 +432,7 @@ class IframeListener {
|
||||||
* @param key The "type" of the query we are answering
|
* @param key The "type" of the query we are answering
|
||||||
* @param callback
|
* @param callback
|
||||||
*/
|
*/
|
||||||
public registerAnswerer<T extends keyof IframeQueryMap>(key: T, callback: (query: IframeQueryMap[T]['query']) => IframeQueryMap[T]['answer']|PromiseLike<IframeQueryMap[T]['answer']> ): void {
|
public registerAnswerer<T extends keyof IframeQueryMap>(key: T, callback: AnswererCallback<T> ): void {
|
||||||
this.answerers[key] = callback;
|
this.answerers[key] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ export enum EventMessage {
|
||||||
TELEPORT = "teleport",
|
TELEPORT = "teleport",
|
||||||
USER_MESSAGE = "user-message",
|
USER_MESSAGE = "user-message",
|
||||||
START_JITSI_ROOM = "start-jitsi-room",
|
START_JITSI_ROOM = "start-jitsi-room",
|
||||||
|
SET_VARIABLE = "set-variable",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PointInterface {
|
export interface PointInterface {
|
||||||
|
@ -105,6 +106,7 @@ export interface RoomJoinedMessageInterface {
|
||||||
//users: MessageUserPositionInterface[],
|
//users: MessageUserPositionInterface[],
|
||||||
//groups: GroupCreatedUpdatedMessageInterface[],
|
//groups: GroupCreatedUpdatedMessageInterface[],
|
||||||
items: { [itemId: number]: unknown };
|
items: { [itemId: number]: unknown };
|
||||||
|
variables: Map<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayGlobalMessageInterface {
|
export interface PlayGlobalMessageInterface {
|
||||||
|
|
|
@ -165,6 +165,9 @@ export class RoomConnection implements RoomConnection {
|
||||||
} else if (subMessage.hasEmoteeventmessage()) {
|
} else if (subMessage.hasEmoteeventmessage()) {
|
||||||
const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage;
|
const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage;
|
||||||
emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote());
|
emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote());
|
||||||
|
} else if (subMessage.hasVariablemessage()) {
|
||||||
|
event = EventMessage.SET_VARIABLE;
|
||||||
|
payload = subMessage.getVariablemessage();
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unexpected batch message type");
|
throw new Error("Unexpected batch message type");
|
||||||
}
|
}
|
||||||
|
@ -174,6 +177,7 @@ export class RoomConnection implements RoomConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (message.hasRoomjoinedmessage()) {
|
} else if (message.hasRoomjoinedmessage()) {
|
||||||
|
console.error('COUCOU')
|
||||||
const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage;
|
const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage;
|
||||||
|
|
||||||
const items: { [itemId: number]: unknown } = {};
|
const items: { [itemId: number]: unknown } = {};
|
||||||
|
@ -181,6 +185,11 @@ export class RoomConnection implements RoomConnection {
|
||||||
items[item.getItemid()] = JSON.parse(item.getStatejson());
|
items[item.getItemid()] = JSON.parse(item.getStatejson());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const variables = new Map<string, unknown>();
|
||||||
|
for (const variable of roomJoinedMessage.getVariableList()) {
|
||||||
|
variables.set(variable.getName(), JSON.parse(variable.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
this.userId = roomJoinedMessage.getCurrentuserid();
|
this.userId = roomJoinedMessage.getCurrentuserid();
|
||||||
this.tags = roomJoinedMessage.getTagList();
|
this.tags = roomJoinedMessage.getTagList();
|
||||||
|
|
||||||
|
@ -188,6 +197,7 @@ export class RoomConnection implements RoomConnection {
|
||||||
connection: this,
|
connection: this,
|
||||||
room: {
|
room: {
|
||||||
items,
|
items,
|
||||||
|
variables,
|
||||||
} as RoomJoinedMessageInterface,
|
} as RoomJoinedMessageInterface,
|
||||||
});
|
});
|
||||||
} else if (message.hasWorldfullmessage()) {
|
} else if (message.hasWorldfullmessage()) {
|
||||||
|
@ -634,6 +644,18 @@ export class RoomConnection implements RoomConnection {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSetVariable(callback: (name: string, value: unknown) => void): void {
|
||||||
|
this.onMessage(EventMessage.SET_VARIABLE, (message: VariableMessage) => {
|
||||||
|
const name = message.getName();
|
||||||
|
const serializedValue = message.getValue();
|
||||||
|
let value: unknown = undefined;
|
||||||
|
if (serializedValue) {
|
||||||
|
value = JSON.parse(serializedValue);
|
||||||
|
}
|
||||||
|
callback(name, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public hasTag(tag: string): boolean {
|
public hasTag(tag: string): boolean {
|
||||||
return this.tags.includes(tag);
|
return this.tags.includes(tag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -707,7 +707,7 @@ export class GameScene extends DirtyScene {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up variables manager
|
// Set up variables manager
|
||||||
this.sharedVariablesManager = new SharedVariablesManager(this.connection, this.gameMap);
|
this.sharedVariablesManager = new SharedVariablesManager(this.connection, this.gameMap, onConnect.room.variables);
|
||||||
|
|
||||||
//this.initUsersPosition(roomJoinedMessage.users);
|
//this.initUsersPosition(roomJoinedMessage.users);
|
||||||
this.connectionAnswerPromiseResolve(onConnect.room);
|
this.connectionAnswerPromiseResolve(onConnect.room);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import type {Subscription} from "rxjs";
|
||||||
import type {GameMap} from "./GameMap";
|
import type {GameMap} from "./GameMap";
|
||||||
import type {ITile, ITiledMapObject} from "../Map/ITiledMap";
|
import type {ITile, ITiledMapObject} from "../Map/ITiledMap";
|
||||||
import type {Var} from "svelte/types/compiler/interfaces";
|
import type {Var} from "svelte/types/compiler/interfaces";
|
||||||
|
import {init} from "svelte/internal";
|
||||||
|
|
||||||
interface Variable {
|
interface Variable {
|
||||||
defaultValue: unknown,
|
defaultValue: unknown,
|
||||||
|
@ -18,7 +19,7 @@ export class SharedVariablesManager {
|
||||||
private _variables = new Map<string, unknown>();
|
private _variables = new Map<string, unknown>();
|
||||||
private variableObjects: Map<string, Variable>;
|
private variableObjects: Map<string, Variable>;
|
||||||
|
|
||||||
constructor(private roomConnection: RoomConnection, private gameMap: GameMap) {
|
constructor(private roomConnection: RoomConnection, private gameMap: GameMap, serverVariables: Map<string, unknown>) {
|
||||||
// We initialize the list of variable object at room start. The objects cannot be edited later
|
// We initialize the list of variable object at room start. The objects cannot be edited later
|
||||||
// (otherwise, this would cause a security issue if the scripting API can edit this list of objects)
|
// (otherwise, this would cause a security issue if the scripting API can edit this list of objects)
|
||||||
this.variableObjects = SharedVariablesManager.findVariablesInMap(gameMap);
|
this.variableObjects = SharedVariablesManager.findVariablesInMap(gameMap);
|
||||||
|
@ -28,6 +29,22 @@ export class SharedVariablesManager {
|
||||||
this._variables.set(name, variableObject.defaultValue);
|
this._variables.set(name, variableObject.defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override default values with the variables from the server:
|
||||||
|
for (const [name, value] of serverVariables) {
|
||||||
|
this._variables.set(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
roomConnection.onSetVariable((name, value) => {
|
||||||
|
console.log('Set Variable received from server');
|
||||||
|
this._variables.set(name, value);
|
||||||
|
|
||||||
|
// On server change, let's notify the iframes
|
||||||
|
iframeListener.setVariable({
|
||||||
|
key: name,
|
||||||
|
value: value,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
// When a variable is modified from an iFrame
|
// When a variable is modified from an iFrame
|
||||||
iframeListener.registerAnswerer('setVariable', (event) => {
|
iframeListener.registerAnswerer('setVariable', (event) => {
|
||||||
const key = event.key;
|
const key = event.key;
|
||||||
|
@ -48,7 +65,8 @@ export class SharedVariablesManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._variables.set(key, event.value);
|
this._variables.set(key, event.value);
|
||||||
// TODO: dispatch to the room connection.
|
|
||||||
|
// Dispatch to the room connection.
|
||||||
this.roomConnection.emitSetVariableEvent(key, event.value);
|
this.roomConnection.emitSetVariableEvent(key, event.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
41
maps/tests/Variables/shared_variables.html
Normal file
41
maps/tests/Variables/shared_variables.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
var script = document.createElement('script');
|
||||||
|
// Don't do this at home kids! The "document.referrer" part is actually inserting a XSS security.
|
||||||
|
// We are OK in this precise case because the HTML page is hosted on the "maps" domain that contains only static files.
|
||||||
|
script.setAttribute('src', document.referrer + 'iframe_api.js');
|
||||||
|
document.head.appendChild(script);
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
console.log('On load');
|
||||||
|
WA.onInit().then(() => {
|
||||||
|
console.log('After WA init');
|
||||||
|
const textField = document.getElementById('textField');
|
||||||
|
textField.value = WA.room.loadVariable('textField');
|
||||||
|
|
||||||
|
textField.addEventListener('change', function (evt) {
|
||||||
|
console.log('saving variable')
|
||||||
|
WA.room.saveVariable('textField', this.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
WA.room.onVariableChange('textField').subscribe((value) => {
|
||||||
|
console.log('variable changed received')
|
||||||
|
textField.value = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('btn').addEventListener('click', () => {
|
||||||
|
console.log(WA.room.loadVariable('textField'));
|
||||||
|
document.getElementById('placeholder').innerText = WA.room.loadVariable('textField');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<input type="text" id="textField" />
|
||||||
|
|
||||||
|
<button id="btn">Display textField variable value</button>
|
||||||
|
<div id="placeholder"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -199,7 +199,15 @@
|
||||||
<input type="radio" name="test-variables"> Success <input type="radio" name="test-variables"> Failure <input type="radio" name="test-variables" checked> Pending
|
<input type="radio" name="test-variables"> Success <input type="radio" name="test-variables"> Failure <input type="radio" name="test-variables" checked> Pending
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#" class="testLink" data-testmap="Variables/variables.json" target="_blank">Testing scripting variables</a>
|
<a href="#" class="testLink" data-testmap="Variables/variables.json" target="_blank">Testing scripting variables locally</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" name="test-shared-variables"> Success <input type="radio" name="test-shared-variables"> Failure <input type="radio" name="test-shared-variables" checked> Pending
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="testLink" data-testmap="Variables/shared_variables.json" target="_blank">Testing shared scripting variables</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
} from "../Messages/generated/messages_pb";
|
} from "../Messages/generated/messages_pb";
|
||||||
import Debug from "debug";
|
import Debug from "debug";
|
||||||
import {ClientReadableStream} from "grpc";
|
import {ClientReadableStream} from "grpc";
|
||||||
|
import {ExAdminSocketInterface} from "_Model/Websocket/ExAdminSocketInterface";
|
||||||
|
|
||||||
const debug = Debug("room");
|
const debug = Debug("room");
|
||||||
|
|
||||||
|
@ -34,8 +35,9 @@ export class PusherRoom {
|
||||||
private backConnection!: ClientReadableStream<BatchToPusherRoomMessage>;
|
private backConnection!: ClientReadableStream<BatchToPusherRoomMessage>;
|
||||||
private isClosing: boolean = false;
|
private isClosing: boolean = false;
|
||||||
private listeners: Set<ExSocketInterface> = new Set<ExSocketInterface>();
|
private listeners: Set<ExSocketInterface> = new Set<ExSocketInterface>();
|
||||||
|
public readonly variables = new Map<string, string>();
|
||||||
|
|
||||||
constructor(public readonly roomId: string, private socketListener: ZoneEventListener, private onBackFailure: (e: Error | null, room: PusherRoom) => void) {
|
constructor(public readonly roomId: string, private socketListener: ZoneEventListener) {
|
||||||
this.public = isRoomAnonymous(roomId);
|
this.public = isRoomAnonymous(roomId);
|
||||||
this.tags = [];
|
this.tags = [];
|
||||||
this.policyType = GameRoomPolicyTypes.ANONYMOUS_POLICY;
|
this.policyType = GameRoomPolicyTypes.ANONYMOUS_POLICY;
|
||||||
|
@ -96,7 +98,11 @@ export class PusherRoom {
|
||||||
for (const message of batch.getPayloadList()) {
|
for (const message of batch.getPayloadList()) {
|
||||||
if (message.hasVariablemessage()) {
|
if (message.hasVariablemessage()) {
|
||||||
const variableMessage = message.getVariablemessage() as VariableMessage;
|
const variableMessage = message.getVariablemessage() as VariableMessage;
|
||||||
// We need to dispatch this variable to all the listeners
|
|
||||||
|
// We need to store all variables to dispatch variables later to the listeners
|
||||||
|
this.variables.set(variableMessage.getName(), variableMessage.getValue());
|
||||||
|
|
||||||
|
// Let's dispatch this variable to all the listeners
|
||||||
for (const listener of this.listeners) {
|
for (const listener of this.listeners) {
|
||||||
const subMessage = new SubMessage();
|
const subMessage = new SubMessage();
|
||||||
subMessage.setVariablemessage(variableMessage);
|
subMessage.setVariablemessage(variableMessage);
|
||||||
|
@ -112,14 +118,22 @@ export class PusherRoom {
|
||||||
if (!this.isClosing) {
|
if (!this.isClosing) {
|
||||||
debug("Error on back connection");
|
debug("Error on back connection");
|
||||||
this.close();
|
this.close();
|
||||||
this.onBackFailure(e, this);
|
// Let's close all connections linked to that room
|
||||||
|
for (const listener of this.listeners) {
|
||||||
|
listener.disconnecting = true;
|
||||||
|
listener.end(1011, "Connection error between pusher and back server")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.backConnection.on("close", () => {
|
this.backConnection.on("close", () => {
|
||||||
if (!this.isClosing) {
|
if (!this.isClosing) {
|
||||||
debug("Close on back connection");
|
debug("Close on back connection");
|
||||||
this.close();
|
this.close();
|
||||||
this.onBackFailure(null, this);
|
// Let's close all connections linked to that room
|
||||||
|
for (const listener of this.listeners) {
|
||||||
|
listener.disconnecting = true;
|
||||||
|
listener.end(1011, "Connection closed between pusher and back server")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -128,10 +142,5 @@ export class PusherRoom {
|
||||||
debug("Closing connection to room %s on back server", this.roomId);
|
debug("Closing connection to room %s on back server", this.roomId);
|
||||||
this.isClosing = true;
|
this.isClosing = true;
|
||||||
this.backConnection.cancel();
|
this.backConnection.cancel();
|
||||||
|
|
||||||
// Let's close all connections linked to that room
|
|
||||||
for (const listener of this.listeners) {
|
|
||||||
listener.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue