Mythruna
March 28, 2024, 03:38:17 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Welcome to the new forums. See "Announcements" for a note for new users.
 
   Home   Help Search Login Register  
Pages: [1]
  Print  
Author Topic: Networking on the brain...  (Read 11233 times)
pspeed
Administrator
Hero Member
*****
Posts: 5612



View Profile
« on: May 25, 2015, 01:35:54 AM »

Life and day job have been hitting pretty hard lately but I've slowly been chipping away at my network library.  This weekend I spent some hard code time trying to relearn what I'd done as it's quite complicated.  Fortunately, I left myself lots of notes.

Still, it's left me with some design decisions to make so there was lots of mental pacing.  The prototype was heavily based on only what I'd need for Mythruna and the new one will be more general and open source... and more better.  But it requires some rethinking of some fundamental things.

Also, the design of the 'reliability' portion changed even over the course of the prototype and based on that some things can be renamed and simplified.  Today in 4 hours of banging my head against it, I discovered even more things that can be cleaned up.  The process is kind of interesting sometimes:
"Gee, how can that work?"
"Ack, there's no way that works it's got to be a bug..."
...frantic diagramming and scribbling...
"Ahah, no this other thing makes it work ok so I did handle it before..."
"Man, there must be a better way to do this so I don't fall into that rabbit hole next time..."

Sometimes I was even nice enough to leave myself comments along the lines of "if you think this is broken, here is why it probably isn't..."

Still, those are all signs of areas that need to be cleaned up.  Confusing code has a bad smell.

One would think this sort of thing can't be too difficult... but it gets quite complicated.  I mean, sending network state is relatively straight forward... it's the sending it efficiently part that will drive you up a tree.  Throw in that you are using a fast but unreliable style of network messaging and things get even more complicated.

For example, if I was sending packets reliably then I could just send only the values that changed since the last message.  The client was guaranteed to get the last message so no problem.  This is a double edged sword, though, because ALL messages are guaranteed to get there.  Even the ones that are now obsolete.  This naive approach can end up with network hiccups causing noticeable (several second) pauses and suddenly all of the objects rapidly go through the history they missed.

On the other hand, sending messages unreliably is faster right out of the gate... but the messages may not ever get there or they may arrive all out of order.  You could just blast the full state of every object every time... that's quite common... and hope for the best.  Each new message makes each previous one kind of obsolete anyway.

The thing is, that turns out to be a lot of data to send and anyway for proper visualization you want some amount of history, too.

I kind of take the best of both worlds in that I send only what has changed but I send it over fast and unreliable transports.  This does mean that I need to handle my own reliability on some level and that's where things get a little complicated.

Here is a glimpse of what I mean:
On the client and the server (per player) we keep a sort of local view of what the world looks like.  For each object that we are tracking, we keep a baseline state and the actual state.

When communicating with the client, we only send the differences between the baseline and the actual state.  For example, if the object hasn't moved but has rotated then we only send rotation.  (There are more values then that: zoneId, objectId, and so on that are all tracked in this way.)

This works as long as the client and the server (per player) have a common idea of what the baseline is.  It's super vital.

Each message that goes out gets a sequence number.  The client sends an "acknowledged" message (ACK) for each sequence number that it saw.  So the server can know that "ahah, the client knows this much state..."

But hold on a sec, what if the server never got that message?  The client can't update its own baseline until it knows the server knows it knows what it knows.  Fun, eh?

Each message that the server sends the client also includes a list of the ACK messages that it has received.  It's a double-ACK.

So, server sends msg 1, msg 2, msg 3...

Client sees msg 2 and send an ACK for 2.

Server sees that ACK and starts sending that as part of its subsequent messages... and its new baseline is now based on that state it knows the client already got.

Client sees msg 4 and knows the server got its ACK for 2... it can update its own baseline based on that... now they are in sync.
...client sends an ACK for message 4 just like always.

Server sees the ACK for message 4 and now knows that any of the double-ACKs it has been sending to the client in every message can now be removed.

