Download premake 5 and copy the premake5 executable somewhere in your path.
If you don't have Visual Studio 2015 you can download the community edition for free.
Go to the command line under the libyojimbo directory and type:
premake5 solution
This creates Yojimbo.sln and a bunch of project files then opens them in Visual Studio for you.
Now you can build the library and run individual test programs as you would for any other visual studio solution.
Download premake 5 then build and install from source.
Next install libsodium and mbedtls.
On MacOS X, this can be done "brew install libsodium mbedtls". If you don't have Brew, you can install it from http://brew.sh.
On Linux, depending on your particular distribution there may be prebuilt packages for libsodium and mbedtls, or you may have to build from source from here libsodium and here mbedtls. Make sure you install the 2.x version of mbedtls. The 1.x version will not work with libyojimbo.
Next go to the command line under the libyojimbo directory and enter:
premake5 gmake
This creates makefiles which you can use to build the source via "make all", or if you prefer, via the following shortcuts:
premake5 test // build and run unit tests
premake5 info // build and run network info (prints list of local network interface IP addresses)
premake5 cs // build and run the local client/server testbed with secure connect tokens
premake5 server // build run your own yojimbo server on localhost on UDP port 40000
premake5 client // build and run the client that connects to the server running on localhost
libyojimbo supports Docker on Windows, Mac and Linux.
First, install the latest version of Docker from http://www.docker.com
If you are running on Windows go into Docker network settings and enable "Expose container ports on localhost".
Now go to the command line at the yojimbo directory and enter:
premake5 docker
This builds and runs a Docker container with a yojimbo server inside it (exactly the same as if you ran "premake5 server" on a Linux box). You can now connect to this server by running a client which connects to 127.0.0.0:40000. For example, "premake5 client" on Mac or Linux, or running the "client" project inside the Yojimbo.sln in Visual Studio.
IMPORTANT: The premake docker action takes a long time initially, because it has a lot of work to do:
-
Start a new docker image from an ubuntu derived base image
-
apt-get update, apt-get install wget, g++
-
Download, build and install premake5
-
Download, build and install libsodium and libucl
-
Build release version of libyojimbo, run tests
-
If all tests pass, clean everything and copy the libyojimbo server to the /home dir
-
When the Docker container is run, start the yojimbo server /home/server on UDP port 40000.
For details see docker/Dockerfile and the premake5.lua file with commands that build and run the container instance.
What's most impressive is that if no dependencies have changed, the numbered steps above are precached as intermediate Docker instances are not rebuilt unless necessary. For example, if you have already downloaded and installed wget, g++, libsodium and premake5 and you run "premake5 docker" again, these steps are skipped.
Try it yourself by running "premake5 docker" once (it should build everything), then run it again. It will go straight to the server running on port 40000. Similarly, if you change some libyojimbo source it automatically rebuilds libyojimbo server and runs tests before starting the server. Impressive!
In order to support authentication and a secure connection between client and server, yojimbo provides a backend written in golang.
The purpose of this backend is to take a client request for connection and provide a connection token which the client uses to securely connect to a dedicated server.
To run this matchmaker backend, run this command:
premake5 matcher
This builds and runs a linux docker instance with matcher.go running on port 8080.
You can verify the matcher instance is working correctly as follows:
curl https://localhost:8080/match/12345/1 --insecure
This should return the match response in JSON which looks something like this:
{"connectToken":"y2R7Sqej9HFg7Y3sBqr9XbK6tyMicrmk13TLsGksxAqniu5LaY89AKhgKDGlQ/mIpxukwFdDwPBtMa5KRPpFUlIds2dD+gNGOjH636Vxh5Svb1Ul8AzajQoiamC1w2TN/qAQjW2+bFp/k6ifDKoEwcchHSlqbgzzIxctgr1iODJADyMb7YdHq7TCWApxeWAIrWnHfapTD1uVJU0oDjAqak/QtSLG6GAMCi9Qxgd66aQlK+V/2nm7bMQ00ubXZC8mSUI5xRssNeoFQsZF68rJcxsZgEIumLg16NOkZiX/K7DGbHR2If+1hqNUJqf7S8N30mNoI1yXMKNSlfiqts7Eze40NjVxiYzseibPfZt6uOXGgkeEFjQeTs9xMB78BzpOn5Z4I6JpqC/1TKewW5S4LpK8mWz3w8z7Px6leo4g0SqOxo/0Beqim6JoelMJe0nan2T+XKkX5GeaTrqnkVzKyDda+RD+22KLyMD3vda89RCq3KJUhpbyBAC7CHUrHGxj9jufzLebaVrsQN+KaZlKJytMbRee3G+NGjuilXiHbAJzY101fqDfGL9vNMgPNQiaiWDbbIdXyGp+H3yhzCnUbALOeHPDhhFnXUyB+XwBjqq/w2JbNIXei3AN+vydjmhSKav7SnSFO8MpMlOv/0ztI/eJPR7CUx/XEFPlfr6EK5UhUWKoPcrUs5pLOohqByckvrkrasfhftAHcr82kQiYo1jKFiKX8fE1/dspdXirkWd26aoT8RQFWIs48z/Rmvv609oaHlQGo3pBj6ogjU82aMyCPGoao2QYRdd2HTjWqBevkDn+O6/YFeJLFkonaa5GsQjpJfi+CrFdJTNldMcLfB7mAuq9tIwhKT3ivHEYsZvnlV45NtZuwXhjLEms2z/YrTgP0OiVC9s6utRG8loJoj+nzDMfnr567Si3VLXJAHwCZ/tJT+fnJvzGXE+T0gw+WKHz8dKVuRLb16FAvRlBHekbs2dj9///djLvXQPp4dDr1KcXDsSt/nBf5d/wnaU1EvltKv8y6oclu195OTqWSmzeJYH2f+Gn0RP9xVVKxTliKcoMz84/h+IQXD2Qd3DR7dtJfQkoZRB/zCEoRHIXLTA6N0BDxqpsG916Fg7fC4c5GDDvmNU0NZV0Vwz2E1ydXsDuS9Q1vxIpuQbqlhfckjRuuHOY4dlQfDOqTEPoSQxIJhUlhsPr4zImhbvhUfqdCZKonjQura62BdJHEE/aTF8KavvNgm5JEyRz8H3b2Aqrjuzj6tQJYpe8TZ3eMspGd7o/S5mu5JS/WcPhlQYiruzSNTuASwPwCxWjHzwNQwyDhM7ZjkHOrEYCp3uj9RYAIah7R7w4gwxeAoQIjkEKFA==","connectNonce":"1","serverAddresses":["MTI3LjAuMC4xOjUwMDAw"],"clientToServerKey":"8oRGFHDQo+Z38lhyKQ+1OjDDlFwjg6o1HkeR+fgw4z0=","serverToClientKey":"D0g58nVdAx0jmT+duZwcWdbHUuFBKWGyj2SZ8g+Ak0o="}
If you have both a matcher and server running you can run the "connect" program to connect a client to that server through the matchmaker.
On MacOSX or Linux, run:
premake5 connect
If you are building under Visual Studio, run the "connect" project from the IDE.
Now lets switch over to running secure servers. You can run a secure server like this:
premake5 secure_server
If you are building under Visual Studio, run the "secure_server" project from the IDE.
Now that the secure server is running, you will see that connecting via "connect" works, but regular connects via "client" do not.
This is the entire point of secure servers. Secure servers only allow connections that come from the matcher.
This is accomplished via an encrypted connect token that the matchmaker generates and passes back to the client. This connect token is valid only for a particular globally unique 64bit client id (of your choice), for a limited period of time, and for a limited whitelist of server addresses.
Connect tokens cannot be decrypted or forged by clients because they are encrypted and signed using a shared private key known only to the matcher and the dedicated server instances. This is why libyojimbo is designed only for games that host dedicated servers. The private key must be known only to the matcher and the dedicated server instances or the security model breaks down.
The latest preview release of yojimbo has support for reliable-ordered messages and blocks.
Messages can be sent in both directions. Client to server and server to client. Messages are reliable, ordered and time critical. They are resent rapidly until acked.
Blocks of data larger than MTU may be sent in the same reliable ordered stream. They are sliced up and reassembled on the other side and injected into the same reliable ordered stream of messages. This is very convenient for example when you need to send down a large block of initial state (eg. initial delta baseline, or state of the world) on client connect.
For more details see soak.cpp.
To see the blocks and messages in action, run:
premake5 soak
This soak program looks forever sending messages and reliable blocks over very bad network conditions.
The soak test is self validating. As long as it keeps receiving messages it's working. CTRL-C to stop.
There is now a profiling testbed for libyojimbo.
This testbed connects local 64 clients to a secure server in the same process and exchanges packets, messages and blocks continuously between them. Packets are still encrypted and sent over sockets so performance is representative of real world usage of the library.
The good news: The performance is very good, and after fixing some minor hotspots (see CHANGES.md) the total cost of libyojimbo library is smaller than the time it takes to call sendto/recvfrom, and much, much less than the time spent encrypting and decrypting packets inside libsodium.
This means that the actual performance cost of libyojimbo connection management and serializing packets is neglible.
If you'd like to look at the profile results yourself, you'll need a copy of Visual Studio 2015 (Community edition is fine).
First it is necessary to make some modifications to premake5.lua so you have debug symbols in release build for the profiler:
In the solution "Yojimbo" section near the top of premake5, add symbols to the release configuration:
configuration "Release"
flags { "Symbols" } -- add this line!
optimize "Speed"
defines { "NDEBUG" }
Now run "premake5 solution", switch to "Release" configuration and rebuild all.
Right click on "profile" project an set it as the startup project.
Press ALT-F2, check "CPU Usage" and click the "Start" to begin profiling.
After about minute you should have enough samples. Close the profile process and view the results. Enjoy.
Some users have expressed interest in using libyojimbo to transmit their own existing message format reliably over UDP. This means that all messages they want to send across libyojimbo are like tiny packets, eg. a block of memory with a size in bytes, rather than a smart C++ class with serialize function.
The good news is that you can adapt yojimbo to work with this sort of system, so I have added a new example program to show how: simple_messages.cpp
This sample set up two channels: a reliable-ordered channel, and an unreliable ordered channel, and two message types: small message and large message.
Two channels are necessary to be able to specify, per-message whether it should be reliably or unreliably. See CHANNEL_RELIABLE, CHANNEL_UNRELIABLE.
Two message types are necessary because even though all messages are blocks of data, if small messages in the reliable-ordered stream were sent as block messages (search the code for BlockMessage) this would stall out the protocol, because only one block message is sent over the wire at the same time. The small message type is a workaround for this, as it serializes its message data in-place, even in the reliable-ordered channel.
This example also shows how to sort of flip the the yojimbo transport concept inside-out to create a function that just writes a connection packet to a buffer in memory, and similarly, can just be pointed at a buffer in memory to read in a connection packet. This way, a connection can be used without the client/server layer, and without sending packets through the SocketTransport in yojimbo, but doing socket sends and receives yourself. If you already have your own connection concept, but still want all of the benefits of the transport like packet encryption, MTU splits and so on, but want to handle the sending of packets yourself, this is the way to do it.
This simple message system example is currently an experiment. It's meant to show that a certain way of working is possibly with yojimbo, but it is not as performant as it could be. For example, message data needs to be copied into yojimbo messages before sent, and the transport shim involves an extra memcpy per-packet on read and write.
But its a start. If you are interested in using yojimbo in this way, especially in having an efficient transport that reads and writes packets to memory instead of forcing you to send and receive packets to sockets owned by libyojimbo, let me know and I can make MemoryTransport an official feature of this library.
This is pre-release software so please email me with any feedback you have [email protected]
Thanks!
- Glenn