Tuesday 6 June 2017

Lag on multiple clients socket.io

I have been making a small multiplayer game using socket.io, and when I use a local websocket (http://127.0.0.1:8080), and I open multiple clients on my computer, there is 0 lag between devices (I can see realtime changes of other players per tab). However, I decided to test it out across multiple devices, and so I used ngrok to expose my local web server to the internet, and now when I open multiple clients (accessible through theajuppal.com/archipelago.io), the other clients do not load smoothly, the position of other players is updated choppily. Is this an issue with ngrok? Or is my application too demanding on the server?

Live demo here

tl;dr

I am sending and receiving multiple events per second, every time I iterate over my main loop (update) with requestAnimationFrame, I request an updated list of the players from the server. Would that be enough to cause the lag I am seeing? Or is it an issue with ngrok?


There is a lot of code but I will attach it below:

app.js

canvas = document.getElementById("canvas")
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

ctx = canvas.getContext('2d');
var socket = io.connect('ws://127.0.0.1:8080');
var paused = false;
socket.on('disconnect', function () {
    $("#coords").css({transition: "5000ms"});
    $("#coords").css({opacity: 0});
    $("#foo").css({backgroundColor: "white"});
    $("#label")[0].innerHTML = "Server has disconnected.<br>Please reload the page.<br>If the problem persists, please contact the admin at aj8uppal@gmail.com.";
    $("#label").css({opacity: 1});
    paused = true;
    $("#foo")[0].appendChild(labelText);


});
socket.on('connect', function(){
    // alert("we back");
});
var startTime;
var tiles;
var started = false;
$(document).ready(function() {
    $("body").animate({scrollTop: 0}, 500);
});
keysPressed = {}
$("#playbutton").on("click", function(){
    lastElementTop = $("canvas").position().top ;
    stopAlert = true;
    $(".site-wrapper").css({"box-shadow": "inset 0 0 200px rgba(17, 51, 80, 0)"})
    setTimeout(function(){
        $("body").animate({scrollTop: lastElementTop}, 1000);
    }, 10);
    setTimeout(function(){
        $("body").animate({scrollTop: lastElementTop}, 1000);
    }, 10000000000);
    started = false;

        SHIP_WIDTH = 20
        SHIP_HEIGHT = 70
        SHIP_COLOR = "rgb("+Math.floor(Math.random()*255)+", "+Math.floor(Math.random()*255)+", "+Math.floor(Math.random()*255)+")";
        SHIP_SPEED = 10;
        SHIP_ROTATE_SPEED = 3;
        BOARD_WIDTH = 12800;
        BOARD_HEIGHT = 6200;
        OFFSET_X = canvas.width/2-SHIP_WIDTH/2;
        OFFSET_Y = canvas.height/2-SHIP_HEIGHT/2;
        // ABS_X = BOARD_WIDTH/2;
        // ABS_Y = BOARD_HEIGHT/2;
        ABS_X = Math.floor(Math.random()*BOARD_WIDTH-OFFSET_X-SHIP_WIDTH);
        ABS_Y = Math.floor(Math.random()*BOARD_HEIGHT-OFFSET_Y-SHIP_HEIGHT);
        mouseX = -1;
        mouseY = -1;
        velX = 0;
        velY = 0;

        desired_angle = 0;
        prev_angle = 0;
        ID = -1;
        ship = {absx: ABS_X, absy: ABS_Y, width: SHIP_WIDTH, height: SHIP_HEIGHT, color: SHIP_COLOR, angle: 0, id: -1}
        players = [ship];
        socket.on("sendingData", function(newPlayers){
            players = newPlayers;
        });
    socket.emit("connected", {absx: ABS_X, absy: ABS_Y, color: SHIP_COLOR, angle: 0});

    socket.on("sendInitialData", function(data){
        ID = data.id;
        ship.id=ID;
        board = data.board;
        setTimeout(function(){
            init();
        }, 100);

    });
});

    function init(){
        $("#coords").css({top: $("canvas").position().top+50, left: 75});
        $("#coords")[0].innerHTML = ABS_X+":"+ABS_Y;
        $("#coords").css({opacity: 1});
    started = true;
    (function animloop(){
      requestAnimationFrame(animloop);
      update();
    })();
}
function Tile(x_pos, y_pos, width, height, color){
    this.x_pos = (x_pos*100)-Math.floor(ship.absx);
    this.y_pos = (y_pos*100)-Math.floor(ship.absy);
    this.width = width;
    this.height = height;
    this.color = color;
    this.getData = function(){
        return {x: this.x_pos, y: this.y_pos, width: this.width, height: this.height, color: this.color};
    };
    this.update = function(){
        updateTile(this.getData());
    };
}
function update(){
    if(!paused){
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    socket.emit("requestData", {player: ship});
    // socket.emit("availableTiles", {width: canvas.width, height: canvas.height, board: board, ship: ship})
    ctx.fillStyle = "rgba(0, 0, 0, 0.25)";
    ctx.beginPath();
    ctx.fillRect(-ship.absx, -ship.absy, 12800, 6200);
    ctx.strokeStyle = "rgb(0, 0, 0)";
    ctx.stroke();
    for(var y = 0; y < board.length; y++){
        for(var x = 0; x < board[y].length; x++){
            tile = board[y][x];
            if(tile == 1){
                if( ((x+1)*100)-Math.floor(ship.absx) > 0 && (x*100)-Math.floor(ship.absx) < canvas.width &&
                ((y+1)*100)-Math.floor(ship.absy) > 0 && (y*100)-Math.floor(ship.absy) < canvas.height){
                    updateTile({x: (x*100)-Math.floor(ship.absx), y: (y*100)-Math.floor(ship.absy), width: 100, height: 100, color: "#8c8c8c"});
                }
            }
        }
    }
    if(mouseX != -1 && mouseY != -1){
        dx = mouseX - (OFFSET_X-ship.width/2);
        dy = mouseY - (OFFSET_Y-ship.height/2);
        angle = Math.atan2(dy, dx);
        velX = (Math.abs(dx)/(canvas.width/2)) * SHIP_SPEED * Math.cos(angle);
        velY = (Math.abs(dy)/(canvas.height/2)) * SHIP_SPEED * Math.sin(angle);
    }
    if(ship.absx <= BOARD_WIDTH-OFFSET_X && ship.absx >= -OFFSET_X){
        ship.absx+=velX;
    }
    if(ship.absy <= BOARD_HEIGHT-OFFSET_Y && ship.absy >= -OFFSET_Y){
        ship.absy+=velY;
    }
    if(ship.absx < -OFFSET_X){
        ship.absx = -OFFSET_X;
    }
    if(ship.absy < -OFFSET_Y){
        ship.absy = -OFFSET_Y;
    }
    if(ship.absx > BOARD_WIDTH-OFFSET_X){
        ship.absx = BOARD_WIDTH-OFFSET_X
    }
    if(ship.absy > BOARD_HEIGHT-OFFSET_Y){
        ship.absy = BOARD_HEIGHT-OFFSET_Y;
    }
    for(var p = 0; p < players.length; p++){
        if(players[p].id == ship.id){
            // debugger;
            updateShip(players[p]);
        }else{
            updatePlayer(players[p]);
        }
    }
}
}
function updatePlayer(player){
        ctx.save();
        ctx.translate(OFFSET_X+(player.absx-ship.absx), OFFSET_Y+(player.absy-ship.absy));
        ctx.rotate((Math.PI/180)*(player.angle-90));
    ctx.fillStyle = player.color;
    ctx.fillRect(-player.width/2, -player.height/2, player.width, player.height);
        ctx.restore();
}
function updateShip(player){
        ctx.save();
        ctx.translate(OFFSET_X, OFFSET_Y);


                player.angle = desired_angle;
        ctx.rotate((Math.PI/180)*(player.angle-90));
    ctx.fillStyle = player.color;
    ctx.fillRect(-player.width/2, -player.height/2, player.width, player.height);
        ctx.restore();
}
function updateTile(tile){
    ctx.fillStyle = tile.color;
    ctx.fillRect(tile.x, tile.y, tile.width, tile.height);
}

$(document).keydown(function(e){
//  console.log("hi");
    keysPressed[e.keyCode] = true;
    if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1){
        e.preventDefault();
    }
});
$(document).keyup(function(e){
    keysPressed[e.keyCode] = false;
});
$(document).mousemove(function(e){
    mouseX = e.clientX;
    mouseY = e.clientY;
    if(started){
        difX = (OFFSET_X-ship.width/2)-e.clientX;
        difY = (OFFSET_Y+ship.height/2)-e.clientY;
        result = 180*Math.atan2(difY, difX)/Math.PI;
        prev_angle = desired_angle;
        desired_angle = result;
        ship.angle = desired_angle;
        if(result < 0){
            result+=360;
        }
    }
});
stopAlert = false;
$("#alert").on("click", function(){
    if($("#alert")[0].innerHTML == "Alert!"){
        doAlert("alert");
        $("#alert")[0].innerHTML = "Stop alert!";
    }else{
        stopAlert = true;
        $("#alert")[0].innerHTML = "Alert!";
    }
});


