453 lines
10 KiB
JavaScript
453 lines
10 KiB
JavaScript
const express = require('express')
|
|
const socketio = require('socket.io')
|
|
const http = require('http')
|
|
const path = require('path')
|
|
|
|
let app = express()
|
|
let server = http.createServer(app)
|
|
let io = socketio(server)
|
|
|
|
app.enable('trust proxy')
|
|
app.disable('x-powered-by')
|
|
|
|
app.use('/', express.static(path.join(__dirname, '/client/')))
|
|
|
|
function playerNameValidation (name) {
|
|
if (/^([A-Z0-9_\-@]{3,20})$/i.test(name)) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
let clients = {}
|
|
let games = {}
|
|
|
|
let totalGames = 0
|
|
|
|
// Generate a random int betweem two ints
|
|
function getRandomInt(min, max) {
|
|
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
}
|
|
|
|
// Generate random string of characters
|
|
function nuid(len) {
|
|
let buf = [],
|
|
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
|
charlen = chars.length
|
|
|
|
for (let i = 0; i < len; ++i) {
|
|
buf.push(chars[getRandomInt(0, charlen - 1)])
|
|
}
|
|
|
|
return buf.join('')
|
|
}
|
|
|
|
function clientsBySocketID (id) {
|
|
let result = null
|
|
|
|
for (let uid in clients) {
|
|
let client = clients[uid]
|
|
if (client.sockID === id) {
|
|
result = uid
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
function determineOpponent (myIndex) {
|
|
let opponent = 'red'
|
|
|
|
if (myIndex === 'red') {
|
|
opponent = 'blue'
|
|
}
|
|
|
|
return opponent
|
|
}
|
|
|
|
function killGamesClientIsIn (uid) {
|
|
for (let gameId in games) {
|
|
let game = games[gameId]
|
|
if (game.blue && game.blue === uid) {
|
|
if (!game.isWaiting && game.red) {
|
|
clients[game.red].socket.emit('game_end', {win: true, result: 0})
|
|
}
|
|
} else if (game.red && game.red === uid) {
|
|
if (clients[game.blue]) {
|
|
clients[game.blue].socket.emit('game_end', {win: true, result: 0})
|
|
}
|
|
} else {
|
|
continue
|
|
}
|
|
delete games[gameId]
|
|
console.log(gameId + ' was ended abruptly on ' + uid + '\'s demand.')
|
|
}
|
|
}
|
|
|
|
function createNewGame (uid) {
|
|
let client = clients[uid]
|
|
let gameId = nuid(16)
|
|
|
|
client.socket.emit('game_new_done', {gameId: gameId})
|
|
|
|
console.log(client.name + ' has started a new game. ID: ' + gameId)
|
|
|
|
games[gameId] = {
|
|
blue: uid,
|
|
red: null,
|
|
isWaiting: true,
|
|
turn: 1,
|
|
places: [[],[],[],[],[],[],[],[],[]],
|
|
created: new Date(),
|
|
started: null
|
|
}
|
|
}
|
|
|
|
function joinGame (uid, gameId) {
|
|
let me = clients[uid]
|
|
|
|
if (!games[gameId]) {
|
|
return me.socket.emit('game_error', {message: 'That game has ended!'})
|
|
}
|
|
|
|
if (games[gameId].red != null) {
|
|
return me.socket.emit('game_error', {message: 'That game has already started!'})
|
|
}
|
|
|
|
if (!clients[games[gameId].blue]) {
|
|
return me.socket.emit('game_error', {message: 'That game has ended!'})
|
|
}
|
|
|
|
let game = games[gameId]
|
|
|
|
game.red = uid
|
|
|
|
game.isWaiting = false
|
|
game.started = new Date()
|
|
|
|
let opponent = clients[game.blue]
|
|
|
|
if (!opponent) {
|
|
return me.socket.emit('game_error', {message: 'Your opponent abruptly dissappeared, what?'})
|
|
}
|
|
|
|
opponent.socket.emit('game_start', {gameId: gameId, opponentId: uid, opponentName: me.name, color: 'blue'})
|
|
me.socket.emit('game_start', {gameId: gameId, opponentId: opponent.uid, opponentName: opponent.name, color: 'red'})
|
|
|
|
game.turn = 'blue'
|
|
clients[game.blue].socket.emit('turn', true)
|
|
clients[uid].socket.emit('turn', false)
|
|
|
|
totalGames += 1
|
|
}
|
|
|
|
function endGame (gameId, victoryId, loserId, status) {
|
|
if (clients[victoryId]) {
|
|
clients[victoryId].socket.emit('game_end', {win: true, result: status})
|
|
}
|
|
|
|
if (clients[loserId]) {
|
|
clients[loserId].socket.emit('game_end', {win: false, result: status})
|
|
}
|
|
|
|
delete games[gameId]
|
|
console.log(gameId + ' ended with ' + victoryId + '\'s victory.')
|
|
}
|
|
|
|
function waitingGamesList (uid) {
|
|
let result = []
|
|
let cap = 0
|
|
|
|
let gamesInSession = 0
|
|
for (let i in games) {
|
|
let game = games[i]
|
|
if (!game.isWaiting) {
|
|
gamesInSession += 1
|
|
}
|
|
}
|
|
|
|
for (let gameId in games) {
|
|
if (cap >= 20) break
|
|
|
|
let game = games[gameId]
|
|
|
|
if (game.isWaiting) {
|
|
let userName = clients[game.blue].name
|
|
if (uid && game.blue === uid) continue
|
|
|
|
result.push({
|
|
gameId: gameId,
|
|
name: userName,
|
|
started: game.started
|
|
})
|
|
|
|
cap += 1
|
|
}
|
|
}
|
|
|
|
return {
|
|
sessions: gamesInSession,
|
|
totalGames: totalGames,
|
|
list: result
|
|
}
|
|
}
|
|
|
|
function determinePlayerById (gameId, uid) {
|
|
let game = games[gameId]
|
|
|
|
if (!game) return null
|
|
|
|
if (game.blue && game.blue === uid) {
|
|
return 'blue'
|
|
} else if (game.red && game.red === uid) {
|
|
return 'red'
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
function getPiece (game, col, index) {
|
|
if (col > 8 || index > 8) return
|
|
col = game.places[col]
|
|
|
|
if (!col) return
|
|
|
|
if (!col.length) {
|
|
return
|
|
}
|
|
|
|
let match = null
|
|
for (let i in col) {
|
|
if (col[i].y === index) {
|
|
match = col[i]
|
|
break
|
|
}
|
|
}
|
|
return match
|
|
}
|
|
|
|
function detectWin (color, game) {
|
|
let win = false
|
|
for (let c in game.places) {
|
|
let col = game.places[c]
|
|
for (let p in col) {
|
|
let piece = col[p]
|
|
let matches = 0
|
|
for (let i = 0; i < 4; i++) {
|
|
let pAt = getPiece(game, parseInt(c) + i, piece.y)
|
|
if (pAt && pAt.color === color) {
|
|
matches += 1
|
|
} else {
|
|
matches = 0
|
|
}
|
|
}
|
|
if (matches >= 4) {
|
|
win = true
|
|
console.log('horizontal win')
|
|
break
|
|
}
|
|
matches = 0
|
|
for (let i = 0; i < 4; i++) {
|
|
let pAt = getPiece(game, parseInt(c), piece.y + i)
|
|
if (pAt && pAt.color === color) {
|
|
matches += 1
|
|
} else {
|
|
matches = 0
|
|
}
|
|
}
|
|
if (matches >= 4) {
|
|
console.log('vertical win')
|
|
win = true
|
|
break
|
|
}
|
|
matches = 0
|
|
for (let i = 0; i < 4; i++) {
|
|
let pAt = getPiece(game, parseInt(c) + i, piece.y - i)
|
|
if (pAt && pAt.color === color) {
|
|
matches += 1
|
|
} else {
|
|
matches = 0
|
|
}
|
|
}
|
|
if (matches >= 4) {
|
|
console.log('diagonal right win')
|
|
win = true
|
|
break
|
|
}
|
|
matches = 0
|
|
for (let i = 0; i < 4; i++) {
|
|
let pAt = getPiece(game, parseInt(c) - i, piece.y - i)
|
|
if (pAt && pAt.color === color) {
|
|
matches += 1
|
|
} else {
|
|
matches = 0
|
|
}
|
|
}
|
|
if (matches >= 4) {
|
|
console.log('diagonal left win')
|
|
win = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return win
|
|
}
|
|
|
|
function detectTie (game) {
|
|
let tie = true
|
|
for (let c in game.places) {
|
|
let col = game.places[c]
|
|
if (col.length !== 9) {
|
|
tie = false
|
|
break
|
|
}
|
|
}
|
|
return tie
|
|
}
|
|
|
|
io.on('connection', (socket) => {
|
|
socket.on('session_create', (data) => {
|
|
if (!data.name) {
|
|
return socket.emit('login_status', {success: false, message: 'Invalid name.'})
|
|
}
|
|
|
|
if (!playerNameValidation(data.name)) {
|
|
return socket.emit('login_status', {success: false, message: 'Invalid name.'})
|
|
}
|
|
|
|
let playerUid = nuid(32)
|
|
|
|
socket.emit('login_status', {success: true, uid: playerUid, name: data.name})
|
|
clients[playerUid] = {
|
|
socket: socket,
|
|
name: data.name,
|
|
sockID: socket.conn.id
|
|
}
|
|
|
|
console.log('New player: "' + data.name + '" with uid ' + playerUid)
|
|
})
|
|
|
|
socket.on('poll_games', () => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
socket.emit('poll_games_res', waitingGamesList(client))
|
|
})
|
|
|
|
socket.on('game_attempt_join', (data) => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
|
|
if (!client) {
|
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
|
socket.emit('force_relog')
|
|
return
|
|
}
|
|
|
|
if (!data.gameId) return
|
|
|
|
joinGame(client, data.gameId)
|
|
})
|
|
|
|
socket.on('leave_game', (data) => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
|
|
if (!client) return
|
|
killGamesClientIsIn(client)
|
|
|
|
socket.emit('left_success')
|
|
})
|
|
|
|
socket.on('new_game', () => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
|
|
if (!client) {
|
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
|
socket.emit('force_relog')
|
|
return
|
|
}
|
|
|
|
createNewGame(client)
|
|
})
|
|
|
|
socket.on('chat_send', (data) => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
|
|
if (!client) {
|
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
|
socket.emit('force_relog')
|
|
return
|
|
}
|
|
|
|
let game = games[data.gameId]
|
|
let playerInGame = determinePlayerById(data.gameId, client)
|
|
|
|
if (!playerInGame) {
|
|
socket.emit('game_error', {message: 'unexpected error. code: 763'})
|
|
return
|
|
}
|
|
|
|
let opponent = determineOpponent(playerInGame)
|
|
let opponentObj = game[opponent]
|
|
let me = game[playerInGame]
|
|
|
|
clients[opponentObj].socket.emit('chat', {name: clients[me].name, message: data.message})
|
|
})
|
|
|
|
socket.on('place_at', (data) => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
|
|
if (!client) {
|
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
|
socket.emit('force_relog')
|
|
return
|
|
}
|
|
|
|
let game = games[data.gameId]
|
|
let playerInGame = determinePlayerById(data.gameId, client)
|
|
|
|
if (!playerInGame) {
|
|
socket.emit('game_error', {message: 'unexpected error. code: 763'})
|
|
return
|
|
}
|
|
|
|
if (data.column == null || data.column > 9) {
|
|
socket.emit('game_error', {message: 'Unexpected column'})
|
|
return
|
|
}
|
|
|
|
let opponent = determineOpponent(playerInGame)
|
|
opponent = game[opponent]
|
|
|
|
let me = game[playerInGame]
|
|
|
|
clients[me].socket.emit('place', {column: data.column, color: playerInGame})
|
|
clients[opponent].socket.emit('place', {column: data.column, color: playerInGame})
|
|
|
|
game.places[data.column].push({color: playerInGame, y: 8 - game.places[data.column].length})
|
|
|
|
clients[me].socket.emit('turn', false)
|
|
clients[opponent].socket.emit('turn', true)
|
|
|
|
if (detectWin(playerInGame, game)) {
|
|
endGame(data.gameId, me, opponent, 1)
|
|
return
|
|
}
|
|
|
|
if (detectTie(game)) {
|
|
endGame(data.gameId, me, opponent, 2)
|
|
return
|
|
}
|
|
})
|
|
|
|
socket.on('disconnect', () => {
|
|
let client = clientsBySocketID(socket.conn.id)
|
|
if (!client) return
|
|
|
|
killGamesClientIsIn(client)
|
|
|
|
console.log('Player uid ' + client + ' left.')
|
|
|
|
delete clients[client]
|
|
})
|
|
})
|
|
|
|
server.listen(8245)
|