initial merge of both repos

This commit is contained in:
2021-02-25 23:11:23 +01:00
commit e1ec6b2db9
138 changed files with 4282 additions and 0 deletions

3
UNO-Backend/README.md Normal file
View File

@@ -0,0 +1,3 @@
# UNO-Backend

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<Name>UNO-Backend</Name>
<RootNamespace>UNO-Backend</RootNamespace>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>d5b44f82-6712-42ae-bcb7-ff5775f7b3dc</ProjectGuid>
<ProjectHome>.</ProjectHome>
<StartupFile>server.ts</StartupFile>
<StartWebBrowser>True</StartWebBrowser>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
<NodejsPort>1337</NodejsPort>
<EnableTypeScript>true</EnableTypeScript>
<StartWebBrowser>true</StartWebBrowser>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Content Include="config\cards.json">
<SubType>Code</SubType>
</Content>
<Content Include="config\game.json">
<SubType>Code</SubType>
</Content>
<Content Include="src\gameLogic\UnoChat.ts">
<SubType>Code</SubType>
</Content>
<Content Include="src\gameLogic\UnoGame.ts">
<SubType>Code</SubType>
</Content>
<Content Include="src\gameLogic\UnoRules.ts">
<SubType>Code</SubType>
</Content>
<Content Include="src\interface\rest\response.ts">
<SubType>Code</SubType>
</Content>
<Content Include="src\util\logging.ts">
<SubType>Code</SubType>
</Content>
<Content Include="src\util\token.ts">
<SubType>Code</SubType>
</Content>
<Content Include="tsconfig.json" />
<Content Include="package.json" />
<Content Include="README.md" />
</ItemGroup>
<ItemGroup>
<Folder Include="config\" />
<Folder Include="src\" />
<Folder Include="src\interface\" />
<Folder Include="src\interface\rest\" />
<Folder Include="src\gameLogic\" />
<Folder Include="src\util\" />
</ItemGroup>
<ItemGroup>
<TypeScriptCompile Include="server.ts">
<SubType>Code</SubType>
</TypeScriptCompile>
<TypeScriptCompile Include="src\gameLogic\UnoSocket.ts">
<SubType>Code</SubType>
</TypeScriptCompile>
</ItemGroup>
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsToolsV2.targets" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:48022/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
<WebProjectProperties>
<StartPageUrl>
</StartPageUrl>
<StartAction>CurrentPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>False</EnableENC>
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
</PropertyGroup>
</Project>

Binary file not shown.

View File

