@inglorious/server 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/game-loader.js +13 -24
- package/src/index.js +19 -53
- package/src/ws-handler.js +18 -25
- package/src/default-game.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A real-time, lightweight server designed to enable multiplayer experiences for games built with the Inglorious Engine.",
|
|
5
5
|
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"pino": "^9.9.4",
|
|
43
43
|
"ws": "^8.18.3",
|
|
44
|
-
"@inglorious/store": "1.
|
|
44
|
+
"@inglorious/store": "1.2.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"globals": "^16.3.0",
|
package/src/game-loader.js
CHANGED
|
@@ -11,39 +11,28 @@ export async function loadGame(gameFilePath, logger) {
|
|
|
11
11
|
let gameConfig = {}
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
if (!gameFilePath) {
|
|
15
|
+
return gameConfig
|
|
16
|
+
}
|
|
17
|
+
|
|
15
18
|
const __filename = fileURLToPath(import.meta.url)
|
|
16
19
|
const __dirname = path.dirname(__filename)
|
|
17
20
|
const modulePath = path.resolve(__dirname, gameFilePath)
|
|
18
|
-
const
|
|
19
|
-
gameConfig = config
|
|
20
|
-
gameConfig.types ??= {}
|
|
21
|
-
gameConfig.entities ??= {}
|
|
21
|
+
const module = await import(`file://${modulePath}`)
|
|
22
22
|
|
|
23
23
|
logger.info(`Loaded game data from ${gameFilePath}`)
|
|
24
|
+
gameConfig = module.default
|
|
25
|
+
return gameConfig
|
|
24
26
|
} catch (error) {
|
|
25
27
|
logger.error(`Failed to load game module: ${gameFilePath}`)
|
|
26
28
|
logger.error(error)
|
|
27
|
-
// Fallback to a basic initial state if loading fails.
|
|
28
|
-
gameConfig = {
|
|
29
|
-
types: {
|
|
30
|
-
player: {
|
|
31
|
-
move: (entity, payload) => {
|
|
32
|
-
logger.info(
|
|
33
|
-
`Player ${entity.id} moved to ${payload.x}, ${payload.y}`,
|
|
34
|
-
)
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
box: {},
|
|
38
|
-
},
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
player1: { id: "player1", type: "player", x: 0, y: 0 },
|
|
42
|
-
box1: { id: "box1", type: "box", x: 10, y: 10 },
|
|
43
|
-
},
|
|
44
|
-
}
|
|
30
|
+
// Fallback to a basic initial state if loading fails.
|
|
45
31
|
logger.warn("Using default game state as a fallback.")
|
|
32
|
+
gameConfig = {}
|
|
33
|
+
return gameConfig
|
|
34
|
+
} finally {
|
|
35
|
+
gameConfig.types ??= {}
|
|
36
|
+
gameConfig.entities ??= {}
|
|
46
37
|
}
|
|
47
|
-
|
|
48
|
-
return gameConfig
|
|
49
38
|
}
|
package/src/index.js
CHANGED
|
@@ -2,69 +2,35 @@ import http from "node:http"
|
|
|
2
2
|
|
|
3
3
|
import { createStore } from "@inglorious/store"
|
|
4
4
|
import pino from "pino"
|
|
5
|
-
import { WebSocketServer } from "ws"
|
|
6
5
|
|
|
7
6
|
import { loadGame } from "./game-loader.js"
|
|
8
|
-
import
|
|
9
|
-
import
|
|
7
|
+
import * as gameLoop from "./game-loop.js"
|
|
8
|
+
import * as wsHandler from "./ws-handler.js"
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const HTTP_STATUS_OK = 200
|
|
10
|
+
const ERROR_CODE = 1
|
|
11
|
+
const DEFAULT_PORT = 3000
|
|
14
12
|
|
|
15
13
|
const logger = pino({ name: "Inglorious Server", level: "info" })
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Get the game file path from command-line arguments.
|
|
22
|
-
let [, , gameFilePath] = process.argv
|
|
23
|
-
gameFilePath ??= "./default-game.js"
|
|
24
|
-
|
|
25
|
-
const gameConfig = await loadGame(gameFilePath, logger)
|
|
26
|
-
|
|
27
|
-
// =========================================================================
|
|
28
|
-
// Server Setup
|
|
29
|
-
// =========================================================================
|
|
30
|
-
|
|
31
|
-
// Create a minimal HTTP server. The WebSocket server will be "attached" to this.
|
|
32
|
-
const httpServer = http.createServer((req, res) => {
|
|
33
|
-
res.writeHead(HTTP_STATUS_OK, { "Content-Type": "text/plain" })
|
|
34
|
-
res.end("Inglorious Server is running.")
|
|
15
|
+
bootstrap().catch((error) => {
|
|
16
|
+
logger.error(error, "Failed to start the server.")
|
|
17
|
+
process.exit(ERROR_CODE)
|
|
35
18
|
})
|
|
36
19
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
20
|
+
async function bootstrap() {
|
|
21
|
+
let [, , gameFilePath] = process.argv
|
|
22
|
+
const gameConfig = await loadGame(gameFilePath, logger)
|
|
40
23
|
|
|
41
|
-
|
|
42
|
-
const store = createStore(gameConfig)
|
|
24
|
+
const store = createStore(gameConfig)
|
|
43
25
|
|
|
44
|
-
|
|
45
|
-
// This can be expanded as needed.
|
|
46
|
-
const api = {
|
|
47
|
-
// A simple broadcast function.
|
|
48
|
-
broadcast: (event) => {
|
|
49
|
-
// Stringify the event for transmission.
|
|
50
|
-
const message = JSON.stringify(event)
|
|
51
|
-
for (const client of clients) {
|
|
52
|
-
client.send(message)
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// =========================================================================
|
|
58
|
-
// Start Game Loop and WebSocket Handling
|
|
59
|
-
// =========================================================================
|
|
26
|
+
const httpServer = http.createServer()
|
|
60
27
|
|
|
61
|
-
|
|
62
|
-
startGameLoop(store, api)
|
|
28
|
+
wsHandler.setup(httpServer, store, logger)
|
|
63
29
|
|
|
64
|
-
|
|
65
|
-
setupWebSocketHandler(wss, store, clients, logger)
|
|
30
|
+
gameLoop.start(store)
|
|
66
31
|
|
|
67
|
-
|
|
68
|
-
httpServer.listen(
|
|
69
|
-
|
|
70
|
-
})
|
|
32
|
+
const port = process.env.PORT || DEFAULT_PORT
|
|
33
|
+
httpServer.listen(port, () => {
|
|
34
|
+
logger.info(`Listening on http://localhost:${port}`)
|
|
35
|
+
})
|
|
36
|
+
}
|
package/src/ws-handler.js
CHANGED
|
@@ -1,49 +1,42 @@
|
|
|
1
|
+
import { WebSocketServer } from "ws"
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {object}
|
|
4
|
+
* Initializes the WebSocket server and sets up event handlers.
|
|
5
|
+
* @param {object} httpServer The HTTP server instance to attach to.
|
|
4
6
|
* @param {object} store The game store instance.
|
|
5
|
-
* @param {Set<object>} clients A Set to keep track of all connected clients.
|
|
6
7
|
* @param {object} logger A Pino logger instance.
|
|
8
|
+
* @returns {void}
|
|
7
9
|
*/
|
|
8
|
-
export function setup(
|
|
9
|
-
|
|
10
|
+
export function setup(httpServer, store, logger) {
|
|
11
|
+
const wss = new WebSocketServer({ server: httpServer })
|
|
12
|
+
|
|
10
13
|
wss.on("connection", (ws) => {
|
|
11
14
|
logger.info("A new client has connected.")
|
|
12
|
-
clients.add(ws)
|
|
13
15
|
|
|
14
|
-
// Send the initial state to the new client.
|
|
15
16
|
ws.send(JSON.stringify({ type: "initialState", payload: store.getState() }))
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
ws.on("message", (message) => {
|
|
18
|
+
ws.on("message", (rawData) => {
|
|
19
19
|
try {
|
|
20
|
-
|
|
21
|
-
const event = JSON.parse(
|
|
22
|
-
|
|
23
|
-
// Dispatch the event to the central store.
|
|
20
|
+
const eventString = rawData.toString()
|
|
21
|
+
const event = JSON.parse(eventString)
|
|
24
22
|
store.dispatch(event)
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (client !== ws) {
|
|
30
|
-
// Exclude the sender from the broadcast.
|
|
31
|
-
client.send(eventMessage)
|
|
24
|
+
for (const client of wss.clients) {
|
|
25
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
26
|
+
client.send(eventString)
|
|
32
27
|
}
|
|
33
28
|
}
|
|
34
29
|
} catch (error) {
|
|
35
|
-
logger.error("Failed to parse message
|
|
30
|
+
logger.error({ error }, "Failed to parse message.")
|
|
36
31
|
}
|
|
37
32
|
})
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
logger.info("A client has disconnected.")
|
|
42
|
-
clients.delete(ws)
|
|
34
|
+
ws.on("close", (code, reason) => {
|
|
35
|
+
logger.info(`Client disconnected. Code: ${code}, Reason: ${reason}`)
|
|
43
36
|
})
|
|
44
37
|
|
|
45
38
|
ws.on("error", (error) => {
|
|
46
|
-
logger.error("WebSocket error
|
|
39
|
+
logger.error({ error }, "WebSocket error occurred.")
|
|
47
40
|
})
|
|
48
41
|
})
|
|
49
42
|
}
|
package/src/default-game.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default {}
|