Collision Detection Frontend / Backend

Hi all, I know this topic is huge and there is no simple answer to it. But I just started to investigate into that and I don’t even know where to start. Maybe you could share some generals ideas or lead me to some topics, packages or tutorials that cover this topic nicely.

For a multiplayer game I would need to do collision detection on both sides: frontend and backend.

  • This means I need to have a visual representation of a map in the browser
  • And a logical representation on the server

The frontend does instant realtime checks for the player to get smooth movements and the backend checks those movements in the background to detect cheats.

So those would be my questions, any hints in the right direction are highly appreciated!

  • Are there any default formats to represent an ego-shooter map with threejs? Lets keep it “simple”, Wolfenstein/Doom style :wink:
  • What collision detection plugins are mostly used for that?
  • How is it possible to logically do the same checks on the headless backend? Running threejs on the server?

Thanks in advance!
Best, Jan

2 Likes

Hi!

Usually collisions and physics, and also gameplay (all game behaviour) is done on the server. The client only receives the data needed to display the world (and audio), and only sends to the server the user input. I mean during the game; of course there is more communication between server and clients to load assets and establish game state.

This is because the network bandwith is a scarce resource, the more network usage, the more latency.

Also it is better conceptually and simpler to implement. At least this is the view I have about this topic.

1 Like

Hi yombo,

thanks for the quick answer. I fully agree that the “truth” has to be on the server.

Do you have any insights what techniques (programming languages, plugins, map config files, etc.) to use together with threejs on the server side to accomplish such a collision detection?

I have 3 examples that try different approaches.
Nothing is perfect. Its a compromise.
I use SocketIO and Cannonjs.

  1. Server relaying client position and quaternions to all connected clients
    SocketIO - Three.js Tutorials
    Open the above link in two separate browser windows. There is no physics in this example. But if I did have physics then it would be calculated client side, and it would still just relay current position and quaternion to the server.

  2. Server side CannonJS physics.
    https://ballgame.sbcode.net/
    Client key presses are sent to the server, server updates physics world, relays new positions and quaternions back to all connected clients and Threejs scene updated.

  3. Combination of server side and client side physics. Not everything needs to be calculated server side, or even at all if it doesn’t really matter that much.
    https://fcs.sbcode.net/

I don’t have any usable examples of using peer to peer architecture.
I have used UDP on C++,C#, Java socket servers I’ve written.
UDP has differnt issues, and TCP resolves those.
But still not perfect.
Look at what big gaming companies do, they spend billions, and they still have the same problems that my examples have.
Out of all the things I’ve tried, my example 3 is the best.

4 Likes

The method i’ve used for front end and backend is sending inputs of each frames to the server to physics can be updated there to make sure that everything is synced, its not only about collision detection, physics should also be synced, delta should be the same on each frame.

Here is a game i was working on a couple weeks ago, where all physics ks syced, calculated on the server and the client, the server just sends the velocity and position every 1000ms or more so it makes sure its always the same…

One thing you need to make sure of is
Creating a shared class, a class that will be used in both the server and the client, so you can then create a custom class extending the shared class on the server dir and the client dir, so that way you don’t server code in the client, which would be a terrible mistake that you will regret in the future

Movement and entities

Grenade & explosive showcade

How i sent input
I’ll just write you a simple code because my english is terrible

// client
let inputs: [...[
    delta: number,
    pitch: number,
    yaw: number,
    commands: number // I wrote something that converts a bunch of booleans into a single byte, so this represents W, S, D, SHOOT, AIM, etc
]]= []

function update(delta, player) {

    inputs.push(
        delta,
        player.pitch,
        player.yaw,
        player.commands
    )
}

setInterval(() => {

    socket.send(inputs)
},1000)

// server
onmessage = (inputs, player) => {

    for (let i = 0; i < inputs.length; i += 4) {
        let delta = inputs[i]
        let pitch = inputs[i+1]
        let yaw = inputs[i+2]
        let commands = inputs[i+3]
        // do something with the inputs

        player.process(delta, pitch, yaw, commands)
    }
}
2 Likes

Thank you all for your examples and background informations about your projects!!!

Even after more and more investigation there doesn’t seem to be a perfect solution. You always have more or less tradeoffs.

But what I’m really wondering about is the fact that there doesn’t seem to be any best practices about server-side collision checks with threejs. Something like a package that takes a GLTF file and calculates collisions on a given point or vector. Maybe even the same package that does something like that in the browser (to be consistent between the systems).

So it seems to end up with a fully custom written code on client and server :frowning: so again its not about writing a game its more like developing a game engine.

Another possible solution is to use a deterministic physic engine
Wich mean results are always the same whatever client/server/platform process them.
this allow to “skip” large amount of processing and double-check

(let’s ignore the whole topic of security and hacks, for simplicity sake :laughing:
that’s too much for a single question)