@@ -0,0 +1,328 @@
{
"cards": [
{
"color": "blue",
"number": "0",
"count": 1,
"tx": "/assets/cards/110.png"
},
{
"color": "blue",
"number": "1",
"count": 2,
"tx": "/assets/cards/111.png"
},
{
"color": "blue",
"number": "2",
"count": 2,
"tx": "/assets/cards/112.png"
},
{
"color": "blue",
"number": "3",
"count": 2,
"tx": "/assets/cards/113.png"
},
{
"color": "blue",
"number": "4",
"count": 2,
"tx": "/assets/cards/114.png"
},
{
"color": "blue",
"number": "5",
"count": 2,
"tx": "/assets/cards/115.png"
},
{
"color": "blue",
"number": "6",
"count": 2,
"tx": "/assets/cards/116.png"
},
{
"color": "blue",
"number": "7",
"count": 2,
"tx": "/assets/cards/117.png"
},
{
"color": "blue",
"number": "8",
"count": 2,
"tx": "/assets/cards/118.png"
},
{
"color": "blue",
"number": "9",
"count": 2,
"tx": "/assets/cards/119.png"
},
{
"color": "blue",
"number": "+2",
"count": 2,
"tx": "/assets/cards/211.png"
},
{
"color": "blue",
"number": "skip",
"count": 2,
"tx": "/assets/cards/213.png"
},
{
"color": "blue",
"number": "reverse",
"count": 2,
"tx": "/assets/cards/212.png"
},
{
"color": "green",
"number": "0",
"count": 1,
"tx": "/assets/cards/120.png"
},
{
"color": "green",
"number": "1",
"count": 2,
"tx": "/assets/cards/121.png"
},
{
"color": "green",
"number": "2",
"count": 2,
"tx": "/assets/cards/122.png"
},
{
"color": "green",
"number": "3",
"count": 2,
"tx": "/assets/cards/123.png"
},
{
"color": "green",
"number": "4",
"count": 2,
"tx": "/assets/cards/124.png"
},
{
"color": "green",
"number": "5",
"count": 2,
"tx": "/assets/cards/125.png"
},
{
"color": "green",
"number": "6",
"count": 2,
"tx": "/assets/cards/126.png"
},
{
"color": "green",
"number": "7",
"count": 2,
"tx": "/assets/cards/127.png"
},
{
"color": "green",
"number": "8",
"count": 2,
"tx": "/assets/cards/128.png"
},
{
"color": "green",
"number": "9",
"count": 2,
"tx": "/assets/cards/129.png"
},
{
"color": "green",
"number": "+2",
"count": 2,
"tx": "/assets/cards/221.png"
},
{
"color": "green",
"number": "skip",
"count": 2,
"tx": "/assets/cards/223.png"
},
{
"color": "green",
"number": "reverse",
"count": 2,
"tx": "/assets/cards/222.png"
},
{
"color": "yellow",
"number": "0",
"count": 1,
"tx": "/assets/cards/140.png"
},
{
"color": "yellow",
"number": "1",
"count": 2,
"tx": "/assets/cards/141.png"
},
{
"color": "yellow",
"number": "2",
"count": 2,
"tx": "/assets/cards/142.png"
},
{
"color": "yellow",
"number": "3",
"count": 2,
"tx": "/assets/cards/143.png"
},
{
"color": "yellow",
"number": "4",
"count": 2,
"tx": "/assets/cards/144.png"
},
{
"color": "yellow",
"number": "5",
"count": 2,
"tx": "/assets/cards/145.png"
},
{
"color": "yellow",
"number": "6",
"count": 2,
"tx": "/assets/cards/146.png"
},
{
"color": "yellow",
"number": "7",
"count": 2,
"tx": "/assets/cards/147.png"
},
{
"color": "yellow",
"number": "8",
"count": 2,
"tx": "/assets/cards/148.png"
},
{
"color": "yellow",
"number": "9",
"count": 2,
"tx": "/assets/cards/149.png"
},
{
"color": "yellow",
"number": "+2",
"count": 2,
"tx": "/assets/cards/241.png"
},
{
"color": "yellow",
"number": "skip",
"count": 2,
"tx": "/assets/cards/243.png"
},
{
"color": "yellow",
"number": "reverse",
"count": 2,
"tx": "/assets/cards/242.png"
},
{
"color": "red",
"number": "0",
"count": 1,
"tx": "/assets/cards/130.png"
},
{
"color": "red",
"number": "1",
"count": 2,
"tx": "/assets/cards/131.png"
},
{
"color": "red",
"number": "2",
"count": 2,
"tx": "/assets/cards/132.png"
},
{
"color": "red",
"number": "3",
"count": 2,
"tx": "/assets/cards/133.png"
},
{
"color": "red",
"number": "4",
"count": 2,
"tx": "/assets/cards/134.png"
},
{
"color": "red",
"number": "5",
"count": 2,
"tx": "/assets/cards/135.png"
},
{
"color": "red",
"number": "6",
"count": 2,
"tx": "/assets/cards/136.png"
},
{
"color": "red",
"number": "7",
"count": 2,
"tx": "/assets/cards/137.png"
},
{
"color": "red",
"number": "8",
"count": 2,
"tx": "/assets/cards/138.png"
},
{
"color": "red",
"number": "9",
"count": 2,
"tx": "/assets/cards/139.png"
},
{
"color": "red",
"number": "+2",
"count": 2,
"tx": "/assets/cards/231.png"
},
{
"color": "red",
"number": "skip",
"count": 2,
"tx": "/assets/cards/233.png"
},
{
"color": "red",
"number": "reverse",
"count": 2,
"tx": "/assets/cards/232.png"
},
{
"color": "select",
"number": "wild",
"count": 4,
"tx": "/assets/cards/301.png"
},
{
"color": "select",
"number": "wild+4",
"count": 4,
"tx": "/assets/cards/302.png"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"kickIdlePlayers": true,
"idleTimeout": 100000,
"serverName": "Dennis Ultrakrasser Uno Server",
"maxPlayers": 10,
"password": "SagIchDirNicht!",
"startCards": 7,
"testMode": false
}

22
UNO-Backend/dockerfile Normal file
View File

@@ -0,0 +1,22 @@
FROM node:12.21-alpine
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
COPY ./dist/out-tsc /home/node/backend
COPY ./package.json /home/node/backend/package.json
WORKDIR /home/node/backend
RUN ls -ltra
#RUN rm node_modules -R
#RUN rm package-lock.json
#RUN chown node /opt/citron3 -Rf
RUN chmod -R 777 /home/node/backend
USER node
RUN npm install
EXPOSE 3000
CMD [ "node", "--experimental-json-modules", "server.js"]

View File

@@ -0,0 +1,2 @@
C:\Users\dennisgunia\source\repos\UNO-Backend\UNO-Backend\obj\Debug\UNO-Backend.njsprojAssemblyReference.cache
C:\Users\dennisgunia\source\repos\UNO-Backend\UNO-Backend\bin\Microsoft.NodejsTools.WebRole.dll

30
UNO-Backend/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "uno-backend",
"version": "0.0.0",
"description": "UNO-Backend",
"main": "server.js",
"author": {
"name": ""
},
"type": "module",
"scripts": {
"build": "tsc --build ",
"clean": "tsc --build --clean"
},
"devDependencies": {
"@types/express": "^4.17.3",
"@types/node": "^8.10.59",
"@types/ws": "^7.2.3",
"typescript": "^3.9.9"
},
"dependencies": {
"body-parser": "^1.19.0",
"express": "^4.17.1",
"express-ws": "^4.0.0",
"nvm": "0.0.4",
"ts-node": "^9.1.1",
"tsc": "^1.20150623.0",
"tslib": "^2.1.0",
"ws": "^7.2.3"
}
}

69
UNO-Backend/server.ts Normal file
View File

@@ -0,0 +1,69 @@
import { UnoSockets } from './src/gameLogic/UnoSocket.js'
import express from "express";
import bodyParser from 'body-parser'
import { UnoSession} from './src/gameLogic/UnoSession.js'
import expressWs from 'express-ws';
import http from 'http';
import WebSocket from 'ws';
import { UnoLobby } from './src/lobby/UnoLobby.js';
import { resp } from './src/interface/rest/response.js';
import { watch } from 'fs';
const app = express();
app.use(bodyParser.json())
let unoLobby = new UnoLobby();
app.get('/', (req, res) => res.send('Hello World!'));
//lobby requests
app.get('/unogame/lobbies', (req,res)=>{
resp(req, res, true, unoLobby.getLobbies(), 200);
})
//lobby requests
app.post('/unogame/create', (req,res)=>{
resp(req, res, true, {
id: unoLobby.createLobby(req.body),
}, 200);
})
//lobby join
app.post('/unogame/join', (req,res)=>{
let data = unoLobby.joinLobby(req.body.nick,req.body.lid);
resp(req, res, data[0], data[1], 200);
})
const port = 3000;
const server = app.listen(port, () => console.log(`Example app listening on port ${port}!`));
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws: WebSocket) => {
console.log("connecting socket...")
ws.on('message', (message: string) => { actionHandler(message,ws);});
ws.send('{"msg":"Hello!"}');
});
function actionHandler(message: string, ws: WebSocket){
try{
const command = JSON.parse(message);
console.log(command);
if (command.action == "register"){
unoLobby.registerLobby(message,ws)
}
}catch(ex){
ws.send('{"reply":"Error"}');
}
}

