dotnetmud: spacemud: optimizing network traffic

I spent some time getting the network load produced by the game down.     The rest of this post, I test out the changes to see how much better everything became.  

Methodology

Two ships connected; I’m going to leave their starting locations as random.  I’m going to have them turn in circles and constantly fire.  This will yield some explosions against the planet, and others against each other, but most of the missiles will be flying out into space.

Running against a deployed web server in the cloud

Using WIFI and my home internet connection.

Collecting data via portal.azure.com’s App Service Monitoring graph set to minutes.  (This didn’t work, I had to go with my local wifi connection)

image

https://github.com/sunnywiz/dotnetmud2015/tree/chatty1

This is where we started at.    Sample packet and network load:

imageimage

 

https://github.com/sunnywiz/dotnetmud2015/tree/chatty2

The main change was to give custom JsonProperty names, as well as to reduce the number of decimal places being transmitted.

imageimage

I use Decimal  instead of Double because in double’s, there’s a chance that the underlying representation isn’t quite so digit friendly; decimals are exact for every digit.  However, decimals are harder on the processor for doing math – supposedly.   I haven’t tested that.

The result:  Not too much better.   5.5 MB came down to 3.1MB on the receive side.

 

imageimage  

https://github.com/sunnywiz/dotnetmud2015/tree/chatty4

chatty3, which I’m skipping, changed the method signatures to two-character method names.  Not too much saved there.

chatty4 added “blanking out” data that doesn’t change much.  It does this by keeping track of what it believes the client thinks the state is, and doing a diff:

image image

imageimage

In order to pull this off, I had to go to a dictionary of objects to render, rather than an array.   I also kept a “full” frame every 10 frames, similar to MPEG encoding G vs I frames.

I thought I’d get fancy and send nulls if various values (DX, DY) had not changed (for example, they do not change for bullets) – however, JSON sends a “:null” which can be longer than just sending the value.  The more advanced version of “don’t include the property if it hasn’t changed” is possible, but it got too complicated, so I ignored that for now.   So this version only does the image and name attributes, but that’s enough to get a nice reduction in size:

 

imageimage

The savings:  another 30%.  Its still a lot of data.  Not quite the gains I was hoping for.

At this point, an average entity on the screen is taking 100 bytes or so, instead of the 500 they were before. 

Network Optimization: Where to go from here?

I’m going to call this good enough for now.  The directions to go from here for network optimization:

  • Instead of using the system JSON serialization, write my own serializer.   This could do things like “If It hasn’t changed, don’t send it”.
    • so instead of: {“DR”:0.0,”DX”:17.109,”DY”:299.512,”ID”:0,”IM”:null,”N”:null,”R”:446.7,”RA”:3.0,”X”:37.0,”Y”:1474.8}
    • “dx17.109dy299.512id341r446.7x37y1474.8”   that’s about half the size.   But needs a lot more code to massage it.
    • Could use a binary packing method as well.   Quick search doesn’t find anything that is happy in both C# and Javascript.
  • Put some logic in as to whether something needs to be sent every time or not.  For example,
    • bullets pretty much go in a straight line once fired.  They don’t need to be sent every time.   In fact, if the current X,Y is approximately where the previous update X,Y + DX,DY would put it, then there’s no need to send those values.
    • Planets currently don’t move. (That will change).      This is a broader case of “nothing has changed in this object from what the client expects, so just acknowledge the object still exists, nothing more”.

What’s next?

I think that authorization / identity of clients (and dealing with disconnection better) is in order.  

Then I can do the “single person logged in, multiple clients” code

And then we can launch a spaceship from the text game.

And then we can design an actual playable game.   (oooo!)

dotnetmud: spacegame: Timing Loops 1: Client / Server timing loop

imageI feel stalled in my project.  It might be that I need to write about the important stuff done so far before I continue to redo/optimize the game; setting the stage to understand the need for some of those optimizations.

Reference code for this post (tag): https://github.com/sunnywiz/dotnetmud2015/tree/blog20160302.

The playable game (which might be updated, not frozen to this post) is: http://dotnetmud.azurewebsites.net/Home/SpaceClient

Game state at this time:   Multiplayer, can shoot missiles, missiles hit things, score is kept.

