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.

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.

Candy Crush Helper

I was listening to Tim Ferris interview Jane McGonigal on the neuro-programming that occurs around playing games.  One of the games mentioned was Candy Crush Saga.  Curious, I decided to start playing it – and now I find it relaxing, a good place to put my mind when my mind is obsessing about other things.

But of course, being me, I started to wonder, “can I program a solution for this?”    Well, of course not – the boards are actually random – but I could write a helper to detect available moves, and possibly future moves.

Turns out a guy already wrote one.  See his video explaining how it works here.   However, I couldn’t get it to run, and it required me running candy crush on my laptop.  And, it didn’t look ahead moves – just the currently available ones.  Still, an impressive piece of work. 

2015-09-12 22.36.39So I started coding one for fun.   I started with this screen:

Question:  How many possible moves do you see?  (A move for the uninitiated, is swapping adjacent candies so they form at least 3 in a row or 3 in a column).  I counted 11 on my initial count.

I wrote some code to load this into a class, and wrote some methods to load candies.   It was a very zen-kata-exercise thing to figure out how to save the board – turns out I didn’t know 2D arrays in C# very well at all (and I still don’t, I chose a different method): image
I wrote a some “possible candy swap” code, and wrote some stuff to find solutions, and wrapped that in a test: image
It says there are 14 moves. What?  I don’t have a clean way of outputting the moves yet, so I will have to highlight them by hand. image (89 ms)

Here’s what it found: 

image

  • Yeah. Bug in my code.  I thought I was doing a copy from one board to another, but it was a shallow copy, so all the “possible” moves were being remembered one after the other, rather than one at a time.  PANDA-MONIUM!

My attempted fix of the code didn’t work.   And its getting late.

I believe the test needs to Assert(13, allPossibleMoves.Count):

image

Or am I missing something?   I will resume this.. some day. 

In the mean time, it raises interesting things like:

  • How to represent a very complicated system in a way that makes sense while developing?
  • How to load test data into complicated systems?

Happy crushes.  I’m at level 35, FWIW.

Oh, and I’m using the test-adapter to run NUnit Tests using Microsoft’s test-running tools.

Figuring out Owin Twitter Authentication

I have been working on an Asp.Net MVC project (personal) that needed to do Twitter authentication.  I learned some things implementing it – presenting here so that I don’t forget them, and also in case anybody else runs into the same problems.