View File

@@ -0,0 +1,126 @@
import { UnoSession, UnoCard, UnoPlayer, } from './UnoSession.js';
import * as logic from './UnoRules.js'
import * as Log from './../util/logging.js'
export function processUiEvents(game: UnoSession, player: UnoPlayer, command: any){
switch(command.button){
case 'ready':
p_ready(game,player);
break;
case 'leave':
p_leave(game,player);
break;
case 'play':
p_play(game,player,command);
break
case 'double':
p_double(game,player,command);
break;
case 'leave':
p_leave(game,player);
break;
case 'pull':
p_pull(game,player);
break;
case 'skip':
p_skip(game,player);
break;
case 'uno':
p_uno(game,player);
break;
}
}
function p_ready (game: UnoSession, player: UnoPlayer){
if (game.started) {
player.socket.send(JSON.stringify({"notify":"game already started"}))
} else {
player.ready = true;
Log.logNotice("Player " + player.nickname + " (" + player.userId + ") is ready");
game.unoChat.writeMessage(player.nickname + " ist bereit");
game.checkReady();
game.clientUpdatePlayer(player);
}
}
function p_leave (game: UnoSession, player: UnoPlayer){
if (game.started) {
logic.leave(game,player)
} else {
//remove player
const newPlayers = game.players.filter(el => el.userId != player.userId);
Log.logNotice("Player " + player.nickname + " (" + player.userId + ") left in lobby");
game.unoChat.writeMessage(player.nickname + " hat die Lobby verlassen");
game.players = newPlayers;
game.checkReady();
player.socket.send(JSON.stringify({kick:"Spiel verlassen."}));
game.clientRemovePlayer(player);
}
}
function p_play(game: UnoSession, player: UnoPlayer,command:any) {
if (!game.started) {
player.socket.send(JSON.stringify({"notify":"game not started"}))
} else {
let ret = logic.playCard(game, player, command.card)
player.socket.send(JSON.stringify({
notify: ret[1],
warning: ret[0],
}))
game.clientUpdatePlayer(player);
}
}
function p_double(game: UnoSession, player: UnoPlayer,command:any) {
if (!game.started) {
player.socket.send(JSON.stringify({"notify":"game not started"}))
} else {
let ret = logic.playCard(game, player, command.card[0], command.card[1])
player.socket.send(JSON.stringify({
notify: ret[1],
warning: ret[0],
}))
game.clientUpdatePlayer(player);
}
}
function p_pull(game: UnoSession, player: UnoPlayer) {
if (!game.started) {
player.socket.send(JSON.stringify({"notify":"game not started"}))
} else {
let ret = logic.pull(game, player)
player.socket.send(JSON.stringify({
notify: ret[1],
warning: ret[0],
}))
game.clientUpdatePlayer(player);
}
}
function p_skip(game: UnoSession, player: UnoPlayer) {
if (!game.started) {
player.socket.send(JSON.stringify({"notify":"game not started"}))
} else {
let ret = logic.skip(game, player)
player.socket.send(JSON.stringify({
notify: ret[1],
warning: ret[0],
}))
}
}
function p_uno(game: UnoSession, player: UnoPlayer) {
if (!game.started) {
player.socket.send(JSON.stringify({"notify":"game not started"}))
} else {
let ret = logic.uno(game, player)
player.socket.send(JSON.stringify({
notify: ret[1],
warning: ret[0],
}))
game.clientUpdatePlayer(player);
}
}

