My son plays an RPG with his friends (think Dungeons and Dragons, but with a Star Wars feel.) I played Dungeons and Dragons as a kid as well. My brothers and our friends would gather around the kitchen table with our manuals, graph paper, dice, and character sheets.

The difference is, when my son and friends play, they’re all in front of their computers chatting over headsets.  It’s not a MMORPG, it’s a real RPG with a game master directing the action.  In an RPG players need to roll dice for combat or skill checks.  They would roll dice on a web site and describe the results to the other players.

Why not have a shared web site where they could roll dice and everyone could see, in real time, what was rolled?

ASP.NET SignalR

A description of SignalR from Microsoft at http://signalr.net/

ASP.NET SignalR is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is “real-time web” functionality? It’s the ability to have your server-side code push content to the connected clients as it happens, in real-time.”

So, why not create a web site so when one player clicks the Roll Dice button in their browser, the server pushes the result out to all the players? It looks like this:

dicewindows

Each user can send a chat message, or roll dice and results are sent out to all the other users.

First, create a web page with controls for a name, message, and what dice to roll. Add an un-ordered list at the end to hold the messages.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="index.aspx.cs" Inherits="BasicChat.index" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Dice Chat</title>
</head>
<body>
<p>Name
    <input type="text" id="name" value=" " /></p>
 <p>Message
    <input type="text" id="msg" value=" " />
    <input type="button" id="send" value="send" /></p>
 <p>Roll
    <input type="text" id="howmany" value=" " size="3" />
    <input type="text" id="typeofdice" value=" " size="2" />
    sided dice
    <input type="button" id="roll" value="roll" /></p>

<ul id="message">
</ul>

Now, we can include the SignalR libraries. The third script below, the /signalr/hubs, is dynamically generated at runtime. It contains a property for each hub on the server. Hubs are where you expose server side methods you can call from clients. It’s just a class that inherits from Hub.

    <script src="Scripts/jquery-1.8.3.min.js"></script>
    <script src="Scripts/jquery.signalR-1.0.0.min.js"></script>
    <script type="text/javascript" src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>

This block of code below creates the functions that will be called from the client (browser) and the server.

The ready function creates the chat variable. It contains a link to your hub on the server. In this case, the hub is diceChat.

Next, a function called send is declared that can be called from the server. It takes a single message parameter and adds it to the unordered list, created above.

Now, once the hub has completed startup, we can register the functions that will be called when the user clicks the Send or Roll button. When Send is clicked, we call the send method on the hub, which is on the server. We pass it a message and the users name. We also clear the message text box once the message has been sent. The send method on the server turns around and calls the chat.client.send method for all connected clients, which adds the message to the unordered list in all their browsers.

For Roll, we call the roll method on the hub. It passes in the number of dice to roll, what type of dice, and the users name. It also clears the input fields. The server rolls the dice and calls the chat.client.send method for all connected clients to display the result of the roll.

<script>
        $(function () {
            var chat = $.connection.diceChat;

            chat.client.send = function (message) {
                $('#message').append('<li>' + message + '</li>');
            };

            $.connection.hub.start().done(function () {
                $('#send').click(function () {
                    chat.server.send($('#msg').val(), $('#name').val());
                    $('#msg').val('').focus();
                });

                $('#roll').click(function () {
                    chat.server.roll($('#howmany').val(), $('#typeofdice').val(), $('#name').val());
                    $('#howmany').val('').focus();
                    $('#typeofdice').val('').focus();
                });
            });
        });
    </script>
</body>
</html>

Now, we get to the server class, DiceChat.cs. It’s a single class that inherits from Hub. It has two methods, Send and Roll.

Send just calls Clients.All.send(), which calls chat.client.send for all the connected clients. In this case, it formats the message and name into one string and sends it. When chat.client.send in the browser receives the message it adds it to the unordered list.

Roll generates the correct count of random numbers and generates a descriptive string, which it returns to all the clients by calling Clients.All.send(). Easy!

using System;
using Microsoft.AspNet.SignalR;

namespace DiceChat
{
    public class DiceChat : Hub
    {
        public void Send(string message, string name)
        {
            Clients.All.send(String.Format("{0}: {1}", name, message));
        }


        public void Roll(int howMany, int typeOfDice, string name)
        {
            int diceTotal = 0;
            string diceRolled = "";
            Random rnd = new Random();
            
            for (int i = 1; i <= howMany; i++)
            {
                int roll;

                roll = rnd.Next(typeOfDice) + 1;
                diceRolled = diceRolled + roll.ToString() + " ";
                diceTotal = diceTotal + roll;
            }
            Clients.All.send(String.Format("{0} rolled {1} {2}-sided dice ({3}) and got a {4}", name, howMany, typeOfDice, diceRolled.Trim(), diceTotal));
        }
    }
}

Finally, there is some plumbing you have to hook up. Under App_Start, we have a RegisterHubs class. This is called at application start up, and it registers the routes needed for the hub to communicate from server to client.

using System.Web;
using System.Web.Routing;
using Microsoft.AspNet.SignalR;

[assembly: PreApplicationStartMethod(typeof(DiceChatStart.RegisterHubs), "Start")]

namespace DiceChatStart
{
    public static class RegisterHubs
    {
        public static void Start()
        {
            var config = new HubConfiguration
            {
                EnableCrossDomain = true
            };

            RouteTable.Routes.MapHubs(config);
        }
    }
}

The complete project is available on GitHub, as DiceChat.

2 thoughts on “Using ASP.NET SignalR To Play an RPG

  1. I had started learning real time gaming application, & found your article really helpful.
    Thanks for sharing.
    Vijay

Leave a Reply

Your email address will not be published.