Initial Commit

main
Benjamin Kraft 2 years ago
commit 8f52472a6f
  1. 4
      .gitignore
  2. 2277
      package-lock.json
  3. 22
      package.json
  4. 168
      ts/client.ts
  5. 76
      ts/definitions/serialized.d.ts
  6. 74
      ts/definitions/settings.d.ts
  7. 104
      ts/games/chainreact.ts
  8. 37
      ts/games/game_standard.ts
  9. 149
      ts/games/global_draw.ts
  10. 14
      ts/games/memory.ts
  11. 10
      ts/games/pong.ts
  12. 90
      ts/lib/vector.ts
  13. 105
      ts/logger.ts
  14. 149
      ts/manager.ts
  15. 150
      ts/room.ts
  16. 35
      ts/server.ts
  17. 13
      tsconfig.json

4
.gitignore vendored

@ -0,0 +1,4 @@
/js
/json_data
/logs
/node_modules

2277
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,22 @@
{
"name": "benjocraeft-web",
"version": "1.1.0",
"private": true,
"scripts": {
"compile-typescript": "npx tsc",
"start": "node js/server.js"
},
"dependencies": {
"@types/node": "^17.0.18",
"@types/socket.io-client": "^1.4.36",
"https": "^1.0.0",
"ini": "^2.0.0",
"socket.io": "^4.4.1",
"socket.io-client": "^4.4.1",
"socket.io-p2p": "^1.2.0",
"socket.io-p2p-server": "^1.2.0"
},
"devDependencies": {
"typescript": "^4.5.5"
}
}

@ -0,0 +1,168 @@
import {Room} from "./room"
import {ConnectionManager, serializeObject} from "./manager"
import {log} from "./logger";
import SocketIO = require("socket.io");
export class Client {
socket: SocketIO.Socket;
name: string;
game: string;
id: string;
isReady: boolean;
isPlayer: boolean;
isSpectator: boolean;
constructor(socket: SocketIO.Socket, manager: ConnectionManager) {
this.socket = socket;
// @ts-ignore
this.name = socket.handshake.query.name;
// @ts-ignore
this.game = socket.handshake.query.game;
this.id = socket.id;
this.setEvents(manager)
}
get serialized(): Serialized.Client {
return {
id: this.id,
name: this.name,
game: this.game,
isReady: this.isReady,
isPlayer: this.isPlayer,
isSpectator: this.isSpectator
};
}
setEvents(mng: ConnectionManager): void {
let s = this.socket;
s.on('room-list', () => this.sendRoomList());
s.on('client-list', () => this.sendClientList());
s.on('set-ready', ready => this.setReady(ready));
s.on('game-settings', settings => this.setGameSettings(settings));
s.on('create-lobby', (settings, name) => this.createRoom(settings, name));
s.on('join-lobby', roomId => this.joinRoom(roomId));
s.on('leave-lobby', roomId => this.leaveRoom(roomId));
s.on('join-spectators', () => this.joinSpectators());
s.on('join-players', () => this.joinPlayers());
s.on('start-game', lobbyId => mng.startGame(this, lobbyId));
s.on('stop-game', lobbyId => mng.stopGame(this, lobbyId));
s.on('feedback', content => mng.saveFeedbackToFile(this, content));
s.on('disconnect', () => mng.disconnected(this));
this.send('connected')
}
sendRoomList(): void {
let rooms = ConnectionManager.RoomListByGame(this.game);
this.send('room-list', rooms)
}
sendClientList(): void {
let clients = ConnectionManager.ClientListByClientId(this.id);
this.send('client-list', clients)
}
setReady(ready: boolean): void {
let room = Room.getByClientId(this.id, ConnectionManager.Instance.rooms);
if (room) {
this.isReady = ready;
room.toAll('client-list', room.clients)
}
}
setGameSettings(settings: any): void {
let room = Room.getByClientId(this.id, ConnectionManager.Instance.rooms);
if (room) {
room.gameSettings = settings;
room.toAll('game-settings', settings)
}
}
createRoom(settings: Settings.Global, name: string): void {
let room = ConnectionManager.Instance.createRoom(settings, name);
room.add(this);
this.send('created-lobby', room);
log('lobby-created', this, room)
}
joinRoom(roomId: string): Room {
let room = Room.getByRoomId(roomId, ConnectionManager.Instance.rooms);
if (!room) {
this.send('join-failed', 'Room does not exist!');
log('join-non-existent', this, new Room('not-existent', roomId))
} else if (room.hasStarted && !room.settings.spectators) {
this.send('join-failed', 'Game has started yet!');
log('join-started', this, room)
} else {
room.add(this);
log('member-joined', this, room)
}
return room
}
leaveRoom(_roomId: string): void {
let room = Room.getByClientId(this.id, ConnectionManager.Instance.rooms);
if (!room)
return;
this.leave(room.id);
if (room.runningGame)
room.runningGame.removeClient(this);
room.clients.splice(room.clients.indexOf(this), 1);
room.toAll('member-left', this.id, this.name);
room.toAll('client-list', room.clients);
this.send('left-lobby');
log('member-left', this, room);
if (room.isEmpty && !room.settings.always) {
ConnectionManager.Instance.deleteRoom(room)
}
}
joinSpectators() {
let room = Room.getByClientId(this.id, ConnectionManager.Instance.rooms);
if (!room)
return;
this.isSpectator = true;
this.isPlayer = false;
room.toAll('client-list', room.clients)
}
joinPlayers() {
let room = Room.getByClientId(this.id, ConnectionManager.Instance.rooms);
if (!room)
return;
if (room.hasStarted)
return;
this.isSpectator = false;
this.isPlayer = true;
room.toAll('client-list', room.clients)
}
send(event: string, ...args: any[]): void {
this.socket.emit(event, ...serializeObject(args))
}
join(roomId: string): void {
this.socket.join(roomId)
}
leave(roomId: string): void {
this.socket.leave(roomId)
}
}