Rapier is a young physics cross-platform engine aiming for this specific purpose.
Disclaimer: Rapier is actively in development (I’m just a curious user and not related to the project), and does NOT contain all features you may find on engine like bullet/ammo/cannon. I suggest to visit their discord/website to answer your issues/question.

The engine is still perfectly okay for basic needs tho

1 Like

This article is awesome: Real Time Multiplayer in HTML5 - Build New Games Source code on GitHub

It is based on these articles:

Use Ammo.js, OimoPhysics, Cannon ES, or Rapier3D for collision detection, movement and so on.

1 Like

Thanks @Oxyn and @8Observer8 for those links! Lots of interesting stuff for the weekend to work through!!! :smiley:

2 Likes

To use Ammo.js on the server side, you must install it with the following commands:

mkdir my-game
cd my-game
npm init -y (to create the package.json file)
npm i github:kripken/ammo.js (to install ammo to the node_modules file)
nul > app.js (to create the app.js file)

Copy the source example below into app.js and run it using node app.js. This will create a ground and a cube. The cube coordinates will be printed to the console. Stop the program using Ctrl+C. These articles will help you get started using Ammo.js: Tutorial on JavaScript Physics Using AmmoJS and ThreeJS

app.js

const AmmoLib = require("ammo.js");

let world, tmpTrans, Ammo;
const rigidBodies = [];

function setupPhysicsWorld()
{
    const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
    const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
    const overlappingPairCache = new Ammo.btDbvtBroadphase();
    const solver = new Ammo.btSequentialImpulseConstraintSolver();

    world = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
    world.setGravity(new Ammo.btVector3(0, -10, 0));
    tmpTrans = new Ammo.btTransform();
}

function createGround()
{
    const pos = { x: 0, y: 0, z: 0 };
    const scale = { x: 50, y: 2, z: 50 };
    const quat = { x: 0, y: 0, z: 0, w: 1 };
    const mass = 0;

    const transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
    transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
    const motionState = new Ammo.btDefaultMotionState(transform);

    const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x / 2, scale.y / 2, scale.z / 2));
    colShape.setMargin(0.05);

    const localInertia = new Ammo.btVector3(0, 0, 0);
    colShape.calculateLocalInertia(mass, localInertia);

    const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia);
    const body = new Ammo.btRigidBody(rbInfo);

    world.addRigidBody(body);
}

function createBox(pos)
{
    const scale = { x: 1, y: 1, z: 1 };
    const quat = { x: 0, y: 0, z: 0, w: 1 };
    const mass = 10;

    const transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
    transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
    const motionState = new Ammo.btDefaultMotionState(transform);

    const colShape = new Ammo.btBoxShape(new Ammo.btVector3(scale.x / 2, scale.y / 2, scale.z / 2));
    colShape.setMargin(0.05);

    const localInertia = new Ammo.btVector3(0, 0, 0);
    colShape.calculateLocalInertia(mass, localInertia);

    const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia);
    const body = new Ammo.btRigidBody(rbInfo);

    world.addRigidBody(body);
    rigidBodies.push(body);
}

function updatePhysics()
{
    world.stepSimulation(0.05, 10);
    
    for (let i = 0; i < rigidBodies.length; i++)
    {
        let objAmmo = rigidBodies[i];
        let ms = objAmmo.getMotionState();
        if (ms)
        {
            ms.getWorldTransform(tmpTrans);
            const p = tmpTrans.getOrigin();
            const q = tmpTrans.getRotation();
            console.log(p.x(), p.y(), p.z());
        }
    }
}

function start()
{
    setupPhysicsWorld();
    createGround();
    createBox({ x: 0, y: 3, z: 0 });
    setInterval(updatePhysics, 1000 / 20);
}

AmmoLib().then((re) => {
    Ammo = re;
    start();
});
1 Like

no need server detection, let physics on client side only.
If you want check for someone trying hacking colisions, no need server test too, other clients can check if someone make impossible movement and disconect hin.
Use blockchain principle, all player moviments need allowed by 2 or more players. Unique way possible for hacking is when have only 1 player online.

@didi_softwares Don’t go around telling people that, its a bad idea, bad example and just terrible in many ways.

There are several reasons why it is beneficial to have physics running on both the server and the client. First, it helps ensure that the game world is consistent for all players. Second, it reduces the amount of data that needs to be sent between the server and clients, which can help improve performance. Finally, it can help create a more immersive and realistic game world.

it can help to reduce cheating and hacking, because players will not be able to modify the physics code to give themselves an advantage. Second, it can help to improve performance, because the server will only need to send data about the positions and velocities of objects, rather than the code needed to simulate the physics. Finally, it can make it easier to implement multiplayer games, because the server can keep track of all the objects in the game and their interactions.

If you where to have a falling cube in front of the player, how would this cube be seen in front of every other player??? p2p? that’s an even worst idea

1 Like

Check some pappers
This an one of many articles

Server physics calculation drop a game server performance a lot when have players.
And not entire efective against hackers, simply because the server can only check the position from time to time. Another papper telling why

Some useful articles: