Game Development Community

Turn-based Multiplayer Questions....

by Chris "Hyena" Vogel · in Torque Game Builder · 01/01/2006 (1:08 am) · 23 replies

Hey everyone, long time no see.

I took a long break from programming and computers all together (became a veterinary technician, hehe) but I have a great game that I'm wanting to work on now, and I had some general questions...

How does the multiplayer support work? I know very, VERY little about client/server communications and I know nothing about how to structure a game so that it will work with 4 players.

What I'm planning is converting a 2-4 player board game into a 2d PC game. I would want 4 players to have a copy of the game, 1 player to host, and the other 3 to connect up to that game, then all 4 play. Is something like that naturally supported with T2D? Can anyone point me towards some resources to help me design my game so it will work as such?

Thanks a ton!
Page «Previous 1 2
#1
01/01/2006 (1:38 am)
Yes, that is definitely supported.

Very (very) basically, you will want to use Torque's TCPObject to communicate. One of the most simple approaches is to offer an item in the gui of each client to either be the host or connect to the host.

There are two major parts to this. First part is the connection layer itself, which in brief is done something like the following:

The hosting client will use the TCPObject's listen() function to open a port and wait for connections from clients. The other players will use the TCPObject's connect() function to open a connection to that host. On the host, when someone connects, some callbacks happen, noteably onConnectRequest(ipAddr, connectionID)) You'll want to implement these callbacks to save the connection information somewhere so you can communicate back to them.

Once the connection is made, the machines can communicate back and forth. The TCPObject has send(), for sending ascii text, and the onLine() callback for receiving from other clients. TGE traditionally has wrappers around some of this, commandToClient() and commandToServer(). These weren't in the original T2D release however, and I'm not 100% sure what state they're in in 1.1. I do believe Matthew Langley may have written the necessary code to re-enable these, you can try searching for 'commandtoserver' and see.


Once you've got communications established, you need the other major part, the communication and state infrastructure. Basically this is your code that sits on top of the communication stuff above, controlling who communicates what and when. The primary features of this code is to exchange state information, both about the game and about itself (client info and status). You have two basic options when writing something along these lines. Either you can have one authoritative host that tells everyone else what's going on (master/slave arrangement) or every client can be responsible for keeping the game state in sync themselves (everyone is duplicating game logic). This is really up to you and what your needs are; there are trade-offs with each approach such as security and fault-tolerance.

This is probably the trickiest bit to design and implement, largely because differs greatly from one project to the next, and it can get pretty hairy if you're not careful.


Obviously the above is barely an 'introduction to turn-based networking' but it should give you a ton of juicy search words to start with, and I'd be happy to answer anything I'm able. There are other permutations beyond this as well, such as dedicated servers instead of peer-to-peer, master servers to help find players, etc. but I'm guessing you're looking to start small and as simple as possible. :)
#2
01/01/2006 (9:12 am)
I'd like to know more about this too.. does T2D work like a P2P app, or does it work with my web server?
I seriously know nothing about this and for a future project this will be necessary.
#3
01/01/2006 (11:32 am)
Awesome post Luke D. Thanks for the info. You gave me just what I needed to get started. Much appreciated.
#4
01/01/2006 (1:09 pm)
Joe,

There are two distinct objects in Torque that are available out of the box. One being the aforementioned TCPObject, and the other being HTTPObject. In reality the HTTPObject is just a wrapper around TCPObject (it is literally subclassed from it in the engine code) that implements a bunch of HTTP protocol-specific stuff. Nothing super-complicated, but it is very handy to use for web page fetching instead of having to write all the handler stuff on top of TCPObject yourself in script. :)

So you actually have the option of doing it both ways as necessary.

Web access has somewhat limited use in terms of synchronous gameplay because of delays, lag and race condition issues, but it can be done (and is probably one of the most simple approaches to a client/server implementation for Torque because most people are (or can become) familiar with PHP or Java server coding). It is more tailored however to fetching files/static data, stuff like MOTD and online high scores, where it is mostly a read-only operation.

