HeroEngine Forums
Welcome, Guest. Please login or Register for HeroCloud Account.

Author Topic: Tutorial For Building a Simple “Character Activation Counter” System  (Read 3758 times)

plusfourgames

  • General Accounts
  • *
  • Posts: 12
    • View Profile

Tutorial For Building a Simple “Character Activation Counter” System

Introduction:
This is a comprehensive tutorial that describes how to code a simple game system from beginning to end. Specifically, this covers how to make a system for counting character activations for an account and showing that information to the user. This sounds like a pretty basic activity, but the system will include many components that demonstrate the fundamentals of several important aspects of HSL. This tutorial will include the following:

-An example of how to extend a clean engine GUI control
-An example of how to handle a GUI event
-An example of how to create and show functional GUI window
-An example of how to handle keyboard input from the user
-An example of how to create fundamental structures of object oriented programming in HSL (classes, members, methods, etc)
-An example of how to extend the clean engine's account system
-An example of how to persistently store data associated with an account on a server
-An example of glomming
-An example of client-server communication using RemoteCall2

Background:
This is a tutorial intended for software engineers who want to learn the basics of programming all aspects of a simple game system in HSL. It assumes some knowledge of programming. This is meant to demonstrate some of the mechanics of programming using HeroBlade and HSL.

Caveats:
-I myself am very new to programming in HSL. There may be better ways to do some of the things in this tutorial, and I would encourage anyone with a better understanding of HSL to contribute suggestions for changes.

-If you intend to use the code samples by copying and pasting, I would recommend sticking to my  names for data structures, prototypes, variables, etc. Since the system this tutorial makes is comprised of a number of subsystems, things can get a bit confusing if you're comparing your code samples with different variable names to mine.

-I haven't had as much time as I'd like to rigorously proofread and refine this tutorial. It's pretty long, and it's pretty raw. I initially wrote most of the parts as a reference for myself to remember how to perform tasks using HeroBlade. However, I figured I'd post this now in the hope that it might be useful to someone else starting out with HSL.

-I try to be as explicit in possible in describing the steps to take in each section. It's intended to read like a cookbook, assuming some basic knowledge of how to get around in HeroBlade. This makes the tutorial fairly text-intensive. I try to be as clear and unambiguous as possible. If the walls of text are intimidating, it might be good to break this tutorial up and to do the pieces in separate programming sessions.

Prerequisites:

-Understand the basics of client scripts and server scripts. Only a rudimentary understanding of how to make these and what they are is required. Going through the tutorials found in the link below should suffice:
http://hewiki.heroengine.com/wiki/Your_First_HSL_Script
-Have at least a cursory understanding of prototypes. These are basically templates for nodes. Read the following page for more information:
http://hewiki.heroengine.com/wiki/Prototypes
-Understand the basics of system nodes. Read the following page for more information:
http://hewiki.heroengine.com/wiki/System_nodes
-Familiarize yourself with the DOM Editor.
http://wiki.heroengine.com/wiki/DOM_Editor
-Read this article to learn about GUI Events, if you have not yet done so:
http://wiki.heroengine.com/wiki/GUI_Events

This explains that all GUI controls are nodes and that GUI events are handled by data classes associated with those nodes. For someone with experience in object oriented GUI development, this should make some sense – give the notion a few minutes to sink in, think hard about what a node is, and come back to the topic later if necessary. Because developers' vocabularies can vary, you might find some of what's written in the above wiki page or this tutorial to be a little bit hard to understand at first. Initially, I did, but hopefully after walking through a later section of this tutorial, it will all make sense.

Think – Then Code

Before building any software system of non-trivial complexity, it is imperative that it be planned out (at least informally) before coding. This is especially true when developing a complex, client-server system, like an MMORPG. Even seemingly basic functionality involves the interaction of many intertwined system components.  As such, this tutorial will start by thinking about what we'll code before we actually do it.

Let's envision the system we'll build before we build it. We'll start with an abstract notion of what we'll do to scope the problem. We want to allow the user to see how many times they've activated  characters on their account by logging into the game world.

Next, we can think about the specifics of how we want our system to work. Let's suppose that we want the user to press ctrl+L and have the client retrieve information from the server about how many times the player's account has activated characters. When the server responds, we want to display the information to the user in a UI window.

Finally, before coding our system, we'll think a bit about some of the technical specifics of our implementation. The clean engine has a built in command-handling system that provides an easy way to handle keyboard input from the game. We'll use that capture the keypress from the user. When the client handles that keypress, it will call to the server asynchronously using RemoteCall2. All communication between the client and server in HSL is accomplished asynchronously, so that's all we'll have the client do on handling the keypress. When the server receives the call from the client, it will retrieve a persistent count of character activations for the account of the calling client. The server will respond to the client by (again, asynchronously) using the RemoteCall2 function to invoke a routine on the client. This routine will show our GUI window and present the information to the client.

What we've done is described our system's goals in more concrete terms, considered the system's desired functionality, and decided on a rudimentary design. For a more complex system or game, I would recommend writing a scoping document, defining good requirements, writing a functional specification, and preparing a detailed technical design. Even a system as basic as the one created in this tutorial required some forethought on my part.

Now that we have an understanding of what we're going to build and roughly how we're going to build it, let's proceed to writing some code.

Creating a GUI for the client

Coding a button that can handle GUI events – the object oriented way

1. We'll start building our system by creating a custom control for the client's GUI.

One of the key components of our GUI will be an “OK” button, that, when clicked, will have to close its parent window. This section provides a step by step guide to creating a custom GUI control that handles events. In a nutshell, the way to do this is to create a class of the archetype GUIControl, extend an existing GUIControl class, create a prototype for that class using the GUI editor, code methods with the correct signatures for handling events in that class's script, and add the new GUI control to a window.

2.The first thing we'll do is to create a class for our GUI control. To do so, open up the DOM editor, and create a client class as you would in any other case. Make sure you set the class's archetype to be a GUI control and give it a reasonable description. In my case, I called my class “PW_buttonOKTest”, per some naming conventions I use.

3. Once the class has been created, give it the class “_GUIButton” as its parent. Now, our button extends the built-in button class.

4. Next, we'll create the GUI prototype for our control. This step will create the GUI XML necessary to use our control on a form. Navigate to the GUI XML tab of the organizer, and click “Create New”, found at the bottom of the tab. This will bring up a dialog that will allow us to create the GUI XML for our control. I named my prototype “PW_buttonOKTestPrototype”. Enter a reasonable description. In the “Inherit From” field, select “_button”. For your class, use the class you created in step 2 (if you're using my names, this would be PW_buttonOKTest”. Click OK. Now, we have generated the GUI XML for our control. Click the XML button at the top of the GUI editor panel to submit the XML to the server.

5. We'll now code the methods for our button class. This is where we'll define how the GUI events are handled. Just like we would do for creating any other class-methods script for a client class, we'll make a new client script and call it ClassNameClassMethods, where ClassName is replaced by our class's name. In my case, I created a script called PW_buttonOKTestClassMethods.

6. Add the following code to your script. The comments provide an explanation of what each part is doing:

//Routine that's run whenever a mouse is clicked on this control.
//It will print a message to the console indicating that this method is
//being run, and then it will close this control's parent window.
method onMouseClick(args references Class GUIMouseEvent)
 
  //This will get the parent window of the button.
  var window = me.rootParent()

  println("This is from a button click.")
 
  //Indicates to the rest of the command stack that this event has been
  //handled. See HEWiki's command tutorial for more information.
  args.handled = true
 
  //This is the part that closes the parent window.
  if window != None
    DestroyNode(window)
  .
.

7. Now, we have a button that will close its parent window when clicked.


Creating the GUI window

1. The next piece of implementing this tutorial will be to make the GUI window for the client. As with most features of HSL, there are a number of ways to accomplish this task. For this tutorial, the window will be created via the GUI editor. The GUI editor is a tool that allows users to create prototypes for GUIs through which users can interact with the game. To start the process of creating a GUI, open the organizer panel, navigate to the GUI XML tab, and click the “Create New” button.

This will launch a dialog that will allow you to configure some properties that define your prototype. For the name of my window, I chose “TestWindow”. The “Inherit From” selection is also important, as this will define the parent class of your GUI control. I used _window. Since we're making a window of our own, it makes sense to choose this. For the class dropdown, select “_GUIBaseWindow”.

After clicking “Okay”, you'll enter the WYSIWYG GUI editor. Here, we'll define the look of our GUI.

2. Modifying GUI control properties
Now that our window has been created, we'll  modify its properties to make it a bit more presentable. With the whole window selected, the properties editor will show the window's properties. Here, we can change things like the window's size. In my case, I didn't change any of its properties, but feel free to fiddle with this if you'd like. I did want to change the window's titlebar text, though. To do so, click on the titlebar in the GUI editor. This will bring up the properties for the titlebar. The titlebar has a property called “text”. I changed the value of this property to read “Counter Window”. This demonstrates how to change a property on a GUI control. It's very similar to most any WYSIWYG GUI builder.

3. Adding GUI controls to the window
As I visiualized my GUI before creating it, the window should have two main controls. One will be a label in which some text and our running count will be displayed. The second will be our “OK” button that will close the window.

To add controls to the window, we have to decide where to put them. The window has a scrollable child, visible in the GUI editor's tree hierarchy, pictured below. This is where our button and label will go in the tree hierarchy. Admittedly, I'm not an expert in scrollables, so it may be better to add the controls to the clientarea. In my case, I added them as children of the scrollable itself, which will suffice for this tutorial. However, the hierarchical notion of parent / child controls should come naturally to a software engineer with GUI development experience. HSL's system is no different from what you would find with Java's swing or .NET.
« Last Edit: May 09, 12, 08:29:28 PM by plusfourgames »
Logged

plusfourgames

  • General Accounts
  • *
  • Posts: 12
    • View Profile

4. Adding a control to another is a straightforward process. First, highlight the intended parent control in the tree. Next, navigate to the “Organizer” panel. From there, select the GUI XML tab, and find the control you want to add. First, let's add the button. In the GUIXML tab, find the PW_buttonOKTestPrototype control that we created earlier in the tutorial. Double click it. This will add an instance of our custom button to the GUI.

After double clicking, you'll see a button appear in your window as pictured below. Drag the button to its desired location within the scrollable area. I put my button at the bottom of the window. Using the properties panel, change the button's name to “PW_buttonOK”. It's always a good practice to name GUI controls. Finally, resize the button as you deem fit, and set its “text” property to read “OK”.

5. Once the button has been added, we have to add a label. This will contain the text that shows our account's character activation counter. We'll add this control in the same way that we added the button. Click on the clientarea of our window's scrollable, navigate to the GUI XML tab in our organizer, find the control named “_label”, and double click it. Position the label in the center of the window and change its name to labelCounter.

6. We've now created a window with a label and button control on it. The last thing we need to do is to publish this window up to the server. This is done by way of creating GUI XML and pushing it to the server. In the GUI editor panel, you'll see a button for generating GUI XML. Clicking that button once will build the GUI XML and send it to the server. Now, your window has been published to the game's repository. Note that the GUI that we published to the server does not show up in the organizer right away. Click the “Refresh” button to make it appear in your list.

Pause for a moment to consider what you've done so far. At this point in the tutorial, we've coded a custom “OK Button” that handles mouse click events by closing its parent window. We've also created a GUI window with a button and a label.

7. Coding a script to show the window:
We've now defined the look and contents of our GUI window. Next, we'll create a base script with a function that will ultimately be responsible for showing the GUI to the user. HOWEVER! This is where the forethought really comes in handy – think back to the first part of this tutorial in which we planned out our system. The game world is a client-server system, and the client won't have anything meaningful to display in its activation counter window without having first received a message from the server.

Remember, the system will work in the following way. First, the user will press a key. The client will receive the event and handle it by running a method that calls out to the server. When the server responds, it will invoke a method on the client to show the count in the window we just created.

As such, all we're now creating is a basic script with a stubbed-out function we can call when we handle the keypress event. We'll make a regular client-side script with a function in it to accomplish this. Create a client-side script and call it “ShowCounterWindow”. Give it a meaningful description.

Add the following code to your script. For now, these methods just print to the console. Later, they will play roles in communicating with the server.

public function callToServerBeforeShowingCounterWindow()
  println("This is the callToServerBeforeShowingCounterWindow function.")
.

remote function receiveActivationCountFromServer(nNumberOfLogins as Integer)
  println("Got message from server.")
.

Next, save, compile, and submit the script.

Now that we have a GUI window created and a stub function for calling to the server, we need a way for the user to invoke that function by way of a keypress. The next section will demonstrate how to capture a keypress event from the user and to open the window we just made.

A note on this section
While this tutorial doesn't show the GUI window until after the server responds to the client's original message, when designing a real game, it may be better to show the GUI as soon as the client handles the keypress. Doing so may provide a smoother user experience. For the sake of this tutorial, we'll just do things in a very simple way. See this HE Wiki page for more information:
http://wiki.heroengine.com/wiki/Combat


Handling Key Press Events

The way that keyboard input is handled in the clean engine is governed by two main system components. First, a special GameKeyBindings.ini file maps key press events to game commands. Second, the Input_Command client script defines what the client does when one of those commands is received. This is a very simplified explanation of keyboard input, and it's presented from a purely functional perspective. For more details on the greater HSL input system, the following wiki page is a good place to start:

http://wiki.heroengine.com/wiki/HSL_Input_System_and_Keybindings

For the purposes of this tutorial, though, a basic understanding of the GameKeyBindings.ini file and the Input_Command client script are all that will be required.

1. Changing the GameKeyBindings.ini file.
The GameKeyBindings.ini file defines what commands will be received by the Input_Command client script when the user presses certain keys. If all you want to do is to override some built-in keypress functionality or to add a new keypress event to your game, you start by modifying this file. The GameKeyBindings.ini file is a game-specific override of the HeroEngineKeyBindings.ini file. For the purpose of this tutorial, we won't pay attention to the HeroEngineKeybindings.ini file.

To modify the GameKeyBindings.ini file, open the repository browser. At the top-level HE directory, you will see the GameKeyBindings.ini file. I simply double click it to open a local copy of the file on my machine in my default text editor. Take a look at the file and note the different layers of input, represented by different sections of the file. The level we're interested in is the “Command” section. At the bottom of this section, add the following lines:

  //Command for showing the character activation counter window.
  OpenLoginCounterWindow = CTRL+L

This specifies that when the control key and L are concurrently pressed, the OpenLoginCounterWindow string will be sent to the Input_Command script.

Now that we've made our change in the text editor, we have to put the changed version of this file up on the server. Using my text editor, I saved the modified version of the file to the C:\ drive. In the repository browser, I navigated the menus to “File → New Local View →” and picked my C drive. Then, from my local view, I dragged the GameKeyBidnings.ini file into the HE view and overwrote the version in the repository.

2. Changing the Input_Comannd client script
The Input_Command client script defines what actions are taken in response to a particular key press event. This is a client-side script and can be edited just like any other. Navigate to the Client Scripts tab of the Organizer Panel, and search for the script named “Input_Command”. Double click this script to open it in the script editor.

Inside this script, you'll see a function called “onCmdStop”. This is run when a command key is released. You should see a lengthy when-clause in this routine that handles all of the various possible commands. At the bottom of this, directly above the default, we'll have to add the piece of code that processes our control+L key event. Add the following code directly above the default in the switch statement:

    //Handles the call to open the login counter window.
    is "OpenLoginCounterWindow"
      ShowCounterWindow:callToServerBeforeShowingCounterWindow()   

This code basically says that, when the “OpenLoginCounterWindow” command is received, the ShowCounterWindow script's callToServerBeforeShowingCounterWindow function will be executed.

After compiling and submitting this script, try pressing ctrl + L from your character. Watch our printed message appear in the console.

3. Let's pause again for a moment and consider what we've done. At this point, we've created our GUI on the client, complete with a custom button that closes its parent window when clicked. We've modified the keyboard input system to handle a new command. Right now, this command just prints some text to the console. We next have to code functionality on the server. We have to make subsystems for keeping track of a character activation count and to respond to the client's request to get that count.


Extending Account Functionality to Count Character Activations

Create a basic class to function as your character-activated counter.

1. Section Overview:
In this section, we're going to create a basic server-side class that can be used to maintain a persistent value. Storing persistent data is one of the fundamental aspects of programming an online world. This section will walk through the creation of the server-side class and the authoring of its methods.

2. Create the Class:
Open the DOM Editor. The top panel in the DOM Editor contains the UI controls that we'll use to create our new server-side class. Make sure that the radio button for “Server” is selected and “Classes” is picked from the combo box.

Click the “New Class” button. This will prompt you with a dialog in which we'll specify attributes of this class. Since this is going to count the number of times an account has a character activated, it makes sense to give it a name that reflects this. I called my class “PW_ActivationCounter”. For its description, I typed “This class is used to count the number of times a character is activated.” Leave the archetype as “data”, and click the OK button to create the class.

3. Create the Field For Counting
For the PW_ActivationCounter class to be meaningful, it needs to have a member (or field) that keeps a count of the number of character activations. Since this is just going to be a count, it makes sense for this member to be an integer.  As such, we'll make a field of type Integer (one of HSL's built-in data types).

To create a field, we'll again use the DOM Editor. The same panel at the top that we used to make a class can be used to make a field. Rather than selecting “Classes” from the combo box, select “Fields”.  Click on the “New Field” button. Again, you'll be prompted with a straightforward dialog into which you'll enter attributes of the field you're going to create. I named my field “m_nActivationCounter”, gave it a description of “An integer used for counting the number of character activations”, gave it a type of “Integer”, set reflect to “No”, set its write strategy to “lazy”, left its watching script blank, and set its private property to “No”.

Click the OK button when you're done to create the field.

4. Add the new field to our new class.
Using the top panel of the DOM editor, select “Classes” from the combo box. Select the class created in step 2 from the list of classes that are shown under the top panel. In my case, I selected the PW_ActivationCounter class. Once this class is selected, its properties will be visible in the lower half of the DOM Editor. Use the drop down boxes at the bottom of the class's list to add the field to the class.

Once the field has been added, be sure to click the “Save” button to commit the change to the server.

5. Create the PW_ActivationCounterClassMethods script.
Next, we have to create a server-side script that contains the methods for the PW_ActivationCounter class. HSL is a bit different from some other object oriented languages in that the data structures that comprise a class is stored separately from the code of its methods. The two are associated by a naming convention. Specifically, the methods of a class must be stored in a script named with the class's name followed by “ClassMethods”. As such, I created a server-side script called PW_ ActivationCounterClassMethods in which I will define the methods on the PW_ActivationCounter class.

6. Code the methods for our counter class.
The purpose of our counter class is simple. All it has to do is keep track of the number of times a character is activated and report that information to a caller. To accomplish this, it needs only 2 methods – one to get the count that it has and another to increment that count. The following is the code for this script:

//Method for incrementing the activation counter.
method incrementActivationCounter()
  me.m_nActivationCounter = me.m_nActivationCounter + 1
.

//Gets the activation counter's integer value.
method getActivationCounter() as Integer
  return me.m_nActivationCounter
.

Once this code is in place, click the “Compile” button to make sure it can compile. Next, click the “Submit” button and write a comment for the history (something like “First version of this class” would suffice).


Create a game-specific extension of the account system.

1. Section Overview:
The Account system has an associated prototype from which the account system node is made. The composition of this prototype can be seen by opening the “System Node Configuration GUI”. This is a tool that allows developers to modify game systems for the purpose of adding game-specific functionality. The dialog can be accessed by pressing the F5 key from the game panel (where you can see your character in the world) to open the utilities interface, navigating to the “Tools” tab, and clicking the “System Nodes Configuration GUI” option.
Once you've opened the system node configuration GUI, navigate to the “ACCOUNT” system by using the drop-down box. This will show you the classes that comprise the account system. If you have not yet changed this, you will see the _Account class and the E_Account class.

The _Account class is described as the base class for the account system node. The E_Account class contains game specific functionality for the account system. If you open up the E_AccountClassMethods server script, you will see the code that gets executed when various account events are handled, such as during the logon process. The specifics of these events are described here:
http://hewiki.heroengine.com/wiki/$ACCOUNT
« Last Edit: May 09, 12, 08:31:43 PM by plusfourgames »
Logged

plusfourgames

  • General Accounts
  • *
  • Posts: 12
    • View Profile

An easy way to extend basic account functionality is by creating a class that extends E_Account. HeroEngine recommends that you name classes that extend _Account by prefixing them with some game specific value. My world is called “PrototypeWorld”, so I prefix my classes with PW. What we'll do is create a class that extends E_Account and override the HE_PostCharacterActivated method to maintain a persistent, running count of the number of times that this method has been run.

The next steps will walk you through the specifics of creating that class.

2. Create the extension of E_Account
We'll use the DOM editor to create a new class that extends E_Account (just to clarify terminology, by “extends”, I mean that our new class will have E_Account as a parent class). First, set up the DOM Editor so that you'll make a new class on the server. At the top of the DOM Editor, there's a panel you can use to create new DOM structures. Select the “Server” radio button, select “Classes” from the list control, and click the “New Class” button. Make sure that the “Read Only” checkbox is unchecked.

Clicking the “New Class” button will bring up a dialog where you'll specify some attributes of your new class. Give it a name that makes clear that this is an extension of the _Account system. I called my class PW_Account. Give the class a description to indicate that it contains game-specific functionality. Its archetype should be “data”. Click the “OK” button when done. This will create your class.

3. Make PW_Account extend E_Account
In the properties of our new PW_Account method in the DOM editor, we'll add a new parent class. This is done by clicking the bottom row of the table for editing the class. Add a “Parent Class” and make that parent the “E_Account” class. Remember to click the “Save” button after this is done.

4. Decide on a way to associate the PW_ActivationCounter with the account.
In a previous section, we created the PW_ActivationCounter class and coded methods so it could keep track of a count. What we need to do next is come up with a means by which we can attach one of those counters to each account in our game. This way, whenever we're dealing with an account node, we can easily access the PW_ActivationCounter for that account node. There are a couple different ways to accomplish this task.

Under normal circumstances, I would recommend making a new field on our PW_Account class of type PW_ActivationCounter. This way, our PW_Account class has a strongly typed reference a PW_ActivationCounter node that we know will always be there (it may be null, but the reference will be there). However, for demonstration purposes, this tutorial will GLOM a PW_ActivationCounter node onto our PW_Account node at runtime. To me, GLOM'ing is an interesting paradigm that may be new to programmers with experience in strongly typed, object oriented languages. Glomming allows programmers to add arbitrary classes to a given node at runtime.

*Semi-technical Aside*
In Java-like terms, I almost think of glomming in the following way: Suppose that every object in the game has particular member. This member is a list of type Object. At runtime, any instance of any class can then be added to that list of objects for a particular instance. Only that instance is affected. This is a somewhat abstract and imperfect analogy, but it's been helpful to me (and perhaps could be to others) in thinking about glomming.

Since glomming is done at runtime, we don't have to do anything else for now. This section is really just about thinking of the way we want to approach the problem. A later section will show how the glomming will actually work.

5. Create the class methods script for the account extension class
In the same way that we created the class methods script for our activation counter class, we will now create a server script to hold the methods for our account class. In my case, I had to name the script PW_AccountClassMethods. Name your class according to the convention (ClassName+”ClassMethods”).

6. Add a method to be run after a character was activated.
Next, we're going to add a method that will run whenever an account's character is activated. To do this, we're going to override a special _Account method to do custom work for our game system. In this case, it will add 1 to our counter that's keeping track of how many times the account's character has logged on. This method will demonstrate how to override a method in HSL, how to invoke a super class's method, and how to store and retrieve persistent data.

Overriding a method in HSL is simple. Since our PW_Account class extends E_Account, all we need to do is to implement a method in our PW_Account class with the same signature as the method in the E_Account class that we want to override. It should have the same name, visibility, number of arguments, and return type as the method we're overriding. In this case, we're going to override the HE_PostCharacterActivated method. See the wiki page on $ACCOUNT for details about character activated methods for the specifics of when this is run:
http://hewiki.heroengine.com/wiki/$ACCOUNT

In general, this will be run when a character loads into the game. In our version of this method, we call the parent class's version on the first line. This way, any necessary work that was already being done when a character was activated will still be done.

Add the following code to your class for the HE_PostCharacterActivated method. Also, read the comments to understand what the lines are doing. There are a few important features to note. Read the comments carefully to understand all of this method's parts.

