Chris Risner . Com

Building a Text Adventure with Azure and Orleans

Posted on: 10/22/2014 9:13:00 PM by

Colossal AdventureBack in May of this year, we did an episode on Cloud Cover focusing on project Orleans.  Orleans is a project created by Microsoft Research that provides an easy way to build distributed high-scale computing applications.  You can build these without having to spend months learning and understanding complex concurrency and scaling patterns which is always nice.  If you’ve done .NET development and are familiar with the awesomeness of async / await, picking up project Orleans is very easy to do.  Orleans does this using the actor model which I’ll talk more about in a minute.  One of the other great things about Orleans is that it’s built from the ground-up with the cloud in mind.  So getting it on to Azure is pretty easy to do. 

TLDR: You can play the game here: http://adventureterre.cloudapp.net/

TLDR: You can check out the source code here.

One of the samples in the Orleans release is a simple text adventure game named, Adventure.  This game is in the same style as Zork or Colossal Adventure which many people are familiar with (if you aren’t, you can actually play Colossal Adventure on AMC’s Halt and Catch Fire website!).  The general idea is that everything is text based.  Players read about their surroundings and then type in what they want to do.  These text adventures, called Interactive Fiction, were some of the first computer games.  I’ve been meaning to get back to doing some more game dev so a little over a month ago, I started on a small side project to make a text adventure game on top of Azure using the Orleans Framework.  I started by combining the Adventure sample with the Azure sample for Orleans and got the basic Adventure game running on Azure.  Before I go over the details of that, I should explain how Orleans works.

The actor model and Orleans

The actor model is a model for concurrent-computation set forth by Carl Hewitt in 1973.  Under the actor model, the actor is the sole primitive.  It handles both behavior and state.  Actors interact with other actors and communicate via asynchronous messages delivered via system-generated proxies.  Actors don’t contain other actors but may have a reference to them (though that reference is just used to send messages as opposed to having direct access).  In Orleans, the actors are called Grains.  So every actor in the system is it’s own Grain.  In order to contain the grains, Orleans has Silos.  As the developer, you don’t develop the silo as much as start a silo.  The Orleans framework then knows how to make use of the silos to contain the grains.  In addition, the “location” where the grains perform all of their actions is within the silo.  When you need access to a grain, you don’t create a new instance using “new” but instead rely on a generated GrainFactory to get an activation for a grain.  In this way, grains are never created nor destroyed.  They are activated or deactivated as needed by the system.  Additionally, grains handle their own state storage (with some work on your part in the Grain code) so persistence is transparent to the client making calls into the Grain.  This all may sound a bit confusing but I’ll follow up this post with another explaining more about the architecture of Orleans and the sample I built so it will make more sense.

Azure and Orleans

When it comes to Orleans, Azure is a natural fit for deployment.  Orleans includes a number of step-by-step tutorials which explain how to get started with Orleans.  One of the most useful is one that explains cloud deployment.  To deploy to Azure, you essentially map your application onto Worker and Web Roles inside of Azure Cloud Services.  The Worker Role(s) run the Orleans Silos where all of your Grains’ functionality takes place.  The Web Role(s) run the client code for your application whether it’s a web site or a web service.  So the web code get’s access to your Grains and request them to perform whatever actions they need.  Orleans is built around C# asynchronous programming so everything uses Async and Await.  So every call you make against a grain is asynchronous and you await it from the client until you get the result.  The great thing about this is that due to the Orleans framework, you get high-scalability and concurrency out of this approach without really needing to know more more than what I just described (there is more to know but I’ll get to that in future posts).  You can optionally tie a GrainState to each Grain.  When you do this, you can specify a Store where your grains’ state should be persisted.  Orleans includes some built in storage providers that make storage super simple by just applying an annotation ([StorageProvider(ProviderName=”name”)]) on your Grain classes.  For example, you can very easily start storing your grains’ state into Azure Table Storage by setting up your store, putting the connection string in your config files, and annotating the classes.  Rather than assume when you want to persist data, Orleans leaves it to you to call State.WriteStateAsync(); in your Grain’s code whenever you want to store the state.  Restoring the state is done automatically whenever you activate a grain that has been idle for too long.  There have been a number of community contributions for storage providers but you can also easily write your own storage provider as well.  You can check out a ton of community contributions for Orleans here.

Adventure Terre

The basic game sample doesn’t have a TON of functionality.  Out of the box it includes the following:

  • View a description of a room
  • Move from room to room
  • Pick up and drop things
  • Kill monsters if you have the right item

