parent
f3cde50871
commit
a88e2b353a
17 changed files with 0 additions and 3477 deletions
@ -1,4 +0,0 @@ |
|||||||
/js |
|
||||||
/json_data |
|
||||||
/logs |
|
||||||
/node_modules |
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@ |
|||||||
{ |
|
||||||
"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" |
|
||||||
} |
|
||||||
} |
|
@ -1,168 +0,0 @@ |
|||||||
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) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,76 +0,0 @@ |
|||||||
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 |
|
||||||
} |
|
||||||
} |
|
@ -1,74 +0,0 @@ |
|||||||
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 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,104 +0,0 @@ |
|||||||
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) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
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') |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,149 +0,0 @@ |
|||||||
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()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
import {ServerGame} from "./game_standard" |
|
||||||
import {Room} from "../room"; |
|
||||||
|
|
||||||
export class Pong extends ServerGame { |
|
||||||
|
|
||||||
constructor(lobby: Room, settings: Settings.Global) { |
|
||||||
super(lobby, settings) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,90 +0,0 @@ |
|||||||
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)) |
|
||||||
} |
|
||||||
}; |
|
@ -1,105 +0,0 @@ |
|||||||
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 + '}'); |
|
||||||
} |
|
@ -1,149 +0,0 @@ |
|||||||
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) |
|
||||||
} |
|
@ -1,150 +0,0 @@ |
|||||||
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)) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,35 +0,0 @@ |
|||||||
'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); |
|
@ -1,13 +0,0 @@ |
|||||||
{ |
|
||||||
"compilerOptions": { |
|
||||||
"target": "es6", |
|
||||||
"module": "commonjs", |
|
||||||
"sourceMap": true, |
|
||||||
"alwaysStrict": true, |
|
||||||
"outDir": "./js", |
|
||||||
"removeComments": true |
|
||||||
}, |
|
||||||
"include": [ |
|
||||||
"./ts" |
|
||||||
] |
|
||||||
} |
|
Loading…
Reference in new issue