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.

DotNetMud: user verb actions, rooms with directions

image

  • This is a screen capture of my two browsers logged in.  Follow the arrows for when what happened, and you can see the messages delivered to the other player.

Implementation Details

There is now a structure by which items can state that they handle various verbs that the users could type in.   I use a JIT lookup to figure out what verbs are available to a player – verbs may be sourced from things in their inventory, in themselves, in the room they are in, or from other objects in the room they are in.

image

image

image

I created some more plumbing so that rooms are now aware of other rooms and can get directions to go to those rooms.

image

As a result, I added a tell_room driver level thing to make communication easier.  However, it needs to be updated to handle different messages, one for the person doing the action, and one for everybody else in the room.

Difficulties

The original mud was a single-threaded thing.  So whenever a command was executing, this_player() was “who was the command running for”. 

My version is multithreaded.  I have to pass around a lot more information – hence the “user action execution context” uaec above.  I had to know who the player was, what the verb was that was executing, etc.   There were several instances where I was using “this.SendOutput()” and the result was that user “Sunny” got messages intended for user “Bunny”.

I keep running into this problem – that I have to pass around “who the current player is”.  This will probably become a generic “execution context” like HttpContext is in web apps.

I also know that the verb system I have in place won’t work when we get monsters.   We need the ability to have a monster override a user’s ability to go in a certain direction easily.  The way this was done in the past was, the monster would override “east”.  In my current setup, this would not work as the dictionary of verbs wouldn’t get refreshed.       Then again, I’m leaving this whole verb things as a “Sample Mudlib” implementation, not part of the driver – all the driver does is get a command to the right user object and its done.

I also know that I need to create a MyStdObject that’s specific to the implemented mudlib.

Where Next

Probably the global player list – who – shout – say, emote, tell.

Possibly switch the client to be true HTML rather than monospaced font.    Philosophical question if I should do that or not.

And then, the hard bit:   Adding polling cycles.

If I can get polling cycles working here, then I’m ready for the space game version.

Code since the last blog post:    https://github.com/sunnywiz/dotnetmud2015/compare/7da38a6f…2497ec8

DotNetMud: Driver finding objects, singletons, etc.

image

 

In the original LPC land, the driver was the only way you could create an in-game object.  Along with this came several little things like:

  • The driver knew where every object was.
  • you could say new_object(“/path/to/object.cs”) and that’s how the driver knew where to grab its source.
  • the created objects were of two flavors – “/std/room/lobby” for example was the room that everybody would go to, but if you did a new_object on it, you’d get “/std/room/lobby#45”, which was a different object.  (this is called “prototyping”, and is also used by Javascript and Ruby)
  • you could USUALLY refer to an object by this string that represented the object.
  • Because of this deferment (string became an object), not everything in the mud loaded at the same time – there was some JIT loading going on.
  • The code was compiled at that point, so you could change the definition of the object and then create a new one of it.

In my DotNet world, I’m not limited by who can create which objects.    What to do?

Solution #1 – Don’t Manage it

Don’t Jit-Load anything.  If users want a singleton, they have to implement Instance.   All assemblies are loaded into the same appdomain. To change code, have to restart the mud.

This is doable, like a Diku, but not where I want to go.   Writing that Instance stuff over and over gets old.    Granted, there’s all kinds of C# stuff you could do, but the target audience for LPC was beginner programmers and game designers, not code poets.  Less code the better..

Solution #2 – Overmanage it

I could go with “all code is on disk and I run the compiler against it to generate it and then suck it in”.   Not going there yet either.

Solution #3 – Put in a layer of Find_Object etc.

This is the route I went. 

  • I decided that rather than “/path/to/object.cs”, I’d use a URI scheme. 
  • I’m using “builtin://TYPENAME” as my current implementation of “yo, go find me an object”.
  • I’m giving two different methods in the driver – one to find a “singleton” (if it already exists, reuse it, else create it), and another to create a new one every time.
  • I’ve set up some internal properties in StdObject that (supposedly) on the driver (and other responsible code) should set.

What I’m doing is leaving open some possibilities:

  • realm://sunnywiz/classname   — this could be a per-wizard realm pointer.  maps to an assembly, that might be loaded into an appdomain.  (appdomain = .Net version of forget this code and reload it)
  • persistent://DotNetMud.Mudlib.Room/WizardingHallway57 – this could be a stub that creates a standard thing of sort and then rehydrates it based on a tag stored in a database.

I’m also not saying “Everybody must use the driver to create their in-game objects.”  No, I’m saying “if you want to use the driver, you can, and I’ll track things, but if you don’t, I can’t guarantee I can keep track of it.”

The code referenced by this blog post is at this diff:  https://github.com/sunnywiz/dotnetmud2015/compare/blog20160106…blog20160107

