Merge pull request #356 from thecodingmachine/develop

Release 2020-10-21
This commit is contained in:
David Négrier 2020-10-21 19:54:25 +02:00 committed by GitHub
commit bf135c0f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 623 additions and 1820 deletions

View File

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"tsc": "tsc", "tsc": "tsc",
"dev": "ts-node-dev --respawn ./server.ts", "dev": "ts-node-dev --respawn ./server.ts",
"prod": "tsc && node ./dist/server.js", "prod": "tsc && node --max-old-space-size=4096 ./dist/server.js",
"profile": "tsc && node --prof ./dist/server.js", "profile": "tsc && node --prof ./dist/server.js",
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
"lint": "node_modules/.bin/eslint src/ . --ext .ts", "lint": "node_modules/.bin/eslint src/ . --ext .ts",

View File

@ -117,4 +117,8 @@ export class Group implements Movable {
this.leave(user); this.leave(user);
} }
} }
get getSize(){
return this.users.size;
}
} }

View File

@ -130,6 +130,7 @@ export class SocketManager {
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(player.position)); userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(player.position));
roomJoinedMessage.addUser(userJoinedMessage); roomJoinedMessage.addUser(userJoinedMessage);
roomJoinedMessage.setTagList(client.tags);
} else if (thing instanceof Group) { } else if (thing instanceof Group) {
const groupUpdateMessage = new GroupUpdateMessage(); const groupUpdateMessage = new GroupUpdateMessage();
groupUpdateMessage.setGroupid(thing.getId()); groupUpdateMessage.setGroupid(thing.getId());
@ -493,6 +494,7 @@ export class SocketManager {
const groupUpdateMessage = new GroupUpdateMessage(); const groupUpdateMessage = new GroupUpdateMessage();
groupUpdateMessage.setGroupid(group.getId()); groupUpdateMessage.setGroupid(group.getId());
groupUpdateMessage.setPosition(pointMessage); groupUpdateMessage.setPosition(pointMessage);
groupUpdateMessage.setGroupsize(group.getSize);
const subMessage = new SubMessage(); const subMessage = new SubMessage();
subMessage.setGroupupdatemessage(groupUpdateMessage); subMessage.setGroupupdatemessage(groupUpdateMessage);

View File

@ -2,6 +2,7 @@ import {RoomConnection} from "../front/src/Connexion/RoomConnection";
import {connectionManager} from "../front/src/Connexion/ConnectionManager"; import {connectionManager} from "../front/src/Connexion/ConnectionManager";
import * as WebSocket from "ws" import * as WebSocket from "ws"
function sleep(ms) { function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
@ -11,15 +12,18 @@ RoomConnection.setWebsocketFactory((url: string) => {
}); });
async function startOneUser(): Promise<void> { async function startOneUser(): Promise<void> {
const connection = await connectionManager.connectToRoomSocket(); await connectionManager.anonymousLogin(true);
connection.emitPlayerDetailsMessage('foo', ['male3']); const connection = await connectionManager.connectToRoomSocket(process.env.ROOM_ID ? process.env.ROOM_ID : '_/global/maps.workadventure.localhost/Floor0/floor0.json', 'TEST', ['male3'],
{
await connection.joinARoom('global__maps.workadventure.localhost/Floor0/floor0', 783, 170, 'down', true, { x: 783,
y: 170
}, {
top: 0, top: 0,
bottom: 200, bottom: 200,
left: 500, left: 500,
right: 800 right: 800
}); });
console.log(connection.getUserId()); console.log(connection.getUserId());
let angle = Math.random() * Math.PI * 2; let angle = Math.random() * Math.PI * 2;

File diff suppressed because it is too large Load Diff

View File

@ -76,11 +76,13 @@ class ConnectionManager {
await Axios.get(`${API_URL}/verify`, {params: {token}}); await Axios.get(`${API_URL}/verify`, {params: {token}});
} }
private async anonymousLogin(): Promise<void> { public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
const data = await Axios.post(`${API_URL}/anonymLogin`).then(res => res.data); const data = await Axios.post(`${API_URL}/anonymLogin`).then(res => res.data);
this.localUser = new LocalUser(data.userUuid, data.authToken, []); this.localUser = new LocalUser(data.userUuid, data.authToken, []);
if (!isBenchmark) { // In benchmark, we don't have a local storage.
localUserStore.saveUser(this.localUser); localUserStore.saveUser(this.localUser);
} }
}
public initBenchmark(): void { public initBenchmark(): void {
this.localUser = new LocalUser('', 'test', []); this.localUser = new LocalUser('', 'test', []);

View File

@ -73,7 +73,8 @@ export interface PositionInterface {
export interface GroupCreatedUpdatedMessageInterface { export interface GroupCreatedUpdatedMessageInterface {
position: PositionInterface, position: PositionInterface,
groupId: number groupId: number,
groupSize: number
} }
export interface WebRtcStartMessageInterface { export interface WebRtcStartMessageInterface {

View File

@ -335,7 +335,8 @@ export class RoomConnection implements RoomConnection {
return { return {
groupId: message.getGroupid(), groupId: message.getGroupid(),
position: position.toObject() position: position.toObject(),
groupSize: message.getGroupsize()
} }
} }

View File

@ -1,5 +1,5 @@
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
const API_URL = (typeof(window) !== 'undefined' ? window.location.protocol : 'http:') + '//' + (process.env.API_URL || "api.workadventure.localhost"); const API_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.API_URL || "api.workadventure.localhost");
const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca"; const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca";
const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com'; const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com';
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$'; const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$';

View File

@ -109,6 +109,7 @@ export class GameScene extends ResizableScene implements CenterListener {
startX!: number; startX!: number;
startY!: number; startY!: number;
circleTexture!: CanvasTexture; circleTexture!: CanvasTexture;
circleRedTexture!: CanvasTexture;
pendingEvents: Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface> = new Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface>(); pendingEvents: Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface> = new Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface>();
private initPosition: PositionInterface|null = null; private initPosition: PositionInterface|null = null;
private playersPositionInterpolator = new PlayersPositionInterpolator(); private playersPositionInterpolator = new PlayersPositionInterpolator();
@ -409,11 +410,18 @@ export class GameScene extends ResizableScene implements CenterListener {
this.initCamera(); this.initCamera();
// Let's generate the circle for the group delimiter // Let's generate the circle for the group delimiter
const circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite'); let circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite-white');
if (circleElement) { if (circleElement) {
this.textures.remove('circleSprite'); this.textures.remove('circleSprite-white');
} }
this.circleTexture = this.textures.createCanvas('circleSprite', 96, 96);
circleElement = Object.values(this.textures.list).find((object: Texture) => object.key === 'circleSprite-red');
if (circleElement) {
this.textures.remove('circleSprite-red');
}
//create white circle canvas use to create sprite
this.circleTexture = this.textures.createCanvas('circleSprite-white', 96, 96);
const context = this.circleTexture.context; const context = this.circleTexture.context;
context.beginPath(); context.beginPath();
context.arc(48, 48, 48, 0, 2 * Math.PI, false); context.arc(48, 48, 48, 0, 2 * Math.PI, false);
@ -422,6 +430,16 @@ export class GameScene extends ResizableScene implements CenterListener {
context.stroke(); context.stroke();
this.circleTexture.refresh(); this.circleTexture.refresh();
//create red circle canvas use to create sprite
this.circleRedTexture = this.textures.createCanvas('circleSprite-red', 96, 96);
const contextRed = this.circleRedTexture.context;
contextRed.beginPath();
contextRed.arc(48, 48, 48, 0, 2 * Math.PI, false);
// context.lineWidth = 5;
contextRed.strokeStyle = '#ff0000';
contextRed.stroke();
this.circleRedTexture.refresh();
// Let's pause the scene if the connection is not established yet // Let's pause the scene if the connection is not established yet
if (this.connection === undefined) { if (this.connection === undefined) {
// Let's wait 0.5 seconds before printing the "connecting" screen to avoid blinking // Let's wait 0.5 seconds before printing the "connecting" screen to avoid blinking
@ -1123,18 +1141,27 @@ export class GameScene extends ResizableScene implements CenterListener {
private doShareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) { private doShareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) {
const groupId = groupPositionMessage.groupId; const groupId = groupPositionMessage.groupId;
const groupSize = groupPositionMessage.groupSize;
const group = this.groups.get(groupId); const group = this.groups.get(groupId);
if (group !== undefined) { if (group !== undefined) {
group.setPosition(Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.y)); group.setPosition(Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.y));
} else { } else {
// TODO: circle radius should not be hard stored // TODO: circle radius should not be hard stored
const positionX = 48;
const positionY = 48;
let texture = 'circleSprite-red';
if(groupSize < 4){
texture = 'circleSprite-white';
}
const sprite = new Sprite( const sprite = new Sprite(
this, this,
Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.x),
Math.round(groupPositionMessage.position.y), Math.round(groupPositionMessage.position.y),
'circleSprite'); texture
sprite.setDisplayOrigin(48, 48); );
sprite.setDisplayOrigin(positionX, positionY);
this.add.existing(sprite); this.add.existing(sprite);
this.groups.set(groupId, sprite); this.groups.set(groupId, sprite);
} }
@ -1266,6 +1293,10 @@ export class GameScene extends ResizableScene implements CenterListener {
private loadSpritesheet(name: string, url: string): Promise<void> { private loadSpritesheet(name: string, url: string): Promise<void> {
return new Promise<void>(((resolve, reject) => { return new Promise<void>(((resolve, reject) => {
if (this.textures.exists(name)) {
resolve();
return;
}
this.load.spritesheet( this.load.spritesheet(
name, name,
url, url,

View File

@ -13,6 +13,8 @@ export class ScreenSharingPeer extends Peer {
* Whether this connection is currently receiving a video stream from a remote user. * Whether this connection is currently receiving a video stream from a remote user.
*/ */
private isReceivingStream:boolean = false; private isReceivingStream:boolean = false;
public toClose: boolean = false;
public _connected: boolean = false;
constructor(private userId: number, initiator: boolean, private connection: RoomConnection) { constructor(private userId: number, initiator: boolean, private connection: RoomConnection) {
super({ super({
@ -42,6 +44,8 @@ export class ScreenSharingPeer extends Peer {
}); });
this.on('close', () => { this.on('close', () => {
this._connected = false;
this.toClose = true;
this.destroy(); this.destroy();
}); });
@ -62,11 +66,16 @@ export class ScreenSharingPeer extends Peer {
}); });
this.on('connect', () => { this.on('connect', () => {
this._connected = true;
// FIXME: we need to put the loader on the screen sharing connection // FIXME: we need to put the loader on the screen sharing connection
mediaManager.isConnected("" + this.userId); mediaManager.isConnected("" + this.userId);
console.info(`connect => ${this.userId}`); console.info(`connect => ${this.userId}`);
}); });
this.once('finish', () => {
this._onFinish();
});
this.pushScreenSharingToRemoteUser(); this.pushScreenSharingToRemoteUser();
} }
@ -100,6 +109,10 @@ export class ScreenSharingPeer extends Peer {
public destroy(error?: Error): void { public destroy(error?: Error): void {
try { try {
this._connected = false
if(!this.toClose){
return;
}
mediaManager.removeActiveScreenSharingVideo("" + this.userId); mediaManager.removeActiveScreenSharingVideo("" + this.userId);
// FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray"
// I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
@ -111,6 +124,18 @@ export class ScreenSharingPeer extends Peer {
} }
} }
_onFinish () {
if (this.destroyed) return
const destroySoon = () => {
this.destroy();
}
if (this._connected) {
destroySoon();
} else {
this.once('connect', destroySoon);
}
}
private pushScreenSharingToRemoteUser() { private pushScreenSharingToRemoteUser() {
const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture; const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture;
if(!localScreenCapture){ if(!localScreenCapture){

View File

@ -108,20 +108,6 @@ export class SimplePeer {
this.createPeerConnection(user); this.createPeerConnection(user);
} }
/**
* server has two people connected, start the meet
*/
private startWebRtc() {
console.warn('startWebRtc startWebRtc');
this.Users.forEach((user: UserSimplePeerInterface) => {
//if it's not an initiator, peer connection will be created when gamer will receive offer signal
if(!user.initiator){
return;
}
this.createPeerConnection(user);
});
}
/** /**
* create peer connection to bind users * create peer connection to bind users
*/ */
@ -179,9 +165,19 @@ export class SimplePeer {
* create peer connection to bind users * create peer connection to bind users
*/ */
private createPeerScreenSharingConnection(user : UserSimplePeerInterface) : ScreenSharingPeer | null{ private createPeerScreenSharingConnection(user : UserSimplePeerInterface) : ScreenSharingPeer | null{
if( const peerConnection = this.PeerScreenSharingConnectionArray.get(user.userId);
this.PeerScreenSharingConnectionArray.has(user.userId) if(peerConnection){
){ if(peerConnection.destroyed){
peerConnection.toClose = true;
peerConnection.destroy();
const peerConnexionDeleted = this.PeerScreenSharingConnectionArray.delete(user.userId);
if(!peerConnexionDeleted){
throw 'Error to delete peer connection';
}
this.createPeerConnection(user);
}else {
peerConnection.toClose = false;
}
return null; return null;
} }

View File

@ -39,13 +39,13 @@ export class VideoPeer extends Peer {
urls: 'stun:stun.l.google.com:19302' urls: 'stun:stun.l.google.com:19302'
}, },
{ {
urls: TURN_SERVER, urls: TURN_SERVER.split(','),
username: TURN_USER, username: TURN_USER,
credential: TURN_PASSWORD credential: TURN_PASSWORD
}, },
] ]
} }
}) });
//start listen signal for the peer connection //start listen signal for the peer connection
this.on('signal', (data: unknown) => { this.on('signal', (data: unknown) => {

View File

@ -125,6 +125,7 @@ message BatchMessage {
message GroupUpdateMessage { message GroupUpdateMessage {
int32 groupId = 1; int32 groupId = 1;
PointMessage position = 2; PointMessage position = 2;
int32 groupSize = 3;
} }
message GroupDeleteMessage { message GroupDeleteMessage {