Using HTTPObject with web services on a more asynchronous type of game is very feasible though; I've always tinkered with the idea of writing a client for the email-based poker tournament I used to participate in, using a web service to parse and send emails. Maybe someday... :)

Using direct TCPObject communication still gives you the choice of going peer-to-peer or client/server of course, it's all in how you write your code and what you need, but the networking support is there.


Chris, I'm very glad it was useful to you. I was worried I was rambling and talking alot without saying much, since it was kind of late. :) Let me know if you have any more questions and I'll do my best to help.
#5
01/01/2006 (1:19 pm)
I'm sure I'll have a ton of questions later on, but I have a lot of background work to do before I get to that point =)

what i'm doing is taking an amazing board game created by a friend of mine and trying to turn it into a PC game. This game is a space conquest board-game that won an interational game-creation award in '99. It's not published anywhere yet, but I hope I can do it justice in a PC version because it's really cool.
#6
01/02/2006 (5:06 am)
That sounds very intriguing Chris. :)

- Melv.
#7
01/03/2006 (2:14 am)
It does. I'll be watching for it.
#8
01/03/2006 (3:27 pm)
In the Alpha T2D 1.1 1 & 2 the commandToServer and commandToClient is working. In fact I included an example chat system and server connection system, just open it up and look at the "Server" drop down menu.

www.razedskyz.com/Bob/torque/tutorials/T2D/serverMenu.JPG
Here is an image of the start server menu

www.razedskyz.com/Bob/torque/tutorials/T2D/startServerMenu.JPG
You can then join the server on a client machine with T2D, or client instance of T2D. To test it click the load chat option on your 'server'... then after its loaded on the server click load chat on the connected client... you can test this either with your T2D on two seperate machines, or you can compile it in debug build and run two instances (or more) on one machine. In the join server menu you have options to connect to an IP or to query LAN servers.
#9
01/03/2006 (3:52 pm)
To find the scripts that run this conneciton system and chat system look in your common folders... specifically common/server/server.cs

here is a snippet of the connection code

createServer("MultiPlayer", "");
      %conn = new GameConnection(ServerConnection);
      RootGroup.add(ServerConnection);
      %conn.setConnectArgs($pref::Player::Name, $playerArmor);
      %conn.setJoinPassword($Client::Password);
      //%conn.connect("192.168.0.101");
	%conn.connectLocal();

This is a basic connection script. Notice I have the .connect command commented out, which will connect to a specific IP. You use .connectLocal to connect locally to yourself, especially to start your own server.

after you have succesfully connected the GameConnection::onConnect() callback is called on the server in common/server/clientConnection.cs... at the end of that function is this line

commandToClient(%client, 'serverRespond');

what this does is actually call this command off of the client stored in the %client variable

function clientCmdServerRespond()
{
   echo("server has responded");

   if(waitingForServer.isAwake())
      canvas.popDialog(waitingForServer);

   MessageBoxOK("Connection Established...", "Connection Established with the server!", "");
}

Here you can see it displayes that the server has responded, it checks to see if the waitingForServer GUI is wake (this is the "connecting" dialog) and if it is it pops the GUI... it then prompts the user with an OK dialog box saying that a connection has established with the server...



you can do the same thing to communicate with the client from the server, but often you'll communicate from the server from the client... you can do this a similar way by using commandToServer(); instead of commandToClient();.

For example in common/server/serverConnection.cs I have this function

function serverCmdTestVal(%client)
{
	%val = " test server ";

	echo("client = " @ %client);
	echo("val = " @ %val);
}

So if your on a client machine you can type this command in the console box.

commandToServer('TestVal');

and it will echo the %client ID and the %val, which is forced to " test server " ... You can pass values like this as well, like this

function serverCmdTestVal(%client, %val)
{
	echo("client = " @ %client);
	echo("val = " @ %val);
}

then you can run this command to pass "test" to the 'TestVal' function

