This PR adds the display of a circle around groups. This is useful to view where you need to go to speak to someone but also to debug.
Note: implementation is suboptimal, relying on a "graphics" object that is known to be slow. In the future, we need to use a circle as a sprite instead.
import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface";
import {World} from "../Model/World";
import {Group} from "_Model/Group";
+import {UserInterface} from "_Model/UserInterface";
enum SockerIoEvent {
CONNECTION = "connection",
WEBRTC_START = "webrtc-start",
WEBRTC_DISCONNECT = "webrtc-disconect",
MESSAGE_ERROR = "message-error",
+ GROUP_CREATE_UPDATE = "group-create-update",
+ GROUP_DELETE = "group-delete",
}
export class IoSocketController {
this.connectedUser(user1, group);
}, (user1: string, group: Group) => {
this.disConnectedUser(user1, group);
- }, MINIMUM_DISTANCE, GROUP_RADIUS);
+ }, MINIMUM_DISTANCE, GROUP_RADIUS, (group: Group) => {
+ this.sendUpdateGroupEvent(group);
+ }, (groupUuid: string, lastUser: UserInterface) => {
+ this.sendDeleteGroupEvent(groupUuid, lastUser);
+ });
+ }
+
+ private sendUpdateGroupEvent(group: Group): void {
+ // Let's get the room of the group. To do this, let's get anyone in the group and find its room.
+ // Note: this is suboptimal
+ let userId = group.getUsers()[0].id;
+ let client: ExSocketInterface|null = this.searchClientById(userId);
+ if (client === null) {
+ return;
+ }
+ let roomId = client.roomId;
+ this.Io.in(roomId).emit(SockerIoEvent.GROUP_CREATE_UPDATE, {
+ position: group.getPosition(),
+ groupId: group.getId()
+ });
+ }
+
+ private sendDeleteGroupEvent(uuid: string, lastUser: UserInterface): void {
+ // Let's get the room of the group. To do this, let's get anyone in the group and find its room.
+ // Note: this is suboptimal
+ let userId = lastUser.id;
+ let client: ExSocketInterface|null = this.searchClientById(userId);
+ if (client === null) {
+ return;
+ }
+ let roomId = client.roomId;
+ this.Io.in(roomId).emit(SockerIoEvent.GROUP_DELETE, uuid);
}
ioConnection() {
}
/**
- *
+ * TODO: each call to this method is suboptimal. It means that instead of passing an ID, we should pass a client object.
* @param userId
*/
searchClientById(userId: string): ExSocketInterface | null {
this.joinWebRtcRoom(Client, group.getId());
}
- //connected user
+ //disconnect user
disConnectedUser(userId: string, group: Group) {
let Client = this.searchClientById(userId);
if (!Client) {
export type ConnectCallback = (user: string, group: Group) => void;
export type DisconnectCallback = (user: string, group: Group) => void;
+// callback called when a group is created or moved or changes users
+export type GroupUpdatedCallback = (group: Group) => void;
+export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void;
+
export class World {
private minDistance: number;
private groupRadius: number;
private connectCallback: ConnectCallback;
private disconnectCallback: DisconnectCallback;
+ private groupUpdatedCallback: GroupUpdatedCallback;
+ private groupDeletedCallback: GroupDeletedCallback;
constructor(connectCallback: ConnectCallback,
disconnectCallback: DisconnectCallback,
minDistance: number,
- groupRadius: number)
+ groupRadius: number,
+ groupUpdatedCallback: GroupUpdatedCallback,
+ groupDeletedCallback: GroupDeletedCallback)
{
this.users = new Map<string, UserInterface>();
this.groups = [];
this.disconnectCallback = disconnectCallback;
this.minDistance = minDistance;
this.groupRadius = groupRadius;
+ this.groupUpdatedCallback = groupUpdatedCallback;
+ this.groupDeletedCallback = groupDeletedCallback;
}
public join(userPosition: MessageUserPosition): void {
this.leaveGroup(user);
}
}
+
+ // At the very end, if the user is part of a group, let's call the callback to update group position
+ if (typeof user.group !== 'undefined') {
+ this.groupUpdatedCallback(user.group);
+ }
}
/**
group.leave(user);
if (group.isEmpty()) {
+ this.groupDeletedCallback(group.getId(), user);
group.destroy();
const index = this.groups.indexOf(group, 0);
if (index === -1) {
throw new Error("Could not find group");
}
this.groups.splice(index, 1);
+ } else {
+ this.groupUpdatedCallback(group);
}
}
}
- let world = new World(connect, disconnect, 160, 160);
+ let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
world.join(new MessageUserPosition({
userId: "foo",
}
- let world = new World(connect, disconnect, 160, 160);
+ let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
world.join(new MessageUserPosition({
userId: "foo",
disconnectCallNumber++;
}
- let world = new World(connect, disconnect, 160, 160);
+ let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
world.join(new MessageUserPosition({
userId: "foo",
JOIN_ROOM = "join-room",
USER_POSITION = "user-position",
MESSAGE_ERROR = "message-error",
- WEBRTC_DISCONNECT = "webrtc-disconect"
+ WEBRTC_DISCONNECT = "webrtc-disconect",
+ GROUP_CREATE_UPDATE = "group-create-update",
+ GROUP_DELETE = "group-delete",
}
class Message {
}
}
+export interface PositionInterface {
+ x: number,
+ y: number
+}
+
+export interface GroupCreatedUpdatedMessageInterface {
+ position: PositionInterface,
+ groupId: string
+}
+
export interface ConnexionInterface {
socket: any;
token: string;
this.errorMessage();
+ this.groupUpdatedOrCreated();
+ this.groupDeleted();
+
return this;
})
.catch((err) => {
});
}
+ private groupUpdatedOrCreated(): void {
+ this.socket.on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => {
+ //console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y)
+ this.GameManager.shareGroupPosition(groupCreateUpdateMessage);
+ })
+ }
+
+ private groupDeleted(): void {
+ this.socket.on(EventMessage.GROUP_DELETE, (groupId: string) => {
+ this.GameManager.deleteGroup(groupId);
+ })
+ }
+
sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) {
return this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({
userId: userId ? userId : this.userId,
disconnectMessage(callback: Function): void {
this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback);
}
-}
\ No newline at end of file
+}
import {GameScene} from "./GameScene";
import {ROOM} from "../../Enum/EnvironmentVariable"
-import {Connexion, ConnexionInterface, ListMessageUserPositionInterface} from "../../Connexion";
+import {
+ Connexion,
+ ConnexionInterface,
+ GroupCreatedUpdatedMessageInterface,
+ ListMessageUserPositionInterface
+} from "../../Connexion";
import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer";
import {LogincScene} from "../Login/LogincScene";
}
}
+ /**
+ * Share group position in game
+ */
+ shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void {
+ if (this.status === StatusGameManagerEnum.IN_PROGRESS) {
+ return;
+ }
+ try {
+ this.currentGameScene.shareGroupPosition(groupPositionMessage)
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ deleteGroup(groupId: string): void {
+ if (this.status === StatusGameManagerEnum.IN_PROGRESS) {
+ return;
+ }
+ try {
+ this.currentGameScene.deleteGroup(groupId)
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
getPlayerName(): string {
return this.playerName;
}
import {GameManager, gameManager, HasMovedEvent, StatusGameManagerEnum} from "./GameManager";
-import {MessageUserPositionInterface} from "../../Connexion";
+import {GroupCreatedUpdatedMessageInterface, MessageUserPositionInterface} from "../../Connexion";
import {CurrentGamerInterface, GamerInterface, hasMovedEventName, Player} from "../Player/Player";
import {DEBUG_MODE, RESOLUTION, ROOM, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
import Tile = Phaser.Tilemaps.Tile;
import {ITiledMap, ITiledTileSet} from "../Map/ITiledMap";
import {cypressAsserter} from "../../Cypress/CypressAsserter";
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter";
+import Circle = Phaser.Geom.Circle;
+import Graphics = Phaser.GameObjects.Graphics;
export const GameSceneName = "GameScene";
export enum Textures {
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
Objects : Array<Phaser.Physics.Arcade.Sprite>;
map: ITiledMap;
+ groups: Map<string, Circle>
startX = 704;// 22 case
startY = 32; // 1 case
+ // Note: graphics object is costly to generate. We should find another way (maybe sprite based way to draw circles)
+ graphics: Graphics;
constructor() {
super({
});
this.GameManager = gameManager;
this.Terrains = [];
+ this.groups = new Map<string, Circle>();
}
//hook preload scene
//initialise camera
this.initCamera();
+
+ this.graphics = this.add.graphics();
}
//todo: in a dedicated class/function?
*/
update(time: number, delta: number) : void {
this.CurrentPlayer.moveUser(delta);
+
+ // Also, let's redraw the circle (can be costly, we need to change this!)
+ this.graphics.clear();
+ this.graphics.lineStyle(1, 0x00ff00, 0.4);
+ this.groups.forEach((circle: Circle) => {
+ this.graphics.strokeCircleShape(circle);
+ })
}
/**
CurrentPlayer.say("Hello, how are you ? ");
});
}
+
+ shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) {
+ let groupId = groupPositionMessage.groupId;
+
+ if (this.groups.has(groupId)) {
+ this.groups.get(groupId).setPosition(groupPositionMessage.position.x, groupPositionMessage.position.y);
+ } else {
+ //console.log('Adding group ', groupId, ' to the scene');
+ // TODO: circle radius should not be hard stored
+ this.groups.set(groupId, new Circle(groupPositionMessage.position.x, groupPositionMessage.position.y, 48));
+ }
+ }
+
+ deleteGroup(groupId: string): void {
+ //console.log('Deleting group ', groupId);
+ this.groups.delete(groupId);
+ }
}
if (direction !== PlayerAnimationNames.None && (!Player.anims.currentAnim || Player.anims.currentAnim.key !== direction)) {
Player.anims.play(direction);
} else if (direction === PlayerAnimationNames.None && Player.anims.currentAnim) {
- //Player.anims.currentAnim.destroy();
Player.anims.stop();
}
}