samedi 9 mai 2015

increasingly high latency in node.js multiplayer html5 game

I created a webpage using node.js, express js, socket.io, and jquery. It is a simple game wherein players join, give themselves a name, and then move themselves(a square) around a canvas on the page. What I've noticed is: when more people join the game and move around, there is enough lag to make the game unplayable (at only three people connected. Two people doesn't lag it much). I can't figure out if this is server-side lag or client-side (this is my first project dealing with multiplayer). I am doing all of the location calculations on the server, and sending an array of all the player objects back to every socket so that each client can render all of the players. The client only sends input and draws the players. Below, I have included the app.js file (the server-side script) for my game. I am not sure exactly where the problem is, so I figured I'd just post the whole thing.

$(document).ready(function()
{
        var socket = io.connect();

        var canvas = document.getElementById("canvas_html");
        var ctx = canvas.getContext("2d");
  
        canvas.width = 512;
        canvas.height = 480;
  
        document.body.appendChild(canvas);

        var player = 
        {
                id: '',
                is_it: false,
                x: canvas.width / 2,
                y: canvas.height / 2,
                velx: 0,
                vely: 0
        }
    
    //tell the server to initialize this client as a new player
        socket.emit('init_client', player);
    
        var client_player_list = [];
    
    //receive a list of the player objects from the server
        socket.on('load_players', function(players)
        {
                client_player_list = players;
        });

        var keysDown = {};

        addEventListener('keydown', function(e)
        {
                keysDown[e.keyCode] = true;
        }, false);

        addEventListener('keyup', function(e)
        {
                delete keysDown[e.keyCode];
        }, false);

        //take input from keys and send input to server
        var update = function()
        {
                if(87 in keysDown)//player holding w
                        socket.emit('up');
                if(83 in keysDown)//player holding s
                        socket.emit('down');
                if(65 in keysDown)//player holding a
                        socket.emit('left');
                if(68 in keysDown)//player holding d
                        socket.emit('right');
                if(74 in keysDown)//player holding j
                        socket.emit('tag');
        };

        //render all players, update players when server updates
        var render = function()
        {
                //ctx.clearColor = "rgba(0, 0, 0, .3)";
                ctx.clearRect( 0, 0, canvas.width, canvas.height);
                ctx.fillStyle = "#079641";
                ctx.textAlign = 'center';
      
        //loop through the players array and render each one
                for(var i = 0; i < client_player_list.length; i++)
                {
            //if the player is_it, render them as red
                        if(client_player_list[i].is_it)
                        {       
                                ctx.fillStyle = "#8F0E0E";
                                ctx.fillRect(client_player_list[i].x, client_player_list[i].y, 20, 20);
                
                //draw the players name above them
                                ctx.fillStyle = "#FFF";
                                ctx.font="15px Arial";
                                ctx.fillText(client_player_list[i].name, client_player_list[i].x + 8, client_player_list[i].y - 3);
                                continue;
                        }
            
            //if the player !is_it, render them as green
            ctx.fillStyle = "#079641";
                        ctx.fillRect(client_player_list[i].x, client_player_list[i].y, 20, 20);
            
            //draw the players name above them
                        ctx.fillStyle = "#FFF";
                        ctx.font="15px Arial";
                        ctx.fillText(client_player_list[i].name, client_player_list[i].x + 8, client_player_list[i].y - 3);
                }
         
        //when the server sends an update, replace the current players array with the one that the server just sent
                socket.on('sv_update', function(players)
                {
                        client_player_list = players;
                });
        };

        //main loop
        var main = function()
        {
                setInterval(function()
                {
                        update();
                        render();
                }, 1000/60);
        };

        main();

        //trigger the disconnect event when a page refreshes or unloads
        $(window).bind('beforeunload', function()
        {
                socket.emit('disconnect');
        });
});

This is the client side script for the game. This is where I handle the input and rendering.

