OpenGL Networked Game

Hi,

I’m writing a (linux based) network game that will accept connections at any point in the game. It uses a client/server organisation where any number of clients can be connected to the server. The server tells the
clients what they can see, and the client uses OpenGL to render this information. The server also uses OpenGL to render a view of everyone in the game (for administration purposes). The server waits for and accepts connections via the select() command.

The problem is that both the select() command and glutMainLoop() are blocking and I don’t see a way of
running both commands. Would it be possible to put the networking part in an OpenGL idle function? or would I have to do it some other way?

Any help at all would be appreciated.

I would just use a seperate thread for the network code.

Cheers DFrey, A quick search for “Programming C threads tutorial”, and I’ve got it working.


Fleejay

Ok, now I have another problem. I’ve got the server happily drawing up the landscape and talking to the clients. The clients are receiving the nessasary data (ie the landscape) but they aren’t drawing it up. My clients have two threads, a drawing thread and a networking thread, could this be the problem?

Are you sending the landscape through the network?.. unless you really want to do that, you aren’t supposed to do that.
Just have the clients load the landscape themself and send changed positions of objects through the network.

But no, it shouldn’t have anything to do with you having them in different threads.
Remember, that you all OpenGL calls need to be in the same thread as the one that created the window/context.

Isaack

fleejay,

Do not use Select(), it has very poor performance. You should put the socket in non-blocking mode instead. Threads are fine, but add some overhead to the game loop. You can just as easily run a game without them.

To put a socket in non-blocking mode, use a call like this:

ulong one = 1;
ret = ioctlsocket( sok, FIOBIO, (ulong *) &one )

Now, after calling a socket func, the return value of WSAEWOULDBLOCK is returned instead of blocking.

I highly recommend the book “Network Programming for Microsoft Windows”, Microsoft Press, ISBN 0-7356-0560-2. The code above is taken nearly verbatim (pp 231).

As for your game. What exactly are you sending to the client? You should send something like the following:
Server: Who are you?
Client: I’m Fred, my password is PASS
Server: Ok. Load Terrain X. You have 50 health, 30 dollars, you are carrying a gun with 10 bullets.
Client (loads and renders terrain, updates screen with health, gun, ammo, etc.)
Client: Ok, I’m ready.
Server: George is at position X, Y; Mary is at position X, Y; etc…
Client: I’m moving forward.
Server: You are now at position X, Y.
etc…

Good luck!
–Bryan

The game is only semi-realtime (ie turnbased) so performance is not an issue. It is written as a testbed for AI. The landscape is only passed when the AI requires it and it is only a small array of 16X16 z values, from which a small fractal landscape model can be created.

The problem is that I have 2 threads, one for the graphics and one for the networking, and I am having problems with them communicating. And ideas?

Maybe thread synchronization iz the trouble?
Dont know about Linux but thereare many thread synchronization objects under Win. Like Semaphores.

Randy

fleejay,

Not sure what your issue is here. As long as you’re running on a single processor architecture, you shouldn’t have problems sharing memory between the processes.

As RandyU said, make sure you’re locking out the other process from moving/deleting the memory when you try to use it.

I’d first verify that the data was received correctly in the network thread’s buffer. Then copy it to a malloc() block & send a windows message to the draw thread with the pointer. Let the draw thread take care of deleting the object.

–Bryan

Heh. The guy went out of his way to say “Linux based”, and yet people are talking about “WSAWOULDBLOCK” and Windows messages

I am not a network programming guru, but the following has worked for me:

In Un*x, the simplest way to have two processes communicate is with a fork and a pipe:

int pipe_fds[2];

pipe(pipe_fds);

switch (fork())
{
case -1:
printf("Can’t fork
");
return;
case 0:
return ChildProcess(pipe_fds[1]);
default:
return ParentProcess(pipe_fds[0]);
}

The child process can then go off and handle network stuff and communicate info to the parent through the file descriptor given in pipe_fds[1]. The parent can do drawing and stuff and communicate info to the child via pipe_fds[0].

You should probably install a SIGCHLD signal handler that kills the parent (patricide and matricide … mmm) in case the child process blows up, or gets interrupted, or exits.

Under Linux, “fork” is quite effecient (at least, so says the documentation… but if you don’t believe it, go look at the source )

Like I said, I’m not a guru, but this has worked for me. You should probably check out “Unix Network Programming” (I think that’s the title… it’s a big blue and white book by Addison Wesley).

Hope this helps.

I think that in place of WSAWOULDBLOCK in windows you would use EINPROGRESS in Linux… You can put a socket into non-blocking mode the same way for both Linux and Win32.

I have seen no mention of “Windows messages” other than that one Winsock-specific constant. Maybe I missed something somewhere. The only other Windows specific thing I noticed was a book based on Windows sockets.

From part of the Linux man page for connect…

RETURN VALUE
If the connection or binding succeeds, zero is returned.
On error, -1 is returned, and errno is set appropriately.

ERRORS
<other errors clipped for brevity>
EINPROGRESS
The socket is non-blocking and the connection cannot be completed immediately. It is possible to
select(2) or poll(2) for completion by selecting
the socket for writing. After select indicates
writability, use getsockopt(2) to read the SO_ERROR
option at level SOL_SOCKET to determine whether
connect completed successfully (SO_ERROR is zero)
or unsuccessfully (SO_ERROR is one of the usual
error codes listed here, explaining the reason for
the failure).

Actually… I just took a quick look in winsock.h and noticed this…

#define EWOULDBLOCK WSAEWOULDBLOCK

I didn’t see any mention of that in the man page for connect, but my guess is that would be the correct message to look for with non-blocking sockets.

[This message has been edited by Deiussum (edited 02-27-2001).]

rts,

Thanx, I thought that pipes or shared memory were the way forward but I wasn’t too sure of the syntax.

Fleejay

Non-blocking sockets are a pain: that’s why I use two processes. The network handling process basically sits in a loop, calling select() on the socket and the pipe, doing things when things are ready, and effeciently waits while things are not ready. In the Windows world, where there is no such thing as “fork”, non-blocking sockets are a necessity.

Hmmm, one small minor nitpick to what rts says. According to MSDN, non-blocking sockets should be avoided if possible. In fact it seems as if Microsoft has been deleting references to non-blocking sockets and emphasizing the use of blocking sockets in a seperate worker thread to effectively obtain non-blocking behavior. The change is because WinSock 1.1 could not assume pre-emptive scheduling and so required non-blocking semantics. But for WinSock 2.0 which only runs on a pre-emptive os, this assumption is not necessary, it works much more like Berkeley’s implementation.

DFrey,

This is news to me, I’ve seen so much ‘non-blocking is the way’ in my research, I’d like to hear something from the other side. Do you have a link or other documentation describing this change of attitude?

I’m trying to keep my networking as cross-platform compatable as I can (thus, sockets and not Windows functions). But I also want the greatest possible throughput, which means as little thread switching as possible. If the worker thread had priority and passed the packets to the main thread when recieved, then blocked/suspended when there was no I/O, this would be just as good to me.

Thanks,
–Bryan