//This method is run after a character is activated in the game.
//This game specific implementation keeps track of the number of times
//this has been done for the character'rs account.
method HE_PostCharacterActivated( theAccount as NodeRef )
 
  //First, call the parent class's method to make sure that its
  //necessary work is done.
  parentClass::HE_PostCharacterActivated(theAccount)
 
  //For this code to run properly, the account node can't be none.
  assert( theAccount != None, "theAccount was None")
 
  //Next, we check to see if theAccount already is an activation counter.
  //If it does, there's no need to make a new persistent node for this
  //account. We need only to increment the one that already exists.
  if theAccount is kindof PW_ActivationCounter
    theAccount.incrementActivationCounter()
   
      //This is a debugging techinque I use. It broadcasts a message about
      //what the server is doing that is visible on the client.
      $CHAT.ChatArea("game", "Account is kind of counter. Incremented.")
   
  //Otherwise, we have to add an activation counter node onto our persistent
  //account node. This will result in the activation counter being stored
  //along with the account.
  else
 
    $CHAT.ChatArea("game", "Account is not type of counter.")
 
    //If a character is activated and it does not have an activation counter, one
    //is always made for it. As such, for this code to be run, it must be the
    //first time the character is being activated. Set its activation counter
    //to 1.
   
    //We glom an activation counter onto this account and set its value to 1.
    glomClass("PW_ActivationCounter", theAccount)
   
    $CHAT.ChatArea("game", "Counter was glommed..")
     
     //This is how you can tell in code whether a PW_ActivationCounter has
    //been glommed onto our account class. If the PW_ActivationCounter node
    //has been glommed on, our account will be a kind of that class. The
    //is kindof operator is very similar to Java's instanceof operator.
    //It's used to tell the type of a node.
    where theAccount is kindof PW_ActivationCounter
      theAccount.m_nActivationCounter = 1
     
      $CHAT.ChatArea("game", "Account was counter. Set to 1.")
    .
  .
.
   

7. Add a method that can respond to the client when it requests the activation counter's value from the server.

Aside from recording character activations and persisting that information along with an account, we also need to provide some way to convey that information along to the client. There are a couple different ways to pass information between the client and server in HSL, but this tutorial will be using remote calls. For more information about remote calls, refer to the following HEWiki article:
http://wiki.heroengine.com/wiki/RemoteCall2

Add the following method to our PW_AccountClassMethods script. Use the comments for help understanding what the code is doing:

//This is a routine that's run in response to a request from a client.
//A method on the client side will be called that will request the
//activation count. This method will receive the call from the client
//and handle it, by sending a message back to the client that contains
//the activation count. It's important to note that all inter-process
//communication in HSL is asynchronous. The client does not wait on
//a response from the server. Rather, it sends a message to the server,
//and the server responds by sending a message back to the client. In
//between the client's sending of the initial request and the server's
//responding, the client continues with whatever processing it may be
//doing.
untrusted method clientCalledGetActivationCounter(account as NodeRef)
  nCount as Integer = 0
 
  //This is a debugging tool I sometimes use. It causes the
  //server to broadcast a chat message that will appear on the client's
  //screen.
  $CHAT.ChatArea("game", "Request received.")
 
  //This is how you can tell in code whether a PW_ActivationCounter has
  //been glommed onto our account class. If the PW_ActivationCounter node
  //has been glommed on, our account will be a kind of that class. The
  //is kindof operator is very similar to Java's instanceof operator.
  //It's used to tell the type of a node.
  where account is kindof PW_ActivationCounter
   
    $CHAT.ChatArea("game", "I am an activation counter.")
    //This line retrieves the count from our activation counter.
    nCount = account.m_nActivationCounter   
  .
 
  //This is another asynchronous communication that completes the
  //client's request. This is how the server responds to the client
  //and provides it with the activation count - what the client
  //requested. The client will then have to handle this call by
  //showing the information to the user.
  call client account ShowWelcomeGUI:receiveActivationCountFromServer(nCount)
.


8. Now, let's pause again and consider the system that's in place. We coded functionality on the server to keep track of our character activation count. We also coded functionality for receiving requests from the client to get that count, and we've coded a way to return that count back to the client. What we now have to do is fill in the stubs we previously made in our client's ShowCounterWindow script. These need to actually call out to the server and handle the server's response by showing the counter window.

9. Open the ShowCounterWindow client script that we previously made. We'll now replace the stub code that we had before with two working functions. The first, which is run when the user presses control + L, will call to the server to get the activation count. The code for this function is shown below:

//This function is run when the user presses a key. It calls to the server,
//requesting the account's activation counter.
public function callToServerBeforeShowingCounterWindow()
  println("This is the callToServerBeforeShowingCounterWindow function.")
 
  //Call out to the server to retrieve the login counter. The myid variable
  //is used to indicate to the server the client to which it should respond.
  myid as ID = me
  println(myid)
 
  //This line calls to the server, requesting the character activation count.
  call server PW_AccountClassMethods:clientCalledGetActivationCounter(myid)
.

10.  The second function we'll code is the one to show the GUI window we made and display the count. The commented code for it appears below:

