Networked 1st person game : everything gets slightly slower every time a new player is added

So I come from a long background of Java, I only recently got into JS so pardon me if I appear somewhat naive on the topic.

I’ve been developing a first person game for the past few months using mainly Three and Socket.io, everything runs fine in the scene, player position / rotation updating and animating are all in a working state and for the most part efficient (as far as i know). When a player first joins the scene all our speeds for moving / jumping / animating are perfect, however once a new player joins everything becomes slightly slower (exponentially I think). once the extra player is removed it becomes faster and returns to the previous speed state. I’ve done what research is available and I think the problem lies in the delta timing for the first person controller.

Here is what that looks like

  run () {
    requestAnimationFrame(() => this.run());
     time1 = performance.now();
     deltaa = ( time1 - prevTime ) / 1000;
    this.update();
    this.render();
}

update () {

    let time = time1;
    let delta = deltaa;

    this.camera.rotation.set(0,0,0);
    this.camera.rotateY(lookY);
    this.camera.rotateX(lookX);

     let forward = new THREE.Vector3(Math.sin(lookY), 0, Math.cos(lookY));
     let right = new THREE.Vector3(Math.cos(lookY), 0, Math.sin(lookY));
     let velocity = new THREE.Vector3(0,0,0);
     let offset = 50;
     let offset1 = 40;

    this.camera.position.x -= velocity.x;
    this.camera.position.y -= velocity.y;
    this.camera.position.z -= velocity.z;

     if (mouselocked) {
         if (moveForward) {
             if (!grounded) {
                 velocity.x -= forward.x * offset * delta;
                 velocity.z -= forward.z * offset * delta;
             } else {
                 velocity.x -= forward.x * offset1 * delta;
                 velocity.z -= forward.z * offset1 * delta;
                 console.log(delta);
             }
             this.emit('walking');
         }

         if (moveBackward) {
             if (!grounded) {
                 velocity.x += forward.x * offset * delta;
                 velocity.z += forward.z * offset * delta;
             } else {
                 velocity.x += forward.x * offset1 * delta;
                 velocity.z += forward.z * offset1 * delta;
             }
             this.emit('walking');
         }

         if (moveRight) {
             if (!grounded) {
                 velocity.x += right.x * offset * delta;
                 velocity.z -= right.z * offset * delta;
             } else {
                 velocity.x += right.x * offset1 * delta;
                 velocity.z -= right.z * offset1 * delta;
             }
             this.emit('walking');
         }

         if (moveLeft) {
             if (!grounded) {
                 velocity.x -= right.x * offset * delta;
                 velocity.z += right.z * offset * delta;
             } else {
                 velocity.x -= right.x * offset1 * delta;
                 velocity.z += right.z * offset1 * delta;
             }
             this.emit('walking');
         }

     }

         if (!moveForward && !moveBackward && !moveLeft && !moveRight) {
             walking = false;
         }

         if (!walking && !jumping) {
             this.emit("idling");
         }

    this.camera.position.x += velocity.x;
    this.camera.position.y += velocity.y;
    this.camera.position.z += velocity.z;

    if(grounded && jumping) {
        verticalVelocity = 1.2;
        grounded = false;
    }

    verticalVelocity -= 0.06 ;
    this.camera.position.y += verticalVelocity * offset1 * delta;

    if(this.camera.position.y < 13){
         this.camera.position.y = 13;
         verticalVelocity = 0;
         grounded = true;
     }
    prevTime = time;
    this.emit("update", this.camera.position, lookX, lookY);
}

render() {
    this.renderer.render(this.scene, this.camera);
}

Apologies if that is not easy on the eyes, but basically I kind of Frankensteined the delta timing from a modified version of PointerLockControls. When a new player joins something affects this and causes something in here to be ran / calculated slower, with or without the timing variables included. so in that I do admit that they may be kind of arbitrary to me right now since they ultimately don’t change the issue but hopefully I’m headed down the right track

I hope this is enough information to help me determine what I might be doing wrong or could do more efficiently. If it’s necessary I can post visual aid or possibly a fiddle.

Ok, well that delta correction should do the opposite, it should ensure that even if some thing speeds up or slows down the game, then actual player speeds will stay the same.

When you talk about a speed change, do you mean the actual movement speed is different, as in the delta correction isn’t working, or is there a change in framerate (but the delta correction still might be ok?)

You’re only showing us the code for the local player, not the code that runs the local logic for remote players - which is almost certainly where the problem is.

I’m guessing, and this is a wild guess - that the remote player logic also has some kind of enterframe event, and this is also adding another delta correction or is adding lag that isn’t being corrected by the delay correction in the local player, shown here.

Does that make sense?

Yeah when I refer to a change in speed I do mean that the delta correction isn’t working, I implemented it to fix the problem but the problem persists. Also I’ll add stats right now, I didn’t think of checking the framerate.

Here’s how the server handles that information and sends it to all connected clients.

index intercepts it first

glScene.on('update', (pos, lookX, lookY)=>{
socket.emit('update', pos, lookX, lookY);
socket.emit('myPos', pos, lookX, lookY);

});

then passes it to the server, which emits it to everyone.

   client.on('update', (position, lookX, lookY)=> {
   io.sockets.emit('update',
       {
           id: client.id,
           position: position,
           lookX: lookX,
           lookY: lookY
       }
   );
    io.sockets.emit('animationLoop',
        {
            id: client.id,
            position: position,
            lookX: lookX,
            lookY: lookY
        }
    );
});

does this look correct?

Its still possible that its a performance issue, I did check and see that I get 7 draw calls per player added. I’m gonna take a look at the FPS right now.

I don’t know very much about sockets.io, not used it myself.

If the connection of a new client is slowing everyone down, and the delta isn’t fixing it, then I can only guess that adding a new player is causing multiple calls to the animation frame = that delta code isn’t designed to be called multiple times.

sockets.io.emit(‘animationLoop’) and io.sockets.emit(‘update’) - aren’t they calling functions that are already running? and making them run multiple times per frame?
Don’t you need to call a different function to update client of remote players positions?

No those functions aren’t already running, the only requestAnimationFrame callback I have is in the scene, which gets triggered after the scene itself loads.

run () {
    requestAnimationFrame(() => this.run());
     time1 = performance.now();
     deltaa = ( time1 - prevTime ) / 1000;
     stats1.begin();
    this.update();
    this.render();
    stats1.end();  
}

in the update function on my first post ( which gets triggered here ) it emits the position and necessary rotations of the camera.

Also I lose 10 frames per player, but even opening a new chrome tab will cause fps loss. so I’m beginning to think its my machine. In that case I need to optimize, which I think would begin with reducing the draw calls to one per player, instead of drawing every geometry its composed of every tick, which is seven at the moment. I think this is called instancing

The update function which the server triggers is what builds the players for the client, currently it loads a whole new gltf file for every player, which I think may be the wrong approach in terms of performance.

Ok, i can’t make sense of this, what you need to do is count how many times each of these functions is being called per frame - because something is being called too often when new players join, and it’s almost certainly to do with the updating of positions.

For one thing - the players need to send individual updates to the server, but the server needs to send a single update to each player, with all of the player positions, it looks like you’re sending multile updates to each player, one for each remote player, and this is calling something many times per frame that should only be called once.

excuse me,have you solved this problem? I have the same problem

When using Axios to request position data frequently, refresh is very slow and there is a jam

I try to delete the background GLB file, and the refresh speed was normal

there are my questions

  1. Will all objects be refreshed when an object location is updated?

  2. Is there any other way to refresh the object location, if I need to add GLB file background?