PDA

View Full Version : [Networking] Improving the engine's networking system



Solokiller
11-05-2016, 08:50 AM
The engine's networking system is pretty old and hard to work with, so i built a prototype client server program that uses ENet to manage the network connection, and Google protobufs to send messages.

Here's the Github repository: https://github.com/SamVanheer/ENetClientServer

I believe this falls under the Feedback category that this subforum is about, so it shouldn't have to be moved.

A quick overview:

ENet (http://enet.bespin.org/) is a cross-platform C library that is designed to handle connections between 2 computers. Hosts connect to each-other, and can send packets once the connection is established.

The library itself is not designed to be a client server architecture, but is fully capable of being used in that manner. This library would replace the engine's NetChan (http://fabiensanglard.net/quakeSource/quakeSourceNetWork.php) system entirely.

Google protobuf (https://developers.google.com/protocol-buffers/) is a library designed to allow messages that are sent over a network to be handled more easily. With protobuf, all you have to do is describe your message (https://github.com/SamVanheer/ENetClientServer/blob/master/src/messages/cl_sv_messages/ClientCmd.proto), compile it, and include the generated code in your project. You can then create an instance of the message, serialize it to a buffer, send it over a network, and parse it back in on the other side.

The main advantages of using protobuf are that it handles endianness automatically, absolving you of the need to byteswap everything manually. It handles (de)serialization for you, making it almost trivial to use. It can also compress data using variable length encoding, which enables messages to be much smaller if they carry data that requires fewer bytes to represent it. For example, a message that contains a 32 bit integer may only require 1 byte to store the value if its range can fit in one byte (actually 7 bits, but that's complicated).

TL;DR:
ENet will take care of connecting and disconnecting from remote hosts, while protobuf handles (de)serialization. Implementing a client server architecture is relatively easy to do as a result.

The prototype isn't fully optimized and doesn't handle every edge case yet, but it demonstrates how an implementation could be done.

Now as to why i did this: aside from wanting to learn how to do this, the team was planning to do this but as usual, didn't have the time to do it, nor was any information shared, if any team members knew anything of this. So i changed that. I'll probably optimize it a bit, add timeout checking, etc. There's not much else that can be done in such a small program.

Since pure code is largely useless to most people, here's a compiled version that you can try: https://dl.dropboxusercontent.com/u/46048979/ENetClientServer.rar

You'll need the VS 2015 redistributable: https://www.microsoft.com/en-us/download/details.aspx?id=48145

There's not much you can do, but this will let you establish a connection to the local host, send messages to the server and get replies back.

First off, you'll need to connect, so type "connect" and hit enter. Once you're connected, you can send messages by typing "sendmessage <message>" and hitting enter, where <message> is the message you want to send. You don't have to give it a message, you can send empty messages too. You'll see the server's reaction and response.

When you're done, you can just type "disconnect" and hit enter. After that, enter "finish" and hit enter to stop the program. Then hit enter again to shut it down.

Sample output:


Starting server
Starting client
Running
connect
Connecting to server...Connection succeeded
Client connected
sendmessage Hello World!
client: Sent message
from client: Hello World!
server: Message Size: 15
from server: Message received
disconnect
Client disconnected
finish
Finished
Shutting down client
Shutting down server
Press ENTER to continue...


Well, what else can i say? Discuss, offer thoughts and feedback. If there are any programmers around, you can offer suggestions and improvements if you want.

Nero
11-05-2016, 11:27 AM
I never took networking in college so I know nothing about things like this.
But I'm all for it anyway! :3

JORGETECH
11-05-2016, 11:48 AM
Very interesting concept for replacing the current one, I do not know a lot about networking in videogames so I can't make a very developed opinion. Only one doubt, can be easily ported to Linux using GCC maybe? I guess this has to be done because the SC Team has porting to Linux in their mind so...

Solokiller
11-05-2016, 12:07 PM
There's nothing platform specific in use here, ENet and protobuf are already supported on all major platforms. The Linux version of the game already works, aside from Miles sound system not playing MP3s and crashing when the game shuts down, as well as font display issues. Seeing as i made it possible to compile and run the Linux client, i very much doubt it could've gotten worse, unless i missed something.

JORGETECH
11-05-2016, 12:18 PM
I would suggest to the SC Team to make Linux PUBLIC Beta so all Linux users could test it out and report problems for fixing them, I personally think that it is the best way to test the Linux version I would very happily start to use that version rather tha using WINE even if it is a Beta. If the ENet and protobuf code is not OS/Kernel specific can I take the source code and compile it with GCC?

Solokiller
11-05-2016, 12:27 PM
Releasing a public Linux beta makes sense, so don't expect to see it any time soon. If you're lucky they'll forget about it and include it by accident. Or they'll include it to prove me wrong, whichever way gets it released.

Both the ENet and protobuf downloads come with a makefile. The program i wrote might have issues sending data from a big endian machine to a little endian machine, and vice versa since it doesn't perform byte swapping. As long as the endianness is the same for the client and server there shouldn't be any issues though.

JORGETECH
11-05-2016, 12:49 PM
Thanks for the info, keep the good work. I suppose the next update to this could add byte swapping if possible of course.

EDIT: I do not think the endian differences are a big issue because x86 and x86_64 are both little-endian but it must be fixed anyways once you get a finished product.

Duggles
11-05-2016, 02:03 PM
Worrying about endianness is irrelevant. All x86/x86_64 architectures follow little endian ordering. It's unlikely that Svencoop will ever be cross-compiled to a Motorola 68k or IBM z/Power chip so big-endian machines are really not a concern. The only potential one would be ARM, but I don't think Svencoop Mobile Edition is going to happen any time soon (and even then ARM can be either little or big endian).

The code repo could have the ENet and protobuf repos as submodules (seeing as the solution file defines them as being in an 'external' directory), meaning that you could recursively clone the repo and pull in the dependencies.

As Solokiller said, the Linux client is buggy. At the moment, it reliably crashes when you quit because of a problem with the sound library, and there are probably other bugs that will surface with further testing.

As proving that you can write a wrapper around a connection library and send messages using a message library, this is great. Ripping out the old network code and replacing it with the ENet-protobuf mixture is, as Solokiller would know (even better than I), somewhat more difficult given the tightly coupled nature of the Half-Life source code and, in particular, the arbitrary nature of network messages, meaning that either all the network messages would need to be refactored into a more uniform set of messages, or you essentially follow the anti-pattern for protobuf where you make an arbitrary number of message types, bloating the code and making communication less efficient.

Solokiller
11-05-2016, 02:14 PM
The only problem when it comes to game defined network messages is that they'd have to be rewritten to use protobuf, but you don't have to. You could simply define all messages used by the engine to be serialized using protobuf, and handle the game's messages as it always has. The engine has to check if it's defined by the game anyway because it handles them differently, so there wouldn't be any overhead that wasn't there before. When it comes to the engine's own messages, you can define your message handling code in such a manner that you could mix protobuf and old style messages seamlessly.

Regarding the Linux client, bugs can only be found by testing it, so releasing it makes sense. There are too few team members (period) that have a Linux OS or VM to rely on them to find all of the problems. Even if there were more than the handful there are now, they wouldn't have any time to test it thoroughly enough. If you want progress, open up to the community. If you don't want progress, you're on track for that goal already.

GiGaBiTe
12-05-2016, 01:33 AM
This would be great and all if it was implemented 15 years ago, not today or some time in the future. Valve already fucked every HL engine mod with Steampipe and the zombie brain surgery of grafting Source's networking protocol into Half-Life, which broke tons of mods and plugins that will never work again.

The HL engine is already a nearly 20 year mess of bolted on hacks, stop perpetuating the insanity. If you want to fix the engine, fix everything at once or nothing at all. We're tired of having to reinvent the wheel every time someone gets this great idea to completely change how part of the engine works "because reasons".

Solokiller
12-05-2016, 03:09 AM
That's a good point. Can you provide a list of things that were broken? I know the SteamPipe update broke mods and plugins that didn't use the engine's file I/O functions, what else is there? Perhaps some of these things could be fixed.

As for fixing everything at once, unless you want to wait 10 years for another update, that's not going to happen.

GiGaBiTe
12-05-2016, 06:50 PM
Natural Selection was completely broken by Steampipe, both on the client and server side. Fortunately, UWE released the source code and someone picked it up and fixed most of the issues. But that's still on a Github repo and doesn't have any real mention in the public so people abandoned it. Several of my favorite Metamod plugins were broken, three being Webmod, Eye and Entmod is partially broken.

A few older HL mods like Gokarts, underworld bloodlines and Zombie don't work properly anymore and frequently crash or misbehave.

Being realistic, none of these issues are going to be fixed. Most of these mods and plugins never had their source released, and I'm sure many of them have been lost by the original authors. Finding a coder to do even small changes to something with existing code pro bono is hard enough, no such coder exists that would reverse engineer an entire plugin or mod to fix it.

Solokiller
13-05-2016, 02:33 AM
Has anyone tried to replace filesystem_stdio with one that ignores the new directories? It's not very difficult to do, you just have to worry about VAC. I don't know if that would fix the crashes, unless those mods are crashing due to failed file loading.

JORGETECH
16-05-2016, 12:22 PM
Worrying about endianness is irrelevant. All x86/x86_64 architectures follow little endian ordering. It's unlikely that Svencoop will ever be cross-compiled to a Motorola 68k or IBM z/Power chip so big-endian machines are really not a concern. The only potential one would be ARM, but I don't think Svencoop Mobile Edition is going to happen any time soon (and even then ARM can be either little or big endian).

Yes I know what are you talking about nearly all computers nowadays use the x86/x86_64 architectue but Sven Co-Op on mobile is not impossible, Xash3D was ported to Android so there is always a possibility.


Regarding the Linux client, bugs can only be found by testing it, so releasing it makes sense. There are too few team members (period) that have a Linux OS or VM to rely on them to find all of the problems. Even if there were more than the handful there are now, they wouldn't have any time to test it thoroughly enough. If you want progress, open up to the community. If you don't want progress, you're on track for that goal already.

That was what I exactly meant if you release it to the public you have more possibilities to find and correct bugs. I hope someone close to the dev team (Solokiller?) suggests this even if it not as stable, beta testing is better with more people.

Solokiller
16-05-2016, 01:02 PM
You'd probably have better luck asking them yourself.

Solokiller
01-06-2016, 01:03 PM
I reworked some stuff, messages are now queued up in a buffer and sent all at once at the end of a frame. This matches Quake/GoldSource engine behavior.
I've also eliminated the memory allocation that was used to serialize messages.

Solokiller
03-06-2016, 10:55 AM
I added network string tables to this prototype. Currently it only sends strings and isn't optimized, but it works.
The program currently creates 2 tables. The first table starts out empty, the second has a single string in it.
When the client connects, all tables are sent over.
Strings can be added to the first table using the "addstrings" command. All strings can be listed using "liststrings".
To list the second table's strings, use "liststrings2".

The addstrings command has 2 optional arguments. The first is which string to use, the second is how many to insert with that name.
Names are <string> <count>.

For example:



Connecting server
Starting server
Connecting client
Starting client
Running
connect
Connecting to server...Connection succeeded
Client connected
liststrings
Number of strings in table table: 0
addstrings "Hello SC" 10
liststrings
Number of strings in table table: 10
String 0: Hello SC 0
String 1: Hello SC 1
String 2: Hello SC 2
String 3: Hello SC 3
String 4: Hello SC 4
String 5: Hello SC 5
String 6: Hello SC 6
String 7: Hello SC 7
String 8: Hello SC 8
String 9: Hello SC 9
disconnect
server: Client disconnected
client: Client disconnected
finish
Finished
Shutting down client
Shutting down server
Press ENTER to continue...


I haven't stress tested it yet, and i'm going to clean up the implementation some more before adding support for per-string binary data, but this on its own is quite useful already.

Solokiller
03-06-2016, 03:07 PM
I've cleaned it up and optimized its bandwidth usage. When using the addstrings command, it can send at least 10000 strings at once without overflowing a 40000 byte buffer. This is due to optimizations in data compression to account for the general case of strings being sent in sequence, and sharing the same base string "foo ".

For example, a model precache list could have the following entries:



models/hgrunt.mdl
models/hgrunt_opfor.mdl
models/hgrunt_medic.mdl


All 3 have the same base string "models/hgrunt". Up to 31 characters can be saved per string after the first one. The same optimization applies to sounds, and any other file path based lists.

Once i've stress tested this enough, it should be good to go.

This implementation can be used to replace the soundcache. Here's how:
Tables for sounds and sentences, as well as a table for miscellaneous data like the custom materials file are created on map start. All data is added as needed, and networked to clients when they connect. The client sound system can then parse the list as it does now with the cache file.

I've also added a callback function, which allows late precaching to occur. Once the sound system has loaded all data, it installs the callback, allowing it to catch all late precached sounds for loading.
The same applies to models.

Next up should be binary data.

[CHN]iAmZC
04-06-2016, 12:55 AM
Oh no, if use google services, Chinese(Mainland) players will can't play Sven Co-op, because it's blocked by our government(People's Republic of China).

Solokiller
04-06-2016, 03:15 AM
Protobuf doesn't use Google services. It's just a library made by Google to provide serialization and de-serialization for messages.