function doAlert(state){
    if(stopAlert){
        stopAlert = false;
        $(".site-wrapper").css({"box-shadow": "inset 0 0 200px rgb(17, 51, 80)"})
        return;
    }
    if(state == "alert"){
        $(".site-wrapper").css({"box-shadow": "inset 0 0 200px #D65653"})
        setTimeout(function(){
        doAlert("calm");
        }, 500);
    }else{
        $(".site-wrapper").css({"box-shadow": "inset 0 0 200px rgb(17, 51, 80)"})
        setTimeout(function(){
        doAlert("alert");
        }, 500);
    }
}

server.js

var config = require('./config');
var app = require('http').createServer();
var io = require('socket.io')(app);
var fs = require('fs');

count = 0;
players = [];
board = [];
for(var y = 0; y < 62; y++){
  sub = []
  for(var x = 0; x < 128; x++){
    sub.push(Math.random() < 0.1 ? 1 : 0);
  }
  board.push(sub);
}

app.listen(config.serverPort);
console.log("Starting server on port " + config.serverPort);
io.on('connection', function(socket) {
  socket.on("connected", function(data){
    players.push({x: data.x, y: data.y, color: data.color, angle: 0, id: count});
    socket.emit("sendInitialData", {id: count, board: board});
    count+=1;
    console.log(count);
  });
  socket.on("requestData", function(data){
    socket.emit("sendingData", players);
    players[data.player.id] = data.player;
  });
});