commandToServer('TestVal', "test");

Two things to note, first that the serverCmd function should always start by passing in the %client, this way you can refer back to that client with a commandToClient(%client, 'testFunction'); type function. Also note that the function named is passed with a 'tagged' string... these are strings with ' and ' not " and " if you don't do this correctly you could run into problems.

check out the different functions in the files refrenced, especially server.cs to see the way I set up the chat system... its a good example :)
#10
01/03/2006 (3:55 pm)
Hope this helps, feel free to ask any questions, a lot of people know hwo this system works, its the same way TGE does turn based networking commands.
#11
01/03/2006 (9:39 pm)
Matthew, just in case no one has told you yet today. You rule!
#12
02/21/2006 (8:00 am)
I'm an experienced C++ coder, I'm relatively new to Torque in general, though so far I'm very happy with the simplicity and flexibility. Anyways, I'm looking at enabling some the real networking code for a real time 2d RPG-like game. I don't feel sending commands is going to be suitable for a real time 2d game. Judging from comments in the source code, changing the parent class of t2dSceneObject to the NetObject Class rather than SimObject is probably the first step, and then implementing pack/unpack routines for the classes under that, such as the 3d shape, static sprites, etc...

What I'm curious about if you could give a high level overview is a few things.

1) Do all NetworkObject subclassed items get networked from the very begging of the server connection? Or rather the ones that are in 'scope' of course? Or do you have to start the process with an initial call to something?

2) Do datablocks typically get networked? Or are they normally expected to exist defined on the client side?

3) Do you have any guidelines on how the server and client scripts are typically divided? I assume you'd want much of the server logic to be completely on the server and unavailable to the clients, and for the clients to primarily be 'dumb' clients, and simply get updates about world entities and such.

4) Are there any hard coded limitations to the number of supported clients? If so are they relatively easy to increase for a game that is not as bandwidth intensive as typical 3d games such as Tribes 2.

5) Can you give a high level rundown of the 'real' networking process. Client connects, immediately server begins sending packed network updates to the clients for all NetObjects that are in 'scope' to that character. Dirty flags on the server limit what is sent to only what has actually changed. Is this correct? Any other relevant info?

6) When the server deletes an object, whether via safedelete or whatever, does that object automatically get deleted on the clients?

7) Are ScriptObjects typically networked? If so, then that'd mean their parent class would have to be changed to NetObject too as well right? Seems to me they probably wouldn't be networked, but I wanted to see.

8) The comment for the t2dSceneObject class. Can you explain what this means:

"We'll need to implement the pack/unpack functionality as well as maintaining the container system on both sides."

What is the container system?

Think that's about it for now. Any info is appreciated.
#13
02/21/2006 (6:37 pm)
General Note: Some of this (in fact most of this) is TGE only, and not implemented in TGB, but at the general discussion level, it's ok to talk about, and I thought folks might be interested. I apologize in advance for those that don't like hearing about documentation, etc., that is available to TGE only, but TGE networking is not part of TGB.

1) No, it's based on scoping rules (which don't currently exist in TGB). Only objects that are marked as "scopeAlways" would be networked automatically from connection to game on. When an object first comes into scope for a particular client, it is delivered with an "InitialUpdateMask" set, which basically sends all of the information required to replicate the object.

2) Yes, and all are transmitted at the beginning of the mission (and only then). Currently, this transmission is not accomplished (it's done via TGE's loadmission.cs functions).

3) Basically, you hit the nail on the head. Note that general TGB development does not make this breakdown at all apparent, and (mostly) doesn't follow it, so you would have to be strict in how to develop it this way.

4) By default stock Torque has a #define for 128, but that's merely an artifact of original Tribes code--it's not in any way required.

5) Pretty close, although you are missing a lot of the stages involved in establishing and authenticating the connection, and then the original tranmission of the mission itself. Otherwise, you are pretty much spot on!

6) Yes. In fact, client simulations even delete objects that go out of scope.