There are 3 interacting timing loops:

Client / Server Update Loop

I made the client “in charge” of this.  Starting in Javascript, the client requests an update from the server:

image

image

Along with the request for game data, the client is also sending the state of controls for the user.  I went with “how many ms has the thrust key been pressed” as my input – there’s a separate set of handlers which watch key-up and key-down events and long how long keys have been pressed for.

This then travels up through Signal/R, and shows up at the server 20-50ms (an eternity) later:

image

The SIgnal/R side of the hub takes the connection, looks up which server-game-object that connection is tied to, and asks that game object (which happens to be a Ship.cs) to craft what it thinks the world looks like.

The Ship has several things it has to do. 

First is to look at the keypresses and turn those into thrust amounts.  I went with a % of thrust:

image

There is similar code for “can I fire another missile yet” (not pictured).

Then, it makes a copy of all the stuff that the client needs to draw the world:

image

It records itself, all the other 2D objects in space, some scores, and what the current “Server Time” is (and rate of change of server time, which is a YAGNIY for when the server gets overloaded and has to go into bullet time).

It packages all that into a return (of type object, which gets JSON-serialized), and SpaceHub happily sends it back to the client:

image

Then, 25-50ms later (another eternity), the client receives the game-update:

image

  • Green – the client updates what it knows about what’s going on on the server.   I tried to keep this in its own “object space”, to indicate that the server is the authority here.
  • Blue – the client updates various things that are of interest to the client
  • Purple – there’s some bulk copying going on that isn’t very sexy and is very network-inefficient.
  • Red – and the client immediately requests another packet.

Places to Improve: Network Traffic.

The server sends ALL the data, ALL the time.   A network packet looks like this (raw, pretty printed):

image image

There is a LOT of stuff in there that is needed once (like image URL’s), and then does not change.   Also, many property names, while nice for humans, is not optimal for network traffic (words like “ClientREquestsPollFromServer”) for example.

“No Big Deal?”  — Doing a test, holding down the fire button and shooting the planet, for a few minutes, in Azure Data:

image

Doing some math:

image

If I have a successful game, and there’s about 1 combat going all the time, we’re looking at $18 per month.

The solution for this is to only send diffs across the network.  That’s on my list for pretty soon.  But it will require some hand-crafting detail.   And some before/after testing.  This will be its own blog post.

Places to Improve: Network Lag

This model is similar to how Kermit used to work – there is a LOT of dead time between updates.   

One way to improve this might be to have the client keep track of the lag between server updates, and then, in its request to the server, ask “please give me additional packets at the following times, until you hear from me again”.    So, the server could send 2 game-updates for every 1 client-inputs-update.     Its on my list, sometime after sending diffs.   Have to be careful and mark the intermediate frames as intermediate, so that only the major frames cause the cycle to continue.

Another direction we could go is completely decouple the client-input from the server-response.   I’m not going to do that, because I like how the client has to constantly remind the server of its existence – if the client goes haywire, I don’t need to write additional server code to detect that and shut down the server sending stuff to the client.   I’m also lazy. Smile 

In Conclusion

So, there’s the ugliest timing loop in the game so far.   The other two are relatively easy, may not get written about soon. 

dotNetMud: Its Alive! (on azure)

The link:  http://dotnetmud.azurewebsites.net/

From there you can either get into the old-fashioned text-and-chat mud:

image

(Valid commands are who, look, shout, say, and east and west)

 

Or you can get into the space-mud side of things:

image

(Valid controls are W for thrust, A and D for rotate)

Neither of these are really interesting until you have more than one client (browser, tab) logged in.  In the case of the latter, perhaps from different computers, with different network speeds.

Corresponding source code:  approximately here (as of these screenshots):  https://github.com/sunnywiz/dotnetmud2015/tree/blog20160220

Where to go from here?

I could try to make the space game more fun – adding missiles and lasers.   More realistic game.   You can’t have a space game without combat.. can you?

I could try to make the space game more single-player interesting – adding gravity, multiple planets. (your mission: try to get into orbit around a planet).  This lends itself to “more realistic server side computational load”.

I could try to make the space game more efficient – optimizing network traffic (right now, VERY large messages).

I could try to make both clients work better – by having “session” be the unifying factor, and the text game launches the space game, both in the context of the same player object, somehow.    This would probably involve hooking up to a database for persistence of user information as well.    This is complicated…  this is probably where I should go if I’m focusing on the “sample game” side of things.

I could try to make space more interesting – by voronoi’ing it, so that it can have 10000 or more objects, but its very easy to find the objects that are within, say, 100000 units of you.   So that I can do hyperdrive between star systems that you can drop out of and be in the middle of nowhere.  Also complicated.. probably comes after multiple planets.

I could try to stress test it – get a bunch of computers attached to it, see how it performs on the azure side.  I have it running on a Free web-node at the moment, and its surprisingly happy – 1.9% CPU.

dotnetmud: Hitting a wall on clients, hubs, and games

Till now, I’ve had this pretty simple layout:

The browser fires up a signal/R client, which connects to a signal/R server, which talks to a driver, which talks to a game-specific engine.

One signal/R client maps to a user in the game somewhere.

This Situation is not Covered:

You are standing in a spaceport

You see:

a ship

>> board ship

You are inside the ship.  you can ‘launch’ to launch the ship.

>> launch

 

At this point, something happens that tells the client “hey, start this other client up” – possibly in a new tab.    Maybe it leaves the current tab in place. The new client connects up to its own hub, with its own mudlib, and does its own kinds of commands. The user can then fly the ship around in space.  The space stuff doesn’t know that the ship is also a “mudlib” object.

This could also be used with Shop Transactions, or a Game of Uno, or whatever – custom client for custom situation, in the same user’s session.

What this Means

a) I need a different sort of Auth mechanism to bind the user to an Identity.  I think I’ll probably go ahead and use the stock website’s authentication methods to get myself an actual UserIdentity;  I don’t know if that would flow down into any signal/R client or not.

b) Every user can have MULTIPLE clients going on at any point in time.   

This is making my head hurt.  I’ll let it percolate.   For now, no cross-jumping between clients.

HOWEVER, I do want to move the <T> that I currently have on the Driver so that the Driver stands alone, and the Signal/R Hub is the one that knows about the mudlib GameSpecific. 

(dotnetmud) spacegame: accelerating to a planet

I shook some dust off some old braincells, and re-remembered some algebra.

My quandry, as hinted by previous post:  GIven that I’m 42000 units away from the planet, and I have a max acceleration of 100 units/(sec^2), how much do I accelerate in the direction of the planet?

Some googling finds this:   https://van.physics.illinois.edu/qa/listing.php?id=116

d = vt + (1/2)at2

I know my distance, 42000.  And I know my a, 100.   What I need to calculate is v.  But not the one in the equation – that’s starting velocity.

Reverse it.  At the planet, starting at v=0, what’s my final velocity when I get to distance d?

The velocity is v = at.  So if I can solve for t, then I can plug that in.

d = (1/2) att

tt = 2d/a

t = Math.sqrt(2*d/a)

v = a * Math.sqrt(2*d/a).

Check the numbers in excel.   I set it up like this:

image

And plug in the formula to see how well it calculates speed:

image

The drift is due to the coarse 1 second time interval on the calculations, I’m pretty sure.

 

So, the answer is, if I’m 42000 u away from the planet, and I can thrust at 100 u/s^2, I want to be going 100 * Math.Sqrt(2*42000/100) = 2895 units/sec in the direction of the planet.

  • If I’m not going this speed, I need to boost in the direction of the planet.
  • If I’m going faster than this speed, I need to boost in the other direction.

To make up for inaccuracies, I’ll set the target velocity to match, say, 90% of my actual available acceleration.

I’m not accounting for my initial velocity at all, even if its not in the right direction.  The point is, at any point in space I can caluate what my velocity should be, and what direction it should be in, for the fastest approach to the planet.  Whatever I’m doing, change to match that.   It should be a nice curve, followed by some nice acceleration, followed by a turnaround, and then some deceleration with some waggles. 

Looking forward to implementing it.  No time tonight, so I just blogged about my resurrection of Physics 101 instead.   Super-excited.

DotNetMud: SpaceGame: Autopilot

