I just finished implementing the game Pong using node.js and socket.io for backend, with jQuery for client-side. I put the game on my free-tier AWS instance and the mechanics work fine but the latency is unplayable. In the game, the paddle of the player who controls it moves locally. The client-side also sends a server request which broadcasts paddle movement to the opponent every requestFrameAnimation. To launch the ball the player whose serve it is presses spacebar which sends a server request, which then emits to both players to start the ball movement.
Both the paddle movement and the ball launching are suffering from latency. For the paddles I think the problem is that I'm sending a server request every requestFrameAnimation which is probably to fast of a rate. Maybe I should make a setInterval that sends the player's paddle position to his opponent every fraction of a second. As for the ball, since the signal for it to start moving is sent by the server I think I need to send an actual time for the ball to launch ahead of the time the spacebar was pressed so each local machine can count down to that time.
Here is a snipped of my client-side code for some context:
function updateFrame(){
paddleSpeed = 0;
if (keysPressed.up){ // Move paddle up
if (!(myPaddle.offset().top <= arena.offset().top)){ // Make sure paddle isn't at top
paddleSpeed -= 8;
}
}
if (keysPressed.down) { // Move paddle down
if (!(myPaddle.offset().top+paddleL.height() >= arena.offset().top + arena.height())){ // Make sure paddle isn't at bottom
paddleSpeed += 8;
}
}
if (paddleSpeed != 0) socket.emit("moveReq", paddleSpeed); // Send server request to be sent to opponent
myPaddle.css({top: '+='+paddleSpeed+'px'}); // Move paddle locally
if (gameInProgress){ // If round has started, move the ball
ball.css({left: '+='+ballSpeed.hor+'px', top: '+='+ballSpeed.ver+'px'});
window.requestAnimationFrame(updateFrame); // Request next frame
and my server-side:
socket.on('moveReq', function(data){
socket.broadcast.emit("movePaddle", data); // Send opponent's paddle movement to user
});
socket.on('launchGame', function(){ // Launch the game
io.sockets.emit('startGame');
});
Does anybody have any tips to reduce the latency on my game, or is my free-tier just too low bandwidth for this sort of web app?
There should be enough bandwidth to run this game easy, so I do not think that is your problem.
Instead I recommend you to take a look at this blog about online game developing. I used the ideas described in there myself when I started developing multiplayer games. It explains several key problems and solutions when developing online games.
Related
I have a game where if the enemy dies they queue free and disappear. But when I play the scene, the enemy does not disappear, and still interacts as a kinematic body 2d.
Script:
if health == 0:
battle_area.enemy_list.erase(self)
self.queue_free()
I have a server side function to check if a player is idle
socket.idle = 0;
socket.cached = {};
socket.checkIdle = function(){
socket.idle++;
console.log(socket.idle);
if(socket.cached.x != players[id].x || socket.cached.y != players[id].y){
socket.idle=0;
}
socket.cached = players[id];
if(socket.idle>12){
socket.disconnect();
}
}
socket.interval = setInterval(socket.checkIdle,1000);
I've noticed that even after the player gets booted/disconnected for being too long. The server still console log the socket.idle for it.
Am I going about this the wrong way? Also should I then clear the interval for when the player disconnects?
socket.on('disconnect', function(){
clearInterval(socket.interval);
});
You certainly shouldn't leave a setInterval() running for a player that is no longer connected. Your server will just have more and more of these running, wasting CPU cycles and possibly impacting your server's responsiveness or scalability.
I've noticed that even after the player gets booted/disconnected for being too long. The server still console log the socket.idle for it.
Yeah, that's because the interval is still running and, in fact, it even keeps the socket object from getting garbage collected. All of that is bad.
Also should I then clear the interval for when the player disconnects?
Yes, when a socket disconnects, you must clear the interval timer associated with that socket.
Am I going about this the wrong way?
If you keep with the architecture of a polling interval timer for each separate socket, then this is the right way to clear the timer when the socket disconnects.
But, I would think that maybe you could come up with another design that doesn't need to regularly "poll" for idle at all. It appears you want to have a 12 second timeout such that if the player hasn't moved within 12 seconds, then you disconnect the user. There's really no reason to check for this every second. You could just set a single timer with setTimeout() for 12 seconds from when the user connects and then each time you get notified of player movement (which your server must already being notified about since you're referencing it in players[id].x and players[id].y), you just clear the old timer and set a new one. When the timer fires, you must have gone 12 seconds without motion and you can then disconnect. This would be more typically how a timeout-type timer would work.
I found 3 other posts about implementing timers for turn based games but none of them had a final answer.
I am working on a poker game project using WebSockets (Socket.io) rooms.
Each room has a timer which takes care of the turns from the server, for example, each player has 10 seconds to choose a card. If there was only one room I was able to do this by using setInterval and changing the turn variable at the end of each interval and emit the new turn to the clients.
But right now, I am confused how to implement a specific timer for each room when there are a lot of rooms considering that node.js is single threaded.
Despite being single threaded, Node.js can manage multiple timers at once. You can just set them. How you do it is up to you. Keep in mind that you'll need to use bind if you're managing your Rooms with objects. You can use variables to manage (for example, cancelling) your timers.
Here is a general example:
var Room = function() {
this.id = String(Date.now());
this.timer = setInterval(this.timerFunction.bind(this), 10000);
}
Room.prototype.timerFunction = function() {
// Example
io.to(this.id).emit("some message");
}
// Create one
new Room();
I'm currently building a HTML5 game using Node.JS, MongoDB and Socket.IO. The purpose of this project is not really creating a finished, fully playable game but rather understanding and implementing some basic concepts of multiplayer game programming and getting used to MongoDB.
Here is the basic server architecture I went with. The server listens to clients through Socket.IO and everytime a message is received, it push it to a queue. A message is received whenever a player wants to move, or effectively alter the game in a way. I'm staying really vague because it does not really matter to show you in details what this game is. So the server receives messages from all the clients and keeps it in memory for a certain time.
Every 50ms the server processes sequentially all the messages in the queue and makes the game state advance, broadcast the changes to clients and then empties the queue and starts listening again to clients.
I'm having some difficulties building this game loop as I'm not really sure of what does MongoDB and if it does it in time, as all the calls are pure async. Let's say the following code is in my game loop, here is my concerns:
for (var i=0; i<queue.length; i++) {
if(queue[i].message.type === "move") {
//The server has first to ensure that the player can effectively move,
//thus making a query to MongoDB. Once the result has been retrieved,
//the server makes an update in MongoDB with the new player position
}
//At this point, due to the async nature of MongoDB,
//I cannot ensure that the queries have been executed nor that the message was effectively handled
}
//Here again, I cannot ensure that the game state gracefully advanced
//and then I cannot broadcast it.
I think the game loop has to be sequential, but I'm not sure if it's possible to do so using MongoDB and I'm not sure MongoDB is the right tool for that job.
I'm using the official Node.JS driver for MongoDB as I'm more interested in nested documents than in Object Data Modeling.
Do you have any clues on building a sequential game loop in this case ? Or am I using MongoDB in a case outside its purpose ?
Seems fairly straight forward.
The solution is not to use a for loop, as you only want to start the next message being processed after the previous one has completed. For this, it is probably easier to use a library like async and the eachSeries function.
https://github.com/caolan/async#each
async.eachSeries(queue, processMessage, allDone);
function processMessage(message, callback) {
// do stuff with message, don't forget to call callback when you have complete all async calls and are done processing the message!
// eg
if(message.type === "move") {
mongo.insert({player: message.player, x:message.x, y:message.y}, function (err, res) {
// error check etc
callback();
}
}
}
function allDone() {
// called when all messages have been proccessed!
// setTimeout(processNewQueue, 50);
}
I'm making a simple online game and I'm suffering from out-of-sync issues. The server is implemented with IOCP, and since the game will be held almost always in LAN, the delay is relatively small.
The core algorithm of network connecting can be described as below: (There are 4 clients in a single fame)
Clients send their actions and the elasped time since the last frame to the server every frame, then wait until get a response from the server.
The server collects all four clients' messages, concatenate them together, then send it to all four clients.
On receiving the response, clients update their game with the messages provided in the response.
Now, I can see that after some time the four games go out of sync. It can be observed that the game I'm controling is different from the other three(which means the other three are the same), and just by walking around makes the problem happen.
Below is the code, if it might be helpful:
First, the server. Every message will be handled in a separate thread.
while(game_host[now_roomnum].Ready(now_playernum)) // wait for the last message to be taken away
{
Sleep(1);
}
game_host[now_roomnum].SetMessage(now_playernum, recv_msg->msg);
game_host[now_roomnum].SetReady(now_playernum, true);
game_host[now_roomnum].SetUsed(now_playernum, false);
while(!game_host[now_roomnum].AllReady()) // wait for all clients' messages
{
Sleep(1);
}
string all_msg = game_host[now_roomnum].GetAllMessage();
game_host[now_roomnum].SetUsed(now_playernum, true);
while(!game_host[now_roomnum].AllUsed()) // wait for all four responses are ready to send
{
Sleep(1);
}
game_host[now_roomnum].SetReady(now_playernum, false);// ready for receiving the next message
strcpy_s(ret.msg, all_msg.c_str());
And the clients' CGame::Update(float game_time) method:
CMessage msg = MakeMessage(game_time);//Make a message with the actions taken in the frame(pushed into a queue) and the elasped time between the two frames
CMessage recv = p_res_manager->m_Client._SendMessage(msg);//send the message and wait for the server's response
stringstream input(recv.msg);
int i;
rest_time -= game_time;
float game_times[MAX_PLAYER+1]={0};
//analyze recv operations
for(i=1; i<=MAX_PLAYER; i++)
{
int n;
input>>n;
input>>game_times[i];//analyze the number of actions n, and player[i]'s elasped game time game_times[i]
for(int oper_i = 1; oper_i <= n; oper_i++)
{
int now_event;
UINT nchar;
input>>now_event>>nchar;
if(now_event == int(Event::KEY_UP))
HandleKeyUpInUpdate(i, nchar);
else //if(now_event == int(Event::KEY_DOWN))
HandleKeyDownInUpdate(i, nchar);
}
}
//update player
for(i=1; i<=MAX_PLAYER; i++)
{
player[i].Update(game_times[i]);//something like s[i] = v[i] * game_time[i]
}
Thank you very much. I'll provide more detail if necassary.
Ur general design is wrong, that's why u get async at some point. The server should never ever deal with the fps of the clients. This is just a horrible design issue. On general the server calculates everything regarding on the input's the clients send to server. And the clients just request the current status of their surroundings of the server. That way u are fps independent on the server side. Which mean u can update the scene on the server as fast as possible, and the clients just retrieve the current status.
When u update the entities on the server fps dependent per user, u would have to keep a local copy of every entity for every client, otherwise it's impossible to transfer for different delta times.
The other design could be that ur server just syncs the clients, so every client calculates the scene on it's own and then send their status to the server. The server then distributes this to the other clients, and the clients decide what to do with this information.
Any other design will lead to major problems, i highly regret to not use any other design.
if u have any further questions feel free to contact me.