Handle physics with sockets?

I’m trying to make a game with sockets but the physics doesn’t act very well between multiple clients. It appears to twitch/glitch on the opposite player.

I have seen some people calculate physics on the server and broadcast to the clients. Is this more correct or can physics be handled client side while just broadcasting the player pos?

const express = require('express');

const app = express();
const server = require(‘http’).Server(app);
const io = require(‘socket.io’).listen(server);
const session = require(‘express-session’)({
secret: ‘ship game’,
resave: true,
saveUninitialized: true,
// cookie: {
// maxAge: 1000 * 60 * 10
// },
});
const sharedsession = require(‘express-socket.io-session’);

const players = {};
/const star = {
x: Math.floor(Math.random() * 700) + 50,
y: Math.floor(Math.random() * 500) + 50,
};
/
/const scores = {
blue: 0,
red: 0,
};
/

app.use(express.static(__dirname + ‘/public’));

app.use(session);
io.use(sharedsession(session));

app.get(’/’, (req, res) => {
req.session.ship_exists = false;
res.sendFile(__dirname + ‘/index.html’);
});

io.on(‘connection’, socket => {
console.log(‘a user connected’);

// if the player doesn’t already have an existing session, create a new player
// (check prevents creating multiple ships when browser auto disconnects
// and reconnects socket)
if (!socket.handshake.session.ship_exists) {
// create a new player and add it to our players object
players[socket.id] = {
rotation: 0, x: Math.floor(Math.random() * 700) + 50,
y: Math.floor(Math.random() * 500) + 50,
playerId: socket.id,
team: Math.floor(Math.random() * 2) == 0 ? ‘red’ : ‘blue’
};
// send the players object to the new player
socket.emit(‘currentPlayers’, players);
// send the star object to the new player
//socket.emit(‘starLocation’, star);
// send the current scores
//socket.emit(‘scoreUpdate’, scores);

// update all other players of the new player
socket.broadcast.emit('newPlayer', players[socket.id]);

socket.handshake.session.ship_exists = true;
socket.handshake.session.save();

}

socket.on(‘disconnect’, () => {
console.log(‘user disconnected’);
// remove this player from our players object
delete players[socket.id];
// emit a message to all players to remove this player
io.emit(‘disconnect’, socket.id);
});

// when a player moves, update the player data
socket.on(‘playerMovement’, movementData => {
players[socket.id].x = movementData.x;
players[socket.id].y = movementData.y;
players[socket.id].rotation = movementData.rotation;
// emit a message to all players about the player that moved
socket.broadcast.emit(‘playerMoved’, players[socket.id]);
});

socket.on(‘starCollected’, () => {
if (players[socket.id].team === ‘red’) {
scores.red += 10;
} else {
scores.blue += 10;
}
star.x = Math.floor(Math.random() * 700) + 50;
star.y = Math.floor(Math.random() * 500) + 50;
io.emit(‘starLocation’, star);
io.emit(‘scoreUpdate’, scores);
})
});

server.listen(8081, () => {
console.log(Listening on ${server.address().port});
});

For handling physics in multiplayer situations, you really want to go with a traditional multiplayer setup. The server needs to be the authority and they need to be calculated only once on the server. Then, depending on how your game plays, you may need to do client-side prediction as well. If the player moves their character around at anything resembling a fast pace (basically, if the game is not turn-based), you will need client-side prediction. Here is a very good set of articles that explain the overall idea of this: Gabriel Gambetta: Fast-Paced Multiplayer

There are situations where you do need to calculate physics on the client to keep the client-side prediction working correctly, but that is not being calculated for the true game state. You are only calculating them on the client so you can move the player character as soon as possible, but the physics calculations on the server are the true state of the world and will override the calculations on the client. In these cases, you want to have the most simple physics possible or may need to do a lot of work on them to make it as deterministic as possible so that the client and the server come up with as close to the same result as possible. A game like Angry Birds would be very difficult to do in this way because the physics are quite detailed and can be non-deterministic. In situations like that, I would probably find a way to forego client-side prediction, try to work the lag into the gameplay and have the physics run only once on the server.

@Jackolantern - Hmm… I see. I had planned on creating a 2D shooter but maybe for a first multiplayer game this would be too much of a challenge. Thanks for this information, its an area I really wouldn’t know much about.

Unfortunately there aren’t a ton of good multiplayer JS tutorials out there that create a real multiplayer setup that will withstand the lag of running on a real server. They write the code assuming a “listen-server”, or client and server running on the same machine with sub-1ms lag. These games become either unstable or unplayable when the client and server are separated by hundreds or thousands of miles like they will be in the real world. The solution is client-side prediction but that essentially requires you to build a lot of your game logic twice (once on the client and again on the server) or come up with a “polymorphic” solution (meaning code that is shared between server and client, which is of course possible with Javascript but still adds a layer of complexity).

So with that in mind, if you are going to roll your idea back a bit, what could you do first to learn more about multiplayer that will get you closer to your goal of making a real-time, fast-paced 2D online game? The most direct route would be to not worry so much about your learning project being fun. I would aim to make a much slower shooting game with Phaser and node. Don’t worry about client-side prediction. Make the clients dumb and slow the game down so it can stay reasonably synced without the prediction. Maybe something like two characters in the game arena and each player can register one move per x seconds. After x seconds the server runs the game logic on the server, resolves everything that happened and then spits out the results to the client which then display what happened. Maybe each player can move or shoot a direction each round but can’t do both.

It doesn’t sound that fun, but you will learn a LOT from making this. You will have infrastructure set up that you could then start adding client-side prediction into and start converting into a real-time game. You will probably start to bump into some of the problems that the techniques in the linked article above fix, but at that speed, the game should still work fine.

The other option is stay with the techniques of client-side prediction, server rewind (possibly), etc. and scale the game way back. A multiplayer game of Pong would also be a good level of challenge if you attempt to implement most of the strategies outlined in the above article series. Only two players allowed. A third or higher player simply sees a message that the game is full, as managing multiple game states is an over-complication at this stage IMHO.

Best of luck with it!

@Jackolantern

I would aim to make a much slower shooting game with Phaser and node. Don’t worry about client-side prediction. Make the clients dumb and slow the game down so it can stay reasonably synced without the prediction. Maybe something like two characters in the game arena and each player can register one move per x seconds. After x seconds the server runs the game logic on the server, resolves everything that happened and then spits out the results to the client which then display what happened. Maybe each player can move or shoot a direction each round but can’t do both.

This sounds like a really good idea to both learn on the topic and get me closer to my goal. I didn’t even think about this I was so focused on the final end goal.

One final question for you. What about this library?

Would it possibly handle all of that horrible lag compensation stuff I would have to deal with manually?

I haven’t used it before. I have actually never even seen it which kind of surprises me as I thought I had seen almost everything out there for Javascript-based real-time multiplayer. Its feature list sounds very impressive. Another really good server/client tech for real-time multiplayer is Isogenic Engine. It is modeled after the Source engine’s multiplayer architecture.

The only downside to these types of engines is that they often end up being extremely complex to work with because they need to support advanced techniques for every usecase. It is very hard to offer a multiplayer solution that is going to work for even most dev’s games. There are so many multiplayer techniques out there and many of them are only appropriate for very specific cases. For example, an FPS game is going to definitely need server-rewind for detecting head shots semi-fairly, dead reckoning, etc. While a lot of these techniques are going to be used in the multiplayer code for a game like Street Fighter V, there are going to be some dark wizardry techniques going on in SFV to make it look like your opponent is reacting in the sub-20ms range even with lag that may not be needed at all in an FPS.

Maybe I am getting a little off-track here, but I would check out these engines. Try to make a simple multiplayer Pong game with them to see what you think. But I know Isogenic has the kind of engineering to it that would put it in a more commercial level that I think is going to be overkill for a lot of browser games. They can be so heavy and deep that sometimes you may be able to just make the game directly writing your server from scratch before you could become proficient with the engine. That said, you could probably go further in the long run with the engine. So it is a balancing act on what you like to develop with (you need to be cool enough with your tech to spend possibly thousands of hours working with it), what your goals are and what you want to learn.

I think Colyseus is very good solution thesedays. check that out~ :slight_smile: