Adding a Pusher container as a middleware/dispatcher between front and back
This commit is contained in:
parent
6c6d046891
commit
4c1e566a6c
86 changed files with 12172 additions and 983 deletions
|
@ -1,6 +1,6 @@
|
|||
import {PointInterface} from "./Websocket/PointInterface";
|
||||
import {Group} from "./Group";
|
||||
import {User} from "./User";
|
||||
import {User, UserSocket} from "./User";
|
||||
import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface";
|
||||
import {PositionInterface} from "_Model/PositionInterface";
|
||||
import {Identificable} from "_Model/Websocket/Identificable";
|
||||
|
@ -11,6 +11,9 @@ import {Movable} from "_Model/Movable";
|
|||
import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier";
|
||||
import {arrayIntersect} from "../Services/ArrayHelper";
|
||||
import {MAX_USERS_PER_ROOM} from "../Enum/EnvironmentVariable";
|
||||
import {JoinRoomMessage} from "../Messages/generated/messages_pb";
|
||||
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
||||
import { ZoneSocket } from "src/RoomManager";
|
||||
|
||||
export type ConnectCallback = (user: User, group: Group) => void;
|
||||
export type DisconnectCallback = (user: User, group: Group) => void;
|
||||
|
@ -42,6 +45,7 @@ export class GameRoom {
|
|||
public readonly roomSlug: string;
|
||||
public readonly worldSlug: string = '';
|
||||
public readonly organizationSlug: string = '';
|
||||
private nextUserId: number = 1;
|
||||
|
||||
constructor(roomId: string,
|
||||
connectCallback: ConnectCallback,
|
||||
|
@ -85,26 +89,34 @@ export class GameRoom {
|
|||
return this.users;
|
||||
}
|
||||
|
||||
public join(socket : ExSocketInterface, userPosition: PointInterface): void {
|
||||
const user = new User(socket.userId, socket.userUuid, userPosition, false, this.positionNotifier, socket);
|
||||
this.users.set(socket.userId, user);
|
||||
public join(socket : UserSocket, joinRoomMessage: JoinRoomMessage): User {
|
||||
const positionMessage = joinRoomMessage.getPositionmessage();
|
||||
if (positionMessage === undefined) {
|
||||
throw new Error('Missing position message');
|
||||
}
|
||||
const position = ProtobufUtils.toPointInterface(positionMessage);
|
||||
|
||||
|
||||
const user = new User(this.nextUserId, joinRoomMessage.getUseruuid(), position, false, this.positionNotifier, socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()));
|
||||
this.nextUserId++;
|
||||
this.users.set(user.id, user);
|
||||
// Let's call update position to trigger the join / leave room
|
||||
//this.updatePosition(socket, userPosition);
|
||||
this.updateUserGroup(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
public leave(user : Identificable){
|
||||
const userObj = this.users.get(user.userId);
|
||||
public leave(user : User){
|
||||
const userObj = this.users.get(user.id);
|
||||
if (userObj === undefined) {
|
||||
console.warn('User ', user.userId, 'does not belong to world! It should!');
|
||||
console.warn('User ', user.id, 'does not belong to this game room! It should!');
|
||||
}
|
||||
if (userObj !== undefined && typeof userObj.group !== 'undefined') {
|
||||
this.leaveGroup(userObj);
|
||||
}
|
||||
this.users.delete(user.userId);
|
||||
this.users.delete(user.id);
|
||||
|
||||
if (userObj !== undefined) {
|
||||
this.positionNotifier.removeViewport(userObj);
|
||||
this.positionNotifier.leave(userObj);
|
||||
}
|
||||
}
|
||||
|
@ -117,12 +129,7 @@ export class GameRoom {
|
|||
return this.users.size === 0;
|
||||
}
|
||||
|
||||
public updatePosition(socket : Identificable, userPosition: PointInterface): void {
|
||||
const user = this.users.get(socket.userId);
|
||||
if(typeof user === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
public updatePosition(user : User, userPosition: PointInterface): void {
|
||||
user.setPosition(userPosition);
|
||||
|
||||
this.updateUserGroup(user);
|
||||
|
@ -170,12 +177,7 @@ export class GameRoom {
|
|||
}
|
||||
}
|
||||
|
||||
setSilent(socket: Identificable, silent: boolean) {
|
||||
const user = this.users.get(socket.userId);
|
||||
if(typeof user === 'undefined') {
|
||||
console.warn('In setSilent, could not find user with ID "'+socket.userId+'" in world.');
|
||||
return;
|
||||
}
|
||||
setSilent(user: User, silent: boolean) {
|
||||
if (user.silent === silent) {
|
||||
return;
|
||||
}
|
||||
|
@ -186,7 +188,7 @@ export class GameRoom {
|
|||
}
|
||||
if (!silent) {
|
||||
// If we are back to life, let's trigger a position update to see if we can join some group.
|
||||
this.updatePosition(socket, user.getPosition());
|
||||
this.updatePosition(user, user.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,17 +283,15 @@ export class GameRoom {
|
|||
return this.itemsState;
|
||||
}
|
||||
|
||||
|
||||
setViewport(socket : Identificable, viewport: ViewportInterface): Movable[] {
|
||||
const user = this.users.get(socket.userId);
|
||||
if(typeof user === 'undefined') {
|
||||
console.warn('In setViewport, could not find user with ID "'+socket.userId+'" in world.');
|
||||
return [];
|
||||
}
|
||||
return this.positionNotifier.setViewport(user, viewport);
|
||||
}
|
||||
|
||||
canAccess(userTags: string[]): boolean {
|
||||
public canAccess(userTags: string[]): boolean {
|
||||
return arrayIntersect(userTags, this.tags);
|
||||
}
|
||||
|
||||
public addZoneListener(call: ZoneSocket, x: number, y: number): Set<Movable> {
|
||||
return this.positionNotifier.addZoneListener(call, x, y);
|
||||
}
|
||||
|
||||
public removeZoneListener(call: ZoneSocket, x: number, y: number): void {
|
||||
return this.positionNotifier.removeZoneListener(call, x, y);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,9 @@
|
|||
* number of players around the current player.
|
||||
*/
|
||||
import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone";
|
||||
import {PointInterface} from "_Model/Websocket/PointInterface";
|
||||
import {User} from "_Model/User";
|
||||
import {ViewportInterface} from "_Model/Websocket/ViewportMessage";
|
||||
import {Movable} from "_Model/Movable";
|
||||
import {PositionInterface} from "_Model/PositionInterface";
|
||||
import {ZoneSocket} from "../RoomManager";
|
||||
|
||||
interface ZoneDescriptor {
|
||||
i: number;
|
||||
|
@ -36,44 +34,6 @@ export class PositionNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the viewport coordinates.
|
||||
* Returns the list of new users to add
|
||||
*/
|
||||
public setViewport(user: User, viewport: ViewportInterface): Movable[] {
|
||||
if (viewport.left > viewport.right || viewport.top > viewport.bottom) {
|
||||
console.warn('Invalid viewport received: ', viewport);
|
||||
return [];
|
||||
}
|
||||
|
||||
const oldZones = user.listenedZones;
|
||||
const newZones = new Set<Zone>();
|
||||
|
||||
const topLeftDesc = this.getZoneDescriptorFromCoordinates(viewport.left, viewport.top);
|
||||
const bottomRightDesc = this.getZoneDescriptorFromCoordinates(viewport.right, viewport.bottom);
|
||||
|
||||
for (let j = topLeftDesc.j; j <= bottomRightDesc.j; j++) {
|
||||
for (let i = topLeftDesc.i; i <= bottomRightDesc.i; i++) {
|
||||
newZones.add(this.getZone(i, j));
|
||||
}
|
||||
}
|
||||
|
||||
const addedZones = [...newZones].filter(x => !oldZones.has(x));
|
||||
const removedZones = [...oldZones].filter(x => !newZones.has(x));
|
||||
|
||||
|
||||
let things: Movable[] = [];
|
||||
for (const zone of addedZones) {
|
||||
zone.startListening(user);
|
||||
things = things.concat(Array.from(zone.getThings()))
|
||||
}
|
||||
for (const zone of removedZones) {
|
||||
zone.stopListening(user);
|
||||
}
|
||||
|
||||
return things;
|
||||
}
|
||||
|
||||
public enter(thing: Movable): void {
|
||||
const position = thing.getPosition();
|
||||
const zoneDesc = this.getZoneDescriptorFromCoordinates(position.x, position.y);
|
||||
|
@ -108,13 +68,6 @@ export class PositionNotifier {
|
|||
oldZone.leave(thing, null);
|
||||
}
|
||||
|
||||
public removeViewport(user: User): void {
|
||||
// Also, let's stop listening on viewports
|
||||
for (const zone of user.listenedZones) {
|
||||
zone.stopListening(user);
|
||||
}
|
||||
}
|
||||
|
||||
private getZone(i: number, j: number): Zone {
|
||||
let zoneRow = this.zones[j];
|
||||
if (zoneRow === undefined) {
|
||||
|
@ -129,4 +82,15 @@ export class PositionNotifier {
|
|||
}
|
||||
return zone;
|
||||
}
|
||||
|
||||
public addZoneListener(call: ZoneSocket, x: number, y: number): Set<Movable> {
|
||||
const zone = this.getZone(x, y);
|
||||
zone.addListener(call);
|
||||
return zone.getThings();
|
||||
}
|
||||
|
||||
public removeZoneListener(call: ZoneSocket, x: number, y: number): void {
|
||||
const zone = this.getZone(x, y);
|
||||
zone.removeListener(call);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@ import { Group } from "./Group";
|
|||
import { PointInterface } from "./Websocket/PointInterface";
|
||||
import {Zone} from "_Model/Zone";
|
||||
import {Movable} from "_Model/Movable";
|
||||
import {PositionInterface} from "_Model/PositionInterface";
|
||||
import {PositionNotifier} from "_Model/PositionNotifier";
|
||||
import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface";
|
||||
import {ServerDuplexStream, ServerWritableStream} from "grpc";
|
||||
import {BatchMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb";
|
||||
import {ProtobufUtils} from "_Model/Websocket/ProtobufUtils";
|
||||
import {CharacterLayer, ExSocketInterface} from "_Model/Websocket/ExSocketInterface";
|
||||
|
||||
export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientMessage>;
|
||||
|
||||
export class User implements Movable {
|
||||
public listenedZones: Set<Zone>;
|
||||
|
@ -12,11 +16,14 @@ export class User implements Movable {
|
|||
|
||||
public constructor(
|
||||
public id: number,
|
||||
public uuid: string,
|
||||
public readonly uuid: string,
|
||||
private position: PointInterface,
|
||||
public silent: boolean,
|
||||
private positionNotifier: PositionNotifier,
|
||||
public readonly socket: ExSocketInterface
|
||||
public readonly socket: UserSocket,
|
||||
public readonly tags: string[],
|
||||
public readonly name: string,
|
||||
public readonly characterLayers: CharacterLayer[]
|
||||
) {
|
||||
this.listenedZones = new Set<Zone>();
|
||||
|
||||
|
@ -32,4 +39,27 @@ export class User implements Movable {
|
|||
this.position = position;
|
||||
this.positionNotifier.updatePosition(this, position, oldPosition);
|
||||
}
|
||||
|
||||
|
||||
private batchedMessages: BatchMessage = new BatchMessage();
|
||||
private batchTimeout: NodeJS.Timeout|null = null;
|
||||
|
||||
public emitInBatch(payload: SubMessage): void {
|
||||
this.batchedMessages.addPayload(payload);
|
||||
|
||||
if (this.batchTimeout === null) {
|
||||
this.batchTimeout = setTimeout(() => {
|
||||
/*if (socket.disconnecting) {
|
||||
return;
|
||||
}*/
|
||||
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
serverToClientMessage.setBatchmessage(this.batchedMessages);
|
||||
|
||||
this.socket.write(serverToClientMessage);
|
||||
this.batchedMessages = new BatchMessage();
|
||||
this.batchTimeout = null;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {PositionInterface} from "_Model/PositionInterface";
|
|||
export class ProtobufUtils {
|
||||
|
||||
public static toPositionMessage(point: PointInterface): PositionMessage {
|
||||
let direction: PositionMessage.DirectionMap[keyof PositionMessage.DirectionMap];
|
||||
let direction: Direction;
|
||||
switch (point.direction) {
|
||||
case 'up':
|
||||
direction = Direction.UP;
|
||||
|
@ -105,4 +105,14 @@ export class ProtobufUtils {
|
|||
return message;
|
||||
});
|
||||
}
|
||||
|
||||
public static toCharacterLayerObjects(characterLayers: CharacterLayerMessage[]): CharacterLayer[] {
|
||||
return characterLayers.map(function(characterLayer): CharacterLayer {
|
||||
const url = characterLayer.getUrl();
|
||||
return {
|
||||
name: characterLayer.getName(),
|
||||
url: url ? url : undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,21 @@ import {User} from "./User";
|
|||
import {PositionInterface} from "_Model/PositionInterface";
|
||||
import {Movable} from "./Movable";
|
||||
import {Group} from "./Group";
|
||||
import {ZoneSocket} from "../RoomManager";
|
||||
|
||||
export type EntersCallback = (thing: Movable, listener: User) => void;
|
||||
export type MovesCallback = (thing: Movable, position: PositionInterface, listener: User) => void;
|
||||
export type LeavesCallback = (thing: Movable, listener: User) => void;
|
||||
export type EntersCallback = (thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => void;
|
||||
export type MovesCallback = (thing: Movable, position: PositionInterface, listener: ZoneSocket) => void;
|
||||
export type LeavesCallback = (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => void;
|
||||
|
||||
export class Zone {
|
||||
private things: Set<Movable> = new Set<Movable>();
|
||||
private listeners: Set<User> = new Set<User>();
|
||||
private listeners: Set<ZoneSocket> = new Set<ZoneSocket>();
|
||||
|
||||
/**
|
||||
* @param x For debugging purpose only
|
||||
* @param y For debugging purpose only
|
||||
*/
|
||||
constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, private x: number, private y: number) {
|
||||
constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, public readonly x: number, public readonly y: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,9 +41,9 @@ export class Zone {
|
|||
*/
|
||||
private notifyLeft(thing: Movable, newZone: Zone|null) {
|
||||
for (const listener of this.listeners) {
|
||||
if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) {
|
||||
this.onLeaves(thing, listener);
|
||||
}
|
||||
//if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) {
|
||||
this.onLeaves(thing, newZone, listener);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,17 +57,20 @@ export class Zone {
|
|||
*/
|
||||
private notifyEnter(thing: Movable, oldZone: Zone|null, position: PositionInterface) {
|
||||
for (const listener of this.listeners) {
|
||||
if (listener === thing) {
|
||||
|
||||
/*if (listener === thing) {
|
||||
continue;
|
||||
}
|
||||
if (oldZone === null || !listener.listenedZones.has(oldZone)) {
|
||||
this.onEnters(thing, listener);
|
||||
} else {
|
||||
this.onMoves(thing, position, listener);
|
||||
}
|
||||
}*/
|
||||
this.onEnters(thing, oldZone, listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public move(thing: Movable, position: PositionInterface) {
|
||||
if (!this.things.has(thing)) {
|
||||
this.things.add(thing);
|
||||
|
@ -75,13 +79,13 @@ export class Zone {
|
|||
}
|
||||
|
||||
for (const listener of this.listeners) {
|
||||
if (listener !== thing) {
|
||||
//if (listener !== thing) {
|
||||
this.onMoves(thing,position, listener);
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
public startListening(listener: User): void {
|
||||
/*public startListening(listener: User): void {
|
||||
for (const thing of this.things) {
|
||||
if (thing !== listener) {
|
||||
this.onEnters(thing, listener);
|
||||
|
@ -101,9 +105,18 @@ export class Zone {
|
|||
|
||||
this.listeners.delete(listener);
|
||||
listener.listenedZones.delete(this);
|
||||
}
|
||||
}*/
|
||||
|
||||
public getThings(): Set<Movable> {
|
||||
return this.things;
|
||||
}
|
||||
|
||||
public addListener(socket: ZoneSocket): void {
|
||||
this.listeners.add(socket);
|
||||
// TODO: here, we should trigger in some way the sending of current items
|
||||
}
|
||||
|
||||
public removeListener(socket: ZoneSocket): void {
|
||||
this.listeners.delete(socket);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue