ID:110131
 
Keywords: library, tutorial
Dakumonki recently asked a question about how to make an executable that shows a list of game servers, which people can join without installing BYOND or registering a key. I gave him the basics of it in his question post, but I figure it might be worthwhile to repost that information here.

Getting a list of public servers for your own game can be a trial for a lot of users, unless you know the secret tips and tricks of BYOND.

Although I mention it all the time, a lot of people haven't yet realised that most BYOND pages come with a text mode version, which allows you to read data very, very easily.

View the text mode of this page.

There was a forum post that explained the text format feature, but it was in the old Features forum that was dissolved when the feature tracker was added. It may be buried in the release notes, but I'm too lazy to find it. So I'll just summarise it here.

You can affix ;format=text to the end of the URL for any user pages and user hubs. This will give you a list of information in text format. If you attach &long=true to the end of it, more in-depth information will be given (screenshot links, medal information etc, blog posts). You can further expand it by adding &num_posts=# to get more posts about it.

Now the really cool thing about this seemingly unknown feature is that it displays text in a readable BYOND Savefile format. If you check out the comments in the savefile reference entry you'll see a comment explaining this. It's with these very features that we'll be able to grab a list of server information.

First and foremost, we'll be storing the server information in a datum. This, at least in my opinion, makes this all drastically easier to deal with.

Seeing as I don't have an active game of my own to test this stuff with, I'll use NEStalgia for my example.

Taking into account all the data there, we know we'll need the following fields to grab all the server information we want.
  • url
  • server_version
  • status
  • players
  • guests
  • host


Time to do some programmin'.

server
var
url = null
version = null
status = null
guests = 0
host = "Unknown"
list
players = new/list

New(url, version, status, guests, host, players)
src.url = url //
src.version = version // These three attributes are present no matter what their value.
src.players = players //
src.status = status ? status : null // (I think)
src.guests = guests ? guests : 0 // These three attributes are excluded if they have no value.
src.host = host ? host : "Unknown" //


To make life easier for ourselves, we'll be populating the datum info in datum/New(). Saves time and effort on our part. Now for actually grabbing the server information, we'll need a proc to grab the data and parse the info.

proc
t_get_servers(hub)
if(!hub) CRASH("No hub supplied for t_get_servers()")

var/http[] = world.Export("http://www.byond.com/hub/[hub];format=text")

if(!http || http["CONTENT-TYPE"] != "text/plain; charset=ISO-8859-1")
world.log << "ERROR: Hub \"[hub]\" doesn't exist or BYOND Hub is currently unavailable."
return FALSE

. = new/list // All error checks have passed, so now we can set the default return to a list.

var/savefile/server = new
server.ImportText("/", file2text(http["CONTENT"]))
server.cd = "/world"

for(var/i in server.dir)
server.cd = "/world/[i]"
var/server/S = new(server["url"], server["server_version"], server["status"], server["guests"], server["host"], server["players"])
. += S


Because this proc uses world.Export(), it's not a very good idea to use it a lot. As it'll hang your world until it's completed it's task. There are ways to get around this with spawn(), but I'll leave it to those commenting to explain how and why.

-- Extra Reading.

What the scripts above do is simple, one is a world/proc which grabs a list of server info from the BYOND hub and stuffs the different server info it gathers into a separate datum and returns a list for the user to use as they see fit. This is really, all that needs to be done.

But some out there are pedantic about this kind of thing, and may want or require more data from the hub (list of medals, list of posts about the game, all that sort of thing). I've no intentions of writing a full library to provide this kind of functionality today (I'm waaay too tired for that). But what I will do is open the door for others now, and finish it off in my own time later on. To achieve this, we're going to want another datum to accompany our server datum, we'll name it "hub".

Ideally, all the separated data would have it's own datum. One for medals, one for servers, one for posts and one for general information. This kind of info I'll expand upon in my next post. For now, I'll just settle with general information and add the rest in later.

If we take a look at the long format of the NEStalgia page, we'll see the different fields we want:
  • type
  • title
  • path
  • short_desc
  • long_desc
  • author
  • version
  • banner
  • icon
  • small_icon
  • single_player
  • multi_player
  • video

Arguably we'd also want the waiting list in there (personally, the waiting list should be part of /general and I honestly have no idea why it isn't). But I digress. It's also assumable that we already know the type. But to assume is to make a mockery of fact, so we wont assume what anyone will be using this library for.

hub
var
h_type = null // Type is an existing read-only variable, attempting to redefine it will cause a compiler error.
title = null
path = null
short_desc = null
long_desc = null
author = null
version = null
banner = null
icon = null
small_icon = null
single_player = FALSE
multi_player = FALSE
video = null
list
servers = new/list


Now to populate the hub datum itself. Unlike the server datum where we just plugged the data into New(), there are, in my opinion, too many variables to do this effectively. So we'll resort to trickery and espionage to achieve this result. Simply meaning, we'll loop through the datum variables (which, with exception to type, have the same name as the data from the website) and if they match, apply the value of the website var to the datum var, like so:

hub
var
h_type = null // Type is an existing read-only variable, attempting to redefine it will cause a compiler error.
title = null
path = null
short_desc = null
long_desc = null
author = null
version = null
banner = null
icon = null
small_icon = null
single_player = FALSE
multi_player = FALSE
video = null
list
servers = new/list

New(hub)
if(!hub) CRASH("No hub supplied for new/hub")

var/http[] = world.Export("http://www.byond.com/hub/[hub];format=text&long=true")

