VV Eekly Update #14 – Networking

Hello, and welcome back to the VV Eekly Update! Today, we’ll be meeting our neighbors, exchanging business cards, and… wait, we’ve done this joke before, haven’t we? *ahem* Anyway, I’ll be telling you about some of the networking challenges in creating a peer-to-peer (P2P) networked multiplayer game and how I’ve addressed some of them.

In my opinion, I think coop games with a small number of players are some of the most fun games. I would like to think that Shrubb Games could eventually become a company that regularly produces small, fun co-op games. In that sense, the networking base is the most important part of Vyn and Verdan! Theoretically, every game I make would build on this networking! Just don’t look too closely at the code yet… it’s a work in progress.

Networking

First, let’s back up and talk about what we need for a multiplayer game over the internet. Essentially, we need for two computers in different places to look convincingly similar enough that the people playing on those computers think they’re looking at the same thing. However, those two computers are typically separated by distance, and, more crucially, by time. How do we make those two computers look alike enough even when they can’t talk to each other immediately?

There are many interesting topics for the general case to dive into here with not nearly enough time. I’ll be talking largely about the solutions I’ve found necessary for Vyn and Verdan. Since Vyn and Verdan is a two-player (low number of players!) and co-op (no competitiveness!) game, there are a number of shortcuts I’ve been able to take!

Here are the four questions I’ll answer here:

  1. What data do the computers send? (Serialization)
  2. How do the computers send data? (Transport)
  3. Who’s responsible for what data? (Authority)
  4. What do the computers do with the data? (State Updates)

Serialization

What are the two computers actually sending to each other? Usually, the data depends largely on the specific purpose, but, in general for Vyn and Verdan, it’s either 1) user inputs, 2) random game decisions, and 3) maintenance state.

Imagine a game like an assembly machine. The machine creates an experience on the screen, but the product is largely already designed. There’s these pixels here, this UI bar here, that enemy sprite showing up there, etc. Like watching a movie in sync, as long as you press “start” at about the same time on each computer, it’ll mostly line up.

The predictability ends with user inputs. You have no way of knowing in advance what buttons the user is going to press! On top of that, most games have some element of chance. There are many arbitrary decisions to be made, e.g. “Which order are upgrades in?”, “Which enemies spawn in this room?”, “Which way will the enemy turn?” All of this information needs to be communicated and synced between the two computers!

The other large component is what I call “maintenance”. We know and have to accept that there may be slight desynchronization between the two computers. Perhaps the enemies turn a little earlier, or the players hit a wall, or the player used their interrupt ability right before an enemy used an ability rather than right after. That’s why some maintenance state is sent to make sure that things eventually line up! Players won’t notice if things are only slightly off, but they will notice when that builds up and things are very misaligned.

Transport

How do the computers actually send data to each other? Back in the day, I would be telling you about UDP, TCP, ENet, etc. Nowadays, it seems like the usual thing to do is to use relay networks.

Relay networks essentially use a server to communicate between two clients. The server is usually well-positioned (and there are servers everywhere!), so both the connection between client A and the server and the connection between client B and the server are solid. There are also perks with hiding IP addresses, preventing DOS attacks and providing privacy.

Where do you get this server? You can usually buy your own server ~on the cloud~, but, since I’m publishing on Steam, I have the benefit of using Steam’s own relay network. Hey, I pay for it with a hefty percentage of sales, and, by golly, I’ll use it! I’ve heard rumblings that Epic also provides a relay network for games for free (getting that good ol’ market share by throwing money at it), but I haven’t looked into it at all.

There’s an addendum here where I could be talking about compression or encryption. I don’t think I have too much to add here – I just use the standard compression offering and don’t bother with encryption since it’s a coop game. Do it! Don’t forget it!

Authority

Which computer is actually responsible for what? This question isn’t usually obvious at first glance, but it becomes more important as you actually implement a game.

In an early playtest, there was this mysterious bug where a player would take damage without anything obvious nearby.

This was because the player was actually taking damage on the other computer. That beetle was near enough to the player – and there was enough lag – that the player was colliding with the beetle and taking constant damage without it being obvious on the screen.

For the game to feel good, the player needs to be able to see immediate reaction on their screen when they move. For the game to feel fair, the player needs to be able to see every time they take damage or something happens to them. This implies that everything that happens to a player needs to be under control of their computer.

For online competitive games, this doesn’t work well because of cheaters. This is why we can’t have nice things! However, for cooperative games, this works fine! You can go ahead and cheat in Vyn and Verdan, and I don’t think anyone will mind.

State Updates

Once the computer actually has the data that was sent over, what do they do with the data?

Well… it depends! For user actions and random game decisions, mostly, you just apply the action or decision. For maintenance state, I do a bit of reflective programming and mostly update whatever properties need updating directly.

Reflection helps programmers make generic software libraries to display data, process different formats of data, perform serialization and deserialization of data for communication, or do bundling and unbundling of data for containers or bursts of communication.

https://en.wikipedia.org/wiki/Reflective_programming

That reflective programming kinda looks like this:

func serialize(obj: Object, path: Path):
    return obj.get_path(path)

func update(obj: Object, path: Path, data: Variant):
    obj.set_path(path, data)

There’s some hand-waving with the specific Godot syntax here, but reflection allows you to arbitrarily serialize and update any path on any object. It’s not very good for maintaining code, since paths are generally strings, but it’s very powerful for serializing and deserializing!

The last point I’ll bring up here is that positions on the screen have a special exemption here. If positions were to be synchronized directly, then whoever isn’t hosting the game might see a lot of skipping around. Instead, I interpolate the updated position with the current position to make the update smoother. There’s no perfect way around updating a position… but this looks smoother!

Whew! That felt like a long update to me. Did that feel long to you? You don’t need to answer that, but if you have any other state or reflections you want to communicate across the networks, please authoritatively relay them into the Discord!

Scroll to Top