Here’s some of it running:

 image

DotNetMud: Its Alive!

Code: https://github.com/sunnywiz/dotnetmud2015/tree/blog20160106

Heading back home today, vacation is about over.   For my little pet project, its been a nice little run.  Here’s what I got done:

image

  • Two different browsers
  • Logging in as two different users
  • Input_to() is implemented to get the user’s name.
  • I have a standard room object to house the players
  • the players each have their own mud-object
  • I’m currently hardcoding the verb/action stuff, only does look()
  • Using Signal/R as the transport between client (browser) and server

Server side tour:

image

Mudhub

This is the Signal/R Hub.  It currently only has two methods / routines / channels – SendOutput and ReceiveInput.  It deals with new players connecting.

It tries to offload everything it can to Driver.  

Driver

Driver is what used to be the driver (efun) level stuff in the old LPC muds.   Its where these kinds of things live:

  • What’s the list of players?
  • send a message to a user
  • find me an object matching a string X  (eventually)

However, Driver does not pretend to know what kind of mud you want to write.  So for that purpose, it relies on IGameSpecifics – which I only have one implementation of – to deal with things like “what object do players use” and “what to do with a new connection”.

IGameSpecifics / SampleGameSpecifics

As mentioned above, this is effectively “master.cs” from the LPC days.   However, there’s not a lot of stuff in it about permissions, and stuff like that, as we’re not yet compiling C# code on the fly.

MudLib

These are all the things that would go into writing your own mud.  I have a standard room and user object, and a single room (Lobby).  The code for “look” is currently built into User, that will need to move out sometime.

Where to go

There are many places left to explore in this little codebase:

  • Standard rooms don’t yet have directions going to other rooms.  That’s probably the most important next thing.
  • Putting in a verb structure so that players, rooms, and objects in a player’s inventory, all get a chance to put in their own verbs.   (there’s many ways to optimize this, I’ll probably brute force it the first time)
  • There definitely is NOT any on-the-fly compilation going on.   Everything is precompiled.
  • I don’t have a method in place to covert a reference like “/core/Lobby” to an actual in-game object.  I have to make some design decisions there.    Do objects claim URI’s, or do I use the URI to detect where the code is at, or ..  ? This affects the next thing –
  • There is no database backend or persistence.  And there probably won’t be, because that’s not where I want to go, this is a sample.
  • There are player disconnections to be handled as well, and reconnections.
  • Once a verb structure is in place, there’s going to be ID() stuff where swords can say that they are the sword that needs to be picked up, for example.

Uh oh, time to go.   Lunch with the family.

DotNetMud: Client Server Poll Loops

The original muds didn’t need this because telnet, but with the spaceship game in mind, I need some way to sync game-data back and forth between server and client.

Without any hard work, the poll looks like this:

  • C:  “Server, hit me.  I got you some inputs: I1”
  • S: “Hit!  H1”
  • C: “Server, hit me, inputs I2”
  • S: “Hit!  H2”

So if you have a 200msec lag time, you get a hit every 400msec.  That’s assuming upload and download lag are similar.

If you go a little crazier you can work with intermediate frames

  • Setup: Real Time clock R = 0, corresponds to CT=1000 and ST=2000.   Neither server nor client can know R.   Upload lag 200ms, download lag 50ms
  • R=0, Client sends “hit me, CT=1000”
  • R=200, Server receives hit request at ST=2200.   maybe it takes 15 msec to put together the response. 
  • R=215, server sends H1 CT1=1000 ST1=2200 ST2=2215
  • R=265, client receivesH1 CT1=1000 ST1=2200 ST2=2215 at CT2=1265
  • Client now knows that round trip time is about 265 msec.   Maybe it takes 20msec to put everything where it goes
  • R=285, client sends “hit me H2 CT=1285  Intermediate frame: 133ms”
  • R=485, server receives the above request.
  • R=500, server sends H2
  • R=550, client receives H2
  • R=633, server sends H2.1 as requested
  • R=683, client receives H2.1

I haven’t coded it yet, but the client is in charge of throttling, and there’s going to be some interesting “what did I try before” “what shall I try now” logic. 

There’s also a limit.   If the client requested too many intermediate frames, the actual network transport might start bundling the frames together and/or delivering them out of order.     

Note that upload and download lag are different – so it might be completely plausible to have 2 or 3 intermediate frames before the client can actually send back its next update.

A more advanced algorithm would have the server asking the client for intermediate frames, but I don’t live in a reality where that needs to be solved.

There’s also some work in the above protocol to figure out server to client time offset.  Its not an easy question to handle.  And there are also drifting lag speeds – as I write this attached to a 3G Karma Go device.  which keeps going from 2 to 3 circles and back.