Rather than do a bunch of links below, doing it here – this code depends a lot on LinqToTwitter (https://linqtotwitter.codeplex.com/)  to talk to Twitter after getting logged in.   Also, for all the snippets of code – I could have used the “C# colorization” CSS stuff, but I chose not to.  My wordpress is set up a bit weird, and it doesn’t work quite right.

What I started out with

I created an ASP.Net MVC app with the standard starter template, which gave me an Owin layer that did registration, and allowed for adding an external login to the system.

It didn’t work well for me

  • I couldn’t follow the code well – it seemed almost magical how the authentication came back.  For another thing, I needed to grab two of the claims, save them, and use them for getting tweets from Twitter.
    • The “built-in” way to do that with LinqToTwitter relies on Session State.   And it worked well, except that, I was deploying to a “free” class Azure website, which kept recycling, and thus loosing session state.
      • Which lead to an investigation of session state in Azure.  Which led me to a pre-2012 and a post-2012 difference – SqlSession vs Azure In-role cache.  Which lead to a difference of how you tool websites, I had the wrong tooling, didn’t have Roles to add an in-role cache to.
        • All very frustrating.  And trying to get something working “in an hour” sitting at Starbucks before dinner and getting directed all different ways by various well-meaning authors, didn’t help.   Everybody had the perfect solution for their own little universe, but the black boxes they were working with didn’t line up with my black boxes.
  • So, I decided to rip it all out and start over.
  • Spoiler, and then it made sense and it worked.

Best Source of Info

I have to give kudos to @EdCharbeneau. I know him personally, and I was quite surprised to find he had written a blog post about Owin that was the perfect post for me:  https://www.simple-talk.com/dotnet/.net-framework/creating-custom-oauth-middleware-for-mvc-5/.   It cleared up several things that were not obvious to me.

Code from “scratch”

Here’s how the flow works:

// GET: Read
[Authorize]
public async System.Threading.Tasks.Task<actionresult> Index()
{

You want in, but [Authorize] says you should be authorized.     So, do something to authorize the user.

At this point, we have to step back and go to the startup.auth that MVC / Owin likes to do.  I won’t get into the details of IAppBuilder and what Owin is, but here’s the relevant configuration code:

app.UseCookieAuthentication(new CookieAuthenticationOptions {
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Login"),
    ReturnUrlParameter = "redirectUri",
    Provider = new CookieAuthenticationProvider() {

    }
});

We’re telling OWIN that we want to use a cookie to prove that the user is logged in. If the user aint got the cookie, then send them to /Login to go get them the cookie. When sending them to /Login, pass a parameter of ?redirectUri=xxxx to tell Login where to send the user back to.

So the user shows up at /Login. What happens next? LoginController:

public class LoginController : Controller
{
    // GET: Login
    public ActionResult Index(string redirectUri)
    {
        var whenDoneLoggingInUrl = Url.Action("ExternalLoginCallback", "Login", new { ReturnUrl = redirectUri });
        return new ChallengeResult("Twitter", whenDoneLoggingInUrl);
    }
    ...

So the way that Oauth works – every provider, has a slightly different way of initiating the go-log-thyself-in thing. In order to work with everybody – there’s a particular type of response that needs to get sent back. In order to hook how to send the right kind of challenge back, we have to write a custom ChallengeResult where we can execute some code as we’re building the result to send back to the browser. This is almost directly lifted from the MVC boilerplate app that ASP.Net provides:

internal class ChallengeResult : HttpUnauthorizedResult
{
    public ChallengeResult(string provider, string redirectUri)
    { 
        LoginProvider = provider;
        RedirectUri = redirectUri;
    }

    public string LoginProvider { get; set; }
    public string RedirectUri { get; set; }
    // public string UserId { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
        // this does the 302 or whatever the login provider wants you to do. 
        context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
    }
}

Ah, but that LoginProvider — how is there code that knows what to do there? That comes in a bit more of startup.auth.cs code:

var options = new TwitterAuthenticationOptions()
{
    ConsumerKey = ConfigurationManager.AppSettings["TwitterConsumerKey"],
    ConsumerSecret = ConfigurationManager.AppSettings["TwitterConsumerSecret"],
    Provider = new LinqToTwitterAuthenticationProvider()
};
app.UseTwitterAuthentication(options);

TwitterAuthenticationOptions is a microsoft-provided class that does the right challenge to send stuff to Twitter; it also handles when twitter redirects back to our app, and handles getting the claims and all that. Perhaps. The LinqToTwitterAuthenticationProvider might do some of that as well, I haven’t delved too deeply into what that does differently from the microsoft provided one.

There’s one more little bit of code that needs to happen to get things to work:

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

This tells the authentication provider how to hold on to information as its doing the login process.

So where does this leave us at? Well, the negotiation with twitter happens; it comes back to the Oauth provider (which we can’t see the code of), the user gets approved, and we pick up some claims from twitter, and everything calls back into the “whenDoneLoggingIn” url we defined above. That leads us to:

public ActionResult ExternalLoginCallback(string returnUrl)
{
    var owinContext = HttpContext.GetOwinContext(); 
    var loginInfo = owinContext.Authentication.GetExternalLoginInfo();
    if (loginInfo == null)
    {
        // did not survive the login process
        return View("LoginFailure");
    }

    // log them in persistently here! 
    // we have to create a new claims identity with authentication type cookie. 
    // i could probably copy the claims over easier... 
    var claims = new List<Claim>();
    foreach (var externalClaim in loginInfo.ExternalIdentity.Claims)
    {
        claims.Add(externalClaim);
    }
    var id = new ClaimsIdentity(claims,
                                DefaultAuthenticationTypes.ApplicationCookie);
    // https://stackoverflow.com/questions/23180896/how-to-remember-the-login-in-mvc5-when-an-external-provider-is-used/23228005#23228005
    owinContext.Authentication.SignIn(
        new AuthenticationProperties
        {
            IsPersistent = true,
            AllowRefresh = true
        }, id);

    return new RedirectResult(returnUrl);
}

Whoo that’s a lot of code. Lets walk it.  Side note:  Every time I find an answer that I use on StackOverflow, I try to attribute it in the code that I generate, and up arrow the answer I used.

First is, the possibility that we didn’t get a successful login from twitter. If so, login failed. Boom. Done.

Otherwise, we have a list of claims about the user. Twitter gives us about 6 claims –

image

I need to copy these claims over to a new list of claims, create a new ClaimsIdentity based on those claims (but with a different AuthenticationType – remember that you can be logged in with 3 identities at the same time), and then log that new identity in; the user gets logged in via CookieAuthentication.

The result of this is a persistent cookie in the browser which is an encrypted version of the 6 claims, plus an expiration time and several other things:

image

And finally, the user is re-directed back to where they started – the Controller which had the [Authorize] attribute that intercepted the request.

This time, when the user requests the page, the cookie is decrypted and unraveled into a list of claims that we know about the user. Then, in the page that was requested, if I want to talk to twitter to get stuff, I can do the following:

var userInfo = _cq.GetLoggedInUser();

using (var twitterCtx = await _cq.GetTwitterContextAsync())
{
    var tweets = _cq.GetHomeTweetsQueryable(twitterCtx).ToList(); 
}

The heavy lifting I’ve put into a CommandsAndQueries class (* controversial; seprate topic) –

public LoggedInUser GetLoggedInUser()
{
    if (_context == null) return null;
    var owinContext = _context.GetOwinContext();
    if (owinContext == null) return null;
    var authman = owinContext.Authentication;
    if (authman == null) return null;
    if (authman.User == null) return null;
    // C#6 will make this prettier. 

    var loggedInUser = new LoggedInUser();
    foreach (var claim in authman.User.Claims)
    {
        // Claims: 
        // http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier = 152333310 
        // http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name = sunjeevgulati 
        // urn:twitter:userid = 152333310 
        // urn:twitter:screenname = sunjeevgulati 
        // TwitterAccessToken =  ; 
        // TwitterAccessTokenSecret = ;
        switch (claim.Type)
        {
            case "urn:twitter:userid": loggedInUser.TwitterId = ulong.Parse(claim.Value); continue;
            case "urn:twitter:screenname": loggedInUser.TwitterScreenName = claim.Value; continue;
            default:
                continue;
        }
    }
    return loggedInUser;
}

This is code which looks at the logged in user, interrogates the claims, and if it finds the claim that has a particular piece of information, extracts just that claim, into a little DTO helper object.

A more complicated one to get a LinqToTwitter context:

public async Task GetTwitterContextAsync()
{

    var credStore = new SessionStateCredentialStore();  // i frequently loose session, so need to rejigger this
    if (!credStore.HasAllCredentials())
    {
        if (_context == null) return null;
        var owinContext = _context.GetOwinContext();
        if (owinContext == null) return null;
        var authman = owinContext.Authentication;
        if (authman == null) return null;
        if (authman.User == null) return null;

        foreach (var claim in authman.User.Claims)
        {
            switch (claim.Type)
            {
                case "urn:twitter:userid": credStore.UserID = ulong.Parse(claim.Value); continue;
                case "urn:twitter:screenname": credStore.ScreenName = claim.Value; continue;
                case "TwitterAccessToken": credStore.OAuthToken = claim.Value; continue;
                case "TwitterAccessTokenSecret": credStore.OAuthTokenSecret = claim.Value; ; continue;
                default:
                    continue;
            }
        }
        credStore.ConsumerKey = ConfigurationManager.AppSettings["TwitterConsumerKey"];
        credStore.ConsumerSecret = ConfigurationManager.AppSettings["TwitterConsumerSecret"];
        await credStore.StoreAsync();
        if (!credStore.HasAllCredentials()) throw new NotSupportedException("Could not get credential store to have all credentials!");
    }
    return new TwitterContext(new MvcAuthorizer() { CredentialStore = credStore });
}

Its the same idea, except that it needs a few more claims, as well as the two things stored in AppSettings.

Finally, the call to get tweets for the home screen, lifted almost directly from the LinqToTwitter documentation:

public IQueryable GetHomeTweetsQueryable(TwitterContext twitterCtx)
{
    return (from tweet in twitterCtx.Status
            where tweet.Type == StatusType.Home
            && tweet.Count == 200
            && tweet.IncludeContributorDetails == true
            && tweet.IncludeEntities==true
            select tweet);
}

Conclusion

That’s a long way to go around; but, that is what it takes, with all these different black boxes talking to each other, to get the app to work. Hope this helps you save some time doing your own things.

Excellent Software Experience

I recently upgrade my mom’s store to use The Uniform Solution – it’s a point-of-sale + inventory + EDI ordering software designed specifically for stores that deal with uniforms and scrubs and stuff like that.   And speaking as a software developer, the experience was awesome

I want to call them out on some of the stuff they did well:

Appropriate System Design

They don’t do anything overly fancy.   They have a SQL Express database; they have a client that interfaces to the database.  The “main computer” has a share, other clients map to the share and run the same executable.     It’s a little unnerving – the share is wide open – however, if you’re enabling WIFI at your store direct to your internal network, you’ve got other problems going on.   And, I’m sure it could be locked down; their target audience usually doesn’t have an IT department, so they did the right thing for their market.

For a web server, we’re not using it (yet), but it looks like a stand-alone executable that hosts the website.    Well done.

Excellent Installation

Server install – Single executable – run – it detects if its upgrading or installing.  It downloads SQLExpress 2012 and totally auto-scripts the install of it (much to my chagrin, I could not easily hack into their database and look at tables directly.. I wanted to see what their schema looked like).   

They stick everything in C:\USW. 

Client install – Map a network drive to the server share; run an executable – it makes sure that firewall settings are set up correctly, sets up an icon, done.

Once their software comes up, it has a failsafe option – if it can’t connect, you can configure it to help it find its SQL Server.    Well done!  as in, they didn’t assume that that part will work all the time.

The running software automatically keeps track of who is logged in concurrently.  I suspect they use some network broadcast type stuff.   I’m reminded of the 1990’s “net send” command, for some reason.

Backup and Restore

They provide their own backup and restore functionality – Everything gets dumped into a single file (extension .bsql – I think it’s a zip of schema + data).   They automatically take a backup first thing in the morning (first time a person logs in to the system in a day).  I found their auto-backup directory, and I added a script to it to sync the latest N files to dropbox as an off-site solution (different blog post).   I did do one restore; I need to do another with the offsite stuff. 

Side note: I tried to use an azure VM to do the DRP restore session, but their software had a hard time installing SQLEXPRESS on Windows Server 2012 and then finding it later.   Will do the restore on my surface at some point just to make sure its possible.   Which is actually perfect – my surface was used as a client for a while – we hooked a usb barcode scanner to it and mom walked through her shop importing inventory with it (we were waiting for the bluetooth wireless scanner to arrive.  Prime is only so fast.)   Ie, just like in the real world, if the server dies, we’ll probably promote client #1 to be the server – so test restoring on that laptop would be the ideal DRP test.

Excellent Technical Customer Service

I had to call them a few times.  Freaking awesome – they people I talked to, not just some help desk lackey.  These were highly competent technical people (I can tell the difference).  At the first sign of miscommunication, they immediately went into “let me remote in and look at your screen” mode and took it from there.    

I could also tell that these people are a team.  They trust each other.   They like what they do.   They don’t want to grow big and take over the world, they want to be excellent and do excellent and completely solve the one corner of the universe that they have adopted.

Well-Iterated UX

After using the software for a bit – especially on their point of sale screen – I came to appreciate the love and care they have put into their product.  Little things, like “type in stuff in the text box – if its decimal and less than 1, it’s a %off; if its integer between 1 and 99, it’s a quantity” etc – made the system very efficient to use.    I came home and used the same idea on our fridge-inventory-scanner app at work.  (different blog post)

Some of the Finer Points of their System Design

  • “Catalogs” = people out there in the world that they maintain catalogs for.  If they maintain a catalog for it, it means they know all the bar codes for everything in that catalog.
  • “Inventory” = things that you might or might not have in your store.   Breaks down to Vendor, Product, Size, Color.    
    • You can add your own Vendor, Product, Size, Color, etc to inventory – you just don’t get the pre-loaded data from the catalog. 
    • Mom does carry other stuff in her store that they don’t have in their catalog – notably, SAS shoes.
    • Everything that gets added to inventory gets its own unique custom bar code number as well.     Think “SKU” from BestBuy.
    • You get to set the prices for inventory (there are standard formulas for per-vendor markups that you can tweak)
    • USW (that’s what they call it) can and will print out a shop-specific bar code + price to stick on the item.
  • Hardware wise, the app knows how to talk with:
    • Barcode scanners (basically, they work as USB keyboard-entry-devices; when you scan something, it types the stuff in and presses enter)
    • Pole Displays (we didn’t get one)
    • Receipt Printers (just another printer, but with a possibility of controlling a cash drawer)
    • Cash Drawers (it tells the receipt printer “open the drawer now”, and there’s a cable between the cash drawer and the printer which sends that signal across)
    • Zebra Thermal Label printers (for printing out cute little labels OMG so cute)
  • At the checkout, you can scan:
    • the original bar code that came with the item (if its something in a known catalog)
    • OR the shop-specific price tag + bar-code
    • OR search inventory to find the thing
    • OR just type in “*9.95” and get a miscellaneous item for $9.95. 
      • But then inventory doesn’t get tracked correctly if the item was actually in inventory.
  • You can set up customer tracking
    • Special customers, for example, don’t get charged sales tax (governmental things)
    • Or, maybe you just want a list of customers to keep track of what they ordered.
    • typing in 3 letters of customer’s name triggers a customer search in the sales entry screen.  (nice touch)
  • There are special modes for dealing with “sucking inventory in the first time”
  • There are special modes for placing orders, and then printing tags specific to the order that was placed.
  • There are user permission levels (initially turned off) to allow for managers vs clerks, etc.
  • There’s a built-in time-card check in/out thing.
  • There’s cash drawer check in / out things.
  • There’s end of day reconciliation reports and all kinds of other reporting.
    • With a Print Preview for seeing it on screen
    • and everything has a Print to PDF option
    • and some of the reports are aware of if you are printing to a receipt printer vs a regular page; and self-format accordingly.
  • They chose to integrate with X-Charge as a credit card processing system – it was a little bit involved, but in the end, how it works:
    • The XCharge guys installed the xcharge server + client on the machine(s).  It can run as a service, but in our case it sits in the system tray.  It deals with talking over the internet, running cards, that kind of thing.
    • USW just knows a username/password to log into the local xcharge thing and posts things that way.
    • USW doesn’t hang on to any credit card information.  That’s all XCharge’s thing.
  • Their pricing model is simple:   Monthly, $250/month, +$25/mo for each additional concurrent user.   Simple, straight forward, and definitely worth the less-than-$10-per-day that its costing my mom.   (Well, one-time cost on the computer hardware)
  • They provide 250 minutes per month of technical support as part of this. And looking at the emails I get, the service they provide is impeccable:
    • The person you talk to very quickly drops down to “here let me show you how this works” mode and remotes in to look at your screen.
    • After the support session, everybody gets an email saying “here’s what I helped you with”
    • And they clearly mark how many minutes of support were used, and how many are left, for the month.
  • Did I mention Video Training on topics of all sorts?   (It needs a little update, but overall, pretty good).

All in all, a big huge THUMBS UP for these dudes.    If I ever get into providing a specific solution to a specific market, I’m totally going to try to live up to these folk’s standards.

The only thing that I would suggest to them – because they have such a wide-ranging system – is perhaps a “day in the life of” video series for prospective customers.  Ie, show a real life scenario of “here’s how a clerk uses the system” vs “here’s how an owner users the system”, etc.    Especially if its people who like using the system, who can show off their favorite shortcuts, etc.