Newer
Older
import DataChannel from "./networking/DataChannel.js";
import Signaler from "./networking/Signaler.js";

Ben Eltschig
committed
/**
* Format of the messages send to the server by the clients.
* @typedef {{action:"join",name:string}|{action:"startGame"}|{action:"changeSettings",rounds:number,drawTime:number}} MessageToServer

Ben Eltschig
committed
*/
/**
* Format of the messages send to the clients by the server.
* @typedef {"yup"|"nope"|{action:"stateUpdate",newState:{players:string[],host:boolean,hasGameStarted:boolean}}|{action:"settingsUpdate",rounds:number,drawTime:number}} MessageToClient

Ben Eltschig
committed
*/
/**
* A local server. Handles all the important game logic, and communicates with clients via DataChannels.
*/
export default class SkribblServer {
/**
* Starts a new SkribblServer.
*/
constructor(){
this._readyPromise = (async()=>{

Ben Eltschig
committed
/** @type {{name:string,dataChannel:DataChannel<MessageToClient,MessageToServer>}[]} */

Ben Eltschig
committed
this._clients = [];
this._rounds = 3;
this._drawTime = 180;
this._id = await Signaler.host(dataChannel=>{

Ben Eltschig
committed
this.connect(DataChannel.from(dataChannel,JSON.stringify,JSON.parse));

Ben Eltschig
committed
/** @type {[DataChannel<MessageToServer,MessageToClient>,DataChannel<MessageToClient,MessageToServer>]} */

Ben Eltschig
committed
let [endpointA,endpointB] = DataChannel.createPair();
this._dataChannel = endpointA;
this.connect(endpointB);
})();
}
/**
* Waits until the server is ready.
*/
async waitUntilReady(){
return this._readyPromise;
}

Ben Eltschig
committed
/**
* A dataChannel talking to this server like any other client.
* @readonly
*/
get dataChannel(){
return this._dataChannel;
}
/**
* The ID others can use to connect to this server.
* @readonly
*/
get id(){
return this._id;
}
/**
* Returns the full url others can use to connect to this server.
* @readonly
*/
get url(){
return document.location.host+document.location.pathname+"#"+this._id;
}
/**
* Adds an incoming connection as a client.

Ben Eltschig
committed
* @param {DataChannel<MessageToClient,MessageToServer>} dataChannel
*/
connect(dataChannel){

Ben Eltschig
committed
(async()=>{
let message = await dataChannel.next();
console.log("message: ",message);

Ben Eltschig
committed
if (message.action==="join"){
let name = message.name;

Ben Eltschig
committed
if (name.length<30&&name.length>=1){
dataChannel.send("yup");
this._clients.push({name,dataChannel});
let players = this._clients.map(({name})=>name);
this._clients.forEach(({dataChannel},index)=>{
dataChannel.send({action:"stateUpdate",newState:{players,host:index==0,hasGameStarted:this._hasGameStarted}});
});
if (!this._hasGameStarted){
dataChannel.send({action:"settingsUpdate",rounds:this._rounds,drawTime:this._drawTime});
}
dataChannel.onMessage(message=>{
let isHost = this._clients[0].dataChannel = dataChannel;
if (message.action=="startGame"){
if (isHost&&!this._hasGameStarted){
this._hasGameStarted = true;
let players = this._clients.map(({name})=>name);
this._clients.forEach(({dataChannel},index)=>{
dataChannel.send({action:"stateUpdate",newState:{players,host:index==0,hasGameStarted:this._hasGameStarted}});
});
}
}else if(message.action=="changeSettings"){
if (isHost&&!this._hasGameStarted){
this._rounds = message.rounds;
this._drawTime = message.drawTime;
this._clients.forEach(({dataChannel})=>{
dataChannel.send({action:"settingsUpdate",rounds:this._rounds,drawTime:this._drawTime});
});
}

Ben Eltschig
committed
}else{
dataChannel.send("nope");
dataChannel.close();
}
}else{
dataChannel.send("nope");
dataChannel.close();
}
})();