Gillius's Programming

GNE FAQ

FYI: Some questions/answers here are copied from conversations by email or other means, so some of the questions have been shortened, and the responses are written as such.

Last updated: June 15, 2010 21:37CDT

How do I get started programming with GNE?
What will GNE do for me? What are its capabilities?
GNE is written in C++, but I want to add networking to my C game.
I like what GNE can do, but I don't need all of it. Can GNE's code be separated (for example use only the threading classes)?
I heard about a list service called GNet. What is it?
What ARE threads?
Why bother with threads for my game, what's wrong with polling?
OK, polling alone is bad, but I use yields or commands like Sleep(0) in Win32, so again, why use threads?
What amount of control can I expect to have over what is sent?
How can you assure that the programmer can enforce or be sure that a written packet will go out "now"?
Writing even a very simple program with GNE seems pretty complicated. Why so much code to start out?

Return to GNE homepage

How do I get started programming with GNE?

There is a beginniner's guide at the start of the main documentation that will point you to the classes and examples to explore first. This FAQ should also help. I hope to improve the learnability of the library in the future when time permits. If you have learned GNE, you could give back to the project by writing some tutorials/examples and contacting me.

Return to Top

What will GNE do for me? What are its capabilities?

The best way to find out about find out about what GNE can do is to try it out! There is also a requirements document that outlines what GNE will do.

The GNE library will be used to connect your program to other game clients or the game server through using the GNE protocol. It cannot be used to connect to non-GNE programs -- for example you cannot use GNE as a general networking library to get data from a website or implement an IRC chat program. However, since GNE uses HawkNL you are more than welcome to use the HawkNL API to create your own connection type, and integrate it into GNE using the ConnectionEventGenerator.

The GNE protocol will be publically published for all to see and use in the hopes that others might make GNE compatable clients/servers and to allow others to implement an alternate, compatable version of GNE in another language like C, Java, or Pascal.

Return to Top

GNE is written in C++, but I want to add networking to my C game.

Only the networking portion of your game that directly uses GNE has to be able to call C++ methods. You'll need to derive your own class from ConnectionListener or use SyncConnections. You'll also need to use C++ to set up the library but this should not require very large amounts of code. Once you write that C++ code you can have your ConnectionListener or the function using the SyncConnection call your C routines with the information they need, thereby sheilding your game engine from GNE.

The Console I/O functions are in C++ but the functions are just like their C counterparts, except they are in the GNE::Console namespace. If your compiler supports C++ and C (as every modern compiler I know does), you can keep your C-style code and still call those C-style functions even though they are C++ code.

The GNE protocol is publically published on this site so if the C++ requirement is unacceptable one can always make a compatable client or server strictly in C as long as it meets the GNE protocol specification which was purposely written to be as language and network transparent as I could.

Also, if someone is willing to make a C wrapper for GNE and it is of good quality I would be very glad and willing to help that person by answering questions and also to include it into the official GNE codebase. But I have to focus on finishing GNE to be a decent, mature library before I can turn my attention to that.

Return to Top

I like what GNE can do, but I don't need all of it. Can GNE's code be separated (for example use only the threading classes)?

Yes, and I may possibly increase the modularity of GNE in the future to allow for easier separation. As of now (June 2002), one could separate the threading classes (Thread, Mutex, ConditionVariable, LockMutex) with some ease. One can also separate and use the timing classes (Time, Timer, TimerCallback, etc), but Timer needs the threading classes. The Address and SocketPair classes might be useful for HawkNL users.

Return to Top

I heard about a list service called GNet. What is it?

After GNE is completed work will begin on GNet, which will be a list service that will list all games that use GNE. Games using the GNE library can use the provided API to interact with GNet to get a list of running servers or register their server so that clients may find them. This will work similar to other services like Half-Life's game finder and Battle.net. Multiple games will be listed on the same service and after an initial release, chat/IM capability might be added into GNet, however the GNet project is still far off at this time and therefore more precise details are not known.

Return to Top

What ARE threads?

Threads simply defined are multiple execution paths in your program at the same time. There is a very good site that explains pthreads, the library that GNE creates C++ wrappers for, so it is directly relevant.

Some examples a thread would be perfect to use:

Return to Top

Why bother with threads for my game, what's wrong with polling?

Polling does not take advantage of the operating system's facilities for controlling the execution of your program. It can cause extra latency and a much lower framerate, espically when the server and client is running on the same system with different processes. When running on a multiprocessor machine or a dedicated server, this isn't as much of a problem, but we still hog more OS resources than we should.

When you set up a server for polling, the OS will give as much time to the server as possible because the program is never telling the OS to wait for anything. When the server process starts up, it will poll its connections and pick up the packets waiting for it, which might take a very little time (0.0005ms). Then it will start to poll -- but say the OS gave 50ms of time to this process -- that means 49.9995ms of CPU time was wasted polling instead of running the client that might be running on this machine or allowing the OS CPU time to check for new packets. Not only is the CPU time wasted, but there is no chance for any more packets to arrive during that time!

When using threads you can effectively tell the OS "only wake us up when packets come in, and once we process those packets, give the CPU to someone else." This is done through the usage of the select() system call. The server only wakes up when packets come in and gives as much time as possible to any other programs running on the system and possibly to a client program running on the same system.

Return to Top

OK, polling alone is bad, but I use yields or commands like Sleep(0) in Win32, so again, why use threads?

(by email) That's still not the optimal solution. You don't abuse the CPU like a busy wait program does but you still have the jerkiness and response issues you would have with busy wait, or they may be intensified. Sleeping is probably better than yielding though if your algorithm isn't affected by it.

In Windows the sleeping granularity is about 10ms. I don't know why as systems can enforce better responses than that but it seems that Sleep rounds your sleep time up to the nearest 10ms. From what I've seen sleeping for 1 ms under Windows will sleep you for 10ms, as well as sleeping for 9 ms will sleep you really for 10. 10ms is quite a long time, and that process is not preemptible so if a packet comes in right after you sleep you do nothing for 9ms while it waits in the queue when it could have been processed and gone during the idle time.

Also if you are just only yielding, CPU time will still be reported as 100% used, and while other programs will still be responsive, you can introduce extra latency into the OS. Yielding is an almost perfect solution, but threading is probably the better choice, so the OS can properly control your program's execution to have it use no CPU or other resources until the the moment the event you are waiting for occurs.

Return to Top

Can the GNE user go to bit level? Do you give absolute control of what goes down the pipes? What are the GNE overheads, if any? Network optimization is important.

(by e-mail) This is a classic example of useability over flexibility. I wanted to make the library to cut out the most work possible. I also took into consideration of this being an indie audience where our time is much more important than absolute sheer performance. My true goal, if attainable, is to make GNE so good one would have no need to optimize it, which is why I wrote the API to not hint at any underlying implementation -- like OpenGL which started as a software library is expanded itself very trivally to take advantage of hardware acceleration as you sent all of the relevant information and OpenGL decided the best way to use it.

Now to actually answer that question, everything must be sent as GNE packets. A GNE packet has an overhead of a single byte. Once you get to that point you can put whatever the heck you want into the packet, and if you don't like the serialization functions you can write and read raw packets of data. I tried to introduce the ability for the user to override and customize how GNE works at every point I saw and was able to design from an engineering standpoint, hence the reliance of a large number of callbacks in the library.

As for true absolute control, you cannot nor will ever have that capability, unless you wrote a new implementation of the Connection class which is entirely possible to write a new class derived from Connection (I provide 2 with the library). Because if one wanted raw control they should just use plain sockets, or the HawkNL library. But thanks to the C++ language if one were truly that dissatisfied with the performance of GNE they can just derive their own implementation of the Connection interface and swap theirs out with mine.

Return to Top

How can you assure that the programmer can enforce or be sure that a written packet will go out "now"?

(by e-mail) That would violate GNE's concept of managing the network for you... HOWEVER -- There is the PacketFeeder class whose idea I got somewhat from reading about the Unreal Tournament netcode. When the writer thread runs low on packets or out (how low is low is customizable), it will call the PacketFeeder to give it a chance to add packets to the write queue. Adding a packet at this time will mean that it will go out as soon as it can. You want to keep some packets in there so GNE can properly optimize and combine the packets to conserve protocol overhead -- And this is better than a low-level library and just simply calling "send" because GNE keeps track of your computer's bandwidth so you know if you are flooding your computer or the server, so you will have a much better idea of when your send will ACTUALLY succeed.

Return to Top

Writing even a very simple program with GNE seems pretty complicated. Why so much code to start out?

(by email) The examples exhello and exsynchello show the minimum code you need to get set up. There is a large overhead in this setup and there is a bit of code, similar to Win32 programming and setting up the event pump, etc. But the thing is that once you get past this overhead you will need very little more GNE code to develop from something very small to something larger. Also the minimum program also gracefully handles all errors and addressing. If you think Allegro is easy to setup and GNE too complicated, that is true, yes, but consider a simple Allegro program of like 3-4 lines how many things will go wrong... To do it REALLY right you need to check all of your returns from Allegro inits and datafiles and then provide messages to the user etc, which is a lot of code. GNE has the higher coding overhead but when something goes wrong your program won't crash but instead will say like "you didn't open the socket" or "connecting to the address timed out" or "could not reach DNS server" or whatever (ie if Address x("www.domain.com") failed). You will end up with more robust and user-friendly programs this way.

Return to Top