Where I left off
Recap for anyone just landing. I’m rebuilding the server for Fury, a 2007 online game switched off in 2008. I have the program that ran on players’ PCs (the client). I do not have the program that ran in the data centre (the server). The plan is to read the client’s code closely enough to build a matching server from scratch.
Last post I got the decompiler, the tool that turns the game’s compiled code back into something a person can read, to handle a handful of real Fury functions. I spot-checked ten of them by hand. Eight came out clean. I called that a win and stopped for the night.
That number was hiding something.
What I tried
I did not want to pull functions out one at a time forever. I wanted the whole
thing on disk: every class in every file, decompiled, in a folder I could grep.
So I wrote a batch runner. Point it at a .u file, it walks every class inside,
decompiles each one to its own .uc text file, and writes a list of anything
that failed.
Fury’s code ships as fourteen of these .u files. I ran the batch over the
small ones first. ServerFramework, GameFramework, Core, the animation
file, the AI file. It just worked. Hundreds of classes, full source, almost no
failures. That felt great for about ten minutes.
Then I ran it on GOGame.u, the big one, and paid attention to one class in
particular: GORealmMasterLink.
That class is the client’s phone line to the master server, the machine that ran matchmaking, chat, guilds, who’s-online, and shoved you from the social hub into an actual match. If there is one class in this entire game I need to read perfectly, it is that one.
150 functions in it. 111 of them crashed the decompiler.
What broke
Every one of the 111 failed the exact same way:
| |
“UnknownObject.” The tool read something, didn’t recognise what it was, filed it under “no idea,” and then a moment later tried to use it as a specific thing and threw its hands up.
Last time a crash like this meant the tool was off by a few bytes and reading junk. So I checked the byte positions against a function that did work. They lined up. Name here, parent there, first variable right where it should be. Not a misalignment this time.
So what was the “unknown” thing? I dumped the list of variables for one of the broken functions:
| |
AvatarIDProperty. And on other broken functions, GroupIDProperty,
GuildIDProperty, TeamIDProperty.
Auran gave themselves custom variable types.
Here’s the idea. In most code, a player ID and a team ID are both just numbers,
plain integers, and nothing stops you accidentally passing one where the other
belongs. Auran didn’t want that. So they made AvatarID its own type,
TeamID its own type, and so on. Under the hood each one is still just an
integer. But the compiler now treats them as different things and slaps your hand
if you mix them up. It’s a genuinely tidy bit of engineering and I’d bet it
caught real bugs.
The decompiler, of course, has never heard of AvatarID. It knows int, it
knows string, it knows the standard set. It hits AvatarIDProperty, goes
“unknown,” and dies.
The fix is almost too small to write about. I told the tool: when you see any of
these four Fury type names, treat it exactly like an int. Four lines.
Rebuilt. Re-ran the batch on GORealmMasterLink.
150 functions, 0 failures.

From 111 crashes to none, with four lines that say “this is basically an int.”
While I was at it the batch turned up one more unknown type, double, sitting in
the engine’s core file (a double is just a number with a decimal point that can
be very precise). Same shape of fix, one more small class.
The other thing: everything was shouting numbers at me
With the classes decompiling, the contents still read badly. Every function call looked like this:
| |
__NFUN_231__ is the tool saying “there’s a call to built-in function number
231 here and I don’t know its name.” 231 is Log. 168 is the $ you use to
glue two bits of text together. 201 is a find-and-replace. The tool knew a call
was happening, knew the arguments, just printed the number instead of the name.
Reason: Fury’s code is spread across fourteen files, and the names of all the
built-in operations live in a couple of those files (mostly Core and
Engine). When you decompile GOGame.u on its own, the tool never opens the
other files, so it’s got the calls but not the dictionary.
So I taught it to peek. Before it decompiles anything, it now does a quick pass
over all the sibling .u files sitting in the same folder, collects every
built-in name it can find, and keeps that list handy. Same function as before,
after the change:
| |
That’s just readable now. Repl is replace, $ and @ glue text together,
LogInternal writes a log line. Real code.
The payoff
I ran the batch over all fourteen files. About 2,000 classes decompiled to disk. The failures that remain are all the same cosmetic thing (more on that below), not missing logic.
And the first useful thing fell out almost immediately. This is from
GOACGame, the mode the client runs during character creation and login:
| |
When a game client connects to a server it sends a connect string, basically a URL with settings tacked on the end. This is the client pulling five specific values out of that string: the login server address, an account name, a password, a “realm update” web address, and a “reserved name.”
That is a chunk of the shape of “how a Fury client asks to join.” Named fields, straight out of the shipped code. When I get to standing up a fake server, this is one of the things it will have to understand. Now I know what to call them.
And once the whole thing is on disk you can just grep it, which is where the afternoon went. Two things jumped out.
One. Every time the server talks to its database, it does it the same way:
call a stored procedure by name. A stored procedure is a canned operation that
lives inside the database itself, so the game code never writes raw database
queries, it just says “run the one called this.” Grepping for those calls gave
me a list of 56 names, and the names are not shy about what they do:
AVA_GetAvatarByName, AVA_AvatarTravel, EQU_GenerateLootItem,
LDR_LogWarzoneAvatarStatistics, SVR_BanPlayerByAvatarName,
GLD_GetGuildByName. That’s a partial menu of everything the old database was
asked to do. I don’t have the database, but I have the list of questions it was
expected to answer.
Two. I found the bouncer. When a match server decides whether to let you in, here is the actual check, cleaned up:
| |
So before you ever connect, the master server tells the match server “expect a
player with this session key.” That goes into a short list with a timer on it.
You connect, you present the key, the match server finds you in the list and
lets you in, or doesn’t and kicks you with WRONGSID. It’s a coat check. The
key is your ticket.
(There’s a wrinkle: in this exact build that check looks like it’s switched off at the code level, and it might really live down in the C++ I can’t read yet. Wrote it down as an open question. Not going to pretend I’m sure.)
What I learned
“Eight out of ten” was a hand-picked sample and it lied to me. I chose those ten functions. The honest version of that test is: dump everything, then read the failure log. The failure log is where the truth is. The class I’d have cared about most was 74% broken and my ten-function sample sailed right past it.
And the pattern with these licensee games held for the fifth time. Every single change Auran made to the engine’s format has been small, local, and boring: one extra field here, four new type names there. Nothing sweeping. Nothing that needed a big rethink. Five surprises now, five one-liners.
Honest bit: it’s not spotless. Around seventy classes, almost all of them
graphics and UI stuff (particle effects, sound nodes, menu widgets), come out
with their defaultproperties block cut short. That’s the section that lists a
class’s default settings. There’s a size mismatch in how Fury writes one kind of
setting that I haven’t cracked yet. It does not touch a single line of actual
logic, and none of those classes are server code, so I’m parking it with a note
and moving on.
Next up
Start actually reading the server’s job. The login and “travel” path first:
what the client sends to get in, what the master server was expected to send
back, where a match server picks the player up. I have GORealmMasterLink in
full now, and the whole codebase sitting in a folder. Time to read it.