Programming & Designing Browser Interfaces: by Unknown Person

Programming & Designing Browser Interfaces

by Unknown Person

The Basics of an Interface

Download Code Supplement (.zip)

[Part One] - [Part Two] - [Part Three]

Introduction

Giving your game a browser interface can make the user interface of your game or utility much more friendly and better looking if used tastefully. Advantages to using browser interfaces include the flexibility of HTML, CSS, and Javascript, BYOND's native support for browsers (having a browser tab) just make it that much more convenient for players to use. Using the browser also has the advantage against Heads-Up Displays (HUDs) because screen objects are not as fast on BYOND (although browsers aren't as dynamic and useful when needed to display quick information such as health). HUDs also tend to slow down and use more resources when there are many objects on the screen.

Why use Browser Interfaces?

The big question most people have when suggested to use browser interfaces rather than other methods is knowing what is so great about them? The reason is that using the browser is much more customizable than using input() or alert() (as both aren't very customizable). You have much more control over customization by using them, as they are just pages that are made in HTML. Browser interfaces are much more flexible, as you can have more than one browser window open at once. They are much easier to manage than on-screen text-box interfaces, since you have to manage screen objects.

Some downsides of browser interfaces include the fact that HTML can easily be edited, and exploits are more easily and frequently found. If you are letting the user input any data to the browser window, you will have to verify what data is sent, who sent it, when it was sent, and whether or not the data is valid. A new HTML page is also created whenever you update a page, which can easily fill the user's cache with hundreds of files. However, with a little bit of organization, these downsides will not become a problem for you. With the release of BYOND 4.0's interfaces, browser interfaces may seem more old fashioned. This is not the case. 4.0 interfaces can be used in combination with a browser interface, and such a combination can make a very intuitive and user friendly interface. BYOND 4.0's new interface should not be seen as a replacement to browser interface, but as a companion to the other assortments of controls.

HTML, JavaScript, and CSS

Since programming a browser form requires using HTML and JavaScript, it would be a good idea to familiarize yourself with the two. If you are not familiar with HTML or JavaScript, there are many websites that have tutorials on the respective subjects, such as W3Schools. A good understanding of CSS (cascading style sheets) will help make your page more interesting than black text on a white background. However, this article will mainly explain the basic aspects of the usage of HTML and JavaScript in the application of situations you may encounter using browser forms. In no way is the usage of HTML and JavaScript the limit of browser forms in DM, and it is encouraged for you to experiment with your own creations.

Using this Article

This article is meant to guide you step by step on how to approach programming and designing browser interfaces. Chapters are ordered in progression of difficulty and in the end of each chapter, there are a couple of "review" questions that you may try to do to strengthen your knowledge on the material. Each chapter also has examples on the ideas discussed, and are analyzed. This article can be used both as a tutorial and a reference.

The Basics

If you know how to use the two procs browse() and browse_rsc() fluently, and understand the concepts behind them, you may skip this chapter.

Displaying a Page

The core basic function that a browser interface requires is the proc browse(). This proc displays text in the browser window (or in a separate window, if you set the specific parameters) that you specify in the first parameter. Here is a small example on how to show a basic window of text.

mob/verb show_brower_text() var/text = {" You have created a browser window. The text inside this window is displayed in your browser tab. "} src << browse(text) // in 4.0 with custom skins // src << browse(text, "browser_id")
Example 1.1: A simple example of displaying text to the browser.

This will load up your browser tab, and show the specified text. You can replace the text variable with any HTML you want. For example, you could set the text to a value of an HTML page, and an HTML page will be sent to the viewer's cache, and displayed. Since the text will represent an HTML page in the user's cache, you may use thing such as CSS, and JavaScript. Be aware that BYOND's browser display uses Internet Explorer, so non-global CSS attributes such the attribute, "border-radius" will not work, although they work on the Mozilla FireFox browser. Keep this in mind when you are writing your HTML for your browser interfaces. You should keep an open mind, and consider the possibility of a change in browsers.

You can also set the text to be displayed in its own window by setting the specific text parameters in browse()'s second argument. By giving the "window" parameter a value, the browser window will be displayed in a window. browse() will always send the text to what window you specify, so if you set the window variable of a browser to "hello", and send it again, the window will refresh instead of creating a new window. You can also specify the size of the window by modifying the "size" parameter. The following is an example of showing text with CSS inside its own small window.

mob/verb show_window_text() var/text = {" <html> <head> <style> body { background-color: '#000000'; color: '#ffffff' } </style> </head> <body> The text inside this window has a black background, with a white font colour. <br /> The text is inside a pop-up window, which is sized 500x300. </body> </html> "} src << browse(text, "window=hello;size=500x300")
Example 1.2: Displays a 500x300 sized browser window to the user using user defined CSS, exactly like an HTML document.

For more information about browse(), refer to the DM Reference.

Sending Data

The next core function you may use in your browser interfaces is browse_rsc(). This proc sends data from the game to a user's cache folder. The cache is used to store information that your browser forms that you may need. All forms sent via browse() is sent into the user's cache folder, which can allow you to grab any file in that folder whenever you need it. If you have a smiley face png icon, then you can send that file into the cache. The first argument for browse_rsc() is the data you are sending (which could be a file, or any image data). The second optional argument is the name of the output file that will end up in the user's cache. If the second argument is not set, then the name of the file (if applicable) is sent. The following example sends a smiley face icon in the form of a PNG file to the user's cache. The page displays the icon using the <img> tag.

mob/Login() ..() src << browse_rsc('icon.dmi', "smiley.png") var/html = {" <img src="smiley.png" width="32px" height="32px" /> This is smiley face. "} src << browse(html, "window=mywindow")
Example 1.3: This example uses browse_rsc() to send an icon in the form of a PNG file prior to sending a page that links to the image.

It is also possible to send icon data by using the icon() proc, and passing the return value of it through the first argument of browse_rsc(). Using this method, you can send dynamically generated icons to the user in a browser form. You must supply the second argument, otherwise you will not know the name of the file.

Remember that you always have to call browse_rsc() prior to using the data. Otherwise, your page will end up having broken images. If you plan on having the user open up a page with resources often, then you should send all of the images and data you will need when the user connects to your game. Sending data right before sending a form all the time may increase load times and use up unneeded bandwidth.

Section Review

  1. Write a program where the player uses a verb that sends them to an HTML page in the browser tab. The page should give the player links to the BYOND Game, Developer, and Member website.
  2. Write a welcome screen that cannot be closed or minimized by the player. It should stay on the screen for 30 seconds before disappearing, and letting the player continue on the game.
  3. Write a command that lets the player see the statistics of another player in a popup browser. This page should show the player's face icon (the player's icon, but with the icon_state of "face", name, health, and mana. It should be neatly organized in a table.

Communication

HTML alone will not be very useful unless all you want to do is display information. BYOND has a feature that lets players send data through a BYOND url. These urls always start with the byond:// protocol, following a ?. The data is then interpreted by client/Topic().

client/Topic() and datum/Topic()

Essentially, client/Topic() is the "hub" of all communication between HTML and BYOND. Every single BYOND link that is clicked goes through client/Topic(), and all of your communication in your browser interfaces will goes through there. When a player clicks a link with a BYOND protocol, client/Topic() will be called with its respective arguments set depending on what the link is. For example, if a player clicks the link, byond://?value=10, then the first argument of client/Topic() would be set to the text string "value=10", while the second argument would be set to a list that would contain "value"="10". The following example will display the value you have chosen in the verb. Additionally, the second argument for Topic() is a version of the first argument, except in list form. It is essentially the params2list() version of the first argument. Keep in mind that all values taken from the href_list are text strings, so you will have to do the appropriate conversions when you need to use the value.

mob/verb test_link(t as text) src << "Click this <a href=byond://?value=[t]>link</a>" client/Topic(href, list/href_list) var/value = href_list["value"] // get the association of the element, "value" src << "The value you have inputed is [value]."
Example 2.1: Lets the player use a verb and see what they typed out when clicking the link generated by the verb.

Keep in mind that if you ever use client/Topic(), you should be calling and returning the default return value all the time. DM's client/Topic() proc by default calls its respective datum/Topic() proc if the src parameter is set in the BYOND url that was clicked. Here is what client/Topic()'s implementation may look like:

client/Topic(href, list/href_list, datum/hsrc) // hsrc is the dereferenced object sent through the src parameter if(hsrc) hsrc.Topic(href, href_list)
Example 2.2: What client/Topic()'s implementation may look like.

You can set the reference by using the \ref text macro, which essentially converts an object to a text reference, and using locate() on the pointer will point to the object that was referenced using \ref. Here is another example using the same example above, but using mob/Topic() by setting the src parameter. Keep in mind that the value of the src parameter is not always the clicker of the link, it only calls its respective Topic() proc. The code examples in this article send the clicker of the link through the src parameter for simplicity. The \ref text macro will be explained later in this chapter.

mob Topic(href, list/href_list) var/value = href_list["value"] src << "The value you have inputed is [value]." verb test_link(t as text) src << "Click this <a href=byond://?src=\ref[src]&value=[t]>link</a>"
Example 2.3: A rewritten version of Example 2.1 using datum/Topic().

So what good are links in the Dream Seeker text box in a browser interface? Since browser interfaces are essentially made up of HTML, you can use the same links shown above inside a browser window. Since the link will call the Topic() proc, you can have browser-player communication from the HTML you input, and your appropriate Topic(). For example, you could create a browser page that will input you on what type of class your player wants to pick when they log into your RPG.

mob var/class Topic(href, list/href_list) var/action = href_list["action"] if(action == "classpick") var/newclass = href_list["value"] if(newclass) src << browse(null, "window=classpick") // close the window src.SelectClass(newclass) verb/choose_class() var/text = \ {" What class would you like to pick? <br /> - <a href=byond://?src=\ref[src]&action=classpick&value=Soldier>Soldier</a><br /> - <a href=byond://?src=\ref[src]&action=classpick&value=Mage>Mage</a><br /> - <a href=byond://?src=\ref[src]&action=classpick&value=Archer>Archer</a> "} src << browse(text, "window=classpick;size=500x300") proc/SelectClass(cls) src << "You are now a [cls]!" src.class = cls
Example 2.4: An example showing how you can create a simple class selection system using hyperlinks.

Sending References through a Link

A nifty feature that easily lets us send data is the \ref text macro. This text macro, as you have seen earlier when we modified what Topic() proc would get called, converts an actual reference to a text-string that holds information about what memory location the object you dereferenced is at. This is a great alternative to sending information about an object and searching all of the objects that are possibly the object you described.

To 'dereference' an object to a text string, you type out \ref, then an instance of an object in square brackets. (as done above) The text location is a hex code enclosed in square brackets, which could look like this: [0x3000000]. If you want to get the actual instance of the object that has been referenced, you use the locate() proc, sending the text reference through the parameter. Here is an example of a "player list" system where players can click the link of another player, and look at his or her statistics.

mob var health mana strength Login() ..() health = rand(50,100) mana = rand(50,100) strength = rand(10,20) Topic(href, list/href_list) ..() var/action = href_list["action"] var/value = href_list["value"] switch(action) if("look") // use locate() to get the reference of the text returned by \ref var/target = locate(value) // if it exists (player hasn't logged out after window refreshed), call the look_at() function if(target) src.look_at(target) if("close") // close the window when the player clicks the 'close' button src << browse(null, "window=playerlist") proc look_at(mob/M) src << "<b>[M]'s stats: </b>" src << "Health: [M.health]" src << "Mana: [M.mana]" src << "Strength: [M.strength]" verb list_players() var/page var/players for(var/client/C) var/mob/M = C.mob players += \ {" <tr> <td width="40%"> [M.name] </td> <td width="60%"> <a href="byond://?src=\ref[src]&action=look&value=\ref[M]"> Click to view statistics </a> </td> </tr> "} page = \ {" <html> <head> <title>Player List</title> </head> <body> <b>Players connected:</b> <table width="100%" border="1"> [players] </table> <a href="byond://?src=\ref[src]&action=close">\[Close Window\]</a> </body> </html> "} src << browse(page, "window=playerlist&size=500x300")
Example 2.5: The user is able to click the links of any player who is currently logged in the game, and see his or her statistics.

In this example, we can see a fairly simple method of displaying all of the players in the world in a neatly organized table which is generated in a separate text string. The program loops through all of the players, and references "M" using the \ref text macro. When the player clicks the link, the text is sent through Topic() and is dereferenced using locate(). We check if target is not a false value because browser windows tell what has happened at the time it was refreshed. The player may have logged out between the time you opened the player list and when you click the link that displays their stats. You should keep this in mind when creating browser interfaces, and the chapter on Data Verification will elaborate on this further.

Keep in mind that both ampersands (&) and semicolons (;) can be used to divide items in links. Both are considered to be correct. However, it is a good idea to keep consistent, and not use both interchangeably.

Section Review

  1. Write a private communication system where a user can privately talk to another user. When the user uses the verb, his or her name should be clickable as a link. The link should let the receiving user use the same private communication verb with the player's name supplied. The player should not be able to talk to his or herself. (Hint: you can use the \ref text macro to send the user's reference)
  2. Write an administration system that uses the browser for moderators to kick, mute, or ban a player. Moderators should not be able to kick themselves, and a link should not be made for moderators. A muted or banned player should not have the mute or banned link beside a player's name. Update the browser form when an action has been made on a player. The browser should be closed with a link. Do not worry about unmuting or unbanning players.
Stay tuned for Part Two: Controls and Forms, coming soon!