View File

@@ -0,0 +1,94 @@
import WebSocket from 'ws';
import { UnoSession, UnoPlayer } from './UnoSession';
export interface UIVoteQuestion {
question: string;
options: string[];
counter?: number[];
}
export class UIVote {
duration: number;
questions: UIVoteQuestion[];
game: UnoSession;
title: string;
votedPlayers: String[];
stopTimer;
constructor(game: UnoSession, title:string, duration: number){
this.duration = duration;
this.game = game;
this.title = title;
this.questions = [];
this.stopTimer = undefined;
this.votedPlayers = [];
}
public addQuestion(question: string, options: string[]){
let countArray = [];
options.forEach(el => countArray.push(0));
this.questions.push({
question: question,
options: options,
counter: countArray,
})
}
public startVote(callback){
[...this.game.players, ...this.game.winners].forEach(el =>{
if (el.socket && el.socket?.OPEN){
el.socket.send(JSON.stringify({
"vote": this.title,
"questions":this.questions,
"time": this.duration
}))
}
})
this.stopTimer = setTimeout(()=>this.endVote(callback), this.duration);
}
private endVote(callback){
[...this.game.players, ...this.game.winners].forEach(el =>{
if (el.socket && el.socket?.OPEN){
el.socket.send(JSON.stringify({"endvote":""}))
}
})
callback(this.questions);
}
private broadcastResults(){
[...this.game.players, ...this.game.winners].forEach(el =>{
if (el.socket && el.socket?.OPEN){
el.socket.send(JSON.stringify({"voteres":this.questions}))
}
})
}
public abortVote(){
clearTimeout(this.stopTimer);
[...this.game.players, ...this.game.winners].forEach(el =>{
if (el.socket && el.socket?.OPEN){
el.socket.send(JSON.stringify({"endvote":""}))
}
})
}
public voteEvent(id,player:UnoPlayer){
if (this.votedPlayers.filter(el => el == player.userId).length == 0){
this.questions[0].counter[id - 1] ++;
this.broadcastResults();
player.socket.send(JSON.stringify({
notify: "Stimme abgegeben",
warning: true,
}))
this.votedPlayers.push(player.userId)
}else{
player.socket.send(JSON.stringify({
notify: "Stimme bereits abgegeben",
warning: false,
}))
}
}
}

View File

@@ -0,0 +1,29 @@
import * as Log from './../util/logging.js'
import { UnoSession, UnoPlayer, } from './UnoSession.js';
export class UnoChat {
private chatHistory: string[];
private game: UnoSession;
constructor(game: UnoSession) {
this.chatHistory = [];
this.game = game;
}
public writeMessage (msg:string ) {
let now = new Date();
let timestamp = now.getHours() + ":" + now.getMinutes();
const line = "<span class='chat-line-date'>" + timestamp + "</span><span class='chat-line-msg'> " + msg + "</span><br />"
Log.logChat(line);
[...this.game.players, ...this.game.winners].forEach(el =>{
if (el.socket && el.socket?.OPEN){
el.socket.send(JSON.stringify({
"chat": line
}))
}
})
}
}