After an aborted run at Rares Trading in Elite Dangerous (idea: pick up rares, go to Fehu, sell them, and then pick up missions there.  Problem: takes many hours to get enough worth selling), I took a look at writing an autopilot in my javascript space game (the part that is not yet Mud-like, but I intend for it to be).

Nice: Javascript has a Math.atan2(y,x) which _possibly_ avoids the x=0 problem that normal Math.atan() solutions do.  (arctangent gets you the angle to something so you know where to point the ship).

However, my code doesn’t work.  My ship very often points AWAY from the target, and applies FULL THRUST FOREVER.  This is NOT how you go somewhere.

There’s another aspect – I’m simulating “keyboard control”, ie, IsThrusting, IsLeft, IsRight.   For the ship to feel responsive when playing the game, need to have good numbers for these.  But with autopilot, there’s a lot of jigger (LRLRLRLR) because it continually overshoots its target.

So, here’s what I have to do for the relatively final solution:

  • Set up the ship so that there’s a finer gradient of control.
    • What I’ve seen other games do is, the longer you have it pressed, the more thrust you get, up to max.
      • This means I need to swap out my current keyboard handler (keyup, keydown) to change to a dictionary of “when was it pressed”, so that I can calculate how long its been held.
    • Or, I could go with W = 12.5% thrust, Ctrl-W = 25%, shift-W= 50%, and ctrl/shift = 100%.  Almost 8 stops.  More instantaneous full thrust available.
      • I want to avoid something where the autopilot can take a shortcut that’s not really available to a player.   So if I did the slow build up, I’d have to have the autopilot also do a slow build up.
  • Change how I do the maths for the autopilot.   Inputs to autopilot:  Who to go to, and what distance to orbit them at when we get there.
    • I see a few major layers.
    • First one is “close the distance”.   This would use the calculation of … http://physics.info/motion-equations/  … ouch, my brain hurts.   Basically, I need to find a solution for:
      • If I start at the orbit distance, and accelerate away at 75% thrust, what velocity am I at when I get to my current distance away?   Reverse that.  That’s the velocity towards the object that I want.   Call that V1.  I want it to shut off as I get close to the destination so that orbital stuff can kick in.
    • Second one is orbital force.   Skip this if I’m more than 2x the desired distance.
      • Figure out if I’m going clockwise or counter clockwise from the destination.  OR, just assume one direction for now.  clockwise.
      • Figure out the vector I need to be in a perfect circular orbit around the object at the desired distance.   Magnitude is known (previous post on orbital mechanics has link to the article), and Direction is 90 degrees from where the destination is.
      • That’s my desired vector V2.
    • Combine V1 and V2.  This is my desired vector V3
    • Look at my current vector V4 and figure out the change that needs to happen V5 = V3-V4.
      • Do a left/right thrust as needed to point me at that vector (do a smoothing function here).
      • if I’m pointing in relatively the right direction, do a thrust along that vector (do a smoothing function here).

This could be really fun.

Image: wikipedia

When Maths goes right

I found out I had been doing gravity wrong.

This is what I was getting:

image

There seemed to be gravity, but it was messy.

I had been using the good old (G*m1*m2)/(r^2) equation.   However, in an effort to be smart, I calculated the (x2-x1) * (y2-y1) to be the (r^2) value..

WHICH IS TRUE.

And when you apply that force to an object of mass m1, the m1 disappears (F=ma; a=F/m) and you get the acceleration, the pull of gravity.

But, the kicker is, I then have to scale the normal vector out by that.   And the normal vector is…  just the x component .. is .. (x2-x1)/r. 

When you do the whole thing, the acceleration felt is proportional to 1/r^3, not r^2.

So I did that.  And I pulled out the gravitational constant, G, to use elsewhere (turns out you can calculate orbital velocities and periods as well). 

The result:

image

No matter where you start, no matter your initial velocity and direction (within reason), you get a nice orbit.

What’s more, I could use the maths where “velocity at distance r” can be calculated, as well as “radians per unit time” can be calculated, for a circular orbit.

This means, I could write an autopilot to get you in a circular orbit around a star or planet at a particular distance pretty easily.  I think.

However, I also found out that due to gravitational effects of planets,  it was very easy to get swept into the sun.. and then gravitationally flung out of the solar system.

