You are here:   Blog
Register   |  Login

 

Dec 1

Written by: Michael Washington
12/1/2013 3:33 PM  RssIcon

image

NOTE: You can play the game at this link: https://manipulyte.lightswitchhelpwebsite.com/HTMLClient (use your username and password from LightSwitchHelpWebsite.com)

Thomas Capps and I sat out to make a HTML SPA turn based game using Visual Studio LightSwitch. We wanted to make a game that can be played on all devices, phone, tablet, and desktop web browser. It had to be a SPA application because that technology is a must for decent performance on a phone or tablet unless you make a native application. Visual Studio LightSwitch HTML Client creates SPA applications, so we chose LightSwitch because development is faster and easier than other options.

Before we began working on the real game, we decided to make a proof of concept that demonstrated the key functionality we would need. This version is not a real game, it is just a simple example that allows two players to take turns rolling the die and drawing cards to see who can move to the end of the small game board first.

However, it demonstrates that with LightSwitch we can implement the following things:

  • A turn based game that works on all HTML devices including mobile phones
  • Implements methods that prevent users from cheating
  • Allows users to design games and play their games with others
  • Allow users to upload their own photos to use in their games
  • Quick codes to allow users to easily log into the application
  • Game lobby to allow users to connect for games
  • Email notifications

 

Walk-Thru

 

image

When users first logs into the application they are presented with a list of games to join (if any) and the ability to start their own game.

They are also presented with the menu:

  • Picture Manager - Upload and manage their own pictures
  • Game Designer - Create and manage their own games
  • Set Quick Code – Create a code that will facilitate fast login that wont require them to type their full username and password
  • Set Email Address – Manage the email address that they will use for game notifications

 

image

Manage Pictures allows users to upload and manage pictures that only they will be able to use in games they create.

image

The game designer allows users to change the picture and text of each of the games spaces.

It also allows them to indicate what spaces will require a user to draw a card, what that card will say, and if it will require a user to move forward or back a space.

image

Any user can start a game session using a game that was created by themselves or another user.

image

When a user starts a game session it will show them that they are waiting for another player to join.

image

When any other player logs in they will see games waiting for a player that they can join.

image

Players receive email notifications notifying them when it is their turn.

Play proceeds until there is a winner of the game.

 

The Command Table Pattern

The most important thing that was learned in creating the proof of concept, was the need for, and the implementation of, the Command Table Pattern. Essentially it means that:

You need a method to allow a user to perform an action that you cannot simply give them the ability to do directly.

Even if you are not creating a video game you will find you may have this need. It is frequently required in business applications. For example, you may not want to allow a user to cancel an order directly.

image

You can see a detailed explanation at the following link: OData Security Using The Command Table Pattern And Permission Elevation.

image

An alternative to the Command Table Pattern is using WCF RIA Services to perform the same functionality as described here: LightSwitch Survey: Handling Complex Business Logic Using WCF RIA Services.

In Manipulyte, the access to most tables like the GameSessions table is restricted:

 

        partial void GameSessions_CanDelete(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.CanWriteToTables);
        }
        partial void GameSessions_CanInsert(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.CanWriteToTables);
        }
        partial void GameSessions_CanUpdate(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.CanWriteToTables);
        }

 

image

The GameSessionsCommands table is used to create and modify records in the GameSessions table.

image

When a user clicks a button to perform an action they insert a record into the GameSessionsCommands table.

The _inserting method for the table is called, and if then logic is used to determine if the identity of the user, and the current state of the GameSession record they are attempting to create or modify, should allow the change to proceed. If it is to proceed, the user’s security level is temporally “elevated” to allow the GameSession table to be modified.

For example, the following code is used to determine if a user can join a game:

 

            if (entity.CommandAction == "Join")
            {
                // Must not already be in a Game
                GameSession currentSession = GetCurrentSession();
                if (currentSession == null)
                {
                    int intGameSessionID = Convert.ToInt32(entity.CommandParameter);
                    var SelectedGameSession = (from objGameSessions in this.GameSessions
                                               where objGameSessions.Id == intGameSessionID
                                               where objGameSessions.GameState == "Created"
                                               select objGameSessions).FirstOrDefault();
                    if (SelectedGameSession != null)
                    {
                        // Elevate the user's permission to allow the Entity to be updated
                        this.Application.User.AddPermissions(Permissions.CanWriteToTables);
                        // Add currentuser as creator & playerone
                        SelectedGameSession.PlayerTwoUserName = this.Application.User.Name;
                        SelectedGameSession.GameState = "Started";
                        // SEND EMAIL TO PLAYER ONE (TAKE TURN) 
                        SendEmail(SelectedGameSession);
                    }
                }
            }

 

 