if(!http || http["CONTENT-TYPE"] != "text/plain; charset=ISO-8859-1")
world.log << "ERROR: Hub \"[hub]\" doesn't exist or BYOND Hub is currently unavailable."
return FALSE

var/savefile/info = new
info.ImportText("/", file2text(http["CONTENT"]))
info.cd = "/general"

// Populate the above variables.
for(var/i in info.dir)
if(i == "type") src.h_type = info["type"]
else
if(i in src.vars) src.vars[i] = info[i]

// Populate servers list.
info.cd = "/world"
for(var/i in info.dir)
info.cd = "/world/[i]"
var/server/S = new(info["url"], info["server_version"], info["status"], info["guests"], info["host"], info["players"])
src.servers += S


And there you have it. That'll give a full list of server information, and the general hub info. As you can see from my example, adding in the rest of the stuff would be pretty easy, I'll get around to doing so at a later date.

The library for this article can be found: Here.

If you have any comments on how to improve all this, I'll be happy to hear them.
Really nice post. I can see it being useful for plenty've peeps.
Further thinking has me theorising that /hub and /server could be combined into a single datum, with /server being a child of /hub. But I'm not sure if that's entirely necessary. It may just complicate things for some people.
Great post :)
I guess I'll go ahead and play devil's advocate here.

How exactly is this going to help someone who wants: an executable that shows a list of game servers, which people can join without installing BYOND or registering a key.

You still need BYOND in the first place in order to take advantage of this.
The EXE that BYOND publishes doesn't require BYOND to be installed. The EXE would point to a central server containing the game servers, and would then link() them to it.
SuperAntx wrote:
I guess I'll go ahead and play devil's advocate here.

You're quite welcome to do so.

How exactly is this going to help someone who wants: an executable that shows a list of game servers, which people can join without installing BYOND or registering a key.

I'll release a demo for that part at a later date. Tomorrow after work maybe. But anyone who already knows how to use the executable feature of BYOND but doesn't know about the text format or even that the text format is a human readable BYOND savefile, will be able to use what they have learned here and apply it.

You still need BYOND in the first place in order to take advantage of this.

See above. And who says that's the only application a library like this could have? Back story again, my good sir.
Murrawhip wrote:
The EXE that BYOND publishes doesn't require BYOND to be installed. The EXE would point to a central server containing the game servers, and would then link() them to it.

A central server wouldn't be necessary, as all the public server info is listed on the hub and grabbed by the above library.

All one would really need to do is grab the info with the library. Parse it into an interface and link() them with the supplied BYOND address.

To make it into an executable. Simply download the zipped BYOND release, place it in the compiled project named "byond", and use the "Make EXE" function (which by default uses the version of BYOND in the "byond" folder instead of what's installed (or not installed) on their system) and supply the exe with the DMB and RSC.
This is an illuminating post; I see there is also an accompanying feature request to provide more information and we can do that. I just wanted to add an aside here:

While it's true that we do provide mechanisms to 1) conveniently access site info and 2) distribute BYOND games without installation, we don't intend for developers to make this the norm for their games. The reason is that we WANT people to visit our site, both because it provides some revenue for us (through ads & membership) and also possibly gains contributing participants to the community (which could use all the help it can get!) The main purpose of these systems is to supplement the normal process, not bypass it.

Carry on!
Tom wrote:
This is an illuminating post; I see there is also an accompanying feature request to provide more information and we can do that. I just wanted to add an aside here:

Not so much a feature request as a notation. Though having these sorts of things mentioned in some official manor might help others to understand. My explanations lack in details.

While it's true that we do provide mechanisms to 1) conveniently access site info and 2) distribute BYOND games without installation, we don't intend for developers to make this the norm for their games. The reason is that we WANT people to visit our site, both because it provides some revenue for us (through ads & membership) and also possibly gains contributing participants to the community (which could use all the help it can get!) The main purpose of these systems is to supplement the normal process, not bypass it.

I'd consider the stuff I speak of above to be supplemental not bypassing. A decent mechanism to get the game some exposure is what these things create. At the end of the day, people are still going to wonder just what the hell that "byond" folder is (anyone who doesn't know will consider it a typo, that alone would make them curious). People will find out. Though normally I don't leave the research up to them. ;)

Though if it doesn't already, you could always release a readme with the engine that gives information like that.

Carry on!

That I shall.
Powered by BYOND- http://donyokuna.com/

But my idea for it has sunken, and I've decided to make everything here, since no matter what happens to BYOND, it'd effect me also.

That's why I was asking you about what "if", my plans would be ruined if it did, and I always kept you guys in the picture. But yeah, if you guys go down no one would be able to connect, the purpose of the site was to get away from the negativity with a group of people, and not be limited to what others say, that are childish, and there not afraid to show it.

Sometimes you guys remind me of rookie cops, pointing your guns at the wrong guy.
Fixing the community issues requires you to not do that however.

The effect is simple: You bring say, 4000 new people to BYOND. Those 4000 new people dilute the community as it is, and although the trolling and negativity will continue, it'd get more and more buried under the activity of new users. ;)
Tiberath wrote:
Fixing the community issues requires you to not do that however.

The effect is simple: You bring say, 4000 new people to BYOND. Those 4000 new people dilute the community as it is, and although the trolling and negativity will continue, it'd get more and more buried under the activity of new users. ;)


_> If I get those 4000 people, they won't help the community at all, but just play my projects, like the thousands that haven't made a post since they joined.

There all hiding on the games they enjoy, something I'd do when I complete one. .-.
Looks awesome.