Welcome, Guest. Please login or register.
April 23, 2024, 06:37:45 AM

Login with username, password and session length

Search:     Advanced search
we're back, baby
*
Home Help Search Login Register
f13.net  |  f13.net General Forums  |  The Gaming Graveyard  |  Game Design/Development  |  Topic: net/event models 0 Members and 1 Guest are viewing this topic.
Pages: [1] Go Down Print
Author Topic: net/event models  (Read 4740 times)
personman
Terracotta Army
Posts: 380


on: August 18, 2004, 04:53:34 AM

Assuming an old-fashioned room-based MUD but with remote clients, scalable to the hundreds of simul users in a room:

Is it better to go with asynch sockets, or synch?

What's a good model to attach "can see/hear" states between objects?  E.g. user object enters room containing a blacksmith NPC and another user, NPC is hammering something and PC is playing an instrument.

Where's a good site to have such discussions? MUD-DEV is too email oriented.
SirBruce
Terracotta Army
Posts: 2551


WWW
Reply #1 on: August 18, 2004, 05:36:41 AM

Good forums for this are:
http://www.igda.org/Forums/
http://www.gamedev.net/community/forums/

As for your sockets question, I haven't programmed in many years, but why in the world wouldn't you want asynch?

Hey, look, gamedev actually has an article about them:
http://www.gamedev.net/reference/programming/features/asyncsock/

Hope this helps.

Bruce
Roac
Terracotta Army
Posts: 3338


Reply #2 on: August 18, 2004, 06:41:54 AM

Quote
why in the world wouldn't you want aynch?


Sync sockets allow you to, with very little effort, create a socket that includes both guaranteed delivery and message throttling.  Also, and obviously, gives easy control over message sequence.  No reason why they couldn't be put into an async mode with a bit of work, though.

-Roac
King of Ravens

"Young people who pretend to be wise to the ways of the world are mostly just cynics. Cynicism masquerades as wisdom, but it is the farthest thing from it. Because cynics don't learn anything. Because cynicism is a self-imposed blindness, a rejection of the world because we are afraid it will hurt us or disappoint us." -SC
personman
Terracotta Army
Posts: 380


Reply #3 on: August 18, 2004, 07:22:17 AM

Thanks all - great resources Bruce, much appreciated.

It's been years since I've done socket dev and I was barely proficient then.  Aren't there scaling issues with doing synch sock?
Roac
Terracotta Army
Posts: 3338


Reply #4 on: August 18, 2004, 08:56:21 AM

Probably worthwhile to note that there are a couple different ways to do sync and async sockets.  Basically, if you're making blocking calls, your app will stop until the blocking call finishes.  Blocking calls are just normal function calls, except they can take a good while to finish, even if they're not "doing" much of anything.  This is bad - you're wasting time you could be churning CPU cycles on something useful, hence the push for non-blocking stuff in the third link Bruce put up.

Ok, if you go non-blocking, what you basically have is a multi-threaded application.  Either you're using an object that does it for you, or you do it yourself, but either way that's how it works.  You have one thread that's running all the neccessary blocking calls in a seperate thread, which won't tie up your main one(s).  Whenever it's done doing whatever it needs to do, it somehow raises a flag.  Nowadays, that's commonly done through an event, which may or may not also be its own thread.  The difference is the same as before; if it doesn't spin off another thread, you run the risk of blocking your socket again, or inadvertantly killing the thread (say, with an error condition).  

The socket is syncronous if you allow for only one outstanding transaction at a time; one side sends a message, and it must wait (or else is ignored by the server) until a response is returned.  The trick is that it depends on "who" you talk to as to whether a socket is async or sync - it's possible different people can give you different answers, depending on whether you're talking about the TCP level, or the application level.  For example; if your socket polling thread (whether you wrote it or are using an object, such as the MS WinSock COM object) spins off its own event threads to handle each incomming receive, then it is async at the TCP level.  If you wrote it such that it will not check for another receive until after your socket thread has processed that receive, then it is sync; you can only do one thing at a time, but it's not blocking your program execution.

However, even if your socket is itself async, that doesn't mean your application needs to allow for async traffic.  Meaning, if the client fires away multiple messages, the server socket will dutifully spawn off enough events to handle them, but the application may flat out ignore some of them until it's good and ready, even though there's no technical reason (such as a blocking call) why it can't.  The inbound messages will stay queued up (unless you overflow the buffer or finally make a recv() call).  