var express = require('express');
var http = require('http');
var io = require('socket.io', { rememberTransport: false, transports: ['WebSocket', 'Flash Socket', 'AJAX long-polling'] });

var app = express();
var server = http.createServer(app);
server.listen(8080);

app.use(express.static('public'));

io = io.listen(server);

//Declare variables for working with the client-side
var player_speed = 5;
var player_size = 20;
var vel_increment = 0.5;
var canvas_height = 480;
var canvas_width = 512;

//Declare list of players connected
var players = [];

io.sockets.on('connection', function(socket)
{
  var socket_id = socket.id;
  var player_index, player_exists = false;
  var this_player;

  var check_bounds = function()
  {
    //Keep player in the canvas
    if(this_player.y < 0)
        this_player.y = 0;
    if(this_player.y + player_size > canvas_height)
        this_player.y = canvas_height - player_size;

    if(this_player.x < 0)
      this_player.x = 0;
    if(this_player.x + player_size > canvas_width)
      this_player.x = canvas_width - player_size;

    //Keep velocity between -5 and 5
    if(this_player.vely > player_speed)
      this_player.vely = player_speed;
    if(this_player.velx > player_speed)
      this_player.velx = player_speed;

    if(this_player.vely < -player_speed)
      this_player.vely = -player_speed;
    if(this_player.velx < -player_speed)
      this_player.velx = -player_speed;
  };

  var sv_update = function()
  {
    io.sockets.emit('sv_update', players);
    if(player_exists)
    {
      if(players.length == 1)
        this_player.is_it = true;
      check_bounds();
    }
  };
  
  //When a client connects, add them to players[]
  //Then update all clients
  socket.on('init_client', function(player)
  {
    player.id = socket.id;
    players.push(player);

    for(var i = 0; i < players.length; i++)
      if(players[i].id == socket_id)
        player_index = i;
    player_exists = true;
    this_player = players[player_index];

    sv_update();
    socket.emit('load_players', players);

    console.log(players);
  });

  //====================CHAT==========================//
  var address = socket.request.connection.remoteAddress;

  socket.on('new user', function(data, callback)
  {
    if(player_exists)
    {
      this_player.name = data;
      console.log(address + " has connected as '" + data + "'.");
      
      callback();
    }
  });

  socket.on('send message', function(data)
  {
    io.sockets.emit('broadcast', this_player.name, data);
  });
  //====================CHAT==========================//
  
  //if player is_it and is within another player, hitting 'j' will make the other player is_it. 
  socket.on('tag', function()
  {
    if(player_exists)
    {
      for(var i = 0; i < players.length; i++)
      {
        if((this_player.x + player_size >= players[i].x && this_player.x + player_size <= players[i].x + player_size )|| 
          (this_player.x <= players[i].x + player_size && this_player.x + player_size >= players[i].x))
          if((this_player.y + player_size >= players[i].y && this_player.y + player_size <= players[i].y + player_size )|| 
          (this_player.y <= players[i].y + player_size && this_player.y + player_size >= players[i].y))
          {
            if(this_player.is_it)
            {
              this_player.is_it = false;
              players[i].is_it = true;
            }
          }
      }
      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('up', function()
  {
    if(player_exists)
    {
      this_player.y -= player_speed;

      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('down', function()
  {
    if(player_exists)
    {
      this_player.y += player_speed;

      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('left', function()
  {
    if(player_exists)
    {
     this_player.x -= player_speed;

      sv_update();
    }
  });

  //Gather key input from users...
  socket.on('right', function()
  {
    if(player_exists)
    {
      this_player.x += player_speed;

      sv_update();
    }
  });

  //When a player disconnects, remove them from players[]
  //Then update all clients
  socket.on('disconnect', function()
  {
    for(var i = 0; i < players.length; i++)
    {
      if(players[i].id == socket.id)
      {
        players.splice(i, 1);
      }
    }

    sv_update();
  });
});

Aucun commentaire:

Enregistrer un commentaire