Uploading and Managing Pictures

image

Manipulyte allows users to upload and manage their own pictures.

image

You can find a detailed explanation of the code at the following link:

LightSwitch HTML Picture Manager Using WCF RIA Services 

 

Game Designer

image

The Edit Game screen, located in the AddEditGame page is the most complex part of the application other than the Play Game screen. It demonstrates that Visual Studio LightSwitch implements fast performing SPA JavaScript that allows complex functionality that even performs well on a cell phone with a slow connection.

image

It also contains the second largest amount of JavaScript for any single page, at nearly 500 lines of code.

Some code is simple, for example the following method is used to delete a game:

 

myapp.AddEditGame.DeleteGame_execute = function (screen) {
    // delete game
    screen.Game.deleteEntity();
    return myapp.commitChanges().then(null, function fail(e) {
        myapp.cancelChanges();
        alert("Cannot delete game when it has been played!");
        throw e;
    });
};

 

Other code is more complex, such as this code to change a picture for a space:

 

myapp.AddEditGame.ChangePicture05_Tap_execute = function (screen) {
    myapp.showBrowseGetUserAndDefaultPictures({
        beforeShown: function (addEditScreen) {
            // Set the text to the current Space Text value
            addEditScreen.SpaceText = screen.Game.Space05;
            // Set the flipswitch to yes/no for optional Card Draw
            addEditScreen.IsDrawCard = screen.Game.IsSpace05Card;
        },
        afterClosed: function (addEditScreen, navigationAction) {
            // Always update DrawCard boolean and Space Text
            screen.Game.IsSpace05Card = addEditScreen.IsDrawCard;
            screen.Game.Space05 = addEditScreen.SpaceText;
            // Get SelectedPictureName
            var ActualFileName = addEditScreen.SelectedPictureName;
            // Only set picture if one were selected
            if (ActualFileName !== undefined && ActualFileName !== null) {
                // update the PictureSpace value 
                screen.Game.PictureSpace05 = ActualFileName;
            }
        }
    });
};

 

To understand the code fully, it is recommended that you read my book:

Creating Web Pages Using the LightSwitch HTML Client In Visual Studio 2012

 

Play Game

image

The Play Game screen is the largest and most complex of any screen in the application.

image

It is comprised of over 500 lines of code.

The following code sets up the game board:

 

myapp.PlayGame.created = function (screen) {
    // Get game 
    screen.GameSession.getGame().then(function (result) {
        // Set Game name
        screen.details.displayName = "Play Game: " + result.GameName;
        // Get Pictures 
        screen.getIndividualGamePicture();
    });
    // set default die image
    screen.imgDice = "Content/DieOne.png";
    // Set CurrentPlayers UserName
    msls.promiseOperation(CallGetUserName).then(function PromiseSuccess(GetUserNameResult) {
        var CurrentPlayerUserName = GetUserNameResult
        // Call CallGetGameStatus to get latest game session info
        msls.promiseOperation(CallGetGameStatus).then(function PromiseSuccess(GetGameStatusResult) {
            // Parse the JSON returned
            var objGameStatus = jQuery.parseJSON(GetGameStatusResult);
            if (objGameStatus.CurrentPlayerUserName !== GetUserNameResult || objGameStatus.GameState == "Created") {
                // It is not the current players turn
                screen.findContentItem("btnRollDie").isVisible = false;
                screen.findContentItem("btnDrawCard").isVisible = false;
                screen.lblMessage = '...waiting for other player...';
            } else {
                // It is the current players turn
                if (objGameStatus.PlayerNeedsToDrawCard) {
                    // draw card needed; hide Roll Die and display Draw Card
                    screen.findContentItem("btnRollDie").isVisible = false;
                    screen.findContentItem("btnDrawCard").isVisible = true;
                    screen.lblMessage = 'Your Turn! Click "Draw Card" to continue...';
                }
                else {
                    // current turn; display Roll Die and hide Draw Card
                    screen.findContentItem("btnRollDie").isVisible = true;
                    screen.findContentItem("btnDrawCard").isVisible = false;
                    screen.lblMessage = 'Your Turn! Click "Roll Die" to continue...';
                }
            }
            // Call SetGamePieces
            SetGamePieces(screen, objGameStatus.PlayerOneCurrentSpace, objGameStatus.PlayerTwoCurrentSpace);
        });
    });
};

 