To a point that's splitting hairs, but you'll want to pay attention to the details if you're planning to write a socket that is available to the public.  There are some limitations; for example, the buffer that holds inbound traffic is only so big, and will start to dump data if you're not quick enough to pick it up.l  VB or .NET applications, which rely on events (if we're talking the WinSock COM object, or something similar), have a limited number of events that are allowed to queue up before it starts ignoring any further requests.  If you allow your socket object to spawn off threads for inbound traffic, you have to manage them or you run the risk of running out of memory if someone decides to flood you with data, or even if TCP traffic gets backed up in the network and gets dumped to you all at once.  Also consider that you will have (you hope) multiple users connected to your system, you will want to try and be "fair" with your resources.  That is, you don't want to allow one user to hog all your bandwidth and effectively DoS everyone else trying to use your app.  For example, assume you and I are on your MUD, and in a room.  I have a macro that will dump 40,000 "look" commands (a more realistic example; a very long pathing command, to let me run from point A to point B).  Meanwhile, you're trying to investigate the blacksmith, or to purchase items he's created.  If you don't manage resources, you may wind up with a system that is "first come, first serve" - in which case I can outright shut you down, because I have 40,000 "first come" items before anything you do.  I can keep running that macro, and it'll affect everyone on the server.

Here's a case where sync calls might be useful.  You could design your socket thread to poll every active connection.  That is, it goes through a loop to check each socket in sequence, and cycle back from the start when completed.  It will run the top command on the stack for that connection, and no more; in this way, every connect gets an even use of the system.  If I dumped my 40k looks, and it was through the 3,000th iteration and you decided to "buy horseshoe" or whatever, your command would have equal weight with my 3,001th command.  I couldn't interfere with your command, because the server would switch its attention to you.  If the socket were spawning threads, you may wind up with many thousands of threads running (and returning in an indeterminite order to boot - THAT will screw up your pathing), and that would totally hose your server.  Every thread would get a roughly equal time slice from the server, meaning you only get 1/40001th of the server CPU.  

If it were designed such that every connection had its own thread (instead of one thread cycling through all the sockets), you may have a variable in your socket object that incremented on every command, and refused to accept more than x commands per second.  Reset the variable to 0 for all socket objects every second.  Early versions of MudOS had a thread that was executed to run a variety of tasks, from NPC movement/reactions to player commands, and would reset the thread every 2 seconds (or something like that... I'm foggy on details now).  It was a good example on how to not scale up well.

The server can take a different approach to sending data.  As a server, you have to protect yourself from clients, which could be hacked or abused.  But, as a server, you trust yourself and don't give a rip about protecting clients from yourself.  You don't want to be sending so much data clients can't cope, of course, but you can avoid a lot of the error checking that's involved with inputs from clients.  You'll need to make sure that the amount of data you send doesn't consume your pipe, and don't forget to scale that according to the number of users you intend to have, and to make sure that on the client side they can fit everything on a 56k (but if you're uploading so much data it will overwhelm a 56k client, you're probably doing something wrong anyhow).  Outside that, you can pretty much send data whenever you want to send it.

-Roac
King of Ravens

"Young people who pretend to be wise to the ways of the world are mostly just cynics. Cynicism masquerades as wisdom, but it is the farthest thing from it. Because cynics don't learn anything. Because cynicism is a self-imposed blindness, a rejection of the world because we are afraid it will hurt us or disappoint us." -SC
personman
Terracotta Army
Posts: 380


Reply #5 on: August 18, 2004, 09:43:24 AM

Good stuff Roac - you painted exactly the scenario that concerns me (Koster's client/enemy quote comes to mind) so that definitely helps me in the design decision between synch/asynch.

I am in fact using .NET/C# and I'm concerned about overall performance/throughput.  RunUO seems to do well but I only have access to DOL's netcode source for reference - I don't know how well DOL scales yet.

I checked into RakNet but it's currently not .NET friendly.  I do like their model of the DNO/Superserver topology though.
Roac
Terracotta Army
Posts: 3338


Reply #6 on: August 18, 2004, 10:52:56 AM

For scalability, some things you can do:

1) Limit how many threads you create.  A single CPU can only do one thing at a time.  Multiprocessing or multirheading only means the CPU flips between different problems extremely quickly, to give the appearance of multitasking.  Spawning 20 threads to do various tasks is slower than having a single thread do it, because there is overhead in switching between tasks.  Not to say there aren't advantages in having threads, just don't go crazy with them.  Anytime a routine has the capability to create a thread, check and doublecheck what conditions must be met for it to execute, so there isn't a chance it can runaway (also, make sure the things terminate).

2) The majority of your time is spent in a minority of your code.  Nested loops are often killers.  Also, look for places where you hit the disk; loading databases (dataset.fill, for example), reading/writing files, and so forth.  Caching stuff to memory, and avoiding disk IO, is a timesaver (but don't forget, memory has limits too).

3) Keep your messaging protocol as simple as you can (unless you're aiming for clients to be telnet-compatible, in which case it's done for you).  If you come up with some complicated binary structure, your server will have to rip apart every input, and build up every output.  It's trivial to do one; not so much if you have to do 100 / sec.

-Roac
King of Ravens

"Young people who pretend to be wise to the ways of the world are mostly just cynics. Cynicism masquerades as wisdom, but it is the farthest thing from it. Because cynics don't learn anything. Because cynicism is a self-imposed blindness, a rejection of the world because we are afraid it will hurt us or disappoint us." -SC
Jayce
Terracotta Army
Posts: 2647

Diluted Fool


Reply #7 on: September 20, 2004, 05:36:25 AM

I'm curious about the predominance of TCP in gaming.  From my initial reading, it seemed that UDP was a better fit for gaming since reliablility takes a backseat to having recent gamestate data (data goes "stale" quickly).  Too many retries due to net congestion or other, I have read, can lead to the data the client is receiving being up to a few seconds out of date.

I have noticed however that many games use TCP.  What's the "conventional wisdom" on this?

Witty banter not included.
Azhrarn
Terracotta Army
Posts: 114


WWW
Reply #8 on: September 20, 2004, 11:49:31 AM

It depends on how critical it is for the client to have certain data.  For things like location updates, you don't need every packet.  But you get noticeable rubber banding/warping if you miss too many.  Same thing with hit points/other stat updates.

Using the hit point example:  If the server just sends "damage dealt", then you would need TCP, as every number MUST make it through for the client to calculate.  If the server just sends "current HP", then it can lose some data in between, as any individual packet is enough to bring it current.  Although if you did that, you have to send damage information as well so the client realizes why it has the HP it does.  Which also may result in more overall traffic.  Then you can mix it with periodic updates along with client calculations...

But that's a fairly simple example anyway.  For most things, the constant stream of updates you can achieve with UDP works out better for keeping things moving.  You can also emulate TCP with UDP packets when you need verification by requiring an acknowledgement before continuing on something (like when sending an in game tell or email).  It may be a bit more work, but lets you have finer grained control over your bandwidth etc.

As far as many games using TCP...  It's easier to work with sometimes?  And if the game doesn't haev a lot of traffic, or all of it is necessary, then it handles everything for you.  There's also a few other tricks you can do with it in regards to routing I believe.  Although I doubt much games make use of that.

I came here to be drugged, electrocuted, and probed.  Not insulted! - H.S.
Roac
Terracotta Army
Posts: 3338


Reply #9 on: September 20, 2004, 09:39:05 PM

TCP is easier to work with, and doesn't give as many fits with firewalls.  UDP is more efficient, in that there is less header to fool with, you can dodge some TCP throttleing built into switches, and you don't have to worry about one lost packet causing the rest of the stream to back up (TCP can arrive out of order, but you can't get too far ahead of the smallest expected index).

-Roac
King of Ravens

"Young people who pretend to be wise to the ways of the world are mostly just cynics. Cynicism masquerades as wisdom, but it is the farthest thing from it. Because cynics don't learn anything. Because cynicism is a self-imposed blindness, a rejection of the world because we are afraid it will hurt us or disappoint us." -SC
Pages: [1] Go Up Print 
f13.net  |  f13.net General Forums  |  The Gaming Graveyard  |  Game Design/Development  |  Topic: net/event models  
Jump to:  

Powered by SMF 1.1.10 | SMF © 2006-2009, Simple Machines LLC