Archived

This topic is now archived and is closed to further replies.

sherman

Developer's Guide to Multiplayer Games

Recommended Posts

I am just wondering if anybody has read Andrew Mulholland''s book, "Developer''s Guide to Multiplayer Games". I tried the code on the CD and even implemented a network program using the network library provided. The problem is that it works perfectly on the local network but doesn''t work very well on the Internet with a "game server connection error" most of the time. I have a feeling that the network library (both TCP and UPD using Winsock) is at fault because I have tried hosting the server on different computers (even on friends'' computers in different countries) but the result is always the same - works fine in the local area network but unreliable over the Internet (possibly works for people from countries that are nearer). A network latency problem perhaps? I am still unsure as the authors should have figured it out while working on the network library. Any idea what is wrong or how I can contact the authors of the book? Thanks ^_^. _________________ Best regards, Sherman Chin www.Sherman3D.com

Share this post


Link to post
Share on other sites
The problem is inherent with the connections you are implementing. TCP/IP is a guaranteed connection and delivery. You usually need to check the length of the packet that you sent to make sure that all of it arrived. Sometimes it doesn''t. It''s guaranteed to get there, but I don''t think it''s guaranteed to get there all in one piece. The other part that''s guaranteed is the ordering. Send packet1 then packet2. Packet1 is guaranteed to arrive before packet2.

UDP is a connectionless packet. You''re not guaranteed that the packet will get there in one piece, in the right order, or even that it will make it. If you send packet1 then packet2, they might arrive at the other end in that order, they might not. Both might get through, one might get through, or neither might get through. UDP is faster in terms of network overhead on the machine and better suited for quick, throwaway messages, but you should never rely on it for guaranteed messaging.

I''m sure that some of my explanation is a bit off, and someone else who has had more experience in this area would have a better explanation, but that should at least point you in the right direction.


Looking for an honest video game publisher? Visit www.gamethoughts.com

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Hmmm, i don''t think thats quite what the problem is, or indeed much related to the problem. "game server connection error" seems to indicate that the network application is having issues with contacting the server (ie. connect()). Are you behind a firewall? Ummm, it''s very hard to diagnose these problems when you know nothing of the underlying code, or why/when an error message occurs Perhaps if you could post some of the code from the network library that you''re using??

Share this post


Link to post
Share on other sites
Server-side code extract:
// Create a network socket object from our NET library.
NET_Socket MOGSocket;

// Create our server with given port number and protocol type.
if(MOGSocket.CreateServer(serverPort, NET_TCP) != 0)
{
LogString("Failed to Create the Server");
return 1;
}

// Timed thread message from our network library to
// accept network messages.
case USMSG_ACCEPT:
// Use our network library to process network messages.
MOGSocket.AcceptConnection();

// Return success.
return 0;

Client-side code extract:
// Create a network socket object from our NET library.
NET_Socket serverSocket;

// Connect to the server
if (
serverSocket.ConnectToServer(serverIP, serverPort, NET_TCP)
== NET_INVALID_SOCKET
)
{
MessageBox(NULL, "Game Server Connection Error", "Fatal Error", MB_OK);
ShowWindow(hWnd_LoginDialog,SW_SHOW);
return 1;
}

As you can see, the problem lies with ConnectToServer() which is
a function in the network library created by Andrew Muholland and
Hakala in their book "Developer''s Guide To Multiplayer Games". Here
is the extract from their network library:

int NET_Socket::ConnectToServer(char *addressText, int port, int Protocol)
{
SOCKET sock;
struct sockaddr_in inetServAddr;

NETMSGDATA_DATASIZE DataMsg;
memset(&DataMsg, 0, sizeof(NETMSGDATA_DATASIZE));

// Check which protocol to use
switch(Protocol)
{
// TCP protocol
case NET_TCP:
sock = socket(AF_INET, SOCK_STREAM, 0);
break;

// UDP protocol
case NET_UDP:
sock = socket(AF_INET, SOCK_DGRAM, 0);
break;

default:
return NET_INVALID_PROTOCOL;
}

if(sock == NET_INVALID_SOCKET)
{
return -1;
}

// Create inet_addr from the IP number
u_long inetAddr = inet_addr(addressText);

// Fill the address information
memset((char *) &inetServAddr, 0, sizeof(inetServAddr));
inetServAddr.sin_family = AF_INET;
inetServAddr.sin_port = htons((u_short) port);
inetServAddr.sin_addr.s_addr = inetAddr;

// Connect using TCP
if(Protocol == NET_TCP)
{
int error = connect(sock, (struct sockaddr *) &inetServAddr,

sizeof(inetServAddr));

// This works only when using TCP
if(error != 0)
{
LogString("ERROR: connect() error (error = %d)", error);

return NET_INVALID_SOCKET;
}
}

#ifdef WIN32
// Create Win32 socket input event handle
RemoteHost[NETID_SERVER].SocketInputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif

// Fill remote host (server) info
RemoteHost[NETID_SERVER].socket = sock;
RemoteHost[NETID_SERVER].inetSockAddr = inetServAddr;
RemoteHost[NETID_SERVER].addrLen = (socklen_t) sizeof(inetServAddr);
RemoteHost[NETID_SERVER].online = 1;
RemoteHosts++;

char *string;
string = inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);

LogString("Sending knock to socket %d, ip: %s, port %d",
RemoteHost[NETID_SERVER].socket, string,
ntohs(RemoteHost[NETID_SERVER].inetSockAddr.sin_port));

// Knock if using UDP
if(Protocol == NET_UDP)
{
NETMSG_GENERIC KnockMsg;

// Fill the message data
KnockMsg.type = NETMSG_KNOCK;
KnockMsg.toId = NETID_SERVER;
KnockMsg.fromId = NETID_UNKNOWN;

DataMsg.type = NETMSG_DATASIZE;
DataMsg.size = sizeof(NETMSG_GENERIC);

char *string;
string = inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);

LogString("Writing knock to socket %d, addrLen %d ip %s",
RemoteHost[NETID_SERVER].socket, RemoteHost[NETID_SERVER].addrLen,

string);

// Write the knock message
int bytes = WriteTo(RemoteHost[NETID_SERVER].socket,
(char *) &KnockMsg, sizeof(NETMSG_GENERIC),
(struct sockaddr *) &RemoteHost[NETID_SERVER].inetSockAddr,
sizeof(RemoteHost[NETID_SERVER].inetSockAddr));

if(!bytes) return NET_INVALID_SOCKET;
}

NETMSG_GENERIC *Msg;
char *recvbuf;

recvbuf = (char *) malloc(NET_BUFFSIZE);
memset((char *) recvbuf, 0, sizeof(char));

LogString("Waiting for localId");

// Set the remote host (server) non-blocking
SetNonBlocking(NETID_SERVER, 1);

int counter = 0;

// Loop while we do not have a local ID number
while(localId < 1)
{
counter++;

// Check if we are here too long, we probably cannot connect to anywhere.
if(counter == 100000)
{
LogString("ERROR: We were waiting for localId for too long");

return NET_INVALID_SOCKET;
}

struct sockaddr_in newAddr;
socklen_t newLen = sizeof(newAddr);

memset((char *) recvbuf, 0, sizeof(char));

// Read data from the server
if(ReadFrom(RemoteHost[NETID_SERVER].socket, recvbuf,
sizeof(NETMSGDATA_DATASIZE),
(struct sockaddr *) &newAddr,
&newLen, 1))
{
Msg = (NETMSG_GENERIC *) recvbuf;

// Update server''s address
if(Protocol == NET_UDP)
{
RemoteHost[NETID_SERVER].inetSockAddr = newAddr;
RemoteHost[NETID_SERVER].addrLen = newLen;
}

switch(Msg->type)
{
// Got local ID number
case NETMSG_GIVEID:
NETMSGDATA_DATASIZE *idMsg;
idMsg = (NETMSGDATA_DATASIZE *) Msg;

// Set the local ID number
localId = idMsg->toId;

string =

inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);

LogString("The server is now on socket %d, ip %s, port %d",
RemoteHost[NETID_SERVER].socket,
string,

ntohs(RemoteHost[NETID_SERVER].inetSockAddr.sin_port));

NETMSG_CONF ConfMsg;
ConfMsg.conf = localId;

// If using UDP, send confirmation of local ID number
if(Protocol == NET_UDP)
{
LogString("Writing confirmation of localId (localId:

%d)", localId);

WriteTo(RemoteHost[NETID_SERVER].socket, (char *)

&ConfMsg,
sizeof(NETMSG_CONF),
(struct sockaddr *)

&RemoteHost[NETID_SERVER].inetSockAddr,
sizeof(RemoteHost[NETID_SERVER].inetSockAddr));
}

break;

default:
continue;
}
}
}

