//imports import * as log from '../util/logging.js' import * as resp from '../interface/rest/response.js' import { mkstring } from '../util/token.js' import * as logic from './UnoRules.js' import { UnoChat } from './UnoChat.js' import { UnoSockets } from './UnoSocket.js' import WebSocket from 'ws'; import jsonCards from '../../config/cards.json' import jsonConf from '../../config/game.json' import { shuffle } from '../util/shuffle.js' import { UIVote } from './UiVote.js' export interface UnoCard { id: number; color: string; number: string; tx: string; } export interface UnoPlayer { nickname: string; lastseen: Date; userId: string; ready: boolean; uno: boolean; cardDrawn: boolean; score: number; rounds: number; hand: UnoCard[]; socket: WebSocket; }1 export interface UnoExtraRules { sw_stacking: boolean; sw_jumpin: boolean; sw_double: boolean; sw_sleep: boolean; sw_seveno: boolean; } export class UnoSession { sessionID: number; //SitzungsID players: UnoPlayer[]; //alle Spielerobjekte winners: UnoPlayer[]; //fertige spieler cardsPlayed: UnoCard[]; //gespielte Karten Satpel cardsMixed: UnoCard[]; //nachziehkarten Stapel started: boolean; //spiel gestartet password: string; //passwort f�r den zutritt currentPlayer: string; //aktueller spieler direction: number; //spielrichtung (1=vor, -1=zur�ck, 0=nicht gestartet) startcards: number; //anzahl der maximalen karten maxPlayers: number; //maximale spieleranzahl serverName: string; //serever Name unoChat: UnoChat; sockets: UnoSockets; lastPlayedBy: UnoPlayer; testmode: boolean; activeVote: UIVote; sessionId: string; canDelete: boolean; extraRules: UnoExtraRules; sleepTimer; sleepValue; stackSize: number; //anzahl der Karten die genzpgen werden müssen private checkTimer; constructor(sessionId,custom: any){ this.extraRules = { sw_stacking: custom.sw_stacking , sw_jumpin: custom.sw_jumpin , sw_double: custom.sw_double , sw_sleep: custom.sw_sleep , sw_seveno: custom.sw_seveno , } console.log(custom) this.players= []; this.winners= []; this.unoChat= new UnoChat(this); this.sockets= undefined; this.sessionId= sessionId; //konfigurationsdate laden log.logNotice("Spiel Starten. Lade Konfigurationsdatei..."); let t_this = jsonConf; this.password = t_this.password; this.startcards = custom.startcards || t_this.startCards; this.maxPlayers = t_this.maxPlayers; this.serverName = t_this.serverName; this.testmode = t_this.testMode; this.sockets = new UnoSockets(this); this.activeVote = undefined; this.canDelete = false; this.initLobby(); //kick inactive this.checkTimer = setInterval(()=>{ [...this.players, ...this.winners].forEach(el =>{ var dif = ((new Date()).getTime()- el.lastseen.getTime())/1000; if(!this.started && dif > 10){ this.kickUser(el, "10 Sekunden Inaktiv") }else if(this.started && dif > 60){ this.kickUser(el, "60 Sekunden Inaktiv") }else if(this.started && dif > 29 && dif < 31){ this.unoChat.writeMessage(el.nickname + " wird in 30 Sekunden wegen Inaktivität gekickt.") } else if(this.started && dif > 44 && dif < 46){ this.unoChat.writeMessage(el.nickname + " wird in 15 Sekunden wegen Inaktivität gekickt.") } }) if ([...this.players, ...this.winners].length == 0){ log.logInfo('Lobby empty. Closing...'); this.canDelete = true; this.deleteSession(); } },1000); } public deleteSession(){ [...this.players, ...this.winners].forEach(el => el.socket.close()) clearInterval(this.sockets.pingTimer) clearInterval(this.checkTimer) this.stopTimer(); } private initLobby() { this.cardsPlayed = []; //gespielte Karten Satpel this.cardsMixed = []; //nachziehkarten Stapel this.started = false; //spiel gestartet this.currentPlayer = ""; this.direction = 0; this.stackSize = 0; //Karten Laden log.logNotice("Spiel Starten. Lade Karten..."); let cards = jsonCards.cards; let counter = 0; cards.forEach(el => { for (let i = 0; i < el.count; i++) { this.cardsMixed.push({ id: counter, color: el.color, number: el.number, tx: el.tx }); counter++; } }); shuffle(this.cardsMixed); //Karten mischen shuffle(this.cardsMixed); //Karten mischen shuffle(this.cardsMixed); //Karten mischen shuffle(this.cardsMixed); //Karten mischen log.logNotice(this.cardsMixed.length.toString() + " Karten geladen."); this.clientUpdateStatus(); } public restartGame() { this.players = [...this.players, ...this.winners]; //alle Spielerobjekte this.winners = []; shuffle(this.players); this.players.forEach(player => { player.hand = []; //übrige karten löschen player.ready = false; player.uno = false; player.cardDrawn = false; player.rounds++; }) this.initLobby(); this.clientUpdateHands(); this.clientUpdateAllPlayers(); } public playerJoin(nickname: string):any[] { if (this.started) { return [false,"Das Spiel wurde bereits gestartet."] } else { if (!nickname) { log.logError("User tried to connect without Nickname") return [false,"Du musst einen benutzernamen angeben."] } //wenn sonst soweit alles passt const playerObj: UnoPlayer = { nickname: nickname, lastseen: new Date(), userId: mkstring(20), ready: false, uno: false, cardDrawn: false, score: 0, rounds: 1, hand: [], socket: undefined, }; this.players.push(playerObj); this.clientUpdatePlayer(playerObj); log.logNotice("Player " + nickname + " joined with id " + playerObj.userId); this.unoChat.writeMessage(nickname + " ist dem Spiel beigetreten"); return [true,{ userId: playerObj.userId }] } } public checkReady() { let all = 0; let ready = 0; this.players.forEach(function (obj, ix) { all++; if (obj.ready) { ready++; } }); log.logNotice(ready + "/" + all + " Players are ready"); this.unoChat.writeMessage(ready + "/" + all + " spieler sind Bereit"); if (all == ready && all > 1) { log.logNotice("Start Game"); this.startGame(); logic.onStart(this); } } public startGame() { this.started = true; log.logNotice("Starting Game. Dealing Cards"); for (let player_ix = 0; player_ix < this.players.length; player_ix++) { let pObj = this.players[player_ix]; pObj.hand = []; for (let i = 0; i < this.startcards; i++) { pObj.hand.push(this.pullCardFromStack()); } this.clientUpdatePlayer(pObj); } this.cardsPlayed.push(this.pullCardFromStack()); log.logNotice("Cards dealt"); this.unoChat.writeMessage("Die Runde wurde gestartet"); this.currentPlayer = this.players[0].userId; this.direction = 1; this.clientUpdatePlayedCard() this.clientUpdateCurrentPlayer(); this.clientUpdateStatus(); this.clientUpdateHands(); } private pullCardFromStack() { let card = this.cardsMixed[this.cardsMixed.length - 1]; this.cardsMixed.pop(); log.logNotice("Pulled Card form Stack. Remaining: " + this.cardsMixed.length); return card; } public register_socket(socket: WebSocket, id: string){ this.sockets.register_socket(socket,id); } public initClient(player: UnoPlayer) { let respObj: any = {}; //add hands let players_public = [] let players_winners = [] this.players.forEach((obj) => { players_public.push(this.getPlayerMeta(obj)); }); this.winners.forEach((obj) => { players_winners.push(this.getPlayerMeta(obj)); }); respObj.hand = player ? player.hand : []; respObj.serverName = this.serverName; respObj.cardsMixed = this.cardsMixed.length; respObj.cardsPlayed = this.cardsPlayed; respObj.started = this.started; respObj.direction = this.direction; respObj.currentPlayer = this.currentPlayer; respObj.players = players_public; respObj.winners = players_winners; respObj.extraRules = this.extraRules; respObj.extraRules.cards = this.startcards; respObj.stack = this.stackSize; player.socket.send(JSON.stringify({ initClientData: respObj })) } public clientHearbeat(player: UnoPlayer){ player.lastseen = new Date(); } public findPlayerIndex(id) { let index = 0; this.players.forEach((obj, ix) => { if (obj.userId == id) { index = ix; } }); return index; } public clientUpdatePlayer(player: UnoPlayer){ //modify or add player [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ player: "modify", data: this.getPlayerMeta(player) })) } }) } public clientRemovePlayer(player: UnoPlayer){ //remove player [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ player: "remove", data: this.getPlayerMeta(player) })) } }) } private getPlayerMeta(player: UnoPlayer){ return { nickname: player.nickname, lastseen: player.lastseen, userId: player.userId, ready: player.ready, cards: player.hand ? player.hand.length : 0, uno: player.uno, score: player.score, rounds: player.rounds, } } public clientUpdateWinner(){ //modify or add player let players_winners = [] this.winners.forEach((obj) => { players_winners.push(this.getPlayerMeta(obj)); }); [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ player: "winners", data: players_winners })) } }) } public clientUpdateAllPlayers(){ let players_public = [] let players_winners = [] this.players.forEach((obj) => { players_public.push(this.getPlayerMeta(obj)); }); this.winners.forEach((obj) => { players_winners.push(this.getPlayerMeta(obj)); }); [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ player: "all", players: players_public, winners: players_winners, })) } }) } public clientUpdatePlayedCard(){ [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ gameUpdate: "playedStack", card: this.cardsPlayed, })) } }) } public clientUpdateCurrentPlayer(){ [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ gameUpdate: "currentPlayer", currentplayer: this.currentPlayer, direction: this.direction, })) } }) } public clientUpdateStatus(){ [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ gameUpdate: "status", started: this.started })) } }) } public clientUpdateHands(){ this.players.forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ hand: "all", cards: el.hand })) } }) } public clientAddCard(player: UnoPlayer, card: UnoCard){ //modify or add player if (player.socket && player.socket?.OPEN){ player.socket.send(JSON.stringify({ hand: "add", card: card })) } } public clientRemoveCard(player: UnoPlayer, card: UnoCard){ //modify or add player if (player.socket && player.socket?.OPEN){ player.socket.send(JSON.stringify({ hand: "remove", card: card })) } } public kickUser(player: UnoPlayer, reason: string){ log.logNotice("Player " + player.nickname + " (" + player.userId + ") kicked for reason : " + reason); this.unoChat.writeMessage(player.nickname + " wurde gekickt. Grund: " + reason); if (player.socket && player.socket?.OPEN){ player.socket.send(JSON.stringify({kick:reason})); } if(this.started){ this.cardsMixed = [...player.hand, ...this.cardsMixed]; if (player.userId == this.currentPlayer) { logic.advancePlayer(this, false, false); } } this.winners = this.winners.filter(el2 => el2.userId != player.userId); this.players = this.players.filter(el2 => el2.userId != player.userId); this.clientUpdateAllPlayers(); } public startTimer(duration: number, event: Function){ this.stopTimer(); this.sendTimer(duration); this.sleepValue = duration; this.sleepTimer = setInterval(()=>{ this.sleepValue --; this.sendTimer(this.sleepValue); if (this.sleepValue == -1){ this.stopTimer(); event(); } },1000) } public stopTimer(){ if (this.sleepTimer){ clearInterval(this.sleepTimer) } } private sendTimer(value: number){ [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ timer: value, })) } }) } public clientPlaySound(player: UnoPlayer, sound: string){ if (player.socket && player.socket?.OPEN){ player.socket.send(JSON.stringify({ sound: sound, })) } } public clientNotify(player: UnoPlayer, message: string, failed: boolean){ if (player.socket && player.socket?.OPEN){ player.socket.send(JSON.stringify({ notify: message, warning: !failed, })) } } public addStackSize(value: number){ this.stackSize += value; [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ stack: this.stackSize })) } }) this.sendStackSize(); } public resetStackSize(){ this.stackSize = 0; this.sendStackSize(); } private sendStackSize(){ [...this.players, ...this.winners].forEach(el =>{ if (el.socket && el.socket?.OPEN){ el.socket.send(JSON.stringify({ stack: this.stackSize })) } }) } }