0

I am building an online browser game where I used Node.js as the server backend and socket.io to handle all the networking connections. However, for the game backend itself, I actually write it in C++ and used node-addon-api to wrap around that backend. I do this because:

  1. I think that the C++ game backend will be a little bit faster than a JS game backend. Plus, this is also the language I used in my work.
  2. I want to leave the possibility of building the server in C++ in the future. Having the backend code in C++ would make the transition more seemless in the future.

The server.js code currently looks like the following.

// Server code
const express = require('express')
const app = express()
const PORT = process.env.PORT || 3000;
app.use(express.static('public'));

const http = require('http').Server(app);
const io = require("socket.io")(http, {
  cors: {
    origin: "*",
  }
});

app.get('/', (req, res) => res.sendFile(__dirname + '/index.html'));
http.listen(PORT, function() {
    console.log(`Listening on ${PORT}`)
})

io.on('connection', connected);

// Move data
class MoveData {
  constructor(playerId, x, y) {
    this.playerId = playerId;
    this.x = x;
    this.y = y;
  }
}

// Backend initialization
const game_wrapper = require('./build/Release/game_wrapper.node')
var gameInstance = new game_wrapper.GameWrapper(4.3);
gameInstance.AddNewPlayer("aloha");
gameInstance.RemovePlayer("aloha");

var playerCreations = [];
var playerDestructions = [];
var playerMoves = []
var objectPositions = {};

function connected(socket) {
  socket.on('newPlayer', data => {
    console.log("New player coming at id " + socket.id);
    playerCreations.push(socket.id);
  });

  socket.on('disconnect', () => {
    playerDestructions.push(socket.id);

    delete objectPositions[socket.id];
    io.emit('updatePlayers', objectPositions);
  });
  
  socket.on('move', moveData => {
    playerMoves.push(
      new MoveData(socket.id, moveData.x, moveData.y)
    );
  });
}

// Server loop
var lastTime = Date.now();
function serverLoop() {
  // Player update
  for (let playerId in playerCreations) {
    gameInstance.AddNewPlayer(playerId);
  }
  playerCreations = [];
  for (let playerId in playerDestructions) {
    gameInstance.RemovePlayer(playerId);
  }
  playerDestructions = [];
  for (let moveData in playerMoves) {
    gameInstance.Move(moveData.playerId, moveData.x, moveData.y);
  }
  playerMoves = [];

  // Game update
  currTime = Date.now();
  timeDelta = (currTime - lastTime) / 1000;
  gameInstance.Update(timeDelta);
  for (let playerId in objectPositions) {
    let point_array = gameInstance.GetPlayerPosition(playerId);
    objectPositions[playerId] = { x: point_array[0], y: point_array[1]};
  }
  console.log("Before");
  io.emit('updatePlayers', objectPositions);
  console.log("After");
  lastTime = currTime;
}
setInterval(serverLoop, 1000/60);

Here, the game_wrapper comes from the Node C++ addon. I created a gameInstance from the library, which creates an instance of the game. New player can join in by connecting to the server, which triggers a creation of a new player in the game backend, identified by the socket ID.

When I called AddNewPlayer and RemovePlayer outside, they seem to work fine. However, when I called AddNewPlayer inside the serverLoop, the server either crashed immediately, or eventually crashed.

Does anyone have a suggestion of what is going on? I am suspecting that the serverLoop is actually a callback function that spawns a new thread. This creates some memory management issue if gameInstance does not handle memory properly, causing crashes. However, I would love to hear from node.js expert or socket.io expert here. Happy to provide further code to help debug this issue. Thanks!

I tried several things such as constructing the function the different way, or changing the way the function works on the backend. But I still have the same issues. It seems that Node Addon C++ doesn't like any object where memory might be dynamically allocated.

Son Pham
  • 21
  • 3

0 Answers0