index.html

<!DOCTYPE HTML>
<html>
    <head>
        <link href="css/bootstrap.min.css" rel="stylesheet">


        <link href="css/cover.css" rel="stylesheet">
        <script src="js/socket.io.min.js"></script>
        <script src="js/modernizr.min.js"></script>
        <script src="js/jquery.min.js"></script>
        <script src="js/bootstrap.min.js"></script>
    </head>
    <body>
<div class="site-wrapper">

      <div class="site-wrapper-inner">

        <div class="cover-container">

          <div class="masthead clearfix">
            <div class="inner">
              <h3 class="masthead-brand">Archipegalo.io</h3>
              <nav>
                <ul class="nav masthead-nav">
                  <li><a href="http://facebook.com">Share on Facebook</a></li>
                  <li><a href="http://twitter.com">Post in Twitter</a></li>
                </ul>
              </nav>
            </div>
          </div>

          <div class="inner cover">
                <form class="form-signin">
            <h1 class="cover-heading">Welcome to Archipegalo.</h1>
            <p class="lead">Pillage and loot your friends (and enemies) in this highly addicting MMO.</p>

                <input type="name" id="inputName" class="form-control" placeholder="Enter your name" required="true" autofocus>
                <button id="playbutton" class="btn btn-lg btn-primary btn-block" type="submit">Play!</button>
<!--        <button id="alert" class="btn btn-lg btn-primary btn-block" type="button">Alert!</button>-->
                </form>
          </div>

          <div class="mastfoot">
            <div class="inner">
              <p>Made by A.J. Uppal</p>
            </div>
          </div>

        </div>

      </div>

    </div>
    <div id="foo" style="display: table; position: absolute; width: 100%; height: 100%; transition: 5000ms;"><span id="label" style="display: table-cell; vertical-align: middle; color: black; font-size: 24px; opacity: 0; transition: 5000ms">Game Over</span></div>
    <canvas id="canvas"></canvas>
    <div id="coords" style="position: absolute; opacity: 0"></div>
    <script>
$("#coords").css({top: $("canvas").position().top+50, left: $("canvas").width()-75});
</script>
    </body>
    <script src="main2.js"></script>
</html>



via ᴬᴶ ᵁᴾᴾᴬᴸ

No comments:

Post a Comment