7)No, they are not, and in fact out of all the challenges for TGB realtime networking, this is the most difficult (arguably over-ridden by maintaining strict deterministic physics synchronization). Torque networking's strength is the fact that you maintain strict control of the bits (literally) sent for each and every date element transmitted, and there isn't a really elegant way (yet) to expose this to script without a lot of work. Yes, they would need to be inheriting NetObject in their ancestry for this to work.

8) The container system is a collision optimization scheme that breaks your scene up into sub-areas. Instead of having to check every object in the entire scene, you can only check objects that are in the same "bin" as the moving object, others can be ignored for that processing tick.

Since you are a TGE owner, there are several excellent resources that detail both the initial connection sequence as well as ghosting and the general networking strategies of Torque, and how they are implemented. Based on the accuracy of your questions and assumptions above, it really wouldn't be that challenging for you to learn and implement for a specific game. The big challenge from TGB's perspective is making it fully generic and completely script accessable to all game types and genres. Look in the TGE section for coders for the article on "TorqueNetworking" (WIP, but good information--I teach from it during my boot camps!), as well as search the forum resources for "TGE Connection Sequence Overview".

For example, the very flexibility of TorqueScript, for example being able to dynamically create variables and use them during runtime goes against how Torque networking was designed...and since TGB is designed to not require source code modification, abstracting this layer is quite a task. Easy enough to do with some knowledge for a single game, but very difficult to do for a game engine based on script.
#14
02/22/2006 (3:58 am)
Just a quick FYI, I have been writing up a TDN article on getting the real networking back in. It's enough to get datablocks working again, as well as your own netobjects + scoping (if you write a scope object). I dont know when I'll have time to finish it, but its not that far off being usable enough to post. I've also been pondering writing up some advanced networking articles, but thats just a thought at the moment.

T.
#15
02/22/2006 (6:42 am)
@Tom, if you drop them in, I'll take a look. Just waiting for script to get indexed by elixir myself so I can kick in more myself on 'em!
#16
02/22/2006 (8:11 am)
Tom, I would absolutely love an article about that. Any chance I could even get an earlier sneak peek? I'll gladly help with anything I can.

Stephen, excellent info. Thanks alot. Some more if you don't mind.

Are all datablocks sent to client regardless? If you can define them in the clients script files could they be skipped or cached in some way? I assume datablocks are relatively small but in a multiplayer RPG that could potentially have thousands of them would it start to become cause for concern?

By "ScopeAlways" you are talking about the mNetFlags.set(ScopeAlways | Ghostable); right? Which access to this can be made by changing the t2dSceneObject parent.

If I remember right from yesterday, the InitialUpdateMask was defined in TGE in a GameBase class I think, that doesn't exist in t2d. I guess the logic that uses that mask then would also be missing from t2d right?

So the InitialUpdateMask would include the initial position/velocity/visual information such as a datablock Id or something/current weapon/anything that effects rendering, etc... right? Then from then on the only things that would be sent are those fields marked dirty. Speaking of marking dirty, pretty much all the accessors would need to be changed to set dirty flags as well correct?

Regarding #6. If clients delete objects that are out of scope, that means that every time they come back in scope they will have to create it again from scratch right? Including getting a new complete InitialUpdateMask update right?

Just for clarification, scope means the object is of interest to a particular client, whether that be in their view or in some way observable. For a 2d game this might mean within the viewable tile range I suppose, possibly a little farther out.

Are ghosted objects the only only objects that would be replicated constantly? Seems to me like nearly all objects would be ghosted, but that's probably wrong. Perhaps just dynamic objects? Can you clarify what sort of things might not be ghosted but that would be NetObjects?

@ #4 - woot!

How would networking be involved with the container system?

Sincerely appreciate the info.

Jeremy
#17
02/22/2006 (8:26 am)
1) Yes. We agree that having a client cache of datablocks and checking against it for updates is an alternate, and feasible way to synchronize datablocks, but stock TGE doesn't do it (most for anti-cheat purposes). For a large scale project like a huge RPG, it would be a great idea.

