Ported server code to nodeJS
This commit is contained in:
@@ -72,7 +72,7 @@ export default {
|
|||||||
login (name) {
|
login (name) {
|
||||||
send({
|
send({
|
||||||
type: 'login',
|
type: 'login',
|
||||||
message: name
|
name: name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export default {
|
|||||||
|
|
||||||
this.player.targetAngle = Math.atan2(dy, dx)
|
this.player.targetAngle = Math.atan2(dy, dx)
|
||||||
this.player.angle = this.player.targetAngle
|
this.player.angle = this.player.targetAngle
|
||||||
send({ message: this.player, type: 'update' })
|
send({ player: this.player, type: 'update' })
|
||||||
},
|
},
|
||||||
updatePlayer (player) {
|
updatePlayer (player) {
|
||||||
player.x += this.settings.playerSpeed * Math.cos(player.angle)
|
player.x += this.settings.playerSpeed * Math.cos(player.angle)
|
||||||
|
|||||||
@@ -31,19 +31,19 @@ export default function createSocketPlugin () {
|
|||||||
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'login':
|
case 'login':
|
||||||
store.dispatch('game/login', data.message)
|
store.dispatch('game/login', data.player)
|
||||||
break
|
break
|
||||||
case 'gameSettings':
|
case 'gameSettings':
|
||||||
store.dispatch('game/settings', data.message)
|
store.dispatch('game/settings', data.gameSettings)
|
||||||
break
|
break
|
||||||
case 'gameUpdate':
|
case 'gameUpdate':
|
||||||
store.dispatch('game/update', data.message)
|
store.dispatch('game/update', data)
|
||||||
break
|
break
|
||||||
case 'gamePlayerDead':
|
case 'gamePlayerDead':
|
||||||
store.dispatch('game/dead', data.message)
|
store.dispatch('game/dead', data.player)
|
||||||
break
|
break
|
||||||
case 'gamePlayerSpawn':
|
case 'gamePlayerSpawn':
|
||||||
store.dispatch('game/spawn', data.message)
|
store.dispatch('game/spawn', data.player)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
5
server/package-lock.json
generated
5
server/package-lock.json
generated
@@ -1910,6 +1910,11 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"mkdirp": "^0.5.1"
|
"mkdirp": "^0.5.1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "7.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||||
|
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "tronio-server",
|
"name": "tronio-server",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"description": "Tron.io server",
|
"description": "Tron.io game server",
|
||||||
"main": "index.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node src/server.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
"author": "gltron",
|
"author": "gltron",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.17.1"
|
"express": "^4.17.1",
|
||||||
|
"ws": "^7.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^7.12.1",
|
"eslint": "^7.12.1",
|
||||||
|
|||||||
107
server/src/game.js
Normal file
107
server/src/game.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
const Player = require('./models/player')
|
||||||
|
const gameSettings = require('./models/gameSettings')
|
||||||
|
|
||||||
|
const players = new Map()
|
||||||
|
const sockets = new Map()
|
||||||
|
|
||||||
|
let lastUpdateTime = Date.now()
|
||||||
|
let doUpdate = true
|
||||||
|
|
||||||
|
function login (connection, name) {
|
||||||
|
sockets[connection.id] = connection
|
||||||
|
players[connection.id] = new Player(connection.id, name)
|
||||||
|
|
||||||
|
connection.send({
|
||||||
|
type: 'login',
|
||||||
|
player: players[connection.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
connection.send({
|
||||||
|
type: 'gameSettings',
|
||||||
|
gameSettings: gameSettings
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout (connection) {
|
||||||
|
sockets.delete(connection.id)
|
||||||
|
players.delete(connection.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function respawn (connection) {
|
||||||
|
players[connection.id].reset()
|
||||||
|
|
||||||
|
connection.send({
|
||||||
|
type: 'gamePlayerSpawn',
|
||||||
|
player: players[connection.id]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function update (connection, player) {
|
||||||
|
players[connection.id].angle = player.angle
|
||||||
|
}
|
||||||
|
|
||||||
|
function kill (player) {
|
||||||
|
player.kill()
|
||||||
|
sockets[player.id].send({
|
||||||
|
type: 'gamePlayerDead',
|
||||||
|
player: player
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function step () {
|
||||||
|
const currentTime = Date.now()
|
||||||
|
const durationSinceLastUpdate = (currentTime - lastUpdateTime) / 1000
|
||||||
|
const tickToSimulate = (durationSinceLastUpdate * 120) / 1000
|
||||||
|
lastUpdateTime = currentTime
|
||||||
|
|
||||||
|
players.forEach((player, id) => {
|
||||||
|
if (player.isOutOfBorders()) kill(player)
|
||||||
|
|
||||||
|
players.forEach((player2, id2) => {
|
||||||
|
if (player.state === 'DEAD') return
|
||||||
|
|
||||||
|
for (let i = 0; i < player2.walls.length - 2; i++) {
|
||||||
|
// Prevent self destroy on last wall
|
||||||
|
if (id === id2 && i >= player2.walls.length - 1) break
|
||||||
|
|
||||||
|
const wallA = player2.walls[i]
|
||||||
|
const wallB = player2.walls[i + 1]
|
||||||
|
|
||||||
|
if (player.isCloseToWall(wallA, wallB)) {
|
||||||
|
if (player.isCrossingLine(wallA, wallB)) {
|
||||||
|
if (id !== id2) player2.score += 300
|
||||||
|
kill(player)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
players.forEach((player, id) => {
|
||||||
|
player.step(tickToSimulate)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (doUpdate) broadcastUpdate()
|
||||||
|
doUpdate = !doUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastUpdate () {
|
||||||
|
const update = {
|
||||||
|
type: 'update',
|
||||||
|
players: players,
|
||||||
|
time: lastUpdateTime
|
||||||
|
}
|
||||||
|
sockets.forEach((connection, id) => {
|
||||||
|
connection.send(update)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(() => step(), 1000 / 60)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
respawn,
|
||||||
|
update
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
const express = require('express')
|
|
||||||
const app = express()
|
|
||||||
const port = 3000
|
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
res.send('Hello World!')
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
|
||||||
console.log(`Example app listening at http://localhost:${port}`)
|
|
||||||
})
|
|
||||||
8
server/src/models/gameSettings.js
Normal file
8
server/src/models/gameSettings.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module.exports = Object.freeze({
|
||||||
|
playerSize: 10,
|
||||||
|
playerSpeed: 2.3,
|
||||||
|
playerTurnSpeed: 10,
|
||||||
|
wallSize: 8,
|
||||||
|
wallUpdate: 20,
|
||||||
|
arenaSize: 1000
|
||||||
|
})
|
||||||
74
server/src/models/player.js
Normal file
74
server/src/models/player.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
const Wall = require('./wall')
|
||||||
|
const gameSettings = require('./gameSettings')
|
||||||
|
|
||||||
|
class Player {
|
||||||
|
constructor (id, name) {
|
||||||
|
this.id = id
|
||||||
|
this.name = name
|
||||||
|
this.bestScore = 0
|
||||||
|
this.angle = 0
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
reset () {
|
||||||
|
this.score = 0
|
||||||
|
this.color = '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6)
|
||||||
|
this.x = gameSettings.arenaSize * (0.25 + Math.random() * 0.5)
|
||||||
|
this.y = gameSettings.arenaSize * (0.25 + Math.random() * 0.5)
|
||||||
|
this.walls = []
|
||||||
|
this.lastWall = 0
|
||||||
|
this.state = 'ALIVE'
|
||||||
|
}
|
||||||
|
|
||||||
|
kill () {
|
||||||
|
this.state = 'DEAD'
|
||||||
|
if (this.bestScore < this.score) this.bestScore = this.score
|
||||||
|
}
|
||||||
|
|
||||||
|
isOutOfBorders () {
|
||||||
|
return this.x - gameSettings.playerSize < 0 ||
|
||||||
|
this.x + gameSettings.playerSize > gameSettings.arenaSize ||
|
||||||
|
this.y - gameSettings.playerSize < 0 ||
|
||||||
|
this.y + gameSettings.playerSize > gameSettings.arenaSize
|
||||||
|
}
|
||||||
|
|
||||||
|
isCloseToWall (wallA, wallB) {
|
||||||
|
const xar = Math.min(wallA.x, wallB.x) - gameSettings.playerSize
|
||||||
|
const yar = Math.min(wallA.y, wallB.y) - gameSettings.playerSize
|
||||||
|
const xbr = Math.min(wallA.x, wallB.x) + gameSettings.playerSize
|
||||||
|
const ybr = Math.min(wallA.y, wallB.y) + gameSettings.playerSize
|
||||||
|
|
||||||
|
return ((this.x >= xar && this.x <= xbr) && (this.y >= yar && this.y <= ybr))
|
||||||
|
}
|
||||||
|
|
||||||
|
isCrossingLine (wallA, wallB) {
|
||||||
|
const xa = wallA.x
|
||||||
|
const ya = wallA.y
|
||||||
|
const xb = wallB.x
|
||||||
|
const yb = wallB.y
|
||||||
|
const xc = this.x
|
||||||
|
const yc = this.y
|
||||||
|
const radius = gameSettings.playerSize
|
||||||
|
|
||||||
|
return Math.abs((yb - ya) * xc - (xb - xa) * yc + xb * ya - yb * xa) / Math.sqrt(Math.pow(xb - xa, 2) + Math.pow(yb - ya, 2)) < radius
|
||||||
|
}
|
||||||
|
|
||||||
|
step (tickToSimulate) {
|
||||||
|
if (this.state === 'DEAD') return
|
||||||
|
|
||||||
|
for (let i = 0; i < tickToSimulate; i++) {
|
||||||
|
this.lastWall++
|
||||||
|
|
||||||
|
if (this.lastWall > gameSettings.wallUpdate) {
|
||||||
|
this.walls.push(new Wall(this.x, this.y))
|
||||||
|
this.score++
|
||||||
|
this.lastWall = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.x = this.x + gameSettings.playerSpeed * Math.cos(this.angle)
|
||||||
|
this.y = this.y + gameSettings.playerSpeed * Math.sin(this.angle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { Player }
|
||||||
8
server/src/models/wall.js
Normal file
8
server/src/models/wall.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class Wall {
|
||||||
|
constructor (x, y) {
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { Wall }
|
||||||
35
server/src/server.js
Normal file
35
server/src/server.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const express = require('express')
|
||||||
|
const app = express()
|
||||||
|
const game = require('./game')
|
||||||
|
const WebSocketServer = require('ws').Server
|
||||||
|
const port = process.env.PORT || 3000
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.send('Hello World!')
|
||||||
|
})
|
||||||
|
|
||||||
|
const server = app.listen(port, () => {
|
||||||
|
console.log(`Tron.io running on port ${port}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ server })
|
||||||
|
|
||||||
|
wss.on('connection', function (connection) {
|
||||||
|
connection.on('close', game.logout(connection))
|
||||||
|
|
||||||
|
connection.on('message', (message) => {
|
||||||
|
let data
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(message)
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case 'login': game.login(connection, data.name); break
|
||||||
|
case 'respawn': game.respawn(connection); break
|
||||||
|
case 'update': game.update(connection, data.player); break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user