@ -0,0 +1,76 @@
declare namespace Serialized {
interface Game {
players: Player[]
balls: Ball[]
finished: boolean
paused: boolean
}
interface Lobby {
id: string
name: string
game: string
clientCounts: number[]
clients: Client[]
hasStarted: boolean
}
interface Client {
id: string
name: string
game: string
isReady: boolean
isPlayer: boolean
isSpectator: boolean
}
interface Ball {
pos: Vector
vel: Vector
color: Color
radius: number
runningUp: boolean
nextStep: Vector
}
interface Player {
id: string
pos: Vector
dim: Vector
vel: Vector
color: Color
points: number
}
interface Vector {
x: number,
y: number
}
interface Color {
stroke: string
fill: string
}
module Player {
interface Input {
up: boolean
down: boolean
}
interface Side {
x: number
y: number
}
}
enum Directions {
Top,
Right,
Bottom,
Left,
Forward,
Back,
Center
}
}

@ -0,0 +1,74 @@
declare module Settings {
interface Global {
project: Project
frameWork: FrameWork
game: Game
always: boolean
spectators: boolean
}
interface Project {
name: string
author: string
playerCounts: number[]
}
interface FrameWork {
frameRate: number
updateRate: number
width: number
height: number
}
interface Game {
ball: Ball
player: Player
cw: number
ch: number
}
interface Ball {
radius: number
velocity: number
acceleration: number
runUp: Ball.RunUp
color: Color
cw: number
ch: number
}
interface Player {
width: number
height: number
margin: number
points: number
normal: State
weakened: State
enhanced: State
cw: number
ch: number
}
interface Color {
stroke: string
fill: string
}
interface State {
vel: Vector
color: Color
moveMargin: number
}
interface Vector {
x: number
y: number
}
module Ball {
interface RunUp {
min: number
max: number
}
}
}