Dialing it back

I don’t need to solve it all now.  For starters – all I need is a simple loop.  When I get to space ships floating around and changing direction based on user input, then I’ll need the intermediate frame update stuff.     Just need to know it is solvable.

 

 

DotNetMud: Start?

I have had some wonderful things happen. 

The first is, I married my wife.  This happened 8 years ago (I asked her), and its been a fun trip so far.   But the best thing is, we keep growing.  In this case, I decided that I trusted her to a new level, and I asked her a question:    Would she be willing to design a schedule for me?

I struggle with scheduling.    More specifically, I struggle with “how much should I be available for others” and life balance. 

I also told her of the desire within me to work on this game.   Its been an omnipresent inside push since 1991.   And there’s so much of it, in the past, when I open up the gates, it floods over and drowns out everything, and burns me out quickly.    And.. I don’t know if she understands, but she’s accepting of me, and nurturing of me. 

So,  I gave her the guidelines of what I had to fit into my life, and she drew me up a plausible schedule.  It includes:

  • Time for Us to Go to the Gym Together. (DATE!)
  • TIme for Us to Watch TV (DATE!)
  • TIme for Us to Eat Dinner (DATE!)

AND,  it also includes:

  • WORKING! like, to get income and pay mortgage and stuff. 
  • DAILY NAPS!  (omg)  (concession: I get home later in the evening)
  • PLAYTIME! (go work on my project or my latest game or whatever)

As it is wife-suggested, I believe it has a high WAF.  (Wife Approval Factor).

So with this established, I can now relax, and let this project flow.  It may flow slow, or it may go very fast, but its accounted for.

Managing the Flow

“Honey, what are you doing?”

“Coding”

As I sit there, leaned back, eyes closed.   That is how much stuff is shifting through my brain.   Its going very fast.   Its like a probe algorithm trying to sort things out.    How the heck do I capture this?

Well, in the end, its what gets committed to github:   https://github.com/sunnywiz/dotnetmud2015

But its also going to be blog posts here.   I run into a quandry.  Do I:

  • First do something, then write about it?  (and not change it ever again)
  • Think about it, and say “I think its going to be something like this” (and then run the risk of never doing it)
  • Write something, write the equivalent of documentation here, but put a caveat on it of “this might change in the future”

The 2nd and 3rd options seem to be the way to go.   In general, everything is “here’s what is intended at this time, things may change in the future”, kinda like pre-documenatation.

I’m going to tag them here with the tag “dotnetmud”.

Overall Project Desires: Intended

I want to write:

  • A very basic server engine.  This deals with client connections, disconnects, reconnections, some level of polling things, etc.
  • A very simple mud library: 
    • users logging in (no save choose your nickname)
    • in a room
    • can see other players in the room and on the global players list
    • can move from room to room
    • emote and say
    • shout
    • tell
    • using a custom web client
    • This is a “sample documentation” game to illustrate the various parts of the client/server communication, such as events and polling.
  • Using the above two things, a very simple space game:
    • users log in to a ship floating in space
    • can fly around in space (thrust, rotate)
    • can see other players’ ships as well
    • maybe will have some missiles to make it fun
    • using HTML5 canvas web client

Then, the fun stuff can happen, where I start making a more advanced game of sort, building on the above.  While trying to keep the above nicely packaged.   This is where my space game really happens, and/or my 2D mud game.

Topics Considered So Far

These could all be individual blog posts.   Either before, or after, code.  All of these have been handled already by industry, I want to expose them for my use with my game(s)

  • Using signal/R – how chatty is it actually, how fast can you poll?
  • server and client time sync –  when you’re dealing with “at server time ST, ship is at X,Y going at DX,DY velocity” => go render intermediate frames till next server update, you need to get very specific about Servertime ST vs client time CT.  There’s a lot of work around here.
  • server and client poll loops (next post, it got too long in here)
  • how generic or hard-coded to make events and polling – I keep waffling.  I start to write something generic, and then realize how much plumbing I’m putting off to the game, and then go back to hard coded.   It’s the difference between:
    • client->server: UpdatePlayerListRequest;   server->client: UpdatePlayerListResponse
    • client->server: Event(“UpdatePlayerListRequest”), server->client: Event(“UpdatePlayerListResponse”)
    • The former puts some game-specific code at the server level; the second avoids that problem but makes for a lot more argument marshalling and passing.  I keep going back to the first one.
  • In Space, dealing with unlimited-space addressing
    • enumerate the things within distance 10 from me from closest to furthest.
      • answer: its similar to voronoi indexing, but more like i-nodes in the original unix file system.

So, yeah, my brain is alive with the sound of code self-organizing.    Easy there big boy .. need to do the long haul, not the short burnout.

And I love my wife.  She is awesome.