Jump to content
  • Advertisement
Sign in to follow this  
spacerat

GameNet: Simple RPC System for Games - Sample Game included (C++11)

This topic is 987 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Summary

 
GameNet  is a client / server RPC system using newest C++11 templates to make the life easier for you. When registering RPCs, function parameters are auto-detected from the function pointer. When calling RPCs, variardic templates allow extracting data from arbitrary parameter counts.
 
 
Features
 
* Supports GLM datatypes for use in 3D Games
* Supported data types : (u)char,(u)short,(u)int,float,double,vector,map, GLM vec,mat and quat
* Support for data type nesting like map [ string , vector ]
* RPC Data-type mismatch or wrong number of parameters are stated as error to ease debugging
* Numbers are automatically sent as the smallest possible datatype (byte, short , .. )
* Reliable and unreliable calls possible
* Function pointers of remote functions are not required
* Based on ENet
* Compression (Enet standard)
* Hack-safe - illegal packets are discarded
* Tested on Cygwin and Windows, should compile in Linux too
* Byte order preserved (hton/ntoh)
 
Limitations
 
* RPCs cannot be class member functions
* No encryption
* Only void functions supported. Non-void functions were tested but complicated everything.
* Client to Client connections are not supported
 
Example Game Features
 
* Lobby 
* Multiple Games
* Handle spawning/removing of game objects
* Simple Shooting functionality
* Intentionally in text mode to make it as simple as possible for you to adapt the code
 
Example Usage
 
Server Side:
 
    NetServer server;
    
    void login(uint clientid, string name, string password)
    {
        // clientid is attached as first parameter for server functions
        server.call(clientid, "set_pos", vec3(1,2,3));    
    }
    
    int main()
    {
        rpc_register_local(server.get_rpc(), login);
        rpc_register_remote(server.get_rpc(), set_pos);    
        server.start();
        core_sleep(10000) ; // wait client to do stuff
        server.stop();
    }
    
Client Side:
 
    NetClient client;
    
    void set_pos(vec3 pos)
    {
        // do something
        exit(0);
    }
    
    int main()
    {
        rpc_register_remote(client.get_rpc(), login);
        rpc_register_local(client.get_rpc(), set_pos);
        client.connect("localhost", 12345);
        client.call("login", "myname", "pass");
        while(1) client.process();
        //client.disconnect();
    }
Screenshots of the included example game: 
Edited by spacerat

Share this post


Link to post
Share on other sites
Advertisement
Thanks for sharing!

Do you have any benchmarks for how it performs in real sized games, or any comparisons to other libraries to add to the information above?

Share this post


Link to post
Share on other sites

Good question! So far I only compiled it on my PC.

 

A first simple test on localhost (Core i7 Notebook) gave:

 

1 Call / Network Update:

 

62.000 unreliable RPC calls/sec [client.call_ex(0,"hello_server", "Greetings")]

58.000 reliable RPC calls/sec  [client.call_ex(1,"hello_server", "Greetings")]

 

10 Calls / Network Update

 

138.000 unreliable RPC calls/sec [client.call_ex(0,"hello_server", "Greetings")]

270.000 reliable RPC calls/sec  [client.call_ex(1,"hello_server", "Greetings")]

 

No idea if thats good or not.. in any case strange that unreliable is slower for grouped calls. Enet drops packets that arrive with a lower number then the current - that could be a reason.

Edited by spacerat

Share this post


Link to post
Share on other sites

Your encoding is broken. You are sending byte representations of complex types through a Union.

 

Among other things, different machines can have different byte orders, and that is before we get to the nightmare that is floating point(*).

 

You need to explicitly encode and decode your data.

 

(*) To quote the C++ Specification, "The value representation of floating-point types is implementation-defined."

Share this post


Link to post
Share on other sites

Not to pick at the code, but seeing this in header file:

 

using namespace std;

using namespace glm;

 

Makes me a little hesitant about the rest of the code.

 

Actually I used std:: for many years, and this is the first project where i tried "using namespace". I must say it made the code really simpler and I dont have issues so far. 

 

Update: I have enclosed the "using namespace" in namespace net now, so ppl using the lib are now independent from that issue

Edited by spacerat

Share this post


Link to post
Share on other sites

Your encoding is broken. You are sending byte representations of complex types through a Union.

 

Among other things, different machines can have different byte orders, and that is before we get to the nightmare that is floating point(*).

 

You need to explicitly encode and decode your data.

 

(*) To quote the C++ Specification, "The value representation of floating-point types is implementation-defined."

 

This is already on my todo list  (See "* Byte order will be supported in the future (htons..)" in limitations)

Since I plan a PC / X86 only game at the moment, this issue didnt have such a high priority

 

Edit: Just found some time to update. Now, network byte order is supported.

Edited by spacerat

Share this post


Link to post
Share on other sites

hm, you said no compression, have you tried just using enet's builtin compression?
also google's snappy does a nice job of fast compression/decompression with decent ratios, and it's dead-simple to use.
for even nicer compression check out some binary delta compression, like bsdiff (https://github.com/mendsley/bsdiff).

Share this post


Link to post
Share on other sites

hm, you said no compression, have you tried just using enet's builtin compression?
also google's snappy does a nice job of fast compression/decompression with decent ratios, and it's dead-simple to use.
for even nicer compression check out some binary delta compression, like bsdiff (https://github.com/mendsley/bsdiff).

 

Good point. I have added the enet compression now - in the benchmark the performance lowered a bit, but thats due to localhost benchmarking. For distant peers it might be a benefit

Share this post


Link to post
Share on other sites

Your code is far too trusting. 

 

For example, looking at the code, it looks like if I send something that purports to be a string, but is not null terminated, you are going to simply cast and go, resulting in either a read of random garbage, or, more likely, an access violation.

 

Similarly, and again, just on a read through, I can cause the server to allocate large blocks of memory and then (most likely) crash by sending a malformed vector.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!