@ -0,0 +1,104 @@
import {Room} from "../room"
import {ServerGame} from "./game_standard"
import {Client} from "../client"
export class Chainreact extends ServerGame {
readyForTurn: Client[];
currentTurnIndex: number;
currentGameData: any;
colorHues: { [id: string]: number };
constructor(room: Room, settings: Settings.Global) {
super(room, settings);
this.readyForTurn = []
}
setEvents(client: Client) {
let socket = client.socket;
socket.on('ready-for-turn', isDead => {
if (isDead) {
client.isPlayer = false;
client.isSpectator = true;
this.room.toAll('client-list', this.room.clients)
} else {
this.readyForTurn.push(client)
}
let allReady = true;
this.room.players.forEach(c => {
if (this.readyForTurn.find(r => r.id === c.id) == null) {
allReady = false
}
});
if (allReady) {
this.nextTurn();
this.readyForTurn = []
}
});
socket.on('set-slot', (fieldsIndex: number, slotsIndex: number) => {
this.room.toAll('set-slot', fieldsIndex, slotsIndex, socket.id)
});
socket.on('game-data', data => this.currentGameData = data)
}
addClient(client: Client): void {
super.addClient(client);
if (client.isSpectator) {
let room = this.room;
let data = this.currentGameData;
let hues = this.colorHues;
let turnId = '';
if (this.room.players[this.currentTurnIndex])
turnId = this.room.players[this.currentTurnIndex].id;
client.send('start-spectate', room, data, hues, turnId)
}
}
removeClient(client: Client): void {
super.removeClient(client);
if (this.room.players.indexOf(client) === this.currentTurnIndex)
this.nextTurn(true);
let s = client.socket;
s.removeAllListeners('set-slot');
s.removeAllListeners('ready-for-turn');
s.removeAllListeners('game-data')
}
nextTurn(skip?: boolean) {
if (this.currentTurnIndex != null && !skip) {
this.currentTurnIndex++;
if (this.currentTurnIndex >= this.room.players.length) {
this.currentTurnIndex = 0
}
} else if (!skip) {
this.setTurnAndColors()
}
let index = this.currentTurnIndex;
if (skip) {
index = this.currentTurnIndex + 1;
if (index >= this.room.players.length) {
index = 0;
this.currentTurnIndex = 0
}
}
if (this.room.players.length) {
this.room.toAll('current-turn', this.room.players[index].id)
}
}
setTurnAndColors() {
this.currentTurnIndex = Math.floor(Math.random() * this.room.players.length);
let colorHues = [0, 60, 120, 240];
this.colorHues = {};
for (let c of this.room.players) {
let index = Math.floor(Math.random() * colorHues.length);
let hue = colorHues[index];
colorHues.splice(index, 1);
this.colorHues[c.id] = hue
}
this.room.toAll('player-colors', this.colorHues)
}
}

@ -0,0 +1,37 @@
import {Room} from "../room"
import {Client} from "../client"
export class ServerGame {
room: Room;
settings: Settings.Global;
game: any;
constructor(room: Room, settings: Settings.Global) {
this.settings = settings;
this.room = room;
this.room.clients.forEach(c => this.addClient(c))
}
addClient(client: Client): void {
this.setEvents(client)
}
removeClient(client: Client): void {
this.removeEvents(client)
}
gameAction(action: string, ...args: any[]): void {
}
setEvents(client: Client): void {
let socket = client.socket;
socket.on('game-action', (action, ...args) => this.gameAction(action, ...args))
}
removeEvents(client: Client): void {
let socket = client.socket;
socket.removeAllListeners('game-action')
}
}