free(recvbuf);
recvbuf = NULL;

LogString("Got localId %d", localId);

// Set the local host online
Online = 1;

FindNextFreeHostIndex();

param.id = NETID_SERVER;
param.netParamSocket = this;

// Start the IO thread
#ifdef WIN32
DWORD threadId;

WSAAsyncSelect(sock, hWnd_netLib, 0, 0);

// Monitor incoming network events
WSAEventSelect(sock, RemoteHost[NETID_SERVER].SocketInputEvent,
FD_READ);

HANDLE threadHandle = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) NET_WinIOThreadFunc,
(void *) ¶m, 0, &threadId);

#else
pthread_t threadId;

pthread_create(&threadId, NULL, NET_UnixIOThreadFunc, (void *) ¶m);
#endif

// Wait for the thread to start
while(!RemoteHost[NETID_SERVER].thread);

return 0;
}

void NET_Socket::AcceptConnection(void)
{
SOCKET connectSock = NET_INVALID_SOCKET;
struct sockaddr_in clientAddr;
socklen_t clientLen;

// Loop as long as the connection has not been accepted
while(connectSock == NET_INVALID_SOCKET)
{
clientLen = sizeof(clientAddr);
connectSock = accept(listenSocket, (struct sockaddr *) &clientAddr, &clientLen);
}

LogString("Accepted a connection");
LogString("Connection on socket %d", connectSock);

// Update the next free host index number
FindNextFreeHostIndex();

// Set the socket non-blocking
SetNonBlocking(NextFreeHostIndex, 1);

#ifdef WIN32
// Create input event handle
RemoteHost[NextFreeHostIndex].SocketInputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif

// Fill remote host information
RemoteHost[NextFreeHostIndex].socket = connectSock;
RemoteHost[NextFreeHostIndex].inetSockAddr = clientAddr;
RemoteHost[NextFreeHostIndex].addrLen = clientLen;
RemoteHost[NextFreeHostIndex].online = 1;
RemoteHosts++;

// Give local id number to the client
GiveLocalIdTCP();

// Fill parameter information for the thread
param.id = NextFreeHostIndex;
param.netParamSocket = this;

// Start the IO thread
#ifdef WIN32
// Stop notifying for incoming connections on this socket
WSAAsyncSelect(connectSock, hWnd_netLib, 0, 0);

// Monitor incoming network events
WSAEventSelect(connectSock, RemoteHost[NextFreeHostIndex].SocketInputEvent,
FD_READ);

// Create the thread
HANDLE threadHandle = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) NET_WinIOThreadFunc,
(void *) ¶m, 0, NULL);
#else
// Create the thread
pthread_create(NULL, NULL, NET_UnixIOThreadFunc, (void *) ¶m);
#endif
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Only things i can think of after quickly looking over that...
I couldn''t see any dns''ing functions in there...?? It probably won''t run if you supply a hostname (google.com, localhost, etc.) instead of a dots and numbers ip address (209.123.231.1). The only other thing i can really suggest is debug their code to find out exactly where it''s failing and/or email them about the problem...

Share this post


Link to post
Share on other sites
Yup, no DNS resolution unfortunately ;_; - have to use IP
addresses directly. Do you have any idea what the e-mail
addresses of the authors are or do they even have websites?
I have done a search in google to no avail and their book
doesn''t seem to have any contact info. Thanks ^_^

_________________
Best regards,
Sherman Chin
www.Sherman3D.com

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
No idea, i''m surprised they don''t have an email address in the book...very strange.
I suppose the best way to find contact info might be to conatact the publisher..

Share this post


Link to post
Share on other sites
Just for the record, I am pretty sure that although UDP does not guarantee delivery or order of delivery it does guarantee that if you do receive the packet you will receive all of the packet. You will not receive a portion of a packet.

Share this post


Link to post
Share on other sites