This is enough to show the concept but isn’t really enough to make a game.  To make the game a bit more full fledged I added the following to the public source release:

  • Store a game state (essentially a collection of string-booleans to track individual flags)
  • Descriptions of rooms, monsters, and NPCs that are shown based off of flags
  • Ability to set flags when a descriptor is shown
  • Ability to make NPCs and Monsters move throughout the world in random directions (using Orleans timers)
  • Ability to send messages down to Players using SignalR (this was really cool to get going so I’ll talk about this in a future post)
  • Ability to speak to NPCs and Monsters
  • Non-Playable Characters (essentially monsters that can’t be killed)
  • Ability to specify where monsters and NPCs should start in the world
  • Ability to change directions a player can move in based off of the game state flags
  • Ability to specify different descriptions for items based off of flags
  • Ability to specify flags that should change when items are used (and how they can be used)
  • Ability to specify State Change Actions (things that should occur when flags are changed)

So I added a ton of features.  I actually added a few more features to the non-public release but very little functionality on top of the above.  The majority of the time was spent working on the above as opposed to the story (that will be apparent if you give it a try). 

AdventureTerre Architecture

I’ll give a little detail today on what the architecture of the game looks like to hopefully provide more detail on how something is built with Orleans.  As mentioned above, everything is a Grain.  So in this game the following Grains exist:

  • Players
  • Rooms
  • Monsters
  • NPCs
  • GameState
  • PushNotifiers (I’ll cover this in the future when I talk about SignalR)

For every player a PlayerGrain is generated and it keeps track of the rooms, monsters, NPCs, and game state.  When a request comes in, the PlayerGrain figures out what needs to be done and calls out to the other grains.  All of these Grains have interfaces which implement some form of IGrain and specify an interface that extends IGrainState.  All of the grains also specify that they use the AzureStore so that whenever their data needs to be written or read, Azure Table Storage is used. 

That all happens within the Worker Roles.  However, players don’t have any direct interaction with those roles.  Instead, when you want to play the game, you go to a website running in the Web Roles.  The interface the player plays in resembles a terminal window (yay nostalgia) which runs locally with JavaScript.  Every command is passed to a web service exposed as part of the web site.  That web service handles getting the PlayerGrain and calling whatever actions on the grain are necessary.

Design Considerations

While I’m happy to consider myself done with this sample for now, there are a number of things I did that if I had thought things out better at the beginning or had more time now, I’d change.  First, the original sample treated Things (items like weapons and food) not as Grains, but objects either in a RoomGrain or on a PlayerGrain.  One feature I added to the final game was the ability to hide and unhide items.  Leaving items as objects in the State of Players and Rooms made this more difficult than if they had been Grains as well.  Another change I’d make was how I treated the GameStateGrain.  Originally there was just a dictionary on the PlayerGrain that contained the state.  While trying to diagnose an issue with await / async (more on this in the future) I moved the dictionary into it’s own Grain.  This worked, and still works, but did make a few things more complicated.  There were a few other pain points I’d change but for now, it works and it’s being shipped.

The Game

The game itself ended up being quite small (definitely smaller than Zork for example) but showed several different capabilities and it works.  If you play it, you’ll easily get though doing absolutely everything there is in less than 20 minutes.  I’m hoping this will spur me to get back into actual game development.  For now, you can pull down the source code and deploy it yourself to Azure by going here.  Finally, I encourage everyone to go play the game for a few minutes and remember that everything is running in a super scalable backend powered by Azure. 

Categories: .Net, Azure, Javascript, Web
Bookmark and Share
First Article

Playing Old Games in Parallels and Windows 8

Posted on: 10/21/2014 10:51:00 PM by

This post is about how to get a specific class of Win95 / Win98 era video games playing properly in Parallels on OS X.  Somewhat less important is that Parallels is running Windows 8 (I don’t see why it wouldn’t work with Windows 7).  If you’re using a computer without a CD drive (like any of the Microsoft Surface Pros) this should still prove to be helpful since a lot of the issue actually has to do with the lack of the CD Drive.

Every once in a while, I get a hankering to play one of the games of my youth.  The games I played back in high school or even college.  The games I played when I had nothing but time to play games.  One of the games I enjoyed playing long ago was Might and Magic 6.  MM6 was one of many role-playing games developed by New World Computing.  NWC was a in-house dev studio of 3DO.  Neither of these still exist, though their games live on.  If you haven’t played any of those games and have free time, they’re arguably worth checking out.  MM6 and many other MM games are available on Good Old Games for an incredible bargain.  If you want to buy the game (or any other game) on GOG, you don’t need to continue reading this, they’ve already solved the problem (at least for MM6).  However, the problem we’re solving here is a bit different.  If you still have the game CDs, read on.