@ -0,0 +1,149 @@
import {ServerGame} from "./game_standard"
import {Room} from "../room"
import {Client} from "../client"
import {log} from "../logger";
import fs = require("fs");
export class GlobalDraw extends ServerGame {
lines: any[];
pixels: any[][];
linesPath = "json_data/global_draw/lines.json";
pixelsPath = "json_data/global_draw/pixels.json";
pixelCount = 1000;
constructor(lobby: Room, settings: Settings.Global) {
super(lobby, settings);
this.lines = [];
this.pixels = [];
for (let x = 0; x < this.pixelCount; x++) {
let column = [];
for (let y = 0; y < this.pixelCount; y++) {
column.push({x: x, y: y, c: "#ffffff"});
}
this.pixels.push(column);
}
let linesLoaded = false;
let pixelsLoaded = false;
this.loadDrawingsFromFile(this.linesPath, (data: any[]) => {
this.lines = data;
}, () => {
linesLoaded = true;
if (pixelsLoaded) {
this.startSaveInterval();
}
});
this.loadDrawingsFromFile(this.pixelsPath, (data: any[]) => {
for (let x = 0; x < this.pixelCount; x++) {
for (let y = 0; y < this.pixelCount; y++) {
if (data[x])
if (data[x][y])
this.pixels[x][y].c = data[x][y].c
}
}
}, () => {
pixelsLoaded = true;
if (linesLoaded) {
this.startSaveInterval();
}
});
}
startSaveInterval() {
this.saveAllDrawingsToFile();
//Saves once every day
setInterval(() => this.saveAllDrawingsToFile(), 1000 * 60 * 60 * 24);
}
addLine(line: any) {
this.lines.push(line);
this.room.toAll('add-line', line)
}
fillPixel(pixel: any) {
this.pixels[pixel.x][pixel.y].c = pixel.c;
this.room.toAll('fill-pixel', pixel)
}
loadDrawingsFromFile(drawingsPath: string, successs: (data: any[]) => void, done: () => void) {
fs.readFile(drawingsPath, 'utf8', (err, data) => {
if (err)
log('load-error', null, this.room, err.message);
else {
try {
let parsed = JSON.parse(data);
log('load-success', null, this.room);
successs(parsed);
} catch (e) {
log('parse-error', null, this.room, e.message);
}
}
done();
});
}
saveDrawingsToFile(drawings: any[], drawingsPath: string, callback: (err: any) => void) {
let splits = drawingsPath.split('/');
let path = splits.slice(0, splits.length - 1).reduce((prev, curr) => prev + '/' + curr);
let name = splits[splits.length - 1];
if (!fs.existsSync(path)) {
fs.mkdirSync(path, {recursive: true});
}
fs.writeFile(drawingsPath, JSON.stringify(drawings), callback);
}
saveAllDrawingsToFile() {
let linesSaved = false;
let pixelsSaved = false;
this.saveDrawingsToFile(this.lines, this.linesPath, (err) => {
if (err)
log('save-error', null, this.room, err.message);
else {
linesSaved = true;
if (pixelsSaved) {
this.room.toAll('all-saved');
linesSaved = false;
pixelsSaved = false
}
log('save-success', null, this.room, 'Successfully saved lines to file')
}
});
this.saveDrawingsToFile(this.pixels, this.pixelsPath, (err) => {
if (err)
log('save-error', null, this.room, err.message);
else {
pixelsSaved = true;
if (linesSaved) {
this.room.toAll('all-saved');
pixelsSaved = false;
linesSaved = false
}
log('save-success', null, this.room, 'Successfully saved pixels to file')
}
});
}
addClient(client: Client): void {
this.setEvents(client);
}
setEvents(client: Client): void {
super.setEvents(client);
let socket = client.socket;
socket.on('add-line', (line) => this.addLine(line));
socket.on('fill-pixel', (pixel) => this.fillPixel(pixel));
socket.on('request-all-lines', () => socket.emit('add-all', this.lines));
socket.on('request-all-pixels', () => socket.emit('fill-all', this.pixels));
socket.on('save-all', () => this.saveAllDrawingsToFile());
}
}

@ -0,0 +1,14 @@
import {Room} from "../room"
import {ServerGame} from "./game_standard"
export class Memory extends ServerGame {
constructor(room: Room, settings: Settings.Global) {
super(room, settings);
}
gameAction(action: string, ...args: any[]) {
this.room.toAll('game-action', action, ...args);
}
}

@ -0,0 +1,10 @@
import {ServerGame} from "./game_standard"
import {Room} from "../room";
export class Pong extends ServerGame {
constructor(lobby: Room, settings: Settings.Global) {
super(lobby, settings)
}
}

