Inside the client

The WorldsPlayer's own code: how it is built, which parts matter, and where to look when something behaves oddly. This is the map you want before decompiling your own copy.

Three layers

WorldsPlayer.exe — 87 KB finds a JVM, calls JNI_CreateJavaVM, starts the class named in the .ini contains none of the client lib/worlds.jar — 1.6 MB, 719 classes UI, chat, friends, world logic, the Shaper — and ALL of the networking the protocol lives here, in plain Java bytecode native DLLs — 368 native methods RenderWare 2.1 (rendering) · Granny3D (animation) · gamma.dll (the JNI bridge) 32-bit Windows only — this is why it needs Wine Of those 368 native methods, exactly 7 touch networking — DNS, Windows DDE, and launching the updater. None is on the protocol path.
Three layers. The one that matters is the middle one: the entire wire protocol is ordinary Java bytecode, so nothing about it is hidden inside the native engine.

There is a common assumption that is wrong: "the .exe is just a launcher, so everything is in the .jar". There are three layers, and the difference between them explains almost every odd behaviour the client has.

The launcher

WorldsPlayer.exe is 87 KB and contains none of the client. It locates a JVM, creates it with JNI_CreateJavaVM, and starts the class named in WorldsPlayer.ini:

[Java Runtime Environment]
Main Class=NET.worlds.console.Gamma
JVM Source=favor_JRE
Minimum Version=1.6
Virtual Machine Parameters=-Xint -esa -Xms64M -Xmx512M

Two of those flags are worth knowing. -esa enables assertions for the system classes, so a malformed packet fails loudly. -Xint turns the JIT off — the client is slower but deterministic, which is a fair trade when you are debugging it.

The jar

lib/worlds.jar is 1.6 MB, 719 classes, four packages:

PackageClassesWhat is in it
NET/worlds/network/84The whole protocol. 17,396 lines. Everything on this site derives from here.
NET/worlds/scape/420The 3D side: worlds, avatars, objects, WorldScripts.
NET/worlds/console/194The interface: chat, friends, lists, dialogs, the login wizard.
NET/worlds/core/21Utilities: the .ini reader, the table manager, assorted helpers.

The native engine

The Java code declares 368 native methods, bound at runtime by gamma.dll. Underneath sit RenderWare 2.1 by Criterion for rendering and Granny3D by RAD Game Tools for skeletal animation — both commercial middleware of the era, both 32-bit Windows.

gamma.dll exports no Java_NET_worlds_* symbols: it registers its natives dynamically, carrying the class-name strings inside itself.

Why the protocol could be read completely

Look at where those 368 native methods actually are:

PackageNative methodsCovering
scape/195Rendering: shapes, surfaces, textures, transforms, animation
console/111Windows, dialogs, the embedded browser control
core/55Fast I/O, the Windows registry, the .ini reader
network/7DNS, Windows DDE, launching the updater
None of those seven is on the protocol path. Packet framing, object ids, properties, all thirty-one commands, the entire handshake — every byte of it is ordinary Java bytecode. Nothing about the wire format is hidden in native code.

The same fact is why the client cannot run on a Linux JVM. However much of its logic is Java, it must load 32-bit Windows DLLs through JNI, so Wine with a 32-bit runtime is the only option and always will be.

The classes that matter

If you decompile the jar, these are the ones to open first. Line counts are from one build and will vary slightly.

FileWhy it matters
network/WorldServer.java
~1,900 lines
The state machine. The heart of the client's networking. If you read one file, read this one.
network/netConst.javaEvery constant in the protocol, in one place.
network/netPacketReader.javaPacket framing. Shows exactly how the length byte is handled.
network/ServerInputStream.java
ServerOutputStream.java
Serialisation both ways. This is where Worlds' non-standard UTF lives.
network/ObjID.javaShort and long ids, and the rules about which is which.
network/PropertyList.java
netProperty.java, net2Property.java
The two property formats, and the code that discards a whole list on one bad byte.
network/*Cmd.javaOne file per protocol command. Easy reading.
network/Galaxy.java
~1,200 lines
The session and the set of servers it knows about.
scape/Pilot.javaYour own avatar. Sends your position.
scape/Drone.javaEveryone else's avatars, and how they get loaded.
scape/PosableShape.javaThe avatar filter. This is the class that turns everyone into the default figure when the permitted list cannot be fetched.
console/Console.javaChat colours, and where the script server address is looked up.
core/ServerTableManager.javaThe tables.dat format and its crypto.

Three behaviours the code explains

Why an unknown property is safe

When the client receives the server's property list it stores the whole thing, then asks for the ids it knows by number:

public void propertyUpdate(PropertyList propList) {
   this._propList = propList;
   net2Property protocol = this._propList.getProperty(3);
   ...
   net2Property http = this._propList.getProperty(24);
   ...
}

There is no loop over the entries, no switch and no default case. A property it has never heard of is stored and never looked at again — which is the whole reason a capability announcement can ride along there without any client noticing. It also exposes what it kept, through getProperty(int), so a modified client can read the announcement without a single patch.

The reply to SESSINIT is the one place that does loop, with a switch whose default prints a line and asserts. That assert only fires when assertions are enabled for the client's own classes, and the launcher passes -esa, which enables them for the system classes only. So it prints one line and carries on — but that is why an announcement belongs in the property list and not there.

Why everyone turns into the same avatar

PosableShape.getPermitted checks each avatar against a list the client fetches from the script server. When that fetch fails, the check falls through and every avatar that is not a plain .mov is silently replaced with the default figure. No error, no message.

A single 404 on that list is therefore the reason "third-party avatars don't work" — and because it happens to everyone in the room at once, it looks like a server bug rather than a missing file.

Why remote avatars freeze or fly off

The update interval travels in microseconds and the client divides by a thousand. A server that sends milliseconds by mistake leaves the client dividing by zero, and every remote avatar stops or launches. The modified client floors the interval; a server should also just send the right unit.

WorldScripts, and the hole in them

A WorldScript is a Java class that makes a world do things. The client downloads WorldScript*.class from whatever address is currently set as the content server and hands it to defineClass — no signature, no sandbox, no prompt.

Those classes run with the full rights of the person playing. This is not a bug in one build; it is how the feature was designed, and it means whoever controls a world's content host has code execution on every visitor. The full explanation, and what to do about it.

Worth knowing: WorldScripts often fail to load anyway, because of a classloader bug in the original client. Failing and falling back to the internal copy is the normal path, not an error.

Data files beside the client

FileWhat it holds
worlds.ini / override.iniConfiguration. Two files, different sections, read by different code. See the client guide.
universe/universe.datThe universe map.
actions.datEmote and action definitions. A data file, so the gestures are editable.
tables/tables.datLookup tables, lightly encrypted. Includes the inventory table the protocol never uses.
avatars/ + avatars.zipThe built-in avatars.
gamma.worldsmarksBookmarks, in the world-URL format.
Gamma.LogThe log. Set debug=2048 and it records every packet in hex.

Reading it yourself

Decompile lib/worlds.jar from your own installation. Vineflower handles it; the output needs a few mechanical repairs before it will recompile, and WorldsClient has a script for each of them.

Start at network/WorldServer.java and follow the commands out from there. Everything on the protocol page came from that file and its neighbours.