@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/server",
3
- "version": "0.1.0",
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.0.0"
44
+ "@inglorious/store": "1.2.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "globals": "^16.3.0",
@@ -11,39 +11,28 @@ export async function loadGame(gameFilePath, logger) {
11
11
  let gameConfig = {}
12
12
 
13
13
  try {
14
- // Dynamically import the game module.
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 { default: config } = await import(`file://${modulePath}`)
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
- entities: {
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 { start as startGameLoop } from "./game-loop.js"
9
- import { setup as setupWebSocketHandler } from "./ws-handler.js"
7
+ import * as gameLoop from "./game-loop.js"
8
+ import * as wsHandler from "./ws-handler.js"
10
9
 
11
- // The server's port. You can change this or use an environment variable.
12
- const PORT = 3000
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
- // Initial Game State
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
- // Create the WebSocket server.
38
- const wss = new WebSocketServer({ server: httpServer })
39
- const clients = new Set() // A Set to keep track of all connected clients.
20
+ async function bootstrap() {
21
+ let [, , gameFilePath] = process.argv
22
+ const gameConfig = await loadGame(gameFilePath, logger)
40
23
 
41
- // Initialize the game store. This is the central source of truth.
42
- const store = createStore(gameConfig)
24
+ const store = createStore(gameConfig)
43
25
 
44
- // A small-scale API for your systems and events to interact with the world.
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
- // Start the core game loop.
62
- startGameLoop(store, api)
28
+ wsHandler.setup(httpServer, store, logger)
63
29
 
64
- // Set up the WebSocket event handlers.
65
- setupWebSocketHandler(wss, store, clients, logger)
30
+ gameLoop.start(store)
66
31
 
67
- // Start the server and listen on the specified port.
68
- httpServer.listen(PORT, () => {
69
- logger.info(`Listening on http://localhost:${PORT}`)
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
- * Sets up WebSocket event handling for a given server and store.
3
- * @param {object} wss The WebSocket server instance.
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(wss, store, clients, logger) {
9
- // Listen for new client connections.
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
- // Listen for messages from the client.
18
- ws.on("message", (message) => {
18
+ ws.on("message", (rawData) => {
19
19
  try {
20
- // Parse the incoming event.
21
- const event = JSON.parse(message)
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
- // Broadcast the event to all other clients.
27
- const eventMessage = JSON.stringify(event)
28
- for (const client of clients) {
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:", error)
30
+ logger.error({ error }, "Failed to parse message.")
36
31
  }
37
32
  })
38
33
 
39
- // Listen for the client disconnecting.
40
- ws.on("close", () => {
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:", error)
39
+ logger.error({ error }, "WebSocket error occurred.")
47
40
  })
48
41
  })
49
42
  }
@@ -1 +0,0 @@
1
- export default {}