@ -0,0 +1,90 @@
export class Vector {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y
}
static fromAngle(angle: number) {
return new Vector(Math.cos(angle), Math.sin(angle))
}
static sub(v1: Vector, v2: Vector) {
return new Vector(v1.x - v2.x, v1.y - v2.y)
}
static div(v1: Vector, divider: number): Vector {
return new Vector(v1.x / divider, v1.y / divider)
}
add(other: Vector): Vector {
this.x += other.x;
this.y += other.y;
return this
}
mult(scalar: number): Vector {
this.x *= scalar;
this.y *= scalar;
return this
}
addMag(length: number): Vector {
this.setMag(this.mag() + length);
return this
}
setMag(length: number): Vector {
let mag = this.mag();
this.x /= mag;
this.y /= mag;
this.x *= length;
this.y *= length;
return this
}
rotate(rad: number): Vector {
let r = this.rotated(rad);
this.x = r.x;
this.y = r.y;
return this
}
copy(): Vector {
return new Vector(this.x, this.y)
}
heading(): number {
let r = this.rotated(Math.PI / -2);
return Math.atan2(r.x, -r.y)
}
mag() {
return Math.sqrt(this.x * this.x + this.y * this.y)
}
serialized(): Serialized.Vector {
return {
x: this.x,
y: this.y
};
}
private rotated(rad: number): Vector {
let x = Math.cos(rad) * this.x - Math.sin(rad) * this.y,
y = Math.sin(rad) * this.x + Math.cos(rad) * this.y;
return new Vector(x, y)
}
}
export let p = {
createVector: (x: number, y: number): Vector => {
return new Vector(x, y)
},
dist: (x1: number, y1: number, x2: number, y2: number): number => {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
}
};

@ -0,0 +1,105 @@
import {Room} from "./room"
import {Client} from "./client"
import fs = require('fs');
import util = require('util');
let logFolder = "./logs";
if (!fs.existsSync(logFolder)) {
fs.mkdirSync(logFolder);
}
let logFile = fs.createWriteStream(logFolder + '/' + new Date().getTime() + '.log', {flags: 'a'});
let logStdout = process.stdout;
console.log = function () {
logFile.write(util.format.apply(null, arguments) + '\n');
logStdout.write(util.format.apply(null, arguments) + '\n');
};
console.error = console.log;
process.on('uncaughtException', err => {
console.error('Uncaught error: ', err);
process.exit(1);
});
process.stdin.pipe(logFile);
export function log(type: string, client: Client, lobby?: Room, msg?: string) {
let now = new Date(Date.now()).toString(), message, name, game;
let date = '[' + now.substring(0, now.indexOf('GMT') - 1) + ']';
if (client) {
game = '[' + client.game + ']';
let short = client.id.substring(0, Math.round(client.id.length / 3));
name = '"' + client.name + '(' + short + '...)"';
} else {
if (type === 'lobby-deleted') {
game = '[' + lobby.gameName + ']';
} else {
game = '[undefined]';
}
name = 'UNKNOWN';
}
if (lobby) {
game = '[' + lobby.gameName + ']';
}
switch (type) {
case 'join-non-existent':
message = name + ' tried to join non-existent lobby "' + lobby.id + '"';
break;
case 'join-started':
message = name + ' tried to join the started game "' + lobby.id + '"';
break;
case 'lobby-created':
message = name + ' created new lobby: "' + lobby.id + '"';
break;
case 'game-started':
message = name + ' started the game: "' + lobby.id + '"';
break;
case 'game-stopped':
message = name + ' stopped the game: "' + lobby.id + '"';
break;
case 'member-joined':
message = name + ' joined the lobby "' + lobby.id + '"';
break;
case 'member-left':
message = name + ' left the lobby "' + lobby.id + '"';
break;
case 'lobby-deleted':
message = 'Lobby "' + lobby.id + '" was deleted';
break;
case 'save-success':
message = msg;
break;
case 'save-error':
message = 'Failed to save contents to file: ' + msg;
break;
case 'load-success':
message = 'Successfully loaded and parsed file contents';
break;
case 'load-error':
message = 'Failed to load file: ' + msg;
break;
case 'parse-error':
message = 'Failed to parse contents: ' + msg;
break;
case 'feedback':
message = 'Saved feedback to file: ' + msg;
break;
case 'connection':
message = name + ' connected';
break;
case 'disconnection':
message = name + ' disconnected';
break;
case 'startup':
message = msg;
break;
}
console.log(date + game + ' ---> {' + message + '}');
}