View File

@@ -0,0 +1,336 @@
import * as log from '../util/logging.js'
import { UnoSession, UnoCard, UnoPlayer, } from './UnoSession.js';
import { UIVote } from './UiVote.js';
export function playCard(game: UnoSession, player: UnoPlayer, card: UnoCard, double?: UnoCard){
log.logNotice("Player " + player.nickname + " (" + player.userId + ") plays card");
let topCard = game.cardsPlayed[game.cardsPlayed.length - 1];
//pr<70>fen ob spieler dran ist
if (!(game.currentPlayer == player.userId)) {
if(game.extraRules.sw_jumpin){
//zwischenwerfen
if ((topCard.number == card.number) && (topCard.color == card.color)) {
game.cardsPlayed.push(card);
game.clientRemoveCard(player,card);
player.hand = player.hand.filter(el => el.id != card.id);
let returnString = "Karte zwischengespielt";
game.unoChat.writeMessage(player.nickname + ' hat eine ' +formatCard(card)+ ' <font color="#FF0000">zwischengespielt</font>.');
if (player.hand.length == 1 && !player.uno) {
drawPlayer(game, 1);
returnString = "Karte zwischengespielt. Da du nicht UNO gesagt hast, musstest du eine Karte ziehen."
log.logNotice("Player " + player.nickname + " (" + player.userId + ") did not mention UNO. Add +2 Cards");
game.unoChat.writeMessage(player.nickname + " hat nicht UNO! gesagt und muss eine Karte ziehen.");
}
if (player.hand.length == 0 && player.uno) {
finishGame(game, player);
}
return [true, returnString]
}else{
return [false, "Du kannst diese Karte nicht einwerfen"]
}
}else{
return [false, "Du bist nicht dran"]
}
}
//pr<70>fen ob Karte passt
if ((topCard.color == "select") || (topCard.color == card.color) || (topCard.number == card.number) || card.number == 'wild' || card.number == 'wild+4' || game.testmode) {
//kann gelegt werden
game.cardsPlayed.push(card);
game.clientRemoveCard(player,card);
player.hand = player.hand.filter(el => el.id != card.id);
let returnString = "Karte gespielt";
//double akti
if(game.extraRules.sw_double && double){
game.cardsPlayed.push(double);
game.clientRemoveCard(player,double);
player.hand = player.hand.filter(el => el.id != double.id);
returnString = "Doppelt gespielt";
game.unoChat.writeMessage(player.nickname + ' hat <font color="#FF0000">zwei</font> ' + formatCard(card)+ ' gelegt.');
}
if(game.extraRules.sw_double && !double){
game.unoChat.writeMessage(player.nickname + ' hat eine ' + formatCard(card)+ ' gelegt.');
}
//uno pr<70>fen
if (player.hand.length == 1 && !player.uno) {
drawPlayer(game, 1);
returnString = "Karte gespielt. Da du nicht UNO gesagt hast, musstest du eine Karte ziehen."
log.logNotice("Player " + player.nickname + " (" + player.userId + ") did not mention UNO. Add +2 Cards");
game.unoChat.writeMessage(player.nickname + " hat nicht UNO! gesagt und muss eine Karte ziehen.");
}
if (player.hand.length == 0 ) {
finishGame(game, player);
}
//n<>chster spieler
advancePlayer(game, card.number == 'reverse', card.number == 'skip');
if (card.number == '+2') {
drawPlayer(game, 2);
game.players.forEach((obj, ix) => {
if (obj.userId == game.currentPlayer) { game.unoChat.writeMessage(obj.nickname + " muss zwei Karten ziehen und setzt eine Runde aus.");}
});
advancePlayer(game, false, false);
}
if (card.number == 'wild+4') {
drawPlayer(game, 4);
game.players.forEach((obj, ix) => {
if (obj.userId == game.currentPlayer) { game.unoChat.writeMessage(obj.nickname + " muss vier Karten ziehen und setzt eine Runde aus."); }
});
advancePlayer(game, false, false);
}
game.clientUpdatePlayedCard();
return [true, returnString]
} else {
return [false, "Du kannst diese Karte hier nicht legen"]
}
}
export function skip (game: UnoSession, player: UnoPlayer){
log.logNotice("Player " + player.nickname + " (" + player.userId + ") skips card");
game.unoChat.writeMessage(player.nickname + " setzt aus.");
//pr<70>fen ob spieler dran ist
if (!(game.currentPlayer == player.userId)) {
return [false, "Du bist nicht dran"]
}
advancePlayer(game, false, false);
return [true, "Ausgesetzt"]
}
export function pull(game: UnoSession, player: UnoPlayer) {
log.logNotice("Player " + player.nickname + " (" + player.userId + ") pulls card");
//pr<70>fen ob spieler dran ist
if (!(game.currentPlayer == player.userId)) {
return [false, "Du bist nicht dran"]
}
if (player.cardDrawn) {
return [false, "Du hast bereits eine Karte gezogen"]
}
game.unoChat.writeMessage(player.nickname + " zieht eine Karte.");
player.cardDrawn = true;
let card = game.cardsMixed[game.cardsMixed.length - 1];
game.cardsMixed.pop();
log.logNotice("Pulled Card form Stack. Remaining: " + game.cardsMixed.length);
game.clientAddCard(player,card);
player.hand.push(card);
player.uno = false;
player.socket.send(JSON.stringify({
changprop:"canskip",
value: true,
}))
return [true, "Karte gezogen"]
}
export function uno(game: UnoSession, player: UnoPlayer) {
log.logNotice("Player " + player.nickname + " (" + player.userId + ") says UNO");
game.unoChat.writeMessage(player.nickname + " sagt UNO!");
//pr<70>fen ob spieler dran ist
if (!(game.currentPlayer == player.userId)) {
return [false, "Du bist nicht dran"]
}
player.uno = true;
return [true, "UNO!"]
}
export function leave(game: UnoSession, player: UnoPlayer) {
log.logNotice("Player " + player.nickname + " (" + player.userId + ") leaves");
game.unoChat.writeMessage(player.nickname + " verl<72>sst das Spiel.");
player.hand.forEach((obj) => {
game.cardsMixed = [obj, ...game.cardsMixed];
});
if (player.userId == game.currentPlayer) {
advancePlayer(game, false, false);
}
player.socket.send(JSON.stringify({kick:"Spiel verlassen."}));
game.players = game.players.filter(el => el.userId != player.userId);
checkRestart(game);
}
export function onStart(game:UnoSession){
if(game.extraRules.sw_sleep){
game.startTimer(8,() => {
let index = 0;
game.players.forEach((obj, ix) => {
if (obj.userId == game.currentPlayer) {index = ix;}
});
drawPlayer(game,1);
game.clientNotify(game.players[index],'Du hast gepennt und musst einer Karte ziehen!',true);
game.clientPlaySound(game.players[index],'../assets/skip.wav')
game.unoChat.writeMessage(game.players[index].nickname + "hat gepennt und muss eine Karte ziehen");
advancePlayer(game,false,false)
})
}
}
export function advancePlayer(game:UnoSession, change, skip) {
let index = 0;
game.players.forEach((obj, ix) => {
if (obj.userId == game.currentPlayer) {index = ix;}
});
if(game.extraRules.sw_sleep){
game.startTimer(8,() => {
let index_sub = 0;
game.players.forEach((obj, ix) => {
if (obj.userId == game.currentPlayer) {index_sub = ix;}
});
drawPlayer(game,1);
game.clientNotify(game.players[index_sub],'Du hast gepennt und musst einer Karte ziehen!',true);
game.clientPlaySound(game.players[index_sub],'../assets/skip.wav')
game.unoChat.writeMessage(game.players[index_sub].nickname + "hat gepennt und muss eine Karte ziehen");
advancePlayer(game,false,false)
})
}
game.players[index].cardDrawn = false; //temp werte resetten
if (change) { game.direction = game.direction * -1 }
let step = game.direction;
if (skip) { step = step * 2 };
let newIndex = index + step;
if (newIndex < 0) { newIndex += game.players.length }
if (newIndex > (game.players.length - 1)) { newIndex -= game.players.length }
console.log(newIndex)
game.currentPlayer = game.players[newIndex].userId;
game.clientUpdateCurrentPlayer();
checkStack(game);
checkRestart(game);
}
function checkRestart(game:UnoSession) {
if (game.players.length == 1) {
log.logNotice("One Player Left. Restarting in 5s");
game.unoChat.writeMessage("Ein Spieler verbleibend. Neustart in 5 Sekunden.");
if(game.activeVote){game.activeVote.abortVote();}
if(game.extraRules.sw_sleep){
game.stopTimer();
}
setTimeout(() => { game.restartGame() }, 5000);
}
}
function drawPlayer(game:UnoSession, amount) {
let index = 0;
game.players.forEach((obj, ix) => {
if (obj.userId == game.currentPlayer) { index = ix; }
});
//draw for player
for (let i = 0; i < amount; i++) {
let card = game.cardsMixed[game.cardsMixed.length - 1];
game.cardsMixed.pop();
log.logNotice("Pulled Card form Stack. Remaining: " + game.cardsMixed.length);
game.players[index].hand.push(card);
game.clientAddCard(game.players[index],card);
}
game.clientUpdatePlayer(game.players[index]);
return [true, ""]
}
function finishGame(game:UnoSession, player: UnoPlayer) {
game.players.forEach((obj, ix) => {
if (obj.userId == player.userId) {
//TODO: Score berechnen
player.score += getScore(game,player);
game.clientRemovePlayer(player)
game.clientUpdateWinner();
game.winners.push(obj)
game.unoChat.writeMessage(obj.nickname + " hat die Runde beendet.");
}
});
game.clientRemovePlayer(player);
game.clientUpdateWinner();
game.players = game.players.filter(el => el.userId != player.userId);
voteEarlyFinish(game);
}
function checkStack(game:UnoSession) {
//remix stack
if (game.cardsPlayed.length > 10) {
log.logNotice("Remix played cards");
game.unoChat.writeMessage("Karten werden neu gemischt.");
for (let i = 0; i < game.cardsPlayed.length - 2; i++) {
let card = game.cardsPlayed[i];
game.cardsMixed.push(game.cardsPlayed[i]);
console.log(game.cardsMixed.length)
}
game.cardsPlayed = [game.cardsPlayed[game.cardsPlayed.length - 1]];
game.cardsMixed.sort(() => Math.random() - 0.5);
game.cardsMixed.sort(() => Math.random() - 0.5);
game.cardsMixed.sort(() => Math.random() - 0.5);
}
}
function getScore(game:UnoSession, player: UnoPlayer) {
let score = 0;
game.players.forEach(pl => {
if (pl.userId != player.userId && game.winners.length == 0) {
pl.hand.forEach(card => {
switch (card.number) {
case 'wild':
score += 50;
break;
case 'wild+4':
score += 50;
break;
case '+2':
score += 20;
break;
case 'reverse':
score += 20;
break;
case 'skip':
score += 20;
break;
default:
break;
}
})
}
})
return score;
}
function voteEarlyFinish(game: UnoSession){
game.activeVote = new UIVote(game,'Vorzeitig beenden',8000);
game.activeVote.addQuestion("Soll die Runde vorzeitig beendet werden?",["Ja","Nein"]);
game.activeVote.startVote((values)=>{
console.log("Vote results", values);
if (values[0].counter[0] > values[0].counter[1]){
game.unoChat.writeMessage("Abstimmung erfolgreich. Nächste Runde wird gestartet.")
game.restartGame();
}
})
}
function formatCard(card: UnoCard){
let color = ''
let number = ''
switch(card.color){
case 'red': color='<font color="#ed1c24">Rote</font>'; break;
case 'blue': color='<font color="#0095da">Blaue</font>'; break;
case 'green': color='<font color="#00ab67">Grüne</font>'; break;
case 'yellow': color='<font color="#ffde00">Gelbe</font>'; break;
}
switch(card.number){
case '+2': number='<font color="#FFFACD">Zieh Zwei Karte</font>'; break;
case 'reverse': number='<font color="#FFFACD">Retoure Karte</font>'; break;
case 'skip': number='<font color="#FFFACD">Aussetzen Karte</font>'; break;
case 'wild': number='<font color="#F08080">Farbwahlkarte</font>'; break;
case 'wild+4': number='<font color="#F08080">Zieh Vier Farbwahlkarte</font>'; break;
default: number = card.number.toString();
}
return color + ' ' + number
}

