Merge from master
This commit is contained in:
commit
b260dc32b5
@ -4,30 +4,33 @@ import * as http from "http";
|
|||||||
import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition"; //TODO fix import by "_Model/.."
|
import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition"; //TODO fix import by "_Model/.."
|
||||||
import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.."
|
import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.."
|
||||||
import Jwt, {JsonWebTokenError} from "jsonwebtoken";
|
import Jwt, {JsonWebTokenError} from "jsonwebtoken";
|
||||||
import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
import {SECRET_KEY, MINIMUM_DISTANCE, GROUP_RADIUS} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
||||||
import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom";
|
import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom";
|
||||||
import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface";
|
import {ExtRoomsInterface} from "../Model/Websocket/ExtRoomsInterface";
|
||||||
import {World} from "../Model/World";
|
import {World} from "../Model/World";
|
||||||
import { uuid } from 'uuidv4';
|
import {Group} from "_Model/Group";
|
||||||
|
|
||||||
enum SockerIoEvent {
|
enum SockerIoEvent {
|
||||||
CONNECTION = "connection",
|
CONNECTION = "connection",
|
||||||
DISCONNECTION = "disconnect",
|
DISCONNECT = "disconnect",
|
||||||
JOIN_ROOM = "join-room",
|
JOIN_ROOM = "join-room",
|
||||||
USER_POSITION = "user-position",
|
USER_POSITION = "user-position",
|
||||||
WEBRTC_SIGNAL = "webrtc-signal",
|
WEBRTC_SIGNAL = "webrtc-signal",
|
||||||
|
WEBRTC_OFFER = "webrtc-offer",
|
||||||
WEBRTC_START = "webrtc-start",
|
WEBRTC_START = "webrtc-start",
|
||||||
|
WEBRTC_DISCONNECT = "webrtc-disconect",
|
||||||
MESSAGE_ERROR = "message-error",
|
MESSAGE_ERROR = "message-error",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IoSocketController{
|
export class IoSocketController {
|
||||||
Io: socketIO.Server;
|
Io: socketIO.Server;
|
||||||
World: World;
|
World: World;
|
||||||
constructor(server : http.Server) {
|
|
||||||
|
constructor(server: http.Server) {
|
||||||
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) => {
|
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'));
|
||||||
}
|
}
|
||||||
@ -44,11 +47,11 @@ export class IoSocketController{
|
|||||||
this.shareUsersPosition();
|
this.shareUsersPosition();
|
||||||
|
|
||||||
//don't send only function because the context will be not this
|
//don't send only function because the context will be not this
|
||||||
this.World = new World((user1 : string, user2 : string) => {
|
this.World = new World((user1: string, group: Group) => {
|
||||||
this.connectedUser(user1, user2);
|
this.connectedUser(user1, group);
|
||||||
}, (user1 : string, user2 : string) => {
|
}, (user1: string, group: Group) => {
|
||||||
this.disConnectedUser(user1, user2);
|
this.disConnectedUser(user1, group);
|
||||||
});
|
}, MINIMUM_DISTANCE, GROUP_RADIUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
ioConnection() {
|
ioConnection() {
|
||||||
@ -61,9 +64,9 @@ export class IoSocketController{
|
|||||||
x: user x position on map
|
x: user x position on map
|
||||||
y: user y position on map
|
y: user y position on map
|
||||||
*/
|
*/
|
||||||
socket.on(SockerIoEvent.JOIN_ROOM, (message : string) => {
|
socket.on(SockerIoEvent.JOIN_ROOM, (message: string) => {
|
||||||
let messageUserPosition = this.hydrateMessageReceive(message);
|
let messageUserPosition = this.hydrateMessageReceive(message);
|
||||||
if(messageUserPosition instanceof Error){
|
if (messageUserPosition instanceof Error) {
|
||||||
return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message}))
|
return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,14 +80,12 @@ export class IoSocketController{
|
|||||||
this.saveUserInformation((socket as ExSocketInterface), messageUserPosition);
|
this.saveUserInformation((socket as ExSocketInterface), messageUserPosition);
|
||||||
|
|
||||||
//add function to refresh position user in real time.
|
//add function to refresh position user in real time.
|
||||||
let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface);
|
this.refreshUserPosition();
|
||||||
rooms.refreshUserPosition = RefreshUserPositionFunction;
|
|
||||||
rooms.refreshUserPosition(rooms, this.Io);
|
|
||||||
|
|
||||||
socket.to(messageUserPosition.roomId).emit(SockerIoEvent.JOIN_ROOM, messageUserPosition.toString());
|
socket.to(messageUserPosition.roomId).emit(SockerIoEvent.JOIN_ROOM, messageUserPosition.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on(SockerIoEvent.USER_POSITION, (message : string) => {
|
socket.on(SockerIoEvent.USER_POSITION, (message: string) => {
|
||||||
let messageUserPosition = this.hydrateMessageReceive(message);
|
let messageUserPosition = this.hydrateMessageReceive(message);
|
||||||
if (messageUserPosition instanceof Error) {
|
if (messageUserPosition instanceof Error) {
|
||||||
return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message}));
|
return socket.emit(SockerIoEvent.MESSAGE_ERROR, JSON.stringify({message: messageUserPosition.message}));
|
||||||
@ -97,30 +98,39 @@ export class IoSocketController{
|
|||||||
this.saveUserInformation((socket as ExSocketInterface), messageUserPosition);
|
this.saveUserInformation((socket as ExSocketInterface), messageUserPosition);
|
||||||
|
|
||||||
//refresh position of all user in all rooms in real time
|
//refresh position of all user in all rooms in real time
|
||||||
let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface);
|
this.refreshUserPosition();
|
||||||
if(!rooms.refreshUserPosition){
|
|
||||||
rooms.refreshUserPosition = RefreshUserPositionFunction;
|
|
||||||
}
|
|
||||||
rooms.refreshUserPosition(rooms, this.Io);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message : string) => {
|
socket.on(SockerIoEvent.WEBRTC_SIGNAL, (message: string) => {
|
||||||
let data : any = JSON.parse(message);
|
let data: any = JSON.parse(message);
|
||||||
|
//send only at user
|
||||||
|
let client = this.searchClientById(data.receiverId);
|
||||||
|
if (!client) {
|
||||||
|
console.error("client doesn't exist for ", data.receiverId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return client.emit(SockerIoEvent.WEBRTC_SIGNAL, message);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on(SockerIoEvent.WEBRTC_OFFER, (message: string) => {
|
||||||
|
let data: any = JSON.parse(message);
|
||||||
|
|
||||||
//send only at user
|
//send only at user
|
||||||
let clients: Array<any> = Object.values(this.Io.sockets.sockets);
|
let client = this.searchClientById(data.receiverId);
|
||||||
for(let i = 0; i < clients.length; i++){
|
if (!client) {
|
||||||
let client : ExSocketInterface = clients[i];
|
console.error("client doesn't exist for ", data.receiverId);
|
||||||
if(client.userId !== data.receiverId){
|
return;
|
||||||
continue
|
|
||||||
}
|
|
||||||
client.emit(SockerIoEvent.WEBRTC_SIGNAL, message);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
client.emit(SockerIoEvent.WEBRTC_OFFER, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on(SockerIoEvent.DISCONNECTION, (reason : string) => {
|
socket.on(SockerIoEvent.DISCONNECT, () => {
|
||||||
let Client = (socket as ExSocketInterface);
|
let Client = (socket as ExSocketInterface);
|
||||||
|
this.sendDisconnectedEvent(Client);
|
||||||
|
|
||||||
|
//refresh position of all user in all rooms in real time
|
||||||
|
this.refreshUserPosition();
|
||||||
|
|
||||||
//leave group of user
|
//leave group of user
|
||||||
this.World.leave(Client);
|
this.World.leave(Client);
|
||||||
|
|
||||||
@ -138,13 +148,39 @@ export class IoSocketController{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
|
searchClientById(userId: string): ExSocketInterface | null {
|
||||||
|
let clients: Array<any> = Object.values(this.Io.sockets.sockets);
|
||||||
|
for (let i = 0; i < clients.length; i++) {
|
||||||
|
let client: ExSocketInterface = clients[i];
|
||||||
|
if (client.userId !== userId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param Client: ExSocketInterface
|
||||||
|
*/
|
||||||
|
sendDisconnectedEvent(Client: ExSocketInterface) {
|
||||||
|
Client.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({
|
||||||
|
userId: Client.userId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param socket
|
* @param socket
|
||||||
* @param roomId
|
* @param roomId
|
||||||
*/
|
*/
|
||||||
joinWebRtcRoom(socket : ExSocketInterface, roomId : string) {
|
joinWebRtcRoom(socket: ExSocketInterface, roomId: string) {
|
||||||
if(socket.webRtcRoomId === roomId){
|
if (socket.webRtcRoomId === roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
socket.join(roomId);
|
socket.join(roomId);
|
||||||
@ -175,17 +211,26 @@ export class IoSocketController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//permit to save user position in socket
|
//permit to save user position in socket
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshUserPosition() {
|
||||||
|
//refresh position of all user in all rooms in real time
|
||||||
|
let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface);
|
||||||
|
if (!rooms.refreshUserPosition) {
|
||||||
|
rooms.refreshUserPosition = RefreshUserPositionFunction;
|
||||||
|
}
|
||||||
|
rooms.refreshUserPosition(rooms, this.Io);
|
||||||
|
}
|
||||||
|
|
||||||
//Hydrate and manage error
|
//Hydrate and manage error
|
||||||
hydrateMessageReceive(message : string) : MessageUserPosition | Error{
|
hydrateMessageReceive(message: string): MessageUserPosition | Error {
|
||||||
try {
|
try {
|
||||||
return new MessageUserPosition(JSON.parse(message));
|
return new MessageUserPosition(JSON.parse(message));
|
||||||
}catch (err) {
|
} catch (err) {
|
||||||
//TODO log error
|
//TODO log error
|
||||||
return new Error(err);
|
return new Error(err);
|
||||||
}
|
}
|
||||||
@ -207,22 +252,23 @@ export class IoSocketController{
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
**/
|
**/
|
||||||
seTimeOutInProgress : any = null;
|
seTimeOutInProgress: any = null;
|
||||||
shareUsersPosition(){
|
|
||||||
if(this.seTimeOutInProgress){
|
shareUsersPosition() {
|
||||||
|
if (this.seTimeOutInProgress) {
|
||||||
clearTimeout(this.seTimeOutInProgress);
|
clearTimeout(this.seTimeOutInProgress);
|
||||||
}
|
}
|
||||||
//send for each room, all data of position user
|
//send for each room, all data of position user
|
||||||
let arrayMap = (this.Io.sockets.adapter.rooms as ExtRooms).userPositionMapByRoom;
|
let arrayMap = (this.Io.sockets.adapter.rooms as ExtRooms).userPositionMapByRoom;
|
||||||
if(!arrayMap){
|
if (!arrayMap) {
|
||||||
this.seTimeOutInProgress = setTimeout(() => {
|
this.seTimeOutInProgress = setTimeout(() => {
|
||||||
this.shareUsersPosition();
|
this.shareUsersPosition();
|
||||||
}, 10);
|
}, 10);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
arrayMap.forEach((value : any) => {
|
arrayMap.forEach((value: any) => {
|
||||||
let roomId = value[0];
|
let roomId = value[0];
|
||||||
this.Io.in(roomId).emit('user-position', JSON.stringify(arrayMap));
|
this.Io.in(roomId).emit(SockerIoEvent.USER_POSITION, JSON.stringify(arrayMap));
|
||||||
});
|
});
|
||||||
this.seTimeOutInProgress = setTimeout(() => {
|
this.seTimeOutInProgress = setTimeout(() => {
|
||||||
this.shareUsersPosition();
|
this.shareUsersPosition();
|
||||||
@ -230,24 +276,20 @@ export class IoSocketController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//connected user
|
//connected user
|
||||||
connectedUser(user1 : string, user2 : string){
|
connectedUser(userId: string, group: Group) {
|
||||||
/* TODO manager room and group user to enter and leave */
|
let Client = this.searchClientById(userId);
|
||||||
let roomId = uuid();
|
if (!Client) {
|
||||||
let clients : Array<any> = Object.values(this.Io.sockets.sockets);
|
return;
|
||||||
let User1 = clients.find((user : ExSocketInterface) => user.userId === user1);
|
|
||||||
let User2 = clients.find((user : ExSocketInterface) => user.userId === user2);
|
|
||||||
|
|
||||||
if(User1) {
|
|
||||||
this.joinWebRtcRoom(User1, roomId);
|
|
||||||
}
|
|
||||||
if(User2) {
|
|
||||||
this.joinWebRtcRoom(User2, roomId);
|
|
||||||
}
|
}
|
||||||
|
this.joinWebRtcRoom(Client, group.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
//connected user
|
//connected user
|
||||||
disConnectedUser(user1 : string, user2 : string){
|
disConnectedUser(userId: string, group: Group) {
|
||||||
console.log("disConnectedUser => user1", user1);
|
let Client = this.searchClientById(userId);
|
||||||
console.log("disConnectedUser => user2", user2);
|
if (!Client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sendDisconnectedEvent(Client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
||||||
const ROOM = process.env.ROOM || "THECODINGMACHINE";
|
const ROOM = process.env.ROOM || "THECODINGMACHINE";
|
||||||
|
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
||||||
|
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SECRET_KEY,
|
SECRET_KEY,
|
||||||
ROOM
|
ROOM,
|
||||||
|
MINIMUM_DISTANCE,
|
||||||
|
GROUP_RADIUS
|
||||||
}
|
}
|
@ -1,19 +1,22 @@
|
|||||||
import { World } from "./World";
|
import { World, ConnectCallback, DisconnectCallback } from "./World";
|
||||||
import { UserInterface } from "./UserInterface";
|
import { UserInterface } from "./UserInterface";
|
||||||
import {PositionInterface} from "_Model/PositionInterface";
|
import {PositionInterface} from "_Model/PositionInterface";
|
||||||
|
import {uuid} from "uuidv4";
|
||||||
|
|
||||||
export class Group {
|
export class Group {
|
||||||
static readonly MAX_PER_GROUP = 4;
|
static readonly MAX_PER_GROUP = 4;
|
||||||
|
|
||||||
|
private id: string;
|
||||||
private users: UserInterface[];
|
private users: UserInterface[];
|
||||||
private connectCallback: (user1: string, user2: string) => void;
|
private connectCallback: ConnectCallback;
|
||||||
private disconnectCallback: (user1: string, user2: string) => void;
|
private disconnectCallback: DisconnectCallback;
|
||||||
|
|
||||||
|
|
||||||
constructor(users: UserInterface[], connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) {
|
constructor(users: UserInterface[], connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback) {
|
||||||
this.users = [];
|
this.users = [];
|
||||||
this.connectCallback = connectCallback;
|
this.connectCallback = connectCallback;
|
||||||
this.disconnectCallback = disconnectCallback;
|
this.disconnectCallback = disconnectCallback;
|
||||||
|
this.id = uuid();
|
||||||
|
|
||||||
users.forEach((user: UserInterface) => {
|
users.forEach((user: UserInterface) => {
|
||||||
this.join(user);
|
this.join(user);
|
||||||
@ -24,6 +27,10 @@ export class Group {
|
|||||||
return this.users;
|
return this.users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getId() : string{
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the barycenter of all users (i.e. the center of the group)
|
* Returns the barycenter of all users (i.e. the center of the group)
|
||||||
*/
|
*/
|
||||||
@ -54,9 +61,7 @@ export class Group {
|
|||||||
join(user: UserInterface): void
|
join(user: UserInterface): void
|
||||||
{
|
{
|
||||||
// Broadcast on the right event
|
// Broadcast on the right event
|
||||||
this.users.forEach((groupUser: UserInterface) => {
|
this.connectCallback(user.id, this);
|
||||||
this.connectCallback(user.id, groupUser.id);
|
|
||||||
});
|
|
||||||
this.users.push(user);
|
this.users.push(user);
|
||||||
user.group = this;
|
user.group = this;
|
||||||
}
|
}
|
||||||
@ -66,23 +71,6 @@ export class Group {
|
|||||||
return this.users.indexOf(user) !== -1;
|
return this.users.indexOf(user) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
isStillIn(user: UserInterface): boolean
|
|
||||||
{
|
|
||||||
if(!this.isPartOfGroup(user)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let stillIn = true;
|
|
||||||
for(let i = 0; i <= this.users.length; i++) {
|
|
||||||
let userInGroup = this.users[i];
|
|
||||||
let distance = World.computeDistance(user, userInGroup);
|
|
||||||
if(distance > World.MIN_DISTANCE) {
|
|
||||||
stillIn = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stillIn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*removeFromGroup(users: UserInterface[]): void
|
/*removeFromGroup(users: UserInterface[]): void
|
||||||
{
|
{
|
||||||
for(let i = 0; i < users.length; i++){
|
for(let i = 0; i < users.length; i++){
|
||||||
@ -105,9 +93,7 @@ export class Group {
|
|||||||
user.group = undefined;
|
user.group = undefined;
|
||||||
|
|
||||||
// Broadcast on the right event
|
// Broadcast on the right event
|
||||||
this.users.forEach((groupUser: UserInterface) => {
|
this.disconnectCallback(user.id, this);
|
||||||
this.disconnectCallback(user.id, groupUser.id);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,22 +6,31 @@ 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";
|
||||||
|
|
||||||
|
export type ConnectCallback = (user: string, group: Group) => void;
|
||||||
|
export type DisconnectCallback = (user: string, group: Group) => void;
|
||||||
|
|
||||||
export class World {
|
export class World {
|
||||||
static readonly MIN_DISTANCE = 160;
|
private minDistance: number;
|
||||||
|
private groupRadius: number;
|
||||||
|
|
||||||
// Users, sorted by ID
|
// Users, sorted by ID
|
||||||
private users: Map<string, UserInterface>;
|
private users: Map<string, UserInterface>;
|
||||||
private groups: Group[];
|
private groups: Group[];
|
||||||
|
|
||||||
private connectCallback: (user1: string, user2: string) => void;
|
private connectCallback: ConnectCallback;
|
||||||
private disconnectCallback: (user1: string, user2: string) => void;
|
private disconnectCallback: DisconnectCallback;
|
||||||
|
|
||||||
constructor(connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void)
|
constructor(connectCallback: ConnectCallback,
|
||||||
|
disconnectCallback: DisconnectCallback,
|
||||||
|
minDistance: number,
|
||||||
|
groupRadius: number)
|
||||||
{
|
{
|
||||||
this.users = new Map<string, UserInterface>();
|
this.users = new Map<string, UserInterface>();
|
||||||
this.groups = [];
|
this.groups = [];
|
||||||
this.connectCallback = connectCallback;
|
this.connectCallback = connectCallback;
|
||||||
this.disconnectCallback = disconnectCallback;
|
this.disconnectCallback = disconnectCallback;
|
||||||
|
this.minDistance = minDistance;
|
||||||
|
this.groupRadius = groupRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
public join(userPosition: MessageUserPosition): void {
|
public join(userPosition: MessageUserPosition): void {
|
||||||
@ -73,7 +82,7 @@ export class World {
|
|||||||
// If the user is part of a group:
|
// If the user is part of a group:
|
||||||
// should he leave the group?
|
// should he leave the group?
|
||||||
let distance = World.computeDistanceBetweenPositions(user.position, user.group.getPosition());
|
let distance = World.computeDistanceBetweenPositions(user.position, user.group.getPosition());
|
||||||
if (distance > World.MIN_DISTANCE) {
|
if (distance > this.groupRadius) {
|
||||||
this.leaveGroup(user);
|
this.leaveGroup(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,15 +112,16 @@ export class World {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks for the closest user that is:
|
* Looks for the closest user that is:
|
||||||
* - close enough (distance <= MIN_DISTANCE)
|
* - close enough (distance <= minDistance)
|
||||||
* - not in a group OR in a group that is not full
|
* - not in a group
|
||||||
|
* OR
|
||||||
|
* - close enough to a group (distance <= groupRadius)
|
||||||
*/
|
*/
|
||||||
private searchClosestAvailableUserOrGroup(user: UserInterface): UserInterface|Group|null
|
private searchClosestAvailableUserOrGroup(user: UserInterface): UserInterface|Group|null
|
||||||
{
|
{
|
||||||
let usersToBeGroupedWith: Distance[] = [];
|
let minimumDistanceFound: number = Math.max(this.minDistance, this.groupRadius);
|
||||||
let minimumDistanceFound: number = World.MIN_DISTANCE;
|
|
||||||
let matchingItem: UserInterface | Group | null = null;
|
let matchingItem: UserInterface | Group | null = null;
|
||||||
this.users.forEach(function(currentUser, userId) {
|
this.users.forEach((currentUser, userId) => {
|
||||||
// Let's only check users that are not part of a group
|
// Let's only check users that are not part of a group
|
||||||
if (typeof currentUser.group !== 'undefined') {
|
if (typeof currentUser.group !== 'undefined') {
|
||||||
return;
|
return;
|
||||||
@ -122,7 +132,7 @@ export class World {
|
|||||||
|
|
||||||
let distance = World.computeDistance(user, currentUser); // compute distance between peers.
|
let distance = World.computeDistance(user, currentUser); // compute distance between peers.
|
||||||
|
|
||||||
if(distance <= minimumDistanceFound) {
|
if(distance <= minimumDistanceFound && distance <= this.minDistance) {
|
||||||
minimumDistanceFound = distance;
|
minimumDistanceFound = distance;
|
||||||
matchingItem = currentUser;
|
matchingItem = currentUser;
|
||||||
}
|
}
|
||||||
@ -162,12 +172,12 @@ export class World {
|
|||||||
*/
|
*/
|
||||||
});
|
});
|
||||||
|
|
||||||
this.groups.forEach(function(group: Group) {
|
this.groups.forEach((group: Group) => {
|
||||||
if (group.isFull()) {
|
if (group.isFull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let distance = World.computeDistanceBetweenPositions(user.position, group.getPosition());
|
let distance = World.computeDistanceBetweenPositions(user.position, group.getPosition());
|
||||||
if(distance <= minimumDistanceFound) {
|
if(distance <= minimumDistanceFound && distance <= this.groupRadius) {
|
||||||
minimumDistanceFound = distance;
|
minimumDistanceFound = distance;
|
||||||
matchingItem = group;
|
matchingItem = group;
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import "jasmine";
|
import "jasmine";
|
||||||
import {Message} from "../src/Model/Websocket/Message";
|
import {Message} from "../src/Model/Websocket/Message";
|
||||||
import {World} from "../src/Model/World";
|
import {World, ConnectCallback, DisconnectCallback } from "../src/Model/World";
|
||||||
import {MessageUserPosition, Point} from "../src/Model/Websocket/MessageUserPosition";
|
import {MessageUserPosition, Point} from "../src/Model/Websocket/MessageUserPosition";
|
||||||
import { Group } from "../src/Model/Group";
|
import { Group } from "../src/Model/Group";
|
||||||
import {Distance} from "../src/Model//Distance";
|
import {Distance} from "../src/Model//Distance";
|
||||||
|
|
||||||
describe("World", () => {
|
describe("World", () => {
|
||||||
it("should connect user1 and user2", () => {
|
it("should connect user1 and user2", () => {
|
||||||
let connectCalled: boolean = false;
|
let connectCalledNumber: number = 0;
|
||||||
let connect = (user1: string, user2: string): void => {
|
let connect = (user: string, group: Group): void => {
|
||||||
connectCalled = true;
|
connectCalledNumber++;
|
||||||
}
|
}
|
||||||
let disconnect = (user1: string, user2: string): void => {
|
let disconnect = (user: string, group: Group): void => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let world = new World(connect, disconnect);
|
let world = new World(connect, disconnect, 160, 160);
|
||||||
|
|
||||||
world.join(new MessageUserPosition({
|
world.join(new MessageUserPosition({
|
||||||
userId: "foo",
|
userId: "foo",
|
||||||
@ -35,7 +35,7 @@ describe("World", () => {
|
|||||||
position: new Point(261, 100)
|
position: new Point(261, 100)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
expect(connectCalled).toBe(false);
|
expect(connectCalledNumber).toBe(0);
|
||||||
|
|
||||||
world.updatePosition(new MessageUserPosition({
|
world.updatePosition(new MessageUserPosition({
|
||||||
userId: "bar",
|
userId: "bar",
|
||||||
@ -43,27 +43,26 @@ describe("World", () => {
|
|||||||
position: new Point(101, 100)
|
position: new Point(101, 100)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
expect(connectCalled).toBe(true);
|
expect(connectCalledNumber).toBe(2);
|
||||||
|
|
||||||
connectCalled = false;
|
|
||||||
world.updatePosition(new MessageUserPosition({
|
world.updatePosition(new MessageUserPosition({
|
||||||
userId: "bar",
|
userId: "bar",
|
||||||
roomId: 1,
|
roomId: 1,
|
||||||
position: new Point(102, 100)
|
position: new Point(102, 100)
|
||||||
}));
|
}));
|
||||||
expect(connectCalled).toBe(false);
|
expect(connectCalledNumber).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should connect 3 users", () => {
|
it("should connect 3 users", () => {
|
||||||
let connectCalled: boolean = false;
|
let connectCalled: boolean = false;
|
||||||
let connect = (user1: string, user2: string): void => {
|
let connect = (user: string, group: Group): void => {
|
||||||
connectCalled = true;
|
connectCalled = true;
|
||||||
}
|
}
|
||||||
let disconnect = (user1: string, user2: string): void => {
|
let disconnect = (user: string, group: Group): void => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let world = new World(connect, disconnect);
|
let world = new World(connect, disconnect, 160, 160);
|
||||||
|
|
||||||
world.join(new MessageUserPosition({
|
world.join(new MessageUserPosition({
|
||||||
userId: "foo",
|
userId: "foo",
|
||||||
@ -100,15 +99,15 @@ describe("World", () => {
|
|||||||
|
|
||||||
it("should disconnect user1 and user2", () => {
|
it("should disconnect user1 and user2", () => {
|
||||||
let connectCalled: boolean = false;
|
let connectCalled: boolean = false;
|
||||||
let disconnectCalled: boolean = false;
|
let disconnectCallNumber: number = 0;
|
||||||
let connect = (user1: string, user2: string): void => {
|
let connect = (user: string, group: Group): void => {
|
||||||
connectCalled = true;
|
connectCalled = true;
|
||||||
}
|
}
|
||||||
let disconnect = (user1: string, user2: string): void => {
|
let disconnect = (user: string, group: Group): void => {
|
||||||
disconnectCalled = true;
|
disconnectCallNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
let world = new World(connect, disconnect);
|
let world = new World(connect, disconnect, 160, 160);
|
||||||
|
|
||||||
world.join(new MessageUserPosition({
|
world.join(new MessageUserPosition({
|
||||||
userId: "foo",
|
userId: "foo",
|
||||||
@ -123,7 +122,7 @@ describe("World", () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
expect(connectCalled).toBe(true);
|
expect(connectCalled).toBe(true);
|
||||||
expect(disconnectCalled).toBe(false);
|
expect(disconnectCallNumber).toBe(0);
|
||||||
|
|
||||||
world.updatePosition(new MessageUserPosition({
|
world.updatePosition(new MessageUserPosition({
|
||||||
userId: "bar",
|
userId: "bar",
|
||||||
@ -131,62 +130,14 @@ describe("World", () => {
|
|||||||
position: new Point(100+160+160+1, 100)
|
position: new Point(100+160+160+1, 100)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
expect(disconnectCalled).toBe(true);
|
expect(disconnectCallNumber).toBe(2);
|
||||||
|
|
||||||
disconnectCalled = false;
|
|
||||||
world.updatePosition(new MessageUserPosition({
|
world.updatePosition(new MessageUserPosition({
|
||||||
userId: "bar",
|
userId: "bar",
|
||||||
roomId: 1,
|
roomId: 1,
|
||||||
position: new Point(262, 100)
|
position: new Point(262, 100)
|
||||||
}));
|
}));
|
||||||
expect(disconnectCalled).toBe(false);
|
expect(disconnectCallNumber).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
it('Should return the distances between all users', () => {
|
|
||||||
let connectCalled: boolean = false;
|
|
||||||
let connect = (user1: string, user2: string): void => {
|
|
||||||
connectCalled = true;
|
|
||||||
}
|
|
||||||
let disconnect = (user1: string, user2: string): void => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let world = new World(connect, disconnect);
|
|
||||||
let user1 = new MessageUserPosition({
|
|
||||||
userId: "foo",
|
|
||||||
roomId: 1,
|
|
||||||
position: new Point(100, 100)
|
|
||||||
});
|
|
||||||
|
|
||||||
world.join(user1);
|
|
||||||
|
|
||||||
let user2 = new MessageUserPosition({
|
|
||||||
userId: "bar",
|
|
||||||
roomId: 1,
|
|
||||||
position: new Point(500, 100)
|
|
||||||
});
|
|
||||||
world.join(user2);
|
|
||||||
|
|
||||||
let user3 = new MessageUserPosition({
|
|
||||||
userId: "baz",
|
|
||||||
roomId: 1,
|
|
||||||
position: new Point(101, 100)
|
|
||||||
});
|
|
||||||
|
|
||||||
let user4 = new MessageUserPosition({
|
|
||||||
userId: "buz",
|
|
||||||
roomId: 1,
|
|
||||||
position: new Point(105, 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
let group = new Group([user1, user2, user3, user4]);
|
|
||||||
|
|
||||||
let distances = world.getDistancesBetweenGroupUsers(group)
|
|
||||||
|
|
||||||
console.log(distances);
|
|
||||||
|
|
||||||
//expect(distances).toBe([]);
|
|
||||||
})
|
|
||||||
**/
|
|
||||||
})
|
})
|
||||||
|
13
front/dist/index.html
vendored
13
front/dist/index.html
vendored
@ -12,11 +12,9 @@
|
|||||||
<script src="bundle.js"></script>
|
<script src="bundle.js"></script>
|
||||||
<div id="webRtc" class="webrtc">
|
<div id="webRtc" class="webrtc">
|
||||||
<div id="activeCam" class="activeCam">
|
<div id="activeCam" class="activeCam">
|
||||||
|
<video id="myCamVideo" autoplay muted></video>
|
||||||
</div>
|
</div>
|
||||||
<div id="myCam" class="myCam">
|
<div class="btn-cam-action">
|
||||||
<video id="myCamVideo" autoplay></video>
|
|
||||||
</div>
|
|
||||||
<div class="btn-cam-action active">
|
|
||||||
<div class="btn-micro">
|
<div class="btn-micro">
|
||||||
<img id="microphone" src="resources/logos/microphone.svg">
|
<img id="microphone" src="resources/logos/microphone.svg">
|
||||||
<img id="microphone-close" src="resources/logos/microphone-close.svg">
|
<img id="microphone-close" src="resources/logos/microphone-close.svg">
|
||||||
@ -25,13 +23,10 @@
|
|||||||
<img id="cinema" src="resources/logos/cinema.svg">
|
<img id="cinema" src="resources/logos/cinema.svg">
|
||||||
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-call">
|
<!--<div class="btn-call">
|
||||||
<img src="resources/logos/phone.svg">
|
<img src="resources/logos/phone.svg">
|
||||||
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div id="phone-open" class="phone-open">
|
|
||||||
<img src="resources/logos/phone-open.svg">
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
144
front/dist/resources/style/style.css
vendored
144
front/dist/resources/style/style.css
vendored
@ -1,77 +1,50 @@
|
|||||||
|
video{
|
||||||
|
-webkit-transform: scaleX(-1);
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
.webrtc{
|
.webrtc{
|
||||||
display: none;
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
height: 100%;
|
||||||
|
width: 300px;
|
||||||
}
|
}
|
||||||
.webrtc.active{
|
.webrtc.active{
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.webrtc, .activeCam{
|
.webrtc, .activeCam{}
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
.activeCam video{
|
.activeCam video{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
height: 25%;
|
||||||
height: 100%;
|
top: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
right: -100px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.webrtc:hover .activeCam video{
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
.activeCam video#myCamVideo{
|
||||||
|
width: 200px;
|
||||||
|
height: 113px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*CSS size for 2 - 3 elements*/
|
/*CSS size for 2 - 3 elements*/
|
||||||
video:nth-child(1):nth-last-child(3),
|
.activeCam video:nth-child(1){
|
||||||
video:nth-child(2):nth-last-child(2),
|
/*this is for camera of user*/
|
||||||
video:nth-child(3):nth-last-child(1),
|
top: 75%;
|
||||||
video:nth-child(1):nth-last-child(2),
|
|
||||||
video:nth-child(2):nth-last-child(1){
|
|
||||||
width: 50%;
|
|
||||||
}
|
}
|
||||||
video:nth-child(1):nth-last-child(3),
|
.activeCam video:nth-child(2){
|
||||||
video:nth-child(2):nth-last-child(2),
|
top: 0%;
|
||||||
video:nth-child(3):nth-last-child(1){
|
|
||||||
height: 50%;
|
|
||||||
}
|
}
|
||||||
|
.activeCam video:nth-child(3){
|
||||||
/*CSS position for 2 elements*/
|
top: 25%;
|
||||||
video:nth-child(1):nth-last-child(2){
|
|
||||||
left: 0;
|
|
||||||
}
|
}
|
||||||
video:nth-child(2):nth-last-child(1){
|
.activeCam video:nth-child(4) {
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*CSS position for 3 elements*/
|
|
||||||
video:nth-child(1):nth-last-child(3){
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
video:nth-child(2):nth-last-child(2){
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
video:nth-child(3):nth-last-child(1) {
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 25%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.myCam{
|
/*btn animation*/
|
||||||
height: 200px;
|
|
||||||
width: 300px;
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
background: black;
|
|
||||||
border: none;
|
|
||||||
bottom: 20px;
|
|
||||||
max-height: 17%;
|
|
||||||
max-width: 17%;
|
|
||||||
opacity: 1;
|
|
||||||
display: block;
|
|
||||||
transition: opacity 1s;
|
|
||||||
}
|
|
||||||
.myCam video{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.btn-cam-action div{
|
.btn-cam-action div{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -79,14 +52,14 @@ video:nth-child(3):nth-last-child(1) {
|
|||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
background: #666;
|
background: #666;
|
||||||
left: 6vw;
|
|
||||||
box-shadow: 2px 2px 24px #444;
|
box-shadow: 2px 2px 24px #444;
|
||||||
border-radius: 48px;
|
border-radius: 48px;
|
||||||
transform: translateX(calc(-6vw - 96px));
|
transform: translateY(12vw);
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
|
bottom: 20px;
|
||||||
}
|
}
|
||||||
.webrtc:hover .btn-cam-action.active div{
|
.webrtc:hover .btn-cam-action div{
|
||||||
transform: translateX(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
.btn-cam-action div:hover{
|
.btn-cam-action div:hover{
|
||||||
background: #407cf7;
|
background: #407cf7;
|
||||||
@ -94,17 +67,17 @@ video:nth-child(3):nth-last-child(1) {
|
|||||||
transition: 280ms;
|
transition: 280ms;
|
||||||
}
|
}
|
||||||
.btn-micro{
|
.btn-micro{
|
||||||
bottom: 277px;
|
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
|
right: 10px;
|
||||||
}
|
}
|
||||||
.btn-video{
|
.btn-video{
|
||||||
bottom: 177px;
|
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
|
right: 114px;
|
||||||
}
|
}
|
||||||
.btn-call{
|
/*.btn-call{
|
||||||
bottom: 77px;
|
|
||||||
transition: all .1s;
|
transition: all .1s;
|
||||||
}
|
left: 0px;
|
||||||
|
}*/
|
||||||
.btn-cam-action div img{
|
.btn-cam-action div img{
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
@ -112,42 +85,3 @@ video:nth-child(3):nth-last-child(1) {
|
|||||||
left: calc(48px - 35px);
|
left: calc(48px - 35px);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.phone-open{
|
|
||||||
position: absolute;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
left: calc(50% - 70px);
|
|
||||||
padding: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
box-shadow: 2px 2px 24px #444;
|
|
||||||
background-color: green;
|
|
||||||
opacity: 0;
|
|
||||||
transition: all .4s ease-in-out;
|
|
||||||
}
|
|
||||||
.phone-open.active{
|
|
||||||
opacity: 1;
|
|
||||||
animation-name: phone-move;
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
}
|
|
||||||
.phone-open:hover{
|
|
||||||
animation: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes phone-move {
|
|
||||||
0% {
|
|
||||||
left: calc(50% - 70px);
|
|
||||||
bottom: 20px;
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
left: calc(50% - 65px);
|
|
||||||
bottom: 15px;
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
left: calc(50% - 75px);
|
|
||||||
bottom: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,9 +7,11 @@ import {API_URL, ROOM} from "./Enum/EnvironmentVariable";
|
|||||||
enum EventMessage{
|
enum EventMessage{
|
||||||
WEBRTC_SIGNAL = "webrtc-signal",
|
WEBRTC_SIGNAL = "webrtc-signal",
|
||||||
WEBRTC_START = "webrtc-start",
|
WEBRTC_START = "webrtc-start",
|
||||||
|
WEBRTC_JOIN_ROOM = "webrtc-join-room",
|
||||||
JOIN_ROOM = "join-room",
|
JOIN_ROOM = "join-room",
|
||||||
USER_POSITION = "user-position",
|
USER_POSITION = "user-position",
|
||||||
MESSAGE_ERROR = "message-error"
|
MESSAGE_ERROR = "message-error",
|
||||||
|
WEBRTC_DISCONNECT = "webrtc-disconect"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
@ -131,6 +133,8 @@ export interface ConnexionInterface {
|
|||||||
receiveWebrtcSignal(callBack: Function): void;
|
receiveWebrtcSignal(callBack: Function): void;
|
||||||
|
|
||||||
receiveWebrtcStart(callBack: Function): void;
|
receiveWebrtcStart(callBack: Function): void;
|
||||||
|
|
||||||
|
disconnectMessage(callBack: Function): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Connexion implements ConnexionInterface {
|
export class Connexion implements ConnexionInterface {
|
||||||
@ -227,7 +231,7 @@ export class Connexion implements ConnexionInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) {
|
sendWebrtcSignal(signal: any, roomId: string, userId? : string, receiverId? : string) {
|
||||||
this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({
|
return this.socket.emit(EventMessage.WEBRTC_SIGNAL, JSON.stringify({
|
||||||
userId: userId ? userId : this.userId,
|
userId: userId ? userId : this.userId,
|
||||||
receiverId: receiverId ? receiverId : this.userId,
|
receiverId: receiverId ? receiverId : this.userId,
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
@ -240,7 +244,7 @@ export class Connexion implements ConnexionInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
receiveWebrtcSignal(callback: Function) {
|
receiveWebrtcSignal(callback: Function) {
|
||||||
this.socket.on(EventMessage.WEBRTC_SIGNAL, callback);
|
return this.socket.on(EventMessage.WEBRTC_SIGNAL, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage(): void {
|
errorMessage(): void {
|
||||||
@ -248,4 +252,8 @@ export class Connexion implements ConnexionInterface {
|
|||||||
console.error(EventMessage.MESSAGE_ERROR, message);
|
console.error(EventMessage.MESSAGE_ERROR, message);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disconnectMessage(callback: Function): void {
|
||||||
|
this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback);
|
||||||
|
}
|
||||||
}
|
}
|
@ -26,8 +26,8 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{
|
|||||||
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
Layers : Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
||||||
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
Objects : Array<Phaser.Physics.Arcade.Sprite>;
|
||||||
map: ITiledMap;
|
map: ITiledMap;
|
||||||
startX = (window.innerWidth / 2) / RESOLUTION;
|
startX = 704;// 22 case
|
||||||
startY = (window.innerHeight / 2) / RESOLUTION;
|
startY = 32; // 1 case
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -4,7 +4,6 @@ import {TextInput} from "../Components/TextInput";
|
|||||||
import {ClickButton} from "../Components/ClickButton";
|
import {ClickButton} from "../Components/ClickButton";
|
||||||
import {GameSceneName} from "../Game/GameScene";
|
import {GameSceneName} from "../Game/GameScene";
|
||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import Key = Phaser.Input.Keyboard.Key;
|
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
export const LoginSceneName = "LoginScene";
|
export const LoginSceneName = "LoginScene";
|
||||||
|
@ -26,6 +26,7 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G
|
|||||||
userId: string;
|
userId: string;
|
||||||
PlayerValue: string;
|
PlayerValue: string;
|
||||||
userInputManager: UserInputManager;
|
userInputManager: UserInputManager;
|
||||||
|
previousMove: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
userId: string,
|
userId: string,
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
const videoConstraint: {width : any, height: any, facingMode : string} = {
|
||||||
|
width: { ideal: 1280 },
|
||||||
|
height: { ideal: 720 },
|
||||||
|
facingMode: "user"
|
||||||
|
};
|
||||||
export class MediaManager {
|
export class MediaManager {
|
||||||
localStream: MediaStream;
|
localStream: MediaStream;
|
||||||
remoteVideo: Array<any> = new Array<any>();
|
remoteVideo: Array<any> = new Array<any>();
|
||||||
@ -6,13 +11,20 @@ export class MediaManager {
|
|||||||
cinema: any = null;
|
cinema: any = null;
|
||||||
microphoneClose: any = null;
|
microphoneClose: any = null;
|
||||||
microphone: any = null;
|
microphone: any = null;
|
||||||
constraintsMedia = {audio: false, video: true};
|
constraintsMedia : {audio : any, video : any} = {
|
||||||
|
audio: true,
|
||||||
|
video: videoConstraint
|
||||||
|
};
|
||||||
getCameraPromise : Promise<any> = null;
|
getCameraPromise : Promise<any> = null;
|
||||||
|
updatedLocalStreamCallBack : Function;
|
||||||
|
|
||||||
|
constructor(updatedLocalStreamCallBack : Function) {
|
||||||
|
this.updatedLocalStreamCallBack = updatedLocalStreamCallBack;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.myCamVideo = document.getElementById('myCamVideo');
|
this.myCamVideo = document.getElementById('myCamVideo');
|
||||||
this.microphoneClose = document.getElementById('microphone-close');
|
|
||||||
|
|
||||||
|
this.microphoneClose = document.getElementById('microphone-close');
|
||||||
|
this.microphoneClose.style.display = "none";
|
||||||
this.microphoneClose.addEventListener('click', (e: any) => {
|
this.microphoneClose.addEventListener('click', (e: any) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.enabledMicrophone();
|
this.enabledMicrophone();
|
||||||
@ -26,6 +38,7 @@ export class MediaManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.cinemaClose = document.getElementById('cinema-close');
|
this.cinemaClose = document.getElementById('cinema-close');
|
||||||
|
this.cinemaClose.style.display = "none";
|
||||||
this.cinemaClose.addEventListener('click', (e: any) => {
|
this.cinemaClose.addEventListener('click', (e: any) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.enabledCamera();
|
this.enabledCamera();
|
||||||
@ -37,9 +50,6 @@ export class MediaManager {
|
|||||||
this.disabledCamera();
|
this.disabledCamera();
|
||||||
//update tracking
|
//update tracking
|
||||||
});
|
});
|
||||||
|
|
||||||
this.enabledCamera();
|
|
||||||
this.enabledMicrophone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activeVisio(){
|
activeVisio(){
|
||||||
@ -50,9 +60,12 @@ export class MediaManager {
|
|||||||
enabledCamera() {
|
enabledCamera() {
|
||||||
this.cinemaClose.style.display = "none";
|
this.cinemaClose.style.display = "none";
|
||||||
this.cinema.style.display = "block";
|
this.cinema.style.display = "block";
|
||||||
this.constraintsMedia.video = true;
|
this.constraintsMedia.video = videoConstraint;
|
||||||
this.localStream = null;
|
this.localStream = null;
|
||||||
this.myCamVideo.srcObject = null;
|
this.myCamVideo.srcObject = null;
|
||||||
|
this.getCamera().then((stream) => {
|
||||||
|
this.updatedLocalStreamCallBack(stream);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disabledCamera() {
|
disabledCamera() {
|
||||||
@ -70,12 +83,18 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
this.localStream = null;
|
this.localStream = null;
|
||||||
this.myCamVideo.srcObject = null;
|
this.myCamVideo.srcObject = null;
|
||||||
|
this.getCamera().then((stream) => {
|
||||||
|
this.updatedLocalStreamCallBack(stream);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
enabledMicrophone() {
|
enabledMicrophone() {
|
||||||
this.microphoneClose.style.display = "none";
|
this.microphoneClose.style.display = "none";
|
||||||
this.microphone.style.display = "block";
|
this.microphone.style.display = "block";
|
||||||
this.constraintsMedia.audio = true;
|
this.constraintsMedia.audio = true;
|
||||||
|
this.getCamera().then((stream) => {
|
||||||
|
this.updatedLocalStreamCallBack(stream);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disabledMicrophone() {
|
disabledMicrophone() {
|
||||||
@ -89,18 +108,9 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
this.getCamera().then((stream) => {
|
||||||
|
this.updatedLocalStreamCallBack(stream);
|
||||||
getElementActivePhone(){
|
});
|
||||||
return document.getElementById('phone-open');
|
|
||||||
}
|
|
||||||
|
|
||||||
activePhoneOpen(){
|
|
||||||
return this.getElementActivePhone().classList.add("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
disablePhoneOpen(){
|
|
||||||
return this.getElementActivePhone().classList.remove("active");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//get camera
|
//get camera
|
||||||
@ -109,6 +119,13 @@ export class MediaManager {
|
|||||||
.then((stream: MediaStream) => {
|
.then((stream: MediaStream) => {
|
||||||
this.localStream = stream;
|
this.localStream = stream;
|
||||||
this.myCamVideo.srcObject = this.localStream;
|
this.myCamVideo.srcObject = this.localStream;
|
||||||
|
|
||||||
|
//TODO resize remote cam
|
||||||
|
/*console.log(this.localStream.getTracks());
|
||||||
|
let videoMediaStreamTrack = this.localStream.getTracks().find((media : MediaStreamTrack) => media.kind === "video");
|
||||||
|
let {width, height} = videoMediaStreamTrack.getSettings();
|
||||||
|
console.info(`${width}x${height}`); // 6*/
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -127,6 +144,15 @@ export class MediaManager {
|
|||||||
this.remoteVideo[(userId as any)] = document.getElementById(userId);
|
this.remoteVideo[(userId as any)] = document.getElementById(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
* @param stream
|
||||||
|
*/
|
||||||
|
addStreamRemoteVideo(userId : string, stream : MediaStream){
|
||||||
|
this.remoteVideo[(userId as any)].srcObject = stream;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param userId
|
* @param userId
|
||||||
|
@ -6,53 +6,51 @@ export interface SimplePeerInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SimplePeer {
|
export class SimplePeer {
|
||||||
Connexion: ConnexionInterface;
|
private Connexion: ConnexionInterface;
|
||||||
MediaManager: MediaManager;
|
private WebRtcRoomId: string;
|
||||||
WebRtcRoomId: string;
|
private Users: Array<any>;
|
||||||
Users: Array<any>;
|
|
||||||
|
|
||||||
PeerConnexionArray: Array<any> = new Array<any>();
|
private MediaManager: MediaManager;
|
||||||
|
|
||||||
|
private PeerConnexionArray: Array<any> = new Array<any>();
|
||||||
|
|
||||||
constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") {
|
constructor(Connexion: ConnexionInterface, WebRtcRoomId: string = "test-webrtc") {
|
||||||
this.Connexion = Connexion;
|
this.Connexion = Connexion;
|
||||||
this.WebRtcRoomId = WebRtcRoomId;
|
this.WebRtcRoomId = WebRtcRoomId;
|
||||||
this.MediaManager = new MediaManager();
|
this.MediaManager = new MediaManager((stream : MediaStream) => {
|
||||||
|
this.updatedLocalStream();
|
||||||
|
});
|
||||||
|
this.PeerConnexionArray = new Array<any>();
|
||||||
|
|
||||||
this.initialise();
|
this.initialise();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* permit to listen when user could start visio
|
* permit to listen when user could start visio
|
||||||
*/
|
*/
|
||||||
private initialise(){
|
private initialise() {
|
||||||
|
|
||||||
|
//receive signal by gemer
|
||||||
|
this.Connexion.receiveWebrtcSignal((message: string) => {
|
||||||
|
this.receiveWebrtcSignal(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.MediaManager.activeVisio();
|
||||||
|
this.MediaManager.getCamera().then(() => {
|
||||||
|
|
||||||
//receive message start
|
//receive message start
|
||||||
this.Connexion.receiveWebrtcStart((message: string) => {
|
this.Connexion.receiveWebrtcStart((message: string) => {
|
||||||
this.receiveWebrtcStart(message);
|
this.receiveWebrtcStart(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
//when button to call is clicked, start video
|
}).catch((err) => {
|
||||||
this.MediaManager.getElementActivePhone().addEventListener("click", () => {
|
console.error("err", err);
|
||||||
this.startWebRtc();
|
|
||||||
this.disablePhone();
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
/**
|
|
||||||
* server has two person connected, start the meet
|
|
||||||
*/
|
|
||||||
startWebRtc() {
|
|
||||||
this.MediaManager.activeVisio();
|
|
||||||
return this.MediaManager.getCamera().then((stream: MediaStream) => {
|
|
||||||
this.MediaManager.localStream = stream;
|
|
||||||
|
|
||||||
//create pear connexion
|
|
||||||
this.createPeerConnexion();
|
|
||||||
|
|
||||||
//receive signal by gemer
|
//receive signal by gemer
|
||||||
this.Connexion.receiveWebrtcSignal((message: string) => {
|
this.Connexion.disconnectMessage((message: string) => {
|
||||||
this.receiveWebrtcSignal(message);
|
let data = JSON.parse(message);
|
||||||
});
|
this.closeConnexion(data.userId);
|
||||||
}).catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,25 +58,57 @@ export class SimplePeer {
|
|||||||
*
|
*
|
||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
receiveWebrtcStart(message: string) {
|
private receiveWebrtcStart(message: string) {
|
||||||
let data = JSON.parse(message);
|
let data = JSON.parse(message);
|
||||||
this.WebRtcRoomId = data.roomId;
|
this.WebRtcRoomId = data.roomId;
|
||||||
this.Users = data.clients;
|
this.Users = data.clients;
|
||||||
|
|
||||||
//active button for player
|
//start connexion
|
||||||
this.activePhone();
|
this.startWebRtc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
createPeerConnexion() {
|
* server has two person connected, start the meet
|
||||||
|
*/
|
||||||
|
private startWebRtc() {
|
||||||
this.Users.forEach((user: any) => {
|
this.Users.forEach((user: any) => {
|
||||||
if(this.PeerConnexionArray[user.userId]){
|
//if it's not an initiator, peer connexion will be created when gamer will receive offer signal
|
||||||
|
if(!user.initiator){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.createPeerConnexion(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create peer connexion to bind users
|
||||||
|
*/
|
||||||
|
private createPeerConnexion(user : any) {
|
||||||
|
if(this.PeerConnexionArray[user.userId]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.MediaManager.removeActiveVideo(user.userId);
|
||||||
this.MediaManager.addActiveVideo(user.userId);
|
this.MediaManager.addActiveVideo(user.userId);
|
||||||
|
|
||||||
this.PeerConnexionArray[user.userId] = new Peer({initiator: user.initiator});
|
this.PeerConnexionArray[user.userId] = new Peer({
|
||||||
|
initiator: user.initiator ? user.initiator : false,
|
||||||
|
reconnectTimer: 10000,
|
||||||
|
config: {
|
||||||
|
iceServers: [
|
||||||
|
{
|
||||||
|
urls: 'stun:stun.l.google.com:19302'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urls: 'turn:numb.viagenie.ca',
|
||||||
|
username: 'g.parant@thecodingmachine.com',
|
||||||
|
credential: 'itcugcOHxle9Acqi$'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//start listen signal for the peer connexion
|
||||||
this.PeerConnexionArray[user.userId].on('signal', (data: any) => {
|
this.PeerConnexionArray[user.userId].on('signal', (data: any) => {
|
||||||
this.sendWebrtcSignal(data, user.userId);
|
this.sendWebrtcSignal(data, user.userId);
|
||||||
});
|
});
|
||||||
@ -87,19 +117,38 @@ export class SimplePeer {
|
|||||||
this.stream(user.userId, stream);
|
this.stream(user.userId, stream);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.PeerConnexionArray[user.userId].on('track', (track: MediaStreamTrack, stream: MediaStream) => {
|
||||||
|
this.stream(user.userId, stream);
|
||||||
|
});
|
||||||
|
|
||||||
this.PeerConnexionArray[user.userId].on('close', () => {
|
this.PeerConnexionArray[user.userId].on('close', () => {
|
||||||
this.closeConnexion(user.userId);
|
this.closeConnexion(user.userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addMedia(user.userId);
|
this.PeerConnexionArray[user.userId].on('error', (err: any) => {
|
||||||
|
console.error(`error => ${user.userId} => ${err.code}`, err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.PeerConnexionArray[user.userId].on('connect', () => {
|
||||||
|
console.info(`connect => ${user.userId}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addMedia(user.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeConnexion(userId : string){
|
private closeConnexion(userId : string) {
|
||||||
|
try {
|
||||||
|
this.MediaManager.removeActiveVideo(userId);
|
||||||
|
if (!this.PeerConnexionArray[(userId as any)]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.PeerConnexionArray[userId] = null;
|
this.PeerConnexionArray[(userId as any)].destroy();
|
||||||
this.MediaManager.removeActiveVideo(userId)
|
this.PeerConnexionArray[(userId as any)] = null;
|
||||||
|
delete this.PeerConnexionArray[(userId as any)];
|
||||||
|
} catch (err) {
|
||||||
|
console.error("closeConnexion", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,20 +156,29 @@ export class SimplePeer {
|
|||||||
* @param userId
|
* @param userId
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
sendWebrtcSignal(data: any, userId : string) {
|
private sendWebrtcSignal(data: any, userId : string) {
|
||||||
|
try {
|
||||||
this.Connexion.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId);
|
this.Connexion.sendWebrtcSignal(data, this.WebRtcRoomId, null, userId);
|
||||||
|
}catch (e) {
|
||||||
|
console.error(`sendWebrtcSignal => ${userId}`, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
receiveWebrtcSignal(message: string) {
|
private receiveWebrtcSignal(message: string) {
|
||||||
let data = JSON.parse(message);
|
let data = JSON.parse(message);
|
||||||
if(!this.PeerConnexionArray[data.userId]){
|
try {
|
||||||
return;
|
//if offer type, create peer connexion
|
||||||
|
if(data.signal.type === "offer"){
|
||||||
|
this.createPeerConnexion(data);
|
||||||
}
|
}
|
||||||
this.PeerConnexionArray[data.userId].signal(data.signal);
|
this.PeerConnexionArray[data.userId].signal(data.signal);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`receiveWebrtcSignal => ${data.userId}`, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,23 +186,28 @@ export class SimplePeer {
|
|||||||
* @param userId
|
* @param userId
|
||||||
* @param stream
|
* @param stream
|
||||||
*/
|
*/
|
||||||
stream(userId : any, stream: MediaStream) {
|
private stream(userId : any, stream: MediaStream) {
|
||||||
this.MediaManager.remoteVideo[userId].srcObject = stream;
|
this.MediaManager.addStreamRemoteVideo(userId, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param userId
|
* @param userId
|
||||||
*/
|
*/
|
||||||
addMedia (userId : any) {
|
private addMedia (userId : any = null) {
|
||||||
this.PeerConnexionArray[userId].addStream(this.MediaManager.localStream) // <- add streams to peer dynamically
|
try {
|
||||||
|
let transceiver : any = null;
|
||||||
|
this.MediaManager.localStream.getTracks().forEach(
|
||||||
|
transceiver = (track: MediaStreamTrack) => this.PeerConnexionArray[userId].addTrack(track, this.MediaManager.localStream)
|
||||||
|
)
|
||||||
|
}catch (e) {
|
||||||
|
console.error(`addMedia => addMedia => ${userId}`, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activePhone(){
|
updatedLocalStream(){
|
||||||
this.MediaManager.activePhoneOpen();
|
this.Users.forEach((user) => {
|
||||||
}
|
this.addMedia(user.userId);
|
||||||
|
})
|
||||||
disablePhone(){
|
|
||||||
this.MediaManager.disablePhoneOpen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user