@ -0,0 +1,149 @@
import {Room} from "./room"
import {Client} from "./client"
import {log} from "./logger"
import fs = require("fs");
import SocketIO = require("socket.io");
export class ConnectionManager {
static Instance: ConnectionManager;
io: SocketIO.Server;
rooms: Room[];
constructor(io: SocketIO.Server) {
ConnectionManager.Instance = this;
this.io = io;
this.rooms = [];
let drawSettings = {
project: {
name: 'global-draw',
playerCounts: null
},
always: true,
spectators: true
};
let drawRoom = this.createRoom(drawSettings, '');
drawRoom.id = 'global-draw-room';
drawRoom.startGame();
this.rooms.push(drawRoom);
}
static RoomListByGame(game: string): Room[] {
return this.Instance.rooms.filter(l => l.gameName === game)
}
static ClientListByClientId(clientId: string): Client[] {
let room = Room.getByClientId(clientId, this.Instance.rooms);
return room.clients
}
newSocket(socket: SocketIO.Socket): void {
let client = new Client(socket, this);
log('connection', client)
}
roomListUpdate(): void {
this.io.sockets.emit('room-list', serializeObject(this.rooms))
}
createRoom(settings: Settings.Global | any, name: string): Room {
let roomId = Room.generateCode(10);
// @ts-ignore
let room = new Room(name, roomId, settings, this.io);
this.rooms.push(room);
this.roomListUpdate();
return room
}
deleteRoom(room: Room): void {
this.rooms.splice(this.rooms.indexOf(room), 1);
this.roomListUpdate();
log('lobby-deleted', null, room)
}
//Starts the game of a room with given id
startGame(client: Client, _roomId: string): void {
let lobby = Room.getByClientId(client.id, this.rooms);
if (!lobby) return;
if (!lobby.hasStarted) {
lobby.startGame();
log('game-started', client, lobby)
}
this.io.sockets.emit('room-list', serializeObject(this.rooms))
}
//Stops the game of a lobby with given id
stopGame(client: Client, lobbyId: string): void {
let lobby = Room.getByRoomId(lobbyId, this.rooms);
if (!lobby) return;
lobby.stopGame(client);
log('game-stopped', client, lobby)
}
//Saves user feedback to a file
saveFeedbackToFile(client: Client, content: string): void {
let date = new Date(Date.now()).toString();
let path = "feedback/" + client.game + '.txt';
let saveToFile = (content: string) => {
fs.writeFile(path, content, (err: any) => {
if (err)
log('save-error', client, null, err.message);
else
log('feedback', client, null, path)
});
};
if (fs.existsSync(path)) {
fs.readFile(path, 'utf8', (err, data) => {
if (err)
log('load-error', client, null, err.message);
else {
log('load-success', client, null);
let newContent = data + '\n\n\n\n' + date + '\n\n' + content;
saveToFile(newContent)
}
})
} else {
saveToFile(date + '\n' + content)
}
}
//Removes a disconnected client from all references
disconnected(client: Client): void {
let room = Room.getByClientId(client.id, this.rooms);
if (room)
client.leaveRoom(room.id);
log('disconnection', client)
}
}
export function serializeObject(object: any): any {
function serialize(obj: any) {
if (!obj)
return obj;
if (obj.serialized)
return obj.serialized;
else if (obj instanceof Array) {
let content = [];
obj.forEach(o => {
content.push(serialize(o))
});
return content
}
return obj
}
return serialize(object)
}