The following code is used to process a Die roll:

 

myapp.PlayGame.RollDie_execute = function (screen) {
    // Create a new Game Session Command
    var GameSessionCommand = new myapp.GameSessionCommand();
    GameSessionCommand.CommandAction = "RollDie";
    GameSessionCommand.CommandActionTime = new Date();
    GameSessionCommand.CommandParameter = " ";
    GameSessionCommand.UserName = " ";
    // Save the GameSessionCommand to execute a die roll
    return myapp.activeDataWorkspace.ApplicationData.saveChanges().then(function () {
        // Call CallGetGameStatus to get latest game session info
        msls.promiseOperation(CallGetGameStatus).then(function PromiseSuccess(GetGameStatusResult) {
            // Parse the JSON returned
            var objGameStatus = jQuery.parseJSON(GetGameStatusResult);
            // Set lblMessage
            screen.lblMessage = objGameStatus.PlayerLastStatusMessage;
            // show correct Die Image to player
            SetDieImage(objGameStatus.PlayerLastDieRoll);
            // no matter what, cannot roll again, so hide that button
            screen.findContentItem("btnRollDie").isVisible = false;
            // determine if Card still needs to be drawn, or if turn over
            if (objGameStatus.PlayerNeedsToDrawCard) {
                // draw card needed; display Draw Card button
                screen.findContentItem("btnDrawCard").isVisible = true;
            }
            else {
                screen.lblMessage = screen.lblMessage + " ...waiting for other player...";
            }
            // call SetGamePieces
            SetGamePieces(screen, objGameStatus.PlayerOneCurrentSpace, objGameStatus.PlayerTwoCurrentSpace);
            // if "null" then game is over; redirecting
            if (objGameStatus.PlayerLastStatusMessage == "null") {
                myapp.showMain();
            }
        });
    });
};

 

WCF RIA Services

image

You will find that WCF RIA Services are useful in the following situations:

  • Combine more than one entity into a single entity.
  • Eliminate unneeded columns in an entity to improve performance (otherwise large amounts of data, for example pictures, will be transmitted even when they are not shown).
  • Implement calculated fields that allow the resulting values to be searchable and sortable.

WCF RIA Services are used in Manipulyte for the following functionality:

  • Retrieving a Quick Code only when the secret string is also passed
  • Creating thumbnails of uploaded pictures
  • Retrieving pictures from the server hard drive

 

Quick Codes

image

When using a mobile device, it takes a considerable amount of time to enter a username and password. Quick codes are implemented to allow a user to create a short code that can be used from a specific device to quickly log in.

See the following article for a full explanation: LightSwitch lsQuickCode: Fast Login For Mobile Applications.

 

Email Notifications

Sending emails is an important part of the application. Users are notified when it is their turn in the game, and are notified as to who the winner of the game is.

image

See the following article: Sending Asynchronous Emails Using LightSwitch HTML Client for more information and sample code on sending emails using Visual Studio LightSwitch.

 

Just a Demo

While care was taken to test the goals of the proof of concept, the code is still considered demonstration code and has not been tested for production level security.

Special Note

Game concept, design and much of the coding is by Thomas Capps. The final game will be much more extensive with the ability to buy and sell spaces.

Download Code

The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

(you must have Visual Studio 2012 Update 2 installed to run the code – it will not run in Visual Studio 2013)

1 comment(s) so far...


Gravatar

Re: Manipulyte: A Proof Of Concept Visual Studio LightSwitch Turn Based HTML5 SPA Game

I can't remember a blog post of yours that I have enjoyed as well!

Now let's get to work on the full version ;)

(And my own thanks to you, Michael, for your assistance so far! Without you this idea would still be on the back burner, with no hope of being made. And Lightswitch certainly helped as well :D )

By Tom Capps on   12/2/2013 12:38 PM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 

Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation