Game Development Community

Network insanity

by Michael Perry · in Torque Game Engine · 03/26/2007 (6:00 am) · 8 replies

Hey all. So I've been working with the network code for a while, and I've managed to get some results, even if they are insane. I could use some help with a couple of bugs, though. . .

Goal:
  • Create second UDP socket, which broadcasts and receives[li]Send special data and events through new socket, without interfering with default Torque game network
Working so far:
  • UDP socket created, initialized, and given its own methods (openPort, closePort, sendTo)[li]Socket broadcasts[li]Socket receives[li]Default Torque game network still works
Bugs:
  • When machine A broadcasts a packet, the packet is received by machine B and C, but A gets it back as well, causing unwanted duplication of events[li]Ghosting architecture is causing duplicate events to be created
So here's the breakdown: I'm trying to re-create some basic features (such as player movement, shooting, and spawning) using my own protocol and my new UDP socket. We'll use the movement process as an example of what is going right and what is going wrong:

We have client A and client B. Client A moves forward a certain amount. His information (ID, velocity, position) is packaged and sent to my new UDP socket for transmission. When client B receives the transmission, he checks to see if client A's entity exists (Sim::findObject(id)). If not, he spawns a new AIPlayer and then updates the movement. The problem is, client A is getting the same transmission, creating an AIPlayer and moving him...at this point the IDs are the same and movement gets all mucked up.

So. . .any suggestions on what to look for? If there are specific questions about the code, let me know. Trying to post the whole system here would take too much space, so asking for something specific will allow me to post only what's needed. . .

#1
03/26/2007 (4:10 pm)
Is this client-to-client or client-to-server-to-client transmission? Client-to-client is not recommended at all as it breaks the client/server paradigm of Torque networking, which is obviously where you are running a-foul.

Not to sound like a broken record, but I'd recommend PyTGE and a nice layer of Python w/ Twisted for your non-server communications. You could setup a port listener to fire Torque function calls based on the datagrams received. This not only keeps it completely insulated from the game network connections, but also exposes the full power of Twisted networking for your processing (did this packet come from me?).
#2
03/26/2007 (5:08 pm)
Thanks very much Bryce. I'm looking into PyTGe. While I have never mentioned it in the forums, I am a Python fanatic and at one point tried to integrate it into everything I did.....I even have a python sticker on my fridge in hopes of future integration =)

All jokes aside, can PyTGE handle UDP and broadcast capabilities? I have to be very specific with this network protocol.
#3
03/27/2007 (3:48 pm)
PyTGE is the wrapper for the Python interpreter. We use Python 2.5 integrated with Torque and it utilizes the Twisted framework for non-game network communications.

Twisted (twistedmatrix.com) is the key. Utilizing their network framework you get a robust, cross platform networking toolkit. Honestly the hardest part of implementing this for your project should be removing the features you don't need.

EDIT: Every time Python/Torque is mentioned I have to add that TorqueScript should be scrapped in favor of Python. :)
#4
03/28/2007 (6:47 am)
So, I may be having a bad day with my searching capabilities, but how does one obtain PyTGE to try out?

*EDIT* - And 10 seconds later I found it...I think... TGEPython?

It's a 2002 resource, which makes me wonder if there is a more updated resource out there.
#5
03/28/2007 (6:52 am)
Look here: http://www.garagegames.com/mg/forums/result.thread.php?qt=47546

We'll be putting up some additional Python resources at http://www.mmoworkshop.com when the opportunity presents itself :)

-Prairie Games, Inc
#6
03/28/2007 (6:57 am)
Thanks for providing that option guys. I'm looking into it now and getting the proposal together.

In the mean time, I can't just leave bugged code in or walk away from what I have currently (that drives me insane). So if someone wants to weigh in on posted bugs, all are welcome and thanks in advance. . .
#7
03/28/2007 (8:01 am)
Could you not just use the same socket and still get your feature going?
#8
03/28/2007 (8:35 am)
I tried at first, but there were some issues with implementation:

In engine\platformWin32\winNet.cc:
void Net::process()
{

   PacketReceiveEvent receiveEvent;
   for(;;)
   {
      S32 addrLen = sizeof(sa);
      S32 bytesRead = SOCKET_ERROR;
      if(udpSocket != INVALID_SOCKET)
         bytesRead = recvfrom(udpSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
      if(bytesRead == SOCKET_ERROR && ipxSocket != INVALID_SOCKET)
      {
         addrLen = sizeof(sa);
         bytesRead = recvfrom(ipxSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
      }

      if(bytesRead == SOCKET_ERROR)
         break;

      if(sa.sa_family == AF_INET)
         IPSocketToNetAddress((SOCKADDR_IN *) &sa, &receiveEvent.sourceAddress);
      else if(sa.sa_family == AF_IPX)
         IPXSocketToNetAddress((SOCKADDR_IPX *) &sa, &receiveEvent.sourceAddress);
      else
         continue;

      NetAddress &na = receiveEvent.sourceAddress;
      if(na.type == NetAddress::IPAddress &&
            na.netNum[0] == 127 &&
            na.netNum[1] == 0 &&
            na.netNum[2] == 0 &&
            na.netNum[3] == 1 &&
            na.port == netPort)
         continue;
      if(bytesRead <= 0)
         continue;
      receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead;
      Game->postEvent(receiveEvent);
[b]      Game->postSpecialEvent(receiveEvent);   // This just seems to muck a lot of stuff[/b]
   }

Game->postEvent(receiveEvent) still needs to be called to process a lot of normal Torque ops. Operating with only one udpSocket, means I would have to perform the code found in the bold section. This caused some issues, so I came up with this:

static SOCKET MySocket = INVALID_SOCKET;
void Net::processSpecial()
{
	SOCKADDR sa;

	PacketReceiveEvent receiveEvent;
	for(;;)
	{
		S32 addrLen = sizeof(sa);
		S32 bytesRead = SOCKET_ERROR;
		if([b]MySocket[/b] != INVALID_SOCKET)
			bytesRead = recvfrom([b]MySocket[/b], (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);

		if(bytesRead == SOCKET_ERROR)
			break;

		if(sa.sa_family == AF_INET)
			IPSocketToNetAddress((SOCKADDR_IN *) &sa, &receiveEvent.sourceAddress);
		else if(sa.sa_family == AF_IPX)
			IPXSocketToNetAddress((SOCKADDR_IPX *) &sa, &receiveEvent.sourceAddress);
		else
			continue;

		

		NetAddress &na = receiveEvent.sourceAddress;
		if(na.type == NetAddress::IPAddress &&
			na.netNum[0] == 127 &&
			na.netNum[1] == 0 &&
			na.netNum[2] == 0 &&
			na.netNum[3] == 1 &&
			na.port == netPort)
			continue;
		if(bytesRead <= 0)
			continue;
		receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead;
		[b]Game->postSpecialEvent(receiveEvent);[/b]
	}
}

This way I can transmit special and weird data on a separate port without interfering with the standard Net::process and the UDP socket it deals with.