@ -0,0 +1,150 @@
import {Client} from "./client"
import {ServerGame} from "./games/game_standard"
import {Memory} from "./games/memory"
import {Pong} from "./games/pong"
import {GlobalDraw} from "./games/global_draw"
import {Chainreact} from "./games/chainreact"
import {serializeObject} from "./manager";
import {Server} from "socket.io";
export class Room {
id: string;
gameName: string;
clientCounts: number[];
io: Server;
clients: Client[];
runningGame: ServerGame;
settings: Settings.Global;
gameSettings: any;
name: string;
constructor(name: string, id: string, settings?: Settings.Global, io?: Server) {
this.id = id;
this.name = name;
if (!io || !settings) return;
this.settings = settings;
this.gameName = settings.project.name;
this.clientCounts = settings.project.playerCounts;
this.io = io;
this.clients = [];
this.gameSettings = {}
}
get leader(): Client {
return this.players[0]
}
get players(): Client[] {
return this.clients.filter(c => c.isPlayer)
}
get spectators(): Client[] {
return this.clients.filter(c => c.isSpectator)
}
get serialized(): Serialized.Lobby {
return {
id: this.id,
name: this.name,
game: this.gameName,
clientCounts: this.clientCounts,
clients: serializeObject(this.clients),
hasStarted: this.hasStarted
};
}
get isEmpty(): boolean {
return !(this.clients.length)
}
get hasStarted(): boolean {
return this.runningGame != null
}
static getByRoomId(id: string, lobbies: Room[]): Room {
for (let l of lobbies) {
if (l.id === id)
return l
}
return null;
}
static getByClientId(id: string, lobbies: Room[]): Room {
for (let l of lobbies) {
for (let c of l.clients) {
if (c.id === id)
return l
}
}
return null;
}
static generateCode(elements: number): string {
let code = '';
let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
while (elements--) {
code += possible.charAt(Math.floor(Math.random() * possible.length))
}
return code
}
startGame(): void {
let seed = Math.random() * 10000;
this.toAll('start-game', seed);
this.runGame()
}
stopGame(client: Client): void {
this.toAll('stop-game', client);
this.runningGame = null
}
add(client: Client): void {
this.clients.push(client);
let isPlayer = !this.hasStarted && this.hasValidPlayerCount();
client.isPlayer = isPlayer;
client.isSpectator = !isPlayer;
client.isReady = false;
client.join(this.id);
this.toAll('member-joined', client.id, client.name);
this.toAll('client-list', this.clients);
this.toAll('game-settings', this.gameSettings);
if (this.hasStarted)
this.runningGame.addClient(client)
}
hasValidPlayerCount(): boolean {
let valid = false;
this.clientCounts.forEach(c => {
if (c === this.clients.length)
valid = true
});
return valid
}
runGame(): void {
switch (this.gameName) {
case 'memory':
this.runningGame = new Memory(this, this.settings);
break;
case 'pong':
this.runningGame = new Pong(this, this.settings);
break;
case 'global-draw':
this.runningGame = new GlobalDraw(this, this.settings);
break;
case 'chainreact':
this.runningGame = new Chainreact(this, this.settings);
break;
}
}
toAll(event: string, ...args: any[]): void {
this.io.to(this.id).emit(event, serializeObject(this), ...serializeObject(args))
}
}

@ -0,0 +1,35 @@
'use strict';
import {log} from "./logger";
import {ConnectionManager} from "./manager";
import https = require('https');
import {Server} from 'socket.io';
const p2p = require('socket.io-p2p-server').Server;
import fs = require('fs');
import ini = require('ini');
let rootDir = __dirname + '/../..';
let httpsPort = ini.parse(fs.readFileSync(rootDir + '/env_config.ini', 'utf-8'))['nodejs_port'];
let cert = fs.readFileSync(rootDir + '/ssl_certificate/cert.pem');
let key = fs.readFileSync(rootDir + '/ssl_certificate/key.pem');
let httpsServer = https.createServer({key: key, cert: cert});
let sIO = new Server(httpsServer, {
cors: {
origin: ["https://play.benjamin-kraft.local", "https://dev.play.benjamin-kraft.eu", "https://play.benjamin-kraft.eu"]
}
});
sIO.use(p2p);
httpsServer.listen(httpsPort);
let connectionManager = new ConnectionManager(sIO);
// On new connection
sIO.on('connection', socket => connectionManager.newSocket(socket));
log('startup', null, null, 'Server is listening on port ' + httpsPort);

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"sourceMap": true,
"alwaysStrict": true,
"outDir": "./js",
"removeComments": true
},
"include": [
"./ts"
]
}
Loading…
Cancel
Save