View 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
}))
}
})
}
}

View File

@@ -0,0 +1,74 @@
import { UnoSession, UnoCard, UnoPlayer, } from './UnoSession.js';
import { processUiEvents } from './UiEvents.js'
import * as Log from './../util/logging.js'
import WebSocket from 'ws';
export class UnoSockets {
private game:UnoSession;
pingTimer;
constructor(game) {
//heatbeat
this.game = game;
this.pingTimer = setInterval(()=>{
this.game.players.forEach(el => {
if(el.socket){
el.socket.send(JSON.stringify({
heartbeat: true
}))
}
})
},10000)
}
public register_socket(socket: WebSocket, id: string){
let valid: boolean = false;
this.game.players.forEach((obj, ix) => {
if (obj.userId == id) {
console.log('registered socket fot ',obj.userId);
obj.socket = socket;
obj.socket.on('message', (message: string) => {
console.log('received message form:',obj.userId,'data', message);
const command = JSON.parse(message);
this.processCommand(command,obj);
});
valid = true;
}
});
this.game.winners.forEach((obj, ix) => {
if (obj.userId == id) {
console.log('registered socket fot ',obj.userId);
obj.socket = socket;
obj.socket.on('message', (message: string) => {
console.log('received message form:',obj.userId,'data', message);
const command = JSON.parse(message);
this.processCommand(command,obj);
});
valid = true;
}
});
if (!valid){
socket.send(JSON.stringify({kick:"Ungültige session."}));
}
}
private processCommand(command: any, player: UnoPlayer){
switch(command.action){
case 'uibutton':
processUiEvents(this.game,player,command)
break;
case 'initData':
this.game.initClient(player);
break;
case 'heartbeat':
this.game.clientHearbeat(player);
break;
default:
//none
}
if (command.vote){
if(this.game.activeVote){this.game.activeVote.voteEvent(command.vote, player)}
}
}
}

View File

@@ -0,0 +1,10 @@
export function resp (req, res, success, data, code) {
res.status(code);
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
success: success,
data: data,
request_time: (new Date()).toUTCString(),
api: "UNOv1"
}));
};

View File

@@ -0,0 +1,76 @@
import { UnoSession } from '../gameLogic/UnoSession.js';
import { mkstring } from '../util/token.js';
import WebSocket from 'ws';
export interface UnoLobbyProperties {
serverName: string;
startcards: number;
}
export interface UnoLobbyElement {
session?: UnoSession;
sessionID: string;
properties: UnoLobbyProperties;
}
export class UnoLobby {
sessions: UnoLobbyElement[];
constructor(){
this.sessions = []
setInterval(()=>{
this.sessions = this.sessions.filter(el => !el.session.canDelete)
},500)
}
public createLobby(properties: UnoLobbyProperties){
const sId: string = mkstring(30);
this.sessions.push({
sessionID: sId,
properties: properties,
session: new UnoSession(sId,properties)
})
return sId;
}
public getLobbies(){
let list = [ ];
this.sessions.forEach(el => {
list.push({
id: el.sessionID,
properties: el.properties,
status: el.session.started,
players: [...el.session.players, ...el.session.winners].length
})
})
return list;
}
public joinLobby(nickname: string, lobbyId: string){
let returnval = [false,"Die Lobby wurde nicht gefunden."]
this.sessions.forEach(el => {
console.log(el.sessionID, lobbyId)
if(el.sessionID == lobbyId){
returnval = el.session.playerJoin(nickname);
console.log("found",returnval)
}
})
console.log("done",returnval)
return returnval;
//
}
public registerLobby(message: string, ws: WebSocket){
const command = JSON.parse(message);
this.sessions.forEach(el => {
if(el.sessionID == command.lobby){
console.log("found session for " + command.id + " @ " + command.lobby )
el.session.register_socket(ws,command.id);
}
})
}
}

View File

@@ -0,0 +1,36 @@
export function logInfo(text) {
var tmp = "\x1b[32m[Info] \x1b[0m" + text;
console.log(tmp)
}
export function logWarning(text) {
var tmp = "\x1b[33m[Warn] \x1b[0m" + text;
console.log(tmp)
}
export function logError(text) {
var tmp = "\x1b[31m[Error] \x1b[0m" + text;
console.log(tmp)
}
export function logCritical(text) {
var tmp = "\x1b[4m\x1b[31m[Critical]\x1b[0m \x1b[4m" + text + "\x1b[0m";
console.log(tmp)
}
export function logNotice (text) {
var tmp = "\x1b[36m[Notice] \x1b[0m" + text;
console.log(tmp)
}
export function logDebug(text) {
var tmp = "\x1b[36m[DEBUG] \x1b[0m" + text;
console.log(tmp)
}
export function logChat (text) {
var tmp = "\x1b[35m[CHAT] \x1b[0m" + text;
console.log(tmp)
}

View File

@@ -0,0 +1,3 @@
export function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}

View File

@@ -0,0 +1,9 @@
export function mkstring(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"importHelpers": true,
"target": "es2015"
},
"exclude": [
"node_modules",
"dist"
]
}

20
UNO-Backend/tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"importHelpers": true,
"target": "es2015",
"allowSyntheticDefaultImports": true
},
"exclude": [
"node_modules",
"dist"
]
}