The problem with many games of that era (the 90s…ahh I’m old) were that they used Mixed Mode CDs.  Mixed Mode CDs solved the problem of having CD quality sound in video games.  This was accomplished by having one data track on the CD (usually track 1) and then all of the other tracks were the actual music tracks.  You might remember having put a game CD into a music CD player and hearing very loud static if the first track played.  This was what happened when a music player tried to play the data.  Smarter, mostly computer, CD-ROM drives could check a bit and realize that the first track was data and skip it.

The real problem comes when you try to duplicate one of these CDs so you can “mount” it on your machine.  If you just want to reproduce the data portion of a game CD to get past the CD-check (back in the day you had to have the game CD in the drive to run the game…or you had to run a no-cd crack) this is easy enough.  You can use any of tons of programs to just copy the cd.  If you want to mount it to your computer or parallels image, all you need to do is generate an ISO of the CD drive. To do this, just get access to a computer with a CD drive, insert the CD, and then search for how to generate an ISO using your operating system.  For OS X I know you can do this very easily with the built-in Disk Utility.  From there, it’s very easy to mount an ISO using either OS X or Win8.  In either system, you just double click the ISO file and it’s done.  You don’t have to do anything special.  However, things are much more complicated for our Mixed-Mode CDs.  When you make the ISO of a Mixed-Mode CD, it only includes the data portion, not the audio.  This mostly works but if you want the music as well, you’re out of luck. 

After a lot of searching (it doesn’t help that “mixed-mode cd” isn’t an official term) I finally stumbled upon this page in the GOG forums.  It is essentially the ONE article after hours of looking that solved the problem.  You can take a look at the post for all the specific details but I’ll also detail them here.  IF YOU DO jump directly to the article, make sure you read the comment on getting the actual device string (for step 6).

Prereqs

In order for this to work, you need to have quite a bit of software installed on OS X.  If you’re not prepared to jump through quite a few hoops, abandon all hope (but you’re trying to play a twenty year old game so what are a few hoops).  First install the following:

  • Xcode – You can get this from the Mac App Store.
  • MacPorts – install instructions are here.  This enables you to install further prereqs.
  • CDRDAO – open a Terminal and type “port install cdrdao”
  • bchunk – in the same Terminal, type “port install bchunk”

Processing the cd

Insert your cd and go back to the Terminal.  Run “mount”.  This should print out all the drives mounted on your computer including the cd drive.  You’ll likely see two things mounted for the cd, so in my case something like this:

/dev/disk2s0 on /Volumes/MM7_Disk2 (cd9660, local, nodev, nosuid, read-only, noowners)
/dev/disk2 on /Volumes/Might & Magic VII (cddafs, local, nodev, nosuid, read-only, noowners)

To unmount this, you’d then type “disktool –u disk2” and both items will be unmounted.  Next we need to get our device path.  We’ll do this by running: “sudo cdrdao scanbus”.  After entering your password you’ll get something like this:

IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/IOAHCISerialATAPI/IOSCSIPeripheralDeviceNub/IOSCSIPeripheralDeviceType05/IODVDServices : MATSHITA, DVD-R   UJ-8A8, HB14

Now that you have that, we’re ready to process our CD.  (You may need to reunmount your cd disk again here).  In the following command, replace <DEVICE> with what you just got above minus the “, DVD-R UJ8A8, HB14” part:

cdrdao read-cd --datafile image.bin --driver generic-mmc:0x20000 --device <DEVICE> --read-raw image.toc

In my case, the command looks like this:

cdrdao read-cd --datafile image.bin --driver generic-mmc:0x20000 --device "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/IOAHCISerialATAPI/IOSCSIPeripheralDeviceNub/IOSCSIPeripheralDeviceType05/IODVDServices" --read-raw image.toc

That’s going to take a little while to run.  The last step is to use a tool installed with CDRDAO named toc2cue.  We’ll use this to create a cue file from the toc file.  Run this in terminal, “toc2cue image.toc image.cue”. 

Now we just need to mount the file in parallels.

Mounting in Parallels

Before you do anything, make sure you’ve shut down the Parallels image.  It can’t just be suspended.  Once that is done, right click on the VM (from the VM list) and go to Configure.  Then choose the Hardware tab.  From this list, click the + in the bottom left and choose to add a CD/DVD.  Click on the Connect To drop down and select Choose an image file…  Choose the image.cue file that you generated and click OK and close the window.  Start your VM up and you should see a CD drive mounted with your game on it and now when you play your game, it should play the music just fine!

One thing to note is that on occasion when playing MM6 the music would come over as static.  I don’t think this had to do with my mounting approach as I seem to remember having this issue long ago when I wasn’t ripping and mounting the cd at all.

Categories: Games
Bookmark and Share
First Article