Merge pull request #150 from thecodingmachine/strict_mode_front

Enabling Typescript strict mode on the front
This commit is contained in:
David Négrier 2020-06-04 22:17:00 +02:00 committed by GitHub
commit 7223c1804d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 302 additions and 231 deletions

View file

@ -98,10 +98,10 @@ export interface GroupCreatedUpdatedMessageInterface {
} }
export interface ConnectionInterface { export interface ConnectionInterface {
socket: any; socket: Socket|null;
token: string; token: string|null;
name: string; name: string|null;
userId: string; userId: string|null;
createConnection(name: string, characterSelected: string): Promise<any>; createConnection(name: string, characterSelected: string): Promise<any>;
@ -112,7 +112,7 @@ export interface ConnectionInterface {
sharePosition(x: number, y: number, direction: string, moving: boolean): void; sharePosition(x: number, y: number, direction: string, moving: boolean): void;
/*webrtc*/ /*webrtc*/
sendWebrtcSignal(signal: any, roomId: string, userId?: string, receiverId?: string): void; sendWebrtcSignal(signal: any, roomId: string, userId?: string|null, receiverId?: string): void;
receiveWebrtcSignal(callBack: Function): void; receiveWebrtcSignal(callBack: Function): void;
@ -122,15 +122,15 @@ export interface ConnectionInterface {
} }
export class Connection implements ConnectionInterface { export class Connection implements ConnectionInterface {
socket: Socket; socket: Socket|null = null;
token: string; token: string|null = null;
name: string; // TODO: drop "name" storage here name: string|null = null; // TODO: drop "name" storage here
character: string; character: string|null = null;
userId: string; userId: string|null = null;
GameManager: GameManager; GameManager: GameManager;
lastPositionShared: PointInterface = null; lastPositionShared: PointInterface|null = null;
lastRoom: string|null = null; lastRoom: string|null = null;
constructor(GameManager: GameManager) { constructor(GameManager: GameManager) {
@ -156,6 +156,13 @@ export class Connection implements ConnectionInterface {
}); });
} }
private getSocket(): Socket {
if (this.socket === null) {
throw new Error('Socket not initialized while using Connection')
}
return this.socket;
}
/** /**
* *
* @param character * @param character
@ -171,7 +178,7 @@ export class Connection implements ConnectionInterface {
this.onUserLeft(); this.onUserLeft();
return new Promise<ConnectionInterface>((resolve, reject) => { return new Promise<ConnectionInterface>((resolve, reject) => {
this.socket.emit(EventMessage.SET_PLAYER_DETAILS, { this.getSocket().emit(EventMessage.SET_PLAYER_DETAILS, {
name: this.name, name: this.name,
character: this.character character: this.character
} as SetPlayerDetailsMessage, (id: string) => { } as SetPlayerDetailsMessage, (id: string) => {
@ -215,7 +222,7 @@ export class Connection implements ConnectionInterface {
} }
joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): void { joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): void {
this.socket.emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => { this.getSocket().emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => {
this.GameManager.initUsersPosition(userPositions); this.GameManager.initUsersPosition(userPositions);
}); });
this.lastRoom = roomId; this.lastRoom = roomId;
@ -227,42 +234,42 @@ export class Connection implements ConnectionInterface {
} }
let point = new Point(x, y, direction, moving); let point = new Point(x, y, direction, moving);
this.lastPositionShared = point; this.lastPositionShared = point;
this.socket.emit(EventMessage.USER_POSITION, point); this.getSocket().emit(EventMessage.USER_POSITION, point);
} }
private onUserJoins(): void { private onUserJoins(): void {
this.socket.on(EventMessage.JOIN_ROOM, (message: MessageUserJoined) => { this.getSocket().on(EventMessage.JOIN_ROOM, (message: MessageUserJoined) => {
this.GameManager.onUserJoins(message); this.GameManager.onUserJoins(message);
}); });
} }
private onUserMoved(): void { private onUserMoved(): void {
this.socket.on(EventMessage.USER_MOVED, (message: MessageUserMovedInterface) => { this.getSocket().on(EventMessage.USER_MOVED, (message: MessageUserMovedInterface) => {
this.GameManager.onUserMoved(message); this.GameManager.onUserMoved(message);
}); });
} }
private onUserLeft(): void { private onUserLeft(): void {
this.socket.on(EventMessage.USER_LEFT, (userId: string) => { this.getSocket().on(EventMessage.USER_LEFT, (userId: string) => {
this.GameManager.onUserLeft(userId); this.GameManager.onUserLeft(userId);
}); });
} }
private groupUpdatedOrCreated(): void { private groupUpdatedOrCreated(): void {
this.socket.on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => { this.getSocket().on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => {
//console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y) //console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y)
this.GameManager.shareGroupPosition(groupCreateUpdateMessage); this.GameManager.shareGroupPosition(groupCreateUpdateMessage);
}) })
} }
private groupDeleted(): void { private groupDeleted(): void {
this.socket.on(EventMessage.GROUP_DELETE, (groupId: string) => { this.getSocket().on(EventMessage.GROUP_DELETE, (groupId: string) => {
this.GameManager.deleteGroup(groupId); this.GameManager.deleteGroup(groupId);
}) })
} }
sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) { sendWebrtcSignal(signal: any, roomId: string, userId? : string|null, receiverId? : string) {
return this.socket.emit(EventMessage.WEBRTC_SIGNAL, { return this.getSocket().emit(EventMessage.WEBRTC_SIGNAL, {
userId: userId ? userId : this.userId, userId: userId ? userId : this.userId,
receiverId: receiverId ? receiverId : this.userId, receiverId: receiverId ? receiverId : this.userId,
roomId: roomId, roomId: roomId,
@ -271,31 +278,34 @@ export class Connection implements ConnectionInterface {
} }
receiveWebrtcStart(callback: Function) { receiveWebrtcStart(callback: Function) {
this.socket.on(EventMessage.WEBRTC_START, callback); this.getSocket().on(EventMessage.WEBRTC_START, callback);
} }
receiveWebrtcSignal(callback: Function) { receiveWebrtcSignal(callback: Function) {
return this.socket.on(EventMessage.WEBRTC_SIGNAL, callback); return this.getSocket().on(EventMessage.WEBRTC_SIGNAL, callback);
} }
private errorMessage(): void { private errorMessage(): void {
this.socket.on(EventMessage.MESSAGE_ERROR, (message: string) => { this.getSocket().on(EventMessage.MESSAGE_ERROR, (message: string) => {
console.error(EventMessage.MESSAGE_ERROR, message); console.error(EventMessage.MESSAGE_ERROR, message);
}) })
} }
private disconnectServer(): void { private disconnectServer(): void {
this.socket.on(EventMessage.CONNECT_ERROR, () => { this.getSocket().on(EventMessage.CONNECT_ERROR, () => {
this.GameManager.switchToDisconnectedScene(); this.GameManager.switchToDisconnectedScene();
}); });
this.socket.on(EventMessage.RECONNECT, () => { this.getSocket().on(EventMessage.RECONNECT, () => {
this.connectSocketServer(); this.connectSocketServer();
if (this.lastPositionShared === null) {
throw new Error('No last position shared found while reconnecting');
}
this.GameManager.reconnectToGameScene(this.lastPositionShared); this.GameManager.reconnectToGameScene(this.lastPositionShared);
}); });
} }
disconnectMessage(callback: Function): void { disconnectMessage(callback: Function): void {
this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback); this.getSocket().on(EventMessage.WEBRTC_DISCONNECT, callback);
} }
} }

View file

@ -3,18 +3,18 @@ export class MessageUI {
static warningMessage(text: string){ static warningMessage(text: string){
this.removeMessage(); this.removeMessage();
let body = document.getElementById("body"); let body = document.getElementById("body");
body.insertAdjacentHTML('afterbegin', ` body?.insertAdjacentHTML('afterbegin', `
<div id="message-reconnect" class="message-info warning"> <div id="message-reconnect" class="message-info warning">
${text} ${text}
</div> </div>
`); `);
} }
static removeMessage(id : string = null) { static removeMessage(id : string|null = null) {
if(!id){ if(!id){
let messages = document.getElementsByClassName("message-info"); let messages = document.getElementsByClassName("message-info");
for (let i = 0; i < messages.length; i++){ for (let i = 0; i < messages.length; i++){
messages.item(i).remove(); messages.item(i)?.remove();
} }
return; return;
} }
@ -24,4 +24,4 @@ export class MessageUI {
} }
previousElement.remove(); previousElement.remove();
} }
} }

View file

@ -24,14 +24,31 @@ export const PLAYER_RESOURCES: Array<any> = [
{name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"/*, x: 128, y: 128*/} {name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"/*, x: 128, y: 128*/}
]; ];
export class PlayableCaracter extends Phaser.Physics.Arcade.Sprite { interface AnimationData {
private bubble: SpeechBubble; key: string;
frameRate: number;
repeat: number;
frameModel: string; //todo use an enum
frameStart: number;
frameEnd: number;
}
export abstract class Character extends Phaser.Physics.Arcade.Sprite {
private bubble: SpeechBubble|null = null;
private readonly playerName: BitmapText; private readonly playerName: BitmapText;
public PlayerValue: string; public PlayerValue: string;
public PlayerTexture: string; public PlayerTexture: string;
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, name: string, frame?: string | number) { constructor(scene: Phaser.Scene,
x: number,
y: number,
texture: string,
name: string,
direction: string,
moving: boolean,
frame?: string | number
) {
super(scene, x, y, texture, frame); super(scene, x, y, texture, frame);
this.PlayerValue = name; this.PlayerValue = name;
@ -51,6 +68,64 @@ export class PlayableCaracter extends Phaser.Physics.Arcade.Sprite {
this.setDepth(-1); this.setDepth(-1);
this.scene.events.on('postupdate', this.postupdate.bind(this)); this.scene.events.on('postupdate', this.postupdate.bind(this));
this.initAnimation();
this.playAnimation(direction, moving);
}
private initAnimation(): void {
this.getPlayerAnimations(this.PlayerTexture).forEach(d => {
this.scene.anims.create({
key: d.key,
frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}),
frameRate: d.frameRate,
repeat: d.repeat
});
})
}
private getPlayerAnimations(name: string): AnimationData[] {
return [{
key: `${name}-${PlayerAnimationNames.WalkDown}`,
frameModel: name,
frameStart: 0,
frameEnd: 2,
frameRate: 10,
repeat: -1
}, {
key: `${name}-${PlayerAnimationNames.WalkLeft}`,
frameModel: name,
frameStart: 3,
frameEnd: 5,
frameRate: 10,
repeat: -1
}, {
key: `${name}-${PlayerAnimationNames.WalkRight}`,
frameModel: name,
frameStart: 6,
frameEnd: 8,
frameRate: 10,
repeat: -1
}, {
key: `${name}-${PlayerAnimationNames.WalkUp}`,
frameModel: name,
frameStart: 9,
frameEnd: 11,
frameRate: 10,
repeat: -1
}];
}
protected playAnimation(direction : string, moving: boolean): void {
if (moving && (!this.anims.currentAnim || this.anims.currentAnim.key !== direction)) {
this.play(this.PlayerTexture+'-'+direction, true);
} else if (!moving) {
/*if (this.anims.currentAnim) {
this.anims.stop();
}*/
this.play(this.PlayerTexture+'-'+direction, true);
this.stop();
}
} }
move(x: number, y: number) { move(x: number, y: number) {
@ -91,8 +166,10 @@ export class PlayableCaracter extends Phaser.Physics.Arcade.Sprite {
this.bubble = new SpeechBubble(this.scene, this, text) this.bubble = new SpeechBubble(this.scene, this, text)
//todo make the bubble destroy on player movement? //todo make the bubble destroy on player movement?
setTimeout(() => { setTimeout(() => {
this.bubble.destroy(); if (this.bubble !== null) {
this.bubble = null; this.bubble.destroy();
this.bubble = null;
}
}, 3000) }, 3000)
} }

View file

@ -0,0 +1,38 @@
import {GameScene} from "../Game/GameScene";
import {PointInterface} from "../../Connection";
import {Character} from "../Entity/Character";
/**
* Class representing the sprite of a remote player (a player that plays on another computer)
*/
export class RemotePlayer extends Character {
userId: string;
previousDirection: string;
wasMoving: boolean;
constructor(
userId: string,
Scene: GameScene,
x: number,
y: number,
name: string,
PlayerTexture: string,
direction: string,
moving: boolean
) {
super(Scene, x, y, PlayerTexture, name, direction, moving, 1);
//set data
this.userId = userId;
//the current player model should be push away by other players to prevent conflict
//this.setImmovable(false);
}
updatePosition(position: PointInterface): void {
this.playAnimation(position.direction, position.moving);
this.setX(position.x);
this.setY(position.y);
this.setDepth(position.y);
}
}

View file

@ -1,5 +1,5 @@
import Scene = Phaser.Scene; import Scene = Phaser.Scene;
import {PlayableCaracter} from "./PlayableCaracter"; import {Character} from "./Character";
export class SpeechBubble { export class SpeechBubble {
private bubble: Phaser.GameObjects.Graphics; private bubble: Phaser.GameObjects.Graphics;
@ -11,7 +11,7 @@ export class SpeechBubble {
* @param player * @param player
* @param text * @param text
*/ */
constructor(scene: Scene, player: PlayableCaracter, text: string = "") { constructor(scene: Scene, player: Character, text: string = "") {
let bubbleHeight = 50; let bubbleHeight = 50;
let bubblePadding = 10; let bubblePadding = 10;
@ -76,13 +76,10 @@ export class SpeechBubble {
this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2)); this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2));
} }
} }
destroy(): void { destroy(): void {
this.bubble.setVisible(false) //todo find a better way this.bubble.setVisible(false) //todo find a better way
this.bubble.destroy(); this.bubble.destroy();
this.bubble = null;
this.content.destroy(); this.content.destroy();
this.content = null;
} }
}
}