//This function is run after the server responds with the account's character
//activation counter. It shows the count in a GUI window.
remote function receiveActivationCountFromServer(nNumberOfLogins as Integer)
  println("Got message from server.")
 
  // This creates a window GUIControl from a prototype
  windowActivationCounterDisplay as NodeRef of Class GUIControl = createNodeFromPrototype("TestWindow")
 
  // Make the new window renderable in the screen
  windowActivationCounterDisplay.build = true
 
  // Configure the window's position in the viewport
  screen as Vector3 = getViewPortSize()
  windowActivationCounterDisplay.position.x = (screen.x - windowActivationCounterDisplay.size.x) / 2
  windowActivationCounterDisplay.position.y = (screen.y - windowActivationCounterDisplay.size.y) / 2
 
  //This is how we get a reference to the label control. We need this to set its text.
  sp as NodeRef of Class GUIControl = FindGUIControlByName(windowActivationCounterDisplay, "scrollable")
  labelCounter as NodeRef of Class GUILabel = FindGUIControlByName(sp, "labelCounter")
 
  //This sets the text of the label.
  labelCounter.text = "You have activated characters in Test World 1" + nNumberOfLogins + " times."
  labelCounter.build = true
.

Now, we need only to compile and submit this script. After doing so, return to the game, press ctrl + L, and you should see the end result of your work. I hope this tutorial was helpful in learning some basics of programming with HeroEngine!
« Last Edit: May 09, 12, 08:33:08 PM by plusfourgames »
Logged

HE-Cooper

  • *****
  • Posts: 2221
    • View Profile

Really great attention to detail and step by step there. If you haven't already posted to the wiki, after it gets vetted and kicked around I'll put it up as a wiki entry for you.
Logged

Gothrek

  • General Accounts
  • *
  • Posts: 92
    • View Profile

a really great work man!!
Logged

webside80

  • General Accounts
  • *
  • Posts: 9
    • View Profile

Thanks for sharing this, plusfourgames. Great to see more tutorials and examples of work.
Logged

plusfourgames

  • General Accounts
  • *
  • Posts: 12
    • View Profile

Thanks for the positive feedback!

If people find this helpful, I'll share more how-to's and tutorials in the future.
Logged

DragonFist

  • General Accounts
  • *
  • Posts: 140
    • View Profile

Really looking forward to doing this one.  Thanks!
Logged

TGSRofar

  • General Accounts
  • *
  • Posts: 153
    • View Profile

I normally do not read through lengthy tutorials unless I plan to attempt the tutorial myself.  However, this one looked like it was well done so I decided to read through it completely.  Nice job with this.  I'm sure this will be helpful to anyone that is just trying to learn HSL.
Logged
Ron Farrell
Lead Programmer
Triad Game Studios

jcsmith562

  • General Accounts
  • *
  • Posts: 190
    • View Profile

Well done. Nice to see people sharing with one another.
Logged

DragonFist

  • General Accounts
  • *
  • Posts: 140
    • View Profile

Okay, having some trouble with the first steps.  Perhaps a configuration issue.  Anyhow, I create the class, make sure that it has the archetype of guicontrol, add _GUIButton as the parent class and save.  When I attempt to create the prototype, I choose _button as the inherits from and try to set the class to my new class but my new class (visibly in the client DOM) is not in the combobox list and if I type it in, I get a message that it is either invalid or not compatible with _button's class.

I believe I've followed the steps up to this correctly.  The fact that it is not in the combobox lends me to think something is either wrong with my class or it is not populating to be viewable from the organizer's dialog.

Edit: Okay got past this, though it was strange.  I had to leave HB and log back in for my class changes to register.  The prototype was created but I was thought it might have been with the wrong class as I had hit okay at some point with the _guibutton class.  So I deleted it and tried again but it was not gone.  I tried deleting from the RB and seemed to work but still seeing it in HB.  Finally, close HB and log back in and all is good.

That's a pretty bad workflow if I have to log out every time I update something.  Hope there is a way around that.
« Last Edit: Jun 26, 12, 06:21:34 PM by DragonFist »
Logged

DragonFist

  • General Accounts
  • *
  • Posts: 140
    • View Profile

Awesome tutorial!  Thank you very much for it, clarified a few things for me.

Small issue: near the end, step 7 I think, in the accountmethods untrusted method the client script is called "ShowWelcomeGUI" where it is referred to as and named as "ShowCounterWindow" everywhere else.

Again, thanks a lot!
Logged