The key is that the double-ACK list is sent with the new object state.  It gives the client something it can be sure of.  It's complicated enough that I had to recheck part of it as I was typing the above. Smiley

Generally, if the network connection is running well then there will never be any more than one or two 'acks' in that header.  Since the header contains all of the client ACKs, any one acknowledged message means we can clear all of them out (except any new ones since that message).  And chances are, if messages are being dropped heavily in one direction then they are being dropped heavily in the other, too, so it's not like the list will grow too quickly.


Anyway, today was family day but the entire time I'd been working over these design ideas in my head.  Took the kids to see "Age of Ultron".. during the lulls I'm thinking about how to rename all of the different classes that have the word "State" in them... there's like 15 of them.  Grilling steaks, I'm trying to figure out how to make the object protocol more configurable.  Roasting marshmallows over the fire pit, I'm trying to keep myself from drifting off but still thinking about if the object delta stuff can be separated out... and so on.

It was a good day, though.  Everyone went to bed and I was ready to tear into design... I've spent the last 4-5 hours just writing things up.  I feel pretty good about it... so it must be time to go to sleep and have nice dreams before the light of day shows me my mistakes. Smiley
Logged
Rayblon
Donators
Hero Member
***
Posts: 1861


Hmmm...


View Profile
« Reply #1 on: May 25, 2015, 04:27:49 AM »

Huh.

This is really informative, I think. It seems so easy to forget that world updates are exchanges and not just the server throwing information at the clients... But I'm just a biology major, not a game developer.

And now I shall go to sleep and have nightmares about my inadequacies in the field of computer science.

Or a dream about the moon exploding while I work at a very strange house that is supposedly a walmart.

Or something even wierder... because that's happened.
Logged

pspeed
Administrator
Hero Member
*****
Posts: 5612



View Profile
« Reply #2 on: May 25, 2015, 04:39:13 AM »

Huh.

This is really informative, I think. It seems so easy to forget that world updates are exchanges and not just the server throwing information at the clients... But I'm just a biology major, not a game developer.

And now I shall go to sleep and have nightmares about my inadequacies in the field of computer science.

Or a dream about the moon exploding while I work at a very strange house that is supposedly a walmart.

Or something even wierder... because that's happened.

"Was it a dream where you see yourself standing in sort of sun-god robes on a pyramid with a thousand naked women screaming and throwing little pickles at you?"
Logged
Rayblon
Donators
Hero Member
***
Posts: 1861


Hmmm...


View Profile
« Reply #3 on: May 25, 2015, 02:15:52 PM »

Huh.

This is really informative, I think. It seems so easy to forget that world updates are exchanges and not just the server throwing information at the clients... But I'm just a biology major, not a game developer.

And now I shall go to sleep and have nightmares about my inadequacies in the field of computer science.

Or a dream about the moon exploding while I work at a very strange house that is supposedly a walmart.

Or something even wierder... because that's happened.

"Was it a dream where you see yourself standing in sort of sun-god robes on a pyramid with a thousand naked women screaming and throwing little pickles at you?"

I dreamt that my family and I was hunted down by knife wielding blademasters that could shoot knives out like they were living machine guns, AFTER moving the shopping carts of a few hobos and casually carrying a car.
Logged

pspeed
Administrator
Hero Member
*****
Posts: 5612



View Profile
« Reply #4 on: May 25, 2015, 06:24:52 PM »

Heheh... mine was a reference from a movie.  A great classic that I use to tell who will get a large percentage of my humor and who won't. Smiley
Logged
Rayblon
Donators
Hero Member
***
Posts: 1861


Hmmm...


View Profile
« Reply #5 on: May 26, 2015, 05:29:42 AM »

Heheh... mine was a reference from a movie.  A great classic that I use to tell who will get a large percentage of my humor and who won't. Smiley

I have no humor but my humours.
Logged

Michael
Donators
Hero Member
***
Posts: 2166