So, I changed it when you get within the radius of a planet, gravity is about constant – like it would be at the surface.  “Airbrakes”, as it were.  

THIS HAS PROMISE!  I spent probably an half hour flying around in this simulation, gravity made it so interesting. 

source: https://github.com/sunnywiz/dotnetmud2015/blob/e5db82581382e43a721f0abc4a5c45710ed7fee4/canvasTest/solarSystemFlight.html

it needs one image from the same directory.  Otherwise, just run that file.

dotnetmud: inheritance and awareness and IoC

I’m running around in circles, so I thought I should try to draw it out.  Click to zoom in if not legible; text below explaining.

image

All the words are driving me nuts.      So I added A,B,C,D to the diagram above.

  • A = the layer that talks network transport stuff, like Signal/R. 
  • B = the layer of the game that cares about booting up, shutting down, who is logged into the game, loading and unloading of realms.
    • This stuff should NOT be directly available to in-game objects to mess with!
  • C = subset of B.  This is the stuff that should be available to in-game objects.  It’s the O/S of the game, as it were.
  • D = game code.  In the case of wizard-coded realms, probably gets scanned to make sure it has no assembly references, and in days of old might have run under CAS. 
    • I’m not actually going to secure it, just want to make sure its probably securable.

Requirements:

  • A should not know about all the stuff in B;  just bare minimums to talk network occurences to.
  • B should definitely not know about the specifics of A.  In fact, I’d say B is an assembly that HAS NO REFERENCE to Signal/R.
  • D should not know about B.  I’d go with: D can ONLY have an assembly reference to C, and NOTHING else; if something is to be made available to code in D, it must be added to C as a pass-through.
  • B & C can know all about each other.

Singletons vs Static vs ..

Lets call “C” the driver layer.   Ie,  IDriver.FindObjectByName() would be an example. 

For all the code in the Mudlib, right now, it does stuff like Driver.Instance.FindObjectByName().

It might be better to use IoC and take an IDriver in the constructor, and then call _driver.FindObjectByName().  HOWEVER, then I get into things like “Can I have IoC limit which objects can be created”, and “I want to make sure as the objects are created that I can keep track of realms and stuff like that”, and all of a sudden IoC is too nice a tool to use.  I’m sure it could be used with guards on it, but ..    leave that to somebody else, I have no joy in solving that.

So then I could go with how many libraries get a hold of things – (static class DotNetMud.XXX).Driver.      Always gets you the same one every time.

But if I’m doing that, why not just have a static class, Driver.FIndObjectByName()?     No, I don’t want to do this, because I MIGHT want people to be able to unit test their mudlib objects, and I want the driver to at least be an IDriver so it can be mocked somehow.

So, I think where its going is, I’ll have a poor man’s IoC.  DotNetMud.Global.Driver   gets you the driver.  

Who References Whom? How to deal with the Inverse?  

D references C for sure.

A references and feeds B, including telling it how to talk back to A (via Lambda’s)

B is configured to know how to get to D.

C, when asked, can create objects from D.  but it only knows D through configuration.

C needs access to stuff in B

B doesn’t need much from C/D, other than the interactives list, I think.

So there’s another guy, @, who:

  • reads configuration to determine
    • Who is D
    • Who is D.GameSpecifics
  • Creates or Gets Singleton of B
  • Creates or Gets Singleton of C
  • Informs B of C or C or B or whatever.   Not sure about this part yet.   Probably they each know each other’s interface.
  • Tells C enough stuff about D so that it can create objects from D on demand.
  • Tells A about B
    • I would say creates A, but … SignalR does its own creation. So really more like when A is created, self-registers the context somewhere and triggers something so that A can talk to B.

 

Right now its all in one assembly.

This stuff is TOUGH.

And it gets tougher with code loaded on the fly.  I don’t think I’m going to deal with that yet.

No wonder when I open up the project, this stuff isn’t flowing well.  Smile 

How to get it there

I think I should probably get everything living in the same assembly at first, except under a web server rather than a console app.

Then, get the singleton-reference stuff in place.   DotNetMud.Global.xxx.  Change all references to do that.

Then, peel out the MudLib to its own thing.  It still references all of B/C combined.

