initial merge of both repos
This commit is contained in:
532
UNO-Backend/src/gameLogic/UnoSession.ts
Normal file
532
UNO-Backend/src/gameLogic/UnoSession.ts
Normal file
@@ -0,0 +1,532 @@
|
||||
//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<75>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
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user