View file

@ -9,7 +9,7 @@ import {
Point, Point,
PointInterface PointInterface
} from "../../Connection"; } from "../../Connection";
import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer"; import {SimplePeer} from "../../WebRtc/SimplePeer";
import {AddPlayerInterface} from "./AddPlayerInterface"; import {AddPlayerInterface} from "./AddPlayerInterface";
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
@ -35,7 +35,7 @@ export class GameManager {
private ConnectionInstance: Connection; private ConnectionInstance: Connection;
private currentGameScene: GameScene; private currentGameScene: GameScene;
private playerName: string; private playerName: string;
SimplePeer : SimplePeerInterface; SimplePeer : SimplePeer;
private characterUserSelected: string; private characterUserSelected: string;
constructor() { constructor() {
@ -139,7 +139,7 @@ export class GameManager {
return this.playerName; return this.playerName;
} }
getPlayerId(): string { getPlayerId(): string|null {
return this.ConnectionInstance.userId; return this.ConnectionInstance.userId;
} }
@ -155,9 +155,8 @@ export class GameManager {
let sceneKey = GameScene.getMapKeyByUrl(mapUrl); let sceneKey = GameScene.getMapKeyByUrl(mapUrl);
let gameIndex = scene.getIndex(sceneKey); let gameIndex = scene.getIndex(sceneKey);
let game : Phaser.Scene = null;
if(gameIndex === -1){ if(gameIndex === -1){
game = GameScene.createFromUrl(mapUrl, instance); let game : Phaser.Scene = GameScene.createFromUrl(mapUrl, instance);
scene.add(sceneKey, game, false); scene.add(sceneKey, game, false);
} }
return sceneKey; return sceneKey;

View file

@ -4,10 +4,10 @@ import {
MessageUserMovedInterface, MessageUserMovedInterface,
MessageUserPositionInterface, PointInterface, PositionInterface MessageUserPositionInterface, PointInterface, PositionInterface
} from "../../Connection"; } from "../../Connection";
import {CurrentGamerInterface, GamerInterface, hasMovedEventName, Player} from "../Player/Player"; import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
import { DEBUG_MODE, ZOOM_LEVEL, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; import { DEBUG_MODE, ZOOM_LEVEL, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
import {ITiledMap, ITiledMapLayer, ITiledTileSet} from "../Map/ITiledMap"; import {ITiledMap, ITiledMapLayer, ITiledTileSet} from "../Map/ITiledMap";
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import {PLAYER_RESOURCES} from "../Entity/Character";
import Texture = Phaser.Textures.Texture; import Texture = Phaser.Textures.Texture;
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;
import CanvasTexture = Phaser.Textures.CanvasTexture; import CanvasTexture = Phaser.Textures.CanvasTexture;
@ -15,6 +15,7 @@ import {AddPlayerInterface} from "./AddPlayerInterface";
import {PlayerAnimationNames} from "../Player/Animation"; import {PlayerAnimationNames} from "../Player/Animation";
import {PlayerMovement} from "./PlayerMovement"; import {PlayerMovement} from "./PlayerMovement";
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
import {RemotePlayer} from "../Entity/RemotePlayer";
export enum Textures { export enum Textures {
Player = "male1" Player = "male1"
@ -29,16 +30,16 @@ export class GameScene extends Phaser.Scene {
Terrains : Array<Phaser.Tilemaps.Tileset>; Terrains : Array<Phaser.Tilemaps.Tileset>;
CurrentPlayer: CurrentGamerInterface; CurrentPlayer: CurrentGamerInterface;
MapPlayers : Phaser.Physics.Arcade.Group; MapPlayers : Phaser.Physics.Arcade.Group;
MapPlayersByKey : Map<string, GamerInterface> = new Map<string, GamerInterface>(); MapPlayersByKey : Map<string, RemotePlayer> = new Map<string, RemotePlayer>();
Map: Phaser.Tilemaps.Tilemap; Map: Phaser.Tilemaps.Tilemap;
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>; Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
Objects : Array<Phaser.Physics.Arcade.Sprite>; Objects : Array<Phaser.Physics.Arcade.Sprite>;
map: ITiledMap; mapFile: ITiledMap;
groups: Map<string, Sprite>; groups: Map<string, Sprite>;
startX = 704;// 22 case startX = 704;// 22 case
startY = 32; // 1 case startY = 32; // 1 case
circleTexture: CanvasTexture; circleTexture: CanvasTexture;
initPosition: PositionInterface; private initPosition: PositionInterface|null = null;
private playersPositionInterpolator = new PlayersPositionInterpolator(); private playersPositionInterpolator = new PlayersPositionInterpolator();
MapKey: string; MapKey: string;
@ -107,9 +108,9 @@ export class GameScene extends Phaser.Scene {
private onMapLoad(data: any): void { private onMapLoad(data: any): void {
// Triggered when the map is loaded // Triggered when the map is loaded
// Load tiles attached to the map recursively // Load tiles attached to the map recursively
this.map = data.data; this.mapFile = data.data;
let url = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); let url = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/'));
this.map.tilesets.forEach((tileset) => { this.mapFile.tilesets.forEach((tileset) => {
if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') { if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') {
console.warn("Don't know how to handle tileset ", tileset) console.warn("Don't know how to handle tileset ", tileset)
return; return;
@ -121,14 +122,16 @@ export class GameScene extends Phaser.Scene {
//hook initialisation //hook initialisation
init(initData : GameSceneInitInterface) { init(initData : GameSceneInitInterface) {
this.initPosition = initData.initPosition; if (initData.initPosition !== undefined) {
this.initPosition = initData.initPosition;
}
} }
//hook create scene //hook create scene
create(): void { create(): void {
//initalise map //initalise map
this.Map = this.add.tilemap(this.MapKey); this.Map = this.add.tilemap(this.MapKey);
this.map.tilesets.forEach((tileset: ITiledTileSet) => { this.mapFile.tilesets.forEach((tileset: ITiledTileSet) => {
this.Terrains.push(this.Map.addTilesetImage(tileset.name, tileset.name)); this.Terrains.push(this.Map.addTilesetImage(tileset.name, tileset.name));
}); });
@ -138,12 +141,12 @@ export class GameScene extends Phaser.Scene {
//add layer on map //add layer on map
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>(); this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
let depth = -2; let depth = -2;
this.map.layers.forEach((layer : ITiledMapLayer) => { this.mapFile.layers.forEach((layer : ITiledMapLayer) => {
if (layer.type === 'tilelayer') { if (layer.type === 'tilelayer') {
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth)); this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
} }
if (layer.type === 'tilelayer' && this.getExitSceneUrl(layer) !== undefined) { if (layer.type === 'tilelayer' && this.getExitSceneUrl(layer) !== undefined) {
this.loadNextGame(layer, this.map.width, this.map.tilewidth, this.map.tileheight); this.loadNextGame(layer, this.mapFile.width, this.mapFile.tilewidth, this.mapFile.tileheight);
} }
if (layer.type === 'tilelayer' && layer.name === "start") { if (layer.type === 'tilelayer' && layer.name === "start") {
let startPosition = this.startUser(layer); let startPosition = this.startUser(layer);
@ -196,7 +199,7 @@ export class GameScene extends Phaser.Scene {
// FIXME: entry should be dictated by a property passed to init() // FIXME: entry should be dictated by a property passed to init()
path += '#'+url.hash; path += '#'+url.hash;
} }
window.history.pushState({}, null, path); window.history.pushState({}, 'WorkAdventure', path);
} }
private getExitSceneUrl(layer: ITiledMapLayer): string|undefined { private getExitSceneUrl(layer: ITiledMapLayer): string|undefined {
@ -232,6 +235,9 @@ export class GameScene extends Phaser.Scene {
*/ */
private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){ private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){
let exitSceneUrl = this.getExitSceneUrl(layer); let exitSceneUrl = this.getExitSceneUrl(layer);
if (exitSceneUrl === undefined) {
throw new Error('Layer is not an exit scene layer.');
}
let instance = this.getExitSceneInstance(layer); let instance = this.getExitSceneInstance(layer);
if (instance === undefined) { if (instance === undefined) {
instance = this.instance; instance = this.instance;
@ -265,7 +271,7 @@ export class GameScene extends Phaser.Scene {
* @param layer * @param layer
*/ */
private startUser(layer: ITiledMapLayer): PositionInterface { private startUser(layer: ITiledMapLayer): PositionInterface {
if (this.initPosition !== undefined) { if (this.initPosition !== null) {
this.startX = this.initPosition.x; this.startX = this.initPosition.x;
this.startY = this.initPosition.y; this.startY = this.initPosition.y;
return { return {
@ -338,7 +344,6 @@ export class GameScene extends Phaser.Scene {
//initialise player //initialise player
//TODO create animation moving between exit and start //TODO create animation moving between exit and start
this.CurrentPlayer = new Player( this.CurrentPlayer = new Player(
null, // The current player is not has no id (because the id can change if connection is lost and we should check that id using the GameManager.
this, this,
this.startX, this.startX,
this.startY, this.startY,
@ -413,7 +418,7 @@ export class GameScene extends Phaser.Scene {
// Let's move all users // Let's move all users
let updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time); let updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time);
updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: string) => { updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: string) => {
let player : GamerInterface | undefined = this.MapPlayersByKey.get(userId); let player : RemotePlayer | undefined = this.MapPlayersByKey.get(userId);
if (player === undefined) { if (player === undefined) {
throw new Error('Cannot find player with ID "' + userId +'"'); throw new Error('Cannot find player with ID "' + userId +'"');
} }
@ -450,11 +455,11 @@ export class GameScene extends Phaser.Scene {
let currentPlayerId = this.GameManager.getPlayerId(); let currentPlayerId = this.GameManager.getPlayerId();
// clean map // clean map
this.MapPlayersByKey.forEach((player: GamerInterface) => { this.MapPlayersByKey.forEach((player: RemotePlayer) => {
player.destroy(); player.destroy();
this.MapPlayers.remove(player); this.MapPlayers.remove(player);
}); });
this.MapPlayersByKey = new Map<string, GamerInterface>(); this.MapPlayersByKey = new Map<string, RemotePlayer>();
// load map // load map
usersPosition.forEach((userPosition : MessageUserPositionInterface) => { usersPosition.forEach((userPosition : MessageUserPositionInterface) => {
@ -478,7 +483,7 @@ export class GameScene extends Phaser.Scene {
return; return;
} }
//initialise player //initialise player
let player = new Player( let player = new RemotePlayer(
addPlayerData.userId, addPlayerData.userId,
this, this,
addPlayerData.position.x, addPlayerData.position.x,
@ -503,15 +508,16 @@ export class GameScene extends Phaser.Scene {
let player = this.MapPlayersByKey.get(userId); let player = this.MapPlayersByKey.get(userId);
if (player === undefined) { if (player === undefined) {
console.error('Cannot find user with id ', userId); console.error('Cannot find user with id ', userId);
} else {
player.destroy();
this.MapPlayers.remove(player);
} }
player.destroy();
this.MapPlayers.remove(player);
this.MapPlayersByKey.delete(userId); this.MapPlayersByKey.delete(userId);
this.playersPositionInterpolator.removePlayer(userId); this.playersPositionInterpolator.removePlayer(userId);
} }
updatePlayerPosition(message: MessageUserMovedInterface): void { updatePlayerPosition(message: MessageUserMovedInterface): void {
let player : GamerInterface | undefined = this.MapPlayersByKey.get(message.userId); let player : RemotePlayer | undefined = this.MapPlayersByKey.get(message.userId);
if (player === undefined) { if (player === undefined) {
throw new Error('Cannot find player with ID "' + message.userId +'"'); throw new Error('Cannot find player with ID "' + message.userId +'"');
} }
@ -525,8 +531,9 @@ export class GameScene extends Phaser.Scene {
shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) { shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface) {
let groupId = groupPositionMessage.groupId; let groupId = groupPositionMessage.groupId;
if (this.groups.has(groupId)) { let group = this.groups.get(groupId);
this.groups.get(groupId).setPosition(Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.y)); if (group !== undefined) {
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
let sprite = new Sprite( let sprite = new Sprite(
@ -541,10 +548,11 @@ export class GameScene extends Phaser.Scene {
} }
deleteGroup(groupId: string): void { deleteGroup(groupId: string): void {
if(!this.groups.get(groupId)){ let group = this.groups.get(groupId);
if(!group){
return; return;
} }
this.groups.get(groupId).destroy(); group.destroy();
this.groups.delete(groupId); this.groups.delete(groupId);
} }

View file

@ -4,7 +4,7 @@ import {TextInput} from "../Components/TextInput";
import {ClickButton} from "../Components/ClickButton"; import {ClickButton} from "../Components/ClickButton";
import Image = Phaser.GameObjects.Image; import Image = Phaser.GameObjects.Image;
import Rectangle = Phaser.GameObjects.Rectangle; import Rectangle = Phaser.GameObjects.Rectangle;
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import {PLAYER_RESOURCES} from "../Entity/Character";
import {cypressAsserter} from "../../Cypress/CypressAsserter"; import {cypressAsserter} from "../../Cypress/CypressAsserter";
import {SelectCharacterSceneInitDataInterface, SelectCharacterSceneName} from "./SelectCharacterScene"; import {SelectCharacterSceneInitDataInterface, SelectCharacterSceneName} from "./SelectCharacterScene";
@ -16,12 +16,12 @@ enum LoginTextures {
} }
export class LoginScene extends Phaser.Scene { export class LoginScene extends Phaser.Scene {
private nameInput: TextInput; private nameInput: TextInput|null = null;
private textField: TextField; private textField: TextField|null = null;
private infoTextField: TextField; private infoTextField: TextField|null = null;
private pressReturnField: TextField; private pressReturnField: TextField|null = null;
private logo: Image; private logo: Image|null = null;
private name: string; private name: string = '';
constructor() { constructor() {
super({ super({
@ -82,9 +82,9 @@ export class LoginScene extends Phaser.Scene {
update(time: number, delta: number): void { update(time: number, delta: number): void {
if (this.name == '') { if (this.name == '') {
this.pressReturnField.setVisible(false); this.pressReturnField?.setVisible(false);
} else { } else {
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); this.pressReturnField?.setVisible(!!(Math.floor(time / 500) % 2));
} }
} }

View file

@ -3,7 +3,7 @@ import {TextField} from "../Components/TextField";
import {ClickButton} from "../Components/ClickButton"; import {ClickButton} from "../Components/ClickButton";
import Image = Phaser.GameObjects.Image; import Image = Phaser.GameObjects.Image;
import Rectangle = Phaser.GameObjects.Rectangle; import Rectangle = Phaser.GameObjects.Rectangle;
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import {PLAYER_RESOURCES} from "../Entity/Character";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene"; export const SelectCharacterSceneName = "SelectCharacterScene";

View file

@ -2,39 +2,21 @@ import {PlayerAnimationNames} from "./Animation";
import {GameScene, Textures} from "../Game/GameScene"; import {GameScene, Textures} from "../Game/GameScene";
import {MessageUserPositionInterface, PointInterface} from "../../Connection"; import {MessageUserPositionInterface, PointInterface} from "../../Connection";
import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
import {PlayableCaracter} from "../Entity/PlayableCaracter"; import {Character} from "../Entity/Character";
export const hasMovedEventName = "hasMoved"; export const hasMovedEventName = "hasMoved";
export interface CurrentGamerInterface extends PlayableCaracter{ export interface CurrentGamerInterface extends Character{
moveUser(delta: number) : void; moveUser(delta: number) : void;
say(text : string) : void; say(text : string) : void;
} }
export interface GamerInterface extends PlayableCaracter{ export class Player extends Character implements CurrentGamerInterface {
userId : string;
updatePosition(position: PointInterface): void;
say(text : string) : void;
}
interface AnimationData {
key: string;
frameRate: number;
repeat: number;
frameModel: string; //todo use an enum
frameStart: number;
frameEnd: number;
}
export class Player extends PlayableCaracter implements CurrentGamerInterface, GamerInterface {
userId: string;
userInputManager: UserInputManager; userInputManager: UserInputManager;
previousDirection: string; previousDirection: string;
wasMoving: boolean; wasMoving: boolean;
constructor( constructor(
userId: string,
Scene: GameScene, Scene: GameScene,
x: number, x: number,
y: number, y: number,
@ -43,62 +25,13 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
direction: string, direction: string,
moving: boolean moving: boolean
) { ) {
super(Scene, x, y, PlayerTexture, name, 1); super(Scene, x, y, PlayerTexture, name, direction, moving, 1);
//create input to move //create input to move
this.userInputManager = new UserInputManager(Scene); this.userInputManager = new UserInputManager(Scene);
//set data
this.userId = userId;
//the current player model should be push away by other players to prevent conflict //the current player model should be push away by other players to prevent conflict
this.setImmovable(false); this.setImmovable(false);
this.initAnimation();
this.playAnimation(direction, moving);
}
private initAnimation(): void {
this.getPlayerAnimations(this.PlayerTexture).forEach(d => {
this.scene.anims.create({
key: d.key,
frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}),
frameRate: d.frameRate,
repeat: d.repeat
});
})
}
private getPlayerAnimations(name: string): AnimationData[] {
return [{
key: `${name}-${PlayerAnimationNames.WalkDown}`,
frameModel: name,
frameStart: 0,
frameEnd: 2,
frameRate: 10,
repeat: -1
}, {
key: `${name}-${PlayerAnimationNames.WalkLeft}`,
frameModel: name,
frameStart: 3,
frameEnd: 5,
frameRate: 10,
repeat: -1
}, {
key: `${name}-${PlayerAnimationNames.WalkRight}`,
frameModel: name,
frameStart: 6,
frameEnd: 8,
frameRate: 10,
repeat: -1
}, {
key: `${name}-${PlayerAnimationNames.WalkUp}`,
frameModel: name,
frameStart: 9,
frameEnd: 11,
frameRate: 10,
repeat: -1
}];
} }
moveUser(delta: number): void { moveUser(delta: number): void {
@ -146,24 +79,4 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
} }
this.wasMoving = moving; this.wasMoving = moving;
} }
//todo: put this method into the NonPlayer class instead
updatePosition(position: PointInterface): void {
this.playAnimation(position.direction, position.moving);
this.setX(position.x);
this.setY(position.y);
this.setDepth(position.y);
}
private playAnimation(direction : string, moving: boolean): void {
if (moving && (!this.anims.currentAnim || this.anims.currentAnim.key !== direction)) {
this.play(this.PlayerTexture+'-'+direction, true);
} else if (!moving) {
/*if (this.anims.currentAnim) {
this.anims.stop();
}*/
this.play(this.PlayerTexture+'-'+direction, true);
this.stop();
}
}
} }

View file

@ -4,7 +4,7 @@ import {TextInput} from "../Components/TextInput";
import {ClickButton} from "../Components/ClickButton"; import {ClickButton} from "../Components/ClickButton";
import Image = Phaser.GameObjects.Image; import Image = Phaser.GameObjects.Image;
import Rectangle = Phaser.GameObjects.Rectangle; import Rectangle = Phaser.GameObjects.Rectangle;
import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import {PLAYER_RESOURCES} from "../Entity/Character";
import {cypressAsserter} from "../../Cypress/CypressAsserter"; import {cypressAsserter} from "../../Cypress/CypressAsserter";
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;

View file

@ -2,7 +2,6 @@ import Map = Phaser.Structs.Map;
import {GameScene} from "../Game/GameScene"; import {GameScene} from "../Game/GameScene";
interface UserInputManagerDatum { interface UserInputManagerDatum {
keyCode: number;
keyInstance: Phaser.Input.Keyboard.Key; keyInstance: Phaser.Input.Keyboard.Key;
event: UserInputEvent event: UserInputEvent
} }
@ -33,27 +32,26 @@ export class ActiveEventList {
//this class is responsible for catching user inputs and listing all active user actions at every game tick events. //this class is responsible for catching user inputs and listing all active user actions at every game tick events.
export class UserInputManager { export class UserInputManager {
private KeysCode: UserInputManagerDatum[] = [ private KeysCode: UserInputManagerDatum[];
{keyCode: Phaser.Input.Keyboard.KeyCodes.Z, event: UserInputEvent.MoveUp, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.Q, event: UserInputEvent.MoveLeft, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.S, event: UserInputEvent.MoveDown, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.D, event: UserInputEvent.MoveRight, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.UP, event: UserInputEvent.MoveUp, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.LEFT, event: UserInputEvent.MoveLeft, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.DOWN, event: UserInputEvent.MoveDown, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.RIGHT, event: UserInputEvent.MoveRight, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.SHIFT, event: UserInputEvent.SpeedUp, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.E, event: UserInputEvent.Interact, keyInstance: null},
{keyCode: Phaser.Input.Keyboard.KeyCodes.F, event: UserInputEvent.Shout, keyInstance: null},
];
constructor(Scene : GameScene) { constructor(Scene : GameScene) {
this.KeysCode.forEach(d => {
d.keyInstance = Scene.input.keyboard.addKey(d.keyCode); this.KeysCode = [
}); {event: UserInputEvent.MoveUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z) },
{event: UserInputEvent.MoveLeft, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q) },
{event: UserInputEvent.MoveDown, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S) },
{event: UserInputEvent.MoveRight, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D) },
{event: UserInputEvent.MoveUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP) },
{event: UserInputEvent.MoveLeft, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT) },
{event: UserInputEvent.MoveDown, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN) },
{event: UserInputEvent.MoveRight, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT) },
{event: UserInputEvent.SpeedUp, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT) },
{event: UserInputEvent.Interact, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E) },
{event: UserInputEvent.Shout, keyInstance: Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F) },
];
} }
getEventListForGameTick(): ActiveEventList { getEventListForGameTick(): ActiveEventList {

View file

@ -4,26 +4,25 @@ const videoConstraint: {width : any, height: any, facingMode : string} = {
facingMode: "user" facingMode: "user"
}; };
export class MediaManager { export class MediaManager {
localStream: MediaStream; localStream: MediaStream|null = null;
remoteVideo: Array<any> = new Array<any>(); remoteVideo: Array<any> = new Array<any>();
myCamVideo: any; myCamVideo: HTMLVideoElement;
cinemaClose: any = null; cinemaClose: any = null;
cinema: any = null; cinema: any = null;
microphoneClose: any = null; microphoneClose: any = null;
microphone: any = null; microphone: any = null;
webrtcInAudio: any; webrtcInAudio: HTMLAudioElement;
constraintsMedia : {audio : any, video : any} = { constraintsMedia : {audio : any, video : any} = {
audio: true, audio: true,
video: videoConstraint video: videoConstraint
}; };
getCameraPromise : Promise<any> = null;
updatedLocalStreamCallBack : Function; updatedLocalStreamCallBack : Function;
constructor(updatedLocalStreamCallBack : Function) { constructor(updatedLocalStreamCallBack : Function) {
this.updatedLocalStreamCallBack = updatedLocalStreamCallBack; this.updatedLocalStreamCallBack = updatedLocalStreamCallBack;
this.myCamVideo = document.getElementById('myCamVideo'); this.myCamVideo = this.getElementByIdOrFail<HTMLVideoElement>('myCamVideo');
this.webrtcInAudio = document.getElementById('audio-webrtc-in'); this.webrtcInAudio = this.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-in');
this.webrtcInAudio.volume = 0.2; this.webrtcInAudio.volume = 0.2;
this.microphoneClose = document.getElementById('microphone-close'); this.microphoneClose = document.getElementById('microphone-close');
@ -56,7 +55,7 @@ export class MediaManager {
} }
activeVisio(){ activeVisio(){
let webRtc = document.getElementById('webRtc'); let webRtc = this.getElementByIdOrFail('webRtc');
webRtc.classList.add('active'); webRtc.classList.add('active');
} }
@ -130,7 +129,7 @@ export class MediaManager {
} catch (e) { } catch (e) {
promise = Promise.reject(false); promise = Promise.reject(false);
} }
return this.getCameraPromise = promise; return promise;
} }
/** /**
@ -139,7 +138,7 @@ export class MediaManager {
*/ */
addActiveVideo(userId : string, userName: string = ""){ addActiveVideo(userId : string, userName: string = ""){
this.webrtcInAudio.play(); this.webrtcInAudio.play();
let elementRemoteVideo = document.getElementById("activeCam"); let elementRemoteVideo = this.getElementByIdOrFail("activeCam");
userName = userName.toUpperCase(); userName = userName.toUpperCase();
let color = this.getColorByString(userName); let color = this.getColorByString(userName);
elementRemoteVideo.insertAdjacentHTML('beforeend', ` elementRemoteVideo.insertAdjacentHTML('beforeend', `
@ -247,4 +246,14 @@ export class MediaManager {
} }
return color; return color;
} }
}
private getElementByIdOrFail<T extends HTMLElement>(id: string): T {
let elem = document.getElementById(id);
if (elem === null) {
throw new Error("Cannot find HTML element with id '"+id+"'");
}
// FIXME: does not check the type of the returned type
return elem as T;
}
}

View file

@ -1,21 +1,21 @@
import {ConnectionInterface} from "../Connection"; import {ConnectionInterface} from "../Connection";
import {MediaManager} from "./MediaManager"; import {MediaManager} from "./MediaManager";
let Peer = require('simple-peer'); import * as SimplePeerNamespace from "simple-peer";
let Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
class UserSimplePear{ class UserSimplePeer{
userId: string; userId: string;
name?: string; name?: string;
initiator?: boolean; initiator?: boolean;
} }
export class SimplePeerInterface {} export class SimplePeer {
export class SimplePeer implements SimplePeerInterface{
private Connection: ConnectionInterface; private Connection: ConnectionInterface;
private WebRtcRoomId: string; private WebRtcRoomId: string;
private Users: Array<UserSimplePear> = new Array<UserSimplePear>(); private Users: Array<UserSimplePeer> = new Array<UserSimplePeer>();
private MediaManager: MediaManager; private MediaManager: MediaManager;
private PeerConnectionArray: Map<string, any> = new Map<string, any>(); private PeerConnectionArray: Map<string, SimplePeerNamespace.Instance> = new Map<string, SimplePeerNamespace.Instance>();
constructor(Connection: ConnectionInterface, WebRtcRoomId: string = "test-webrtc") { constructor(Connection: ConnectionInterface, WebRtcRoomId: string = "test-webrtc") {
this.Connection = Connection; this.Connection = Connection;
@ -66,7 +66,7 @@ export class SimplePeer implements SimplePeerInterface{
* server has two person connected, start the meet * server has two person connected, start the meet
*/ */
private startWebRtc() { private startWebRtc() {
this.Users.forEach((user: UserSimplePear) => { this.Users.forEach((user: UserSimplePeer) => {
//if it's not an initiator, peer connection will be created when gamer will receive offer signal //if it's not an initiator, peer connection will be created when gamer will receive offer signal
if(!user.initiator){ if(!user.initiator){
return; return;
@ -78,14 +78,14 @@ export class SimplePeer implements SimplePeerInterface{
/** /**
* create peer connection to bind users * create peer connection to bind users
*/ */
private createPeerConnection(user : UserSimplePear) { private createPeerConnection(user : UserSimplePeer) {
if(this.PeerConnectionArray.has(user.userId)) { if(this.PeerConnectionArray.has(user.userId)) {
return; return;
} }
let name = user.name; let name = user.name;
if(!name){ if(!name){
let userSearch = this.Users.find((userSearch: UserSimplePear) => userSearch.userId === user.userId); let userSearch = this.Users.find((userSearch: UserSimplePeer) => userSearch.userId === user.userId);
if(userSearch) { if(userSearch) {
name = userSearch.name; name = userSearch.name;
} }
@ -112,11 +112,11 @@ export class SimplePeer implements SimplePeerInterface{
this.PeerConnectionArray.set(user.userId, peer); this.PeerConnectionArray.set(user.userId, peer);
//start listen signal for the peer connection //start listen signal for the peer connection
this.PeerConnectionArray.get(user.userId).on('signal', (data: any) => { peer.on('signal', (data: any) => {
this.sendWebrtcSignal(data, user.userId); this.sendWebrtcSignal(data, user.userId);
}); });
this.PeerConnectionArray.get(user.userId).on('stream', (stream: MediaStream) => { peer.on('stream', (stream: MediaStream) => {
let videoActive = false; let videoActive = false;
let microphoneActive = false; let microphoneActive = false;
stream.getTracks().forEach((track : MediaStreamTrack) => { stream.getTracks().forEach((track : MediaStreamTrack) => {
@ -141,23 +141,23 @@ export class SimplePeer implements SimplePeerInterface{
this.stream(user.userId, stream); this.stream(user.userId, stream);
}); });
/*this.PeerConnectionArray.get(user.userId).on('track', (track: MediaStreamTrack, stream: MediaStream) => { /*peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => {
this.stream(user.userId, stream); this.stream(user.userId, stream);
});*/ });*/
this.PeerConnectionArray.get(user.userId).on('close', () => { peer.on('close', () => {
this.closeConnection(user.userId); this.closeConnection(user.userId);
}); });
this.PeerConnectionArray.get(user.userId).on('error', (err: any) => { peer.on('error', (err: any) => {
console.error(`error => ${user.userId} => ${err.code}`, err); console.error(`error => ${user.userId} => ${err.code}`, err);
}); });
this.PeerConnectionArray.get(user.userId).on('connect', () => { peer.on('connect', () => {
console.info(`connect => ${user.userId}`); console.info(`connect => ${user.userId}`);
}); });
this.PeerConnectionArray.get(user.userId).on('data', (chunk: Buffer) => { peer.on('data', (chunk: Buffer) => {
let data = JSON.parse(chunk.toString('utf8')); let data = JSON.parse(chunk.toString('utf8'));
if(data.type === "stream"){ if(data.type === "stream"){
this.stream(user.userId, data.stream); this.stream(user.userId, data.stream);
@ -174,7 +174,7 @@ export class SimplePeer implements SimplePeerInterface{
return; return;
} }
// @ts-ignore // @ts-ignore
this.PeerConnectionArray.get(userId).destroy(); this.PeerConnectionArray.get(userId)?.destroy();
this.PeerConnectionArray.delete(userId) this.PeerConnectionArray.delete(userId)
} catch (err) { } catch (err) {
console.error("closeConnection", err) console.error("closeConnection", err)
@ -200,7 +200,12 @@ export class SimplePeer implements SimplePeerInterface{
if(data.signal.type === "offer"){ if(data.signal.type === "offer"){
this.createPeerConnection(data); this.createPeerConnection(data);
} }
this.PeerConnectionArray.get(data.userId).signal(data.signal); let peer = this.PeerConnectionArray.get(data.userId);
if (peer !== undefined) {
peer.signal(data.signal);
} else {
console.error('Could not find peer whose ID is "'+data.userId+'" in PeerConnectionArray');
}
} catch (e) { } catch (e) {
console.error(`receiveWebrtcSignal => ${data.userId}`, e); console.error(`receiveWebrtcSignal => ${data.userId}`, e);
} }
@ -227,27 +232,32 @@ export class SimplePeer implements SimplePeerInterface{
private addMedia (userId : any = null) { private addMedia (userId : any = null) {
try { try {
let transceiver : any = null; let transceiver : any = null;
if(!this.MediaManager.localStream){ let localStream: MediaStream|null = this.MediaManager.localStream;
let peer = this.PeerConnectionArray.get(userId);
if(localStream === null) {
//send fake signal //send fake signal
if(!this.PeerConnectionArray.has(userId)){ if(peer === undefined){
return; return;
} }
this.PeerConnectionArray.get(userId).write(new Buffer(JSON.stringify({ peer.write(new Buffer(JSON.stringify({
type: "stream", type: "stream",
stream: null stream: null
}))); })));
return; return;
} }
this.MediaManager.localStream.getTracks().forEach( if (peer === undefined) {
transceiver = (track: MediaStreamTrack) => this.PeerConnectionArray.get(userId).addTrack(track, this.MediaManager.localStream) throw new Error('While adding media, cannot find user with ID '+userId);
) }
for (const track of localStream.getTracks()) {
peer.addTrack(track, localStream);
}
}catch (e) { }catch (e) {
console.error(`addMedia => addMedia => ${userId}`, e); console.error(`addMedia => addMedia => ${userId}`, e);
} }
} }
updatedLocalStream(){ updatedLocalStream(){
this.Users.forEach((user: UserSimplePear) => { this.Users.forEach((user: UserSimplePeer) => {
this.addMedia(user.userId); this.addMedia(user.userId);
}) })
} }

View file

@ -7,6 +7,18 @@
"module": "CommonJS", "module": "CommonJS",
"target": "es5", "target": "es5",
"jsx": "react", "jsx": "react",
"allowJs": true "allowJs": true,
"strict": false, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
"strictFunctionTypes": true, /* Enable strict checking of function types. */
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */
} }
} }