Then, peel out C to its own thing. 

Then, peel out B to its own thing, separate from A.

This is a several-hour project, I fear.

dotnetmud: space: gravity

image

playing with Html5 Canvas – doing a control loop with thrusters (blue) and rotation control —   adding in two fixed points which have gravity.

I’m using m/(r^2) here .. its not quite right, as the two objects are not orbiting each other.  I can’t get it to orbit nicely Smile 

I turned off the “clear canvas” bit so that the paths could be seen.

The red bits are drawing of the force lines due to gravity calculations.

dotnetmud: where to go next

As of https://github.com/sunnywiz/dotnetmud2015/tree/blog20160110, I have the following things working:

  • User logs in and provides a name.
  • User can move back and forth between two rooms (room directions are hooked up)
  • User can ‘look’ to see what’s in the room.  They will see other users there if they are there.
  • User can ‘say’ something in a room
  • User can ‘who’ to see who all are logged in
  • User can ‘shout’ to send a message to everybody regardless of which room they are in.

I am now at a crossroads.  There are many directions I can go.  I’m crossing things out as I decide that they are not the priority:

1. Develop the “mud” as a communication mud more:

  • Continue with all the communication stuff –  tell, emote.  
  • Add aliases for commands, like ‘e’ for east and ‘ for say and : for emote
  • Fix the say and shout so that the original command line is available rather than string[] arg (ie, I’m loosing whitespace)
  • add the Id() system so that objects can respond to nouns, so that ‘look <personname>’ works
  • Add last activity timestamp to user so that can detect idle time and show that in the who list
  • Show which room the user is in, in the who-list.

2.  Add in realms

  • Users can.. hook up a pointer to a git url ?  to point at their realm code. 
  • Mud sucks the code down, compiles it, loads/unloads appdomains, etc.
  • Might have some hidden difficulties here around MarshalByRefObj type stuff.
  • Maybe there’s an intermediate stopping point here – where I appdomain-unload-load the rooms that the users use as Lobby.

3. HTML/Pretty the mud

  • Right now the text being displayed to the user is all mono-spaced, no HTML allowed.
  • I could change that so that HTML was allowed.
  • As you enter a room, a picture of the room shows up.  
  • People get gravatar icons next to their names.
  • Add fun emoticons and stuff.
  • Who is shown in a table.

4. Clean up referencing

  • Move “driver” stuff into its own assembly.  Gets a DriverObject.  This does ObjectID and TypeURI stuff, and knows about IInteractive, and talks to the Signal/R Hub, but that’s about it.
  • Move “mudlib” stuff into its own assembly.  Gets its own MudLibObject which inherits from DriverObject.
  • clean up who references who, who finds who.   Use the .config file more to provide the links going in the wrong direction.
  • Move some stuff (like “inventory”, and “moveObject”) into the mudlib, rather than at the driver level.   These are text-based game things, and would not be (as) applicable for a Space-2D game.
  • Wrestle more with “should I directly map hub commands to the mudlib”, or make all communication with the hub generic.  Latter is more painful, but would work with more gametypes without changing the driver.

4.5 Deploy as a website to Azure

  • RIght now, it’s a console app.   Convert it to be a full website, which runs the hub in the background
    • I don’t know how Azure’s “free hosting” will work with a signal/r hub which wants to stay instantiated.   May have to put this on a paid plan
    • Luckily my work gives me an MSDN account now, and there’s free azure money there that’s use is or loose it.
    • Maybe put some stuff in like a welcome page.
    • Maybe do some integration with your identity as you log in coming from the MVC side rather than entirely in the mud.

5. Deal with Polling

  • For this, I’ll change the client so that it has panels:
    • One for “where you are”
    • One for “what you see here”
    • (future) One for your inventory
    • And then an updates window where “stuff” happens.
  • The poll cycle will be, once every X, if something has changed, refresh the where-you-are and what-you-see-here windows.
  • Additionally, for each user, give them a “how long they have been idle” thing on their short description
    • The poll should detect these changes and feed them down correctly.

If I get the above stuff done – definitely 4 and 5 – I think I’ll be in a place where I can start working on my 2D space game.

One day at a time, one session at a time.