Merge pull request #116 from thecodingmachine/remove_userid

Completely getting rid of "userid"
This commit is contained in:
David Négrier 2020-05-15 21:34:12 +02:00 committed by GitHub
commit 2411a3f85a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 130 additions and 84 deletions

View File

@ -4,7 +4,7 @@ import {BAD_REQUEST, OK} from "http-status-codes";
import {SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
import { uuid } from 'uuidv4'; import { uuid } from 'uuidv4';
export class AuthenticateController{ export class AuthenticateController {
App : Application; App : Application;
constructor(App : Application) { constructor(App : Application) {
@ -14,7 +14,8 @@ export class AuthenticateController{
//permit to login on application. Return token to connect on Websocket IO. //permit to login on application. Return token to connect on Websocket IO.
login(){ login(){
this.App.post("/login", (req: Request, res: Response) => { // For now, let's completely forget the /login route.
/*this.App.post("/login", (req: Request, res: Response) => {
let param = req.body; let param = req.body;
if(!param.email){ if(!param.email){
return res.status(BAD_REQUEST).send({ return res.status(BAD_REQUEST).send({
@ -29,6 +30,6 @@ export class AuthenticateController{
mapUrlStart: URL_ROOM_STARTED, mapUrlStart: URL_ROOM_STARTED,
userId: userId, userId: userId,
}); });
}); });*/
} }
} }

View File

@ -14,6 +14,7 @@ import {UserInterface} from "_Model/UserInterface";
enum SockerIoEvent { enum SockerIoEvent {
CONNECTION = "connection", CONNECTION = "connection",
DISCONNECT = "disconnect", DISCONNECT = "disconnect",
ATTRIBUTE_USER_ID = "attribute-user-id", // Sent from server to client just after the connexion is established to give the client its unique id.
JOIN_ROOM = "join-room", JOIN_ROOM = "join-room",
USER_POSITION = "user-position", USER_POSITION = "user-position",
WEBRTC_SIGNAL = "webrtc-signal", WEBRTC_SIGNAL = "webrtc-signal",
@ -33,7 +34,8 @@ export class IoSocketController {
this.Io = socketIO(server); this.Io = socketIO(server);
// Authentication with token. it will be decoded and stored in the socket. // Authentication with token. it will be decoded and stored in the socket.
this.Io.use((socket: Socket, next) => { // Completely commented for now, as we do not use the "/login" route at all.
/*this.Io.use((socket: Socket, next) => {
if (!socket.handshake.query || !socket.handshake.query.token) { if (!socket.handshake.query || !socket.handshake.query.token) {
return next(new Error('Authentication error')); return next(new Error('Authentication error'));
} }
@ -47,7 +49,7 @@ export class IoSocketController {
(socket as ExSocketInterface).token = tokenDecoded; (socket as ExSocketInterface).token = tokenDecoded;
next(); next();
}); });
}); });*/
this.ioConnection(); this.ioConnection();
this.shareUsersPosition(); this.shareUsersPosition();
@ -74,6 +76,7 @@ export class IoSocketController {
let userId = lastUser.id; let userId = lastUser.id;
let client: ExSocketInterface|null = this.searchClientById(userId); let client: ExSocketInterface|null = this.searchClientById(userId);
if (client === null) { if (client === null) {
console.warn('Could not find client ', userId, ' in group')
return; return;
} }
let roomId = client.roomId; let roomId = client.roomId;
@ -180,7 +183,6 @@ export class IoSocketController {
socket.leave(Client.webRtcRoomId); socket.leave(Client.webRtcRoomId);
//delete all socket information //delete all socket information
delete Client.userId;
delete Client.webRtcRoomId; delete Client.webRtcRoomId;
delete Client.roomId; delete Client.roomId;
delete Client.token; delete Client.token;
@ -190,6 +192,9 @@ export class IoSocketController {
console.error(e); console.error(e);
} }
}); });
// Let's send the user id to the user
socket.emit(SockerIoEvent.ATTRIBUTE_USER_ID, socket.id);
}); });
} }
@ -201,11 +206,12 @@ export class IoSocketController {
let clients: Array<any> = Object.values(this.Io.sockets.sockets); let clients: Array<any> = Object.values(this.Io.sockets.sockets);
for (let i = 0; i < clients.length; i++) { for (let i = 0; i < clients.length; i++) {
let client: ExSocketInterface = clients[i]; let client: ExSocketInterface = clients[i];
if (client.userId !== userId) { if (client.id !== userId) {
continue continue;
} }
return client; return client;
} }
console.log("Could not find user with id ", userId);
return null; return null;
} }
@ -216,7 +222,7 @@ export class IoSocketController {
let clients: Array<any> = Object.values(this.Io.sockets.sockets); let clients: Array<any> = Object.values(this.Io.sockets.sockets);
for (let i = 0; i < clients.length; i++) { for (let i = 0; i < clients.length; i++) {
let client: ExSocketInterface = clients[i]; let client: ExSocketInterface = clients[i];
if (client.userId !== userId) { if (client.id !== userId) {
continue continue
} }
return client; return client;
@ -230,7 +236,7 @@ export class IoSocketController {
*/ */
sendDisconnectedEvent(Client: ExSocketInterface) { sendDisconnectedEvent(Client: ExSocketInterface) {
Client.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({ Client.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({
userId: Client.userId userId: Client.id
})); }));
//disconnect webrtc room //disconnect webrtc room
@ -248,14 +254,16 @@ export class IoSocketController {
leaveRoom(Client : ExSocketInterface){ leaveRoom(Client : ExSocketInterface){
//lease previous room and world //lease previous room and world
if(Client.roomId){ if(Client.roomId){
//user leave previous room
Client.leave(Client.roomId);
//user leave previous world //user leave previous world
let world : World|undefined = this.Worlds.get(Client.roomId); let world : World|undefined = this.Worlds.get(Client.roomId);
if(world){ if(world){
console.log('Entering world.leave')
world.leave(Client); world.leave(Client);
this.Worlds.set(Client.roomId, world); //this.Worlds.set(Client.roomId, world);
} }
//user leave previous room
Client.leave(Client.roomId);
delete Client.roomId;
} }
} }
/** /**
@ -293,7 +301,7 @@ export class IoSocketController {
}); });
}); });
//join world //join world
world.join(messageUserPosition); world.join(Client, messageUserPosition);
this.Worlds.set(messageUserPosition.roomId, world); this.Worlds.set(messageUserPosition.roomId, world);
} }
@ -322,11 +330,11 @@ export class IoSocketController {
clients.forEach((client: ExSocketInterface, index: number) => { clients.forEach((client: ExSocketInterface, index: number) => {
let clientsId = clients.reduce((tabs: Array<any>, clientId: ExSocketInterface, indexClientId: number) => { let clientsId = clients.reduce((tabs: Array<any>, clientId: ExSocketInterface, indexClientId: number) => {
if (!clientId.userId || clientId.userId === client.userId) { if (!clientId.id || clientId.id === client.id) {
return tabs; return tabs;
} }
tabs.push({ tabs.push({
userId: clientId.userId, userId: clientId.id,
name: clientId.name, name: clientId.name,
initiator: index <= indexClientId initiator: index <= indexClientId
}); });
@ -341,7 +349,7 @@ export class IoSocketController {
saveUserInformation(socket: ExSocketInterface, message: MessageUserPosition) { saveUserInformation(socket: ExSocketInterface, message: MessageUserPosition) {
socket.position = message.position; socket.position = message.position;
socket.roomId = message.roomId; socket.roomId = message.roomId;
socket.userId = message.userId; //socket.userId = message.userId;
socket.name = message.name; socket.name = message.name;
socket.character = message.character; socket.character = message.character;
} }
@ -354,9 +362,9 @@ export class IoSocketController {
} }
rooms.refreshUserPosition(rooms, this.Io); rooms.refreshUserPosition(rooms, this.Io);
// update position in the worl // update position in the world
let data = { let data = {
userId: Client.userId, userId: Client.id,
roomId: Client.roomId, roomId: Client.roomId,
position: Client.position, position: Client.position,
name: Client.name, name: Client.name,
@ -367,7 +375,7 @@ export class IoSocketController {
if (!world) { if (!world) {
return; return;
} }
world.updatePosition(messageUserPosition); world.updatePosition(Client, messageUserPosition);
this.Worlds.set(messageUserPosition.roomId, world); this.Worlds.set(messageUserPosition.roomId, world);
} }

View File

@ -1,11 +1,12 @@
import {Socket} from "socket.io"; import {Socket} from "socket.io";
import {PointInterface} from "./PointInterface"; import {PointInterface} from "./PointInterface";
import {Identificable} from "./Identificable";
export interface ExSocketInterface extends Socket { export interface ExSocketInterface extends Socket, Identificable {
token: any; token: any;
roomId: string; roomId: string;
webRtcRoomId: string; webRtcRoomId: string;
userId: string; //userId: string;
name: string; name: string;
character: string; character: string;
position: PointInterface; position: PointInterface;

View File

@ -22,7 +22,7 @@ let RefreshUserPositionFunction = function(rooms : ExtRooms, Io: socketIO.Server
continue; continue;
} }
let data = { let data = {
userId: socket.userId, userId: socket.id,
roomId: socket.roomId, roomId: socket.roomId,
position: socket.position, position: socket.position,
name: socket.name, name: socket.name,

View File

@ -0,0 +1,3 @@
export interface Identificable {
id: string;
}

View File

@ -16,6 +16,7 @@ export class Message {
} }
toJson() { toJson() {
return { return {
userId: this.userId, userId: this.userId,
roomId: this.roomId, roomId: this.roomId,

View File

@ -5,6 +5,7 @@ import {Distance} from "./Distance";
import {UserInterface} from "./UserInterface"; import {UserInterface} from "./UserInterface";
import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface";
import {PositionInterface} from "_Model/PositionInterface"; import {PositionInterface} from "_Model/PositionInterface";
import {Identificable} from "_Model/Websocket/Identificable";
export type ConnectCallback = (user: string, group: Group) => void; export type ConnectCallback = (user: string, group: Group) => void;
export type DisconnectCallback = (user: string, group: Group) => void; export type DisconnectCallback = (user: string, group: Group) => void;
@ -14,17 +15,17 @@ export type GroupUpdatedCallback = (group: Group) => void;
export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void; export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void;
export class World { export class World {
private minDistance: number; private readonly minDistance: number;
private groupRadius: number; private readonly groupRadius: number;
// Users, sorted by ID // Users, sorted by ID
private users: Map<string, UserInterface>; private readonly users: Map<string, UserInterface>;
private groups: Group[]; private readonly groups: Group[];
private connectCallback: ConnectCallback; private readonly connectCallback: ConnectCallback;
private disconnectCallback: DisconnectCallback; private readonly disconnectCallback: DisconnectCallback;
private groupUpdatedCallback: GroupUpdatedCallback; private readonly groupUpdatedCallback: GroupUpdatedCallback;
private groupDeletedCallback: GroupDeletedCallback; private readonly groupDeletedCallback: GroupDeletedCallback;
constructor(connectCallback: ConnectCallback, constructor(connectCallback: ConnectCallback,
disconnectCallback: DisconnectCallback, disconnectCallback: DisconnectCallback,
@ -47,25 +48,29 @@ export class World {
return this.groups; return this.groups;
} }
public join(userPosition: MessageUserPosition): void { public join(socket : Identificable, userPosition: MessageUserPosition): void {
this.users.set(userPosition.userId, { this.users.set(socket.id, {
id: userPosition.userId, id: socket.id,
position: userPosition.position position: userPosition.position
}); });
// Let's call update position to trigger the join / leave room // Let's call update position to trigger the join / leave room
this.updatePosition(userPosition); this.updatePosition(socket, userPosition);
} }
public leave(user : ExSocketInterface){ public leave(user : Identificable){
let userObj = this.users.get(user.id); let userObj = this.users.get(user.id);
if (userObj !== undefined && typeof userObj.group !== 'undefined') { if (userObj === undefined) {
this.leaveGroup(user); // FIXME: this seems always wrong. I guess user.id is different from userPosition.userId
console.warn('User ', user.id, 'does not belong to world! It should!');
} }
this.users.delete(user.userId); if (userObj !== undefined && typeof userObj.group !== 'undefined') {
this.leaveGroup(userObj);
}
this.users.delete(user.id);
} }
public updatePosition(userPosition: MessageUserPosition): void { public updatePosition(socket : Identificable, userPosition: MessageUserPosition): void {
let user = this.users.get(userPosition.userId); let user = this.users.get(socket.id);
if(typeof user === 'undefined') { if(typeof user === 'undefined') {
return; return;
} }
@ -118,7 +123,6 @@ export class World {
throw new Error("The user is part of no group"); throw new Error("The user is part of no group");
} }
group.leave(user); group.leave(user);
if (group.isEmpty()) { if (group.isEmpty()) {
this.groupDeletedCallback(group.getId(), user); this.groupDeletedCallback(group.getId(), user);
group.destroy(); group.destroy();

View File

@ -17,36 +17,36 @@ describe("World", () => {
let world = new World(connect, disconnect, 160, 160, () => {}, () => {}); let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
world.join(new MessageUserPosition({ world.join({ id: "foo" }, new MessageUserPosition({
userId: "foo", userId: "foofoo",
roomId: 1, roomId: 1,
position: new Point(100, 100) position: new Point(100, 100)
})); }));
world.join(new MessageUserPosition({ world.join({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(500, 100) position: new Point(500, 100)
})); }));
world.updatePosition(new MessageUserPosition({ world.updatePosition({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(261, 100) position: new Point(261, 100)
})); }));
expect(connectCalledNumber).toBe(0); expect(connectCalledNumber).toBe(0);
world.updatePosition(new MessageUserPosition({ world.updatePosition({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(101, 100) position: new Point(101, 100)
})); }));
expect(connectCalledNumber).toBe(2); expect(connectCalledNumber).toBe(2);
world.updatePosition(new MessageUserPosition({ world.updatePosition({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(102, 100) position: new Point(102, 100)
})); }));
@ -64,14 +64,14 @@ describe("World", () => {
let world = new World(connect, disconnect, 160, 160, () => {}, () => {}); let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
world.join(new MessageUserPosition({ world.join({ id: "foo" }, new MessageUserPosition({
userId: "foo", userId: "foofoo",
roomId: 1, roomId: 1,
position: new Point(100, 100) position: new Point(100, 100)
})); }));
world.join(new MessageUserPosition({ world.join({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(200, 100) position: new Point(200, 100)
})); }));
@ -80,16 +80,16 @@ describe("World", () => {
connectCalled = false; connectCalled = false;
// baz joins at the outer limit of the group // baz joins at the outer limit of the group
world.join(new MessageUserPosition({ world.join({ id: "baz" }, new MessageUserPosition({
userId: "baz", userId: "bazbaz",
roomId: 1, roomId: 1,
position: new Point(311, 100) position: new Point(311, 100)
})); }));
expect(connectCalled).toBe(false); expect(connectCalled).toBe(false);
world.updatePosition(new MessageUserPosition({ world.updatePosition({ id: "baz" }, new MessageUserPosition({
userId: "baz", userId: "bazbaz",
roomId: 1, roomId: 1,
position: new Point(309, 100) position: new Point(309, 100)
})); }));
@ -109,14 +109,14 @@ describe("World", () => {
let world = new World(connect, disconnect, 160, 160, () => {}, () => {}); let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
world.join(new MessageUserPosition({ world.join({ id: "foo" }, new MessageUserPosition({
userId: "foo", userId: "foofoo",
roomId: 1, roomId: 1,
position: new Point(100, 100) position: new Point(100, 100)
})); }));
world.join(new MessageUserPosition({ world.join({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(259, 100) position: new Point(259, 100)
})); }));
@ -124,16 +124,16 @@ describe("World", () => {
expect(connectCalled).toBe(true); expect(connectCalled).toBe(true);
expect(disconnectCallNumber).toBe(0); expect(disconnectCallNumber).toBe(0);
world.updatePosition(new MessageUserPosition({ world.updatePosition({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(100+160+160+1, 100) position: new Point(100+160+160+1, 100)
})); }));
expect(disconnectCallNumber).toBe(2); expect(disconnectCallNumber).toBe(2);
world.updatePosition(new MessageUserPosition({ world.updatePosition({ id: "bar" }, new MessageUserPosition({
userId: "bar", userId: "barbar",
roomId: 1, roomId: 1,
position: new Point(262, 100) position: new Point(262, 100)
})); }));

View File

@ -18,7 +18,8 @@ enum EventMessage{
GROUP_DELETE = "group-delete", GROUP_DELETE = "group-delete",
CONNECT_ERROR = "connect_error", CONNECT_ERROR = "connect_error",
RECONNECT = "reconnect" RECONNECT = "reconnect",
ATTRIBUTE_USER_ID = "attribute-user-id" // Sent from server to client just after the connexion is established to give the client its unique id.
} }
class Message { class Message {
@ -184,25 +185,37 @@ export class Connexion implements ConnexionInterface {
* @param characterSelected * @param characterSelected
*/ */
createConnexion(characterSelected: string): Promise<ConnexionInterface> { createConnexion(characterSelected: string): Promise<ConnexionInterface> {
return Axios.post(`${API_URL}/login`, {email: this.email}) /*return Axios.post(`${API_URL}/login`, {email: this.email})
.then((res) => { .then((res) => {
this.token = res.data.token; this.token = res.data.token;
this.userId = res.data.userId; this.userId = res.data.userId;*/
this.socket = SocketIo(`${API_URL}`, { this.socket = SocketIo(`${API_URL}`, {
query: { /*query: {
token: this.token token: this.token
} }*/
}); });
this.connectSocketServer(); this.connectSocketServer();
return res.data; // TODO: maybe trigger promise only when connexion is established?
let promise = new Promise<ConnexionInterface>((resolve, reject) => {
/*console.log('PROMISE CREATED')
this.socket.on('connection', () => {
console.log('CONNECTED');
resolve(this);
});*/
resolve(this);
});
return promise;
/* return res.data;
}) })
.catch((err) => { .catch((err) => {
console.error(err); console.error(err);
throw err; throw err;
}); });*/
} }
/** /**
@ -229,6 +242,7 @@ export class Connexion implements ConnexionInterface {
} }
//listen event //listen event
this.attributeUserId();
this.positionOfAllUser(); this.positionOfAllUser();
this.disconnectServer(); this.disconnectServer();
this.errorMessage(); this.errorMessage();
@ -286,6 +300,15 @@ export class Connexion implements ConnexionInterface {
this.socket.emit(EventMessage.USER_POSITION, messageUserPosition.toString()); this.socket.emit(EventMessage.USER_POSITION, messageUserPosition.toString());
} }
attributeUserId(): void {
// This event is received as soon as the connexion is established.
// It allows informing the browser of its own user id.
this.socket.on(EventMessage.ATTRIBUTE_USER_ID, (userId: string) => {
console.log('Received my user id: ', userId);
this.userId = userId;
});
}
/** /**
* The data sent is an array with information for each user : * The data sent is an array with information for each user :
* [ * [

View File

@ -67,7 +67,7 @@ export class GameManager {
*/ */
createCurrentPlayer(): void { createCurrentPlayer(): void {
//Get started room send by the backend //Get started room send by the backend
this.currentGameScene.createCurrentPlayer(this.ConnexionInstance.userId); this.currentGameScene.createCurrentPlayer();
this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; this.status = StatusGameManagerEnum.CURRENT_USER_CREATED;
} }
@ -119,6 +119,10 @@ export class GameManager {
return this.playerName; return this.playerName;
} }
getPlayerId(): string {
return this.ConnexionInstance.userId;
}
getCharacterSelected(): string { getCharacterSelected(): string {
return this.characterUserSelected; return this.characterUserSelected;
} }

View File

@ -15,7 +15,7 @@ export enum Textures {
export interface GameSceneInterface extends Phaser.Scene { export interface GameSceneInterface extends Phaser.Scene {
Map: Phaser.Tilemaps.Tilemap; Map: Phaser.Tilemaps.Tilemap;
createCurrentPlayer(UserId : string) : void; createCurrentPlayer() : void;
shareUserPosition(UsersPosition : Array<MessageUserPositionInterface>): void; shareUserPosition(UsersPosition : Array<MessageUserPositionInterface>): void;
shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void; shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void;
updateOrCreateMapPlayer(UsersPosition : Array<MessageUserPositionInterface>): void; updateOrCreateMapPlayer(UsersPosition : Array<MessageUserPositionInterface>): void;
@ -266,11 +266,11 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat
}) })
} }
createCurrentPlayer(UserId : string){ createCurrentPlayer(){
//initialise player //initialise player
//TODO create animation moving between exit and strat //TODO create animation moving between exit and strat
this.CurrentPlayer = new Player( this.CurrentPlayer = new Player(
UserId, null, // The current player is not has no id (because the id can change if connexion is lost and we should check that id using the GameManager.
this, this,
this.startX, this.startX,
this.startY, this.startY,
@ -347,9 +347,11 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat
return; return;
} }
let currentPlayerId = this.GameManager.getPlayerId();
//add or create new user //add or create new user
UsersPosition.forEach((userPosition : MessageUserPositionInterface) => { UsersPosition.forEach((userPosition : MessageUserPositionInterface) => {
if(userPosition.userId === this.CurrentPlayer.userId){ if(userPosition.userId === currentPlayerId){
return; return;
} }
let player = this.findPlayerInMap(userPosition.userId); let player = this.findPlayerInMap(userPosition.userId);

View File

@ -90,7 +90,7 @@ export class LogincScene extends Phaser.Scene implements GameSceneInterface {
}); });
/*create user*/ /*create user*/
this.createCurrentPlayer("test"); this.createCurrentPlayer();
cypressAsserter.initFinished(); cypressAsserter.initFinished();
} }
@ -144,7 +144,7 @@ export class LogincScene extends Phaser.Scene implements GameSceneInterface {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
createCurrentPlayer(UserId: string): void { createCurrentPlayer(): void {
for (let i = 0; i <PLAYER_RESOURCES.length; i++) { for (let i = 0; i <PLAYER_RESOURCES.length; i++) {
let playerResource = PLAYER_RESOURCES[i]; let playerResource = PLAYER_RESOURCES[i];
let player = this.physics.add.sprite(playerResource.x, playerResource.y, playerResource.name, playerResource.name); let player = this.physics.add.sprite(playerResource.x, playerResource.y, playerResource.name, playerResource.name);

View File

@ -7,7 +7,6 @@ import {PlayableCaracter} from "../Entity/PlayableCaracter";
export const hasMovedEventName = "hasMoved"; export const hasMovedEventName = "hasMoved";
export interface CurrentGamerInterface extends PlayableCaracter{ export interface CurrentGamerInterface extends PlayableCaracter{
userId : string;
initAnimation() : void; initAnimation() : void;
moveUser(delta: number) : void; moveUser(delta: number) : void;
say(text : string) : void; say(text : string) : void;