View Profile
« Reply #6 on: May 27, 2015, 08:51:51 AM »

"Was it a dream where you see yourself standing in sort of sun-god robes on a pyramid with a thousand naked women screaming and throwing little pickles at you?"
I remember you throwing that humorous comment somewhere else before, it always at least makes me smile. Smiley

Anyways, looking at all of the reliability & unreliability, it makes me think that just sending UPD messages from Server->Client & Client->Server (Client->Server should probably be reliable at least?) is going to be a bit more complicated than I was expecting overall... I'm not very big on making test programs, but rather make tests within the main program and rewrite it later not such a good idea. I'll need to do plenty of testing and making sure it won't break and screw the whole server/game up... lol this will be fun.
Logged
pspeed
Administrator
Hero Member
*****
Posts: 5612



View Profile
« Reply #7 on: May 27, 2015, 09:01:09 AM »

"Was it a dream where you see yourself standing in sort of sun-god robes on a pyramid with a thousand naked women screaming and throwing little pickles at you?"
I remember you throwing that humorous comment somewhere else before, it always at least makes me smile. Smiley

It's from the movie Real Genius.  A classic 80s comedy.

Anyways, looking at all of the reliability & unreliability, it makes me think that just sending UPD messages from Server->Client & Client->Server (Client->Server should probably be reliable at least?) is going to be a bit more complicated than I was expecting overall... I'm not very big on making test programs, but rather make tests within the main program and rewrite it later not such a good idea. I'll need to do plenty of testing and making sure it won't break and screw the whole server/game up... lol this will be fun.

UDP is not complicated if you don't need reliability.  It only becomes complicated when you must rely on something about it.  Just blasting pure state should be fine as each new message supersedes the last.  My case is different because I'm sending only what has changed to avoid redundancy and shrink my message sizes.

Note: the whole point of this is to open source the networking library... so at some point (hopefully soon) you'll be able to look at the code and/or just use it.
Logged
pspeed
Administrator
Hero Member
*****
Posts: 5612



View Profile
« Reply #8 on: May 28, 2015, 10:24:05 PM »

I've had some more time to play with this off and on over this week as it burrows through my brain.  Lots of tedious dotting of i's and crossing of t's as I bang this into shape.

Tonight, I finally have the server publishing messages to my client... a debug log:
Code:
Serializing outbound state:SentState[messageId=-1, created=2066993764023919, frames=[FrameState[sequence=20669
93714059008, columnId=-1, states=[espace.ethereal.net.ObjectState[id=10, realId=39, zoneId=2, positionBits=cec
400004e3d, rotationBits=f04bd5800800]]], FrameState[sequence=2066993730678528, columnId=-1, states=[espace.eth
ereal.net.ObjectState[id=10, realId=39, zoneId=2, positionBits=cec400004e3d, rotationBits=f04bd5800800]]], Fra
meState[sequence=2066993746304256, columnId=-1, states=[espace.ethereal.net.ObjectState[id=10, realId=39, zone
Id=2, positionBits=cec400004e3d, rotationBits=f04bd5800800]]], FrameState[sequence=2066993762899968, columnId=
-1, states=[espace.ethereal.net.ObjectState[id=10, realId=39, zoneId=2, positionBits=cec400004e3d, rotationBit
s=f04bd5800800]]]]]
Sending:ObjectStateMessage[id=113, time=2066993765313468, size=169]

I'm not receiving the message on the client yet.  I'm not doing anything with it yet... I'm not even sure the data in it is actually accurate... but getting all of those layers hooked up on the server feels good and I felt I should tell someone.

It's also interesting that the entire "SentState" object above fits in 169 bytes. Smiley  And given that most of the state is the same in that message, it occurs to me that I could probably compress it further at the cost of adding probably a lot of complexity.  I will let that one burrow in my brain a while.  169 bytes is not as small as you'd think in the scheme of things... even if it is four frames of object data.
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.20 | SMF © 2013, Simple Machines Valid XHTML 1.0! Valid CSS!