2) Correct. (scopeAlways).

3) Correct (GameBase). This is another of the reasons why real time networking isn't part of TGB--most of the actual usage implementation is within classes that don't even apply to TGB (such as GameBase). Also correct on marking dirty (setMaskBits), which is again one of the difficulties of TGB realtime networking--in a perfect world you don't want scripters to have to worry about it, but that means modifying the console byte code processor to do it for you when a dynamic field changes (as well as auto-networking dynamic fields). Rough stuff.

4) Deleting objects: Correct. There is a hysterisis threshold based on time and minor scope movement, but in essence yes.

5) (what's replicated) Actually, pretty much everything that is in scope is replicated in TGE. I can't think of -anything- that would be a NetObject, but not have ghostable set off the top of my head.
#18
02/22/2006 (10:47 am)
Thanks alot man. Appreciate your time.
#19
02/22/2006 (2:27 pm)
I know this isnt what you're asking, but some free advice from someone who's been there:

TGB had the datablock code removed. Therefore, datablocks in stock TGB are completely useless for their proper purpose until you restore that code from TGE. Other fun issues involve TGB almost exclusively using names of datablocks internally where it should be using pointers, and datablock names arent sent over the network.

How "bad" it is for you depends entirely on your game. For me, I knew I was going to have to do this when I started so I wrote the parts of my code I knew I was going to have to network in C++. However, I am really only using image maps and tile maps from T2D ... the rest is utterly useless for my game so I can get away with avoiding networking everything except image maps. I cheated with image maps: my map code has a bunch of meta data used to generate the tilemap, and per-player visibility info for fog of war. That meant that all I had to do was write a NetEvent to send my meta data/vis info over the network and the client updates the tilemap when it receives it.

Once you've got them working again, networking the T2D datablocks is pretty easy and more boring then anything else. It's mostly just a matter of implementing the packData() and unpackData() methods, which you can figure out from the persist fields easily enough. Scoping is also pretty easy to figure out in a game specific way. It only gets complicated if you need to network things like physics etc. In that case you're going to have to implement the T2D classes you need as NetObjects, and you'll probably need some NetEvents. Where it gets trickier is if you need to do client side prediction etc. It's theoretically not that bad if you know what you're doing, just a lot of work. Another thing you will find is that you'll be fighting T2D quite a bit. There are a number of things that were done a certain way to make it easier for the n00bs to follow the code, but they really get in the way when you want to do real networking.

As far as the TDN article goes, I think I got as far as a writeup on what needs to be done to get datablocks working again as well as providing the code for t2dImageMap's (un)packData() methods. There are a few notes on other things but I havent really gotten into any detail on that yet. I will probably throw it up on TDN at the weekend in whatever state it is in then, but I dont really have time to do it before then.

@Steve,

Actually, you do need to reimplement GameBase for. It implements a few things that are neccessary to be able to correctly send pointers to datablocks over the network from a NetObject. Without it you're in for a ton of annoying debugging, as happened to me ;-) Copying GameBase is not really that hard. If I remember correctly, it's mostly just a NetObject that has the code you need to get datablocks working properly and some phsyics/tick related stuff that you can remove for T2D.

T.

(PS. Bloody name change. 25% of the time I remember and call it TGB, the rest I forget and call it T2D.)
#20
02/22/2006 (7:24 pm)
@Tom: I agree--pretty sure we're saying the same thing in different ways! Much of the things related to networking indirectly (datablocks, as Tom mentions, as well as tick processing) is implemented in GameBase, which isn't part of TGB. You will need to reimplement this functionality somewhere (call it gamebase, or take the functionality and put it elsewhere) for fully TGE-like networking.

Hmm...I wonder if anyone else notices that I get much more verbose about topics during/immediately after bootcamps--must be because I spend all day talking about topics like this, hehehe. Can't wait for the Torque Game Builder Boot Camp in 2 weeks!
Page «Previous 1 2