Wednesday, June 27, 2012

Nototriously Databased v0.23

With the Admin panel finished, it's time to start working the the player's UI. Ideally I'd like the user to log in and go directly to a list of all his characters in the various games. From there they should be able to:
 -  submit new turns
 - view the results of the previous turn
 - view their current dungeon state
 - view old turns
 - view public information for the whole game

Off to build a new controller. Most of the UI will be the same as the CharacterController for now, so it's a good one to pinch. Pretty easy < 2mins.

Limiting PlayerController
Looks pretty good, but is showing all characters, not the currently logged in user's characters. Since I haven't created the link between User and Account, I'll leave that for now and see if I can limit the list to a single ID.

After a number of false starts, it seems that grabbing everything from CharacterController wasn't as good as grabbing the extensions I did on UserController's edit action. Changed it over so that I could pass in a User model that is limited to the single ID and includes Characters.
User user = db.Users.Include("Characters").Single(u => u.UserID == 1);
return View(user);
Took a couple of hours of wrong turns and dead ends to get there, but at least it's working and looks pretty clean and simple. Now to change it to the logged in user...

Currently Logged In User
The intenetion of using the ASP.NET default account management was to make sure passwords etc were handled a little more securely than if I did it myself. The upshot of that choice meant that I'd make a User table that shadowed the account and use it to link all the game data to. Since I'd successfully added other tables when generating the creation of games, I set about modifying the AccountController to also give me a User record if they successfully registered.

Username and email were simple as they already existed in the Register action, but I'd also wanted to link the user table to the account using the guid. Try as I might, the standard code to return the logged in user's Id simply returns null:
Guid userGuid = (Guid)Membership.GetUser(User.Identity.Name).ProviderUserKey;
I'm assuming that the Register action has just created the account and they haven't actually logged in until exiting the action.

It would have been nice getting the account ID, but I'll have to suffice with the username. It'll be a little less secure, but fine for the scope of this little project. User record created and working!

Limiting to Logged In User
There should now be a User record for each account, and since the "User Info" page isn't visible until someone logs in, all I need to do now is find a way to get the current user. While searching for a way to get the account ID, there seemed to be a number of ways to get the current account name, so dropping back to username rather than ID has made this step rather easy:

User user = db.Users.Include("Characters").Single(u => u.Username == User.Identity.Name);  // assign currently logged in user
return View(user);
 Tidy up
Now that the page is working moderately well, I spent a little bit of time to rewrite the intro and remove / reword the character list fields to make it cleaner. All ready to make the submission...

Custom Data
For the next section I'll need to pass in some custom data to tell if the character is ready to submit a turn for their game. I might be able to do it in the view, but I confirmed through Andrew that it's definitely the controller's job to collect and prepare that sort of stuff. So how do I get it to the view when I am already passing in the User model along with associated Characters?

It seems that I need to create a separate model specifically for the view and pass in only the information that the view needs to display. I don't need all the Character fields, I do need some other fields from DungeonState and Game, and I also do need some custom variables to tell whether the turn needs to be submitted (or changed).

With Andrew's help, I made up a new ViewModel specifically for the Player controller with all the character information added as another class. In the end it felt more like creating a view in SQL from related data, but I could construct almost anything to put in there. A little bit of pre-work for a much more simple and clean implementation. I like it.

Now that the custom ViewModel is in place, I have to redo the way the Character table is laid out on the view. It needed a change anyway as I also took the opportunity to bring in all the data I'm expecting to show, as well as the IDs for all the relevant actions.