The modified client
Building WorldsClient from your own copy of the WorldsPlayer, and then using it. Everything in the client guide still applies — this is what is different.
1. What you need
| Requirement | Why |
|---|---|
| A WorldsPlayer installation | yours; never modified, only read |
| JDK 8 | the newest javac that can still target Java 6, which is what the client is |
| Java 17+ | to run the decompiler |
| Bottles, with a bottle holding a 32-bit JRE 1.6 | the client is a Java program that loads a 32-bit native renderer |
tools/compile.sh tells you which of these it cannot find, and
where it looked. Start there if something is missing.
2. Building it
./tools/build-release.sh # compile, package, refresh client/
./tools/build-release.sh --install DIR # also copy the build into a prefix
./tools/build-release.sh --dist # make a zip to hand to someone else
./tools/selftest.sh # the checks that need no client
client/WorldsPlayer.exe # play
Under that one command, this is what happens. Every step is its own script, so the whole thing can be redone against a newer jar:
| Step | What it does |
|---|---|
vineflower.jar | Decompiles lib/worlds.jar. Fetched once, checksum verified. |
fix-diamonds.py | Removes Java 7 syntax the decompiler emits, which Java 6 cannot compile. |
fix-artifacts.py | Repairs the handful of things javac rejects. |
fix-redeclared.py | Turns duplicate declarations into assignments. |
fix-unreachable.py | Deletes dead code left after a return. |
apply-hooks.py | Applies the patches listed in tests/hook-sites.txt. |
compile.sh | Compiles against a real JRE 6, targeting Java 6. |
package.sh | Repackages worlds.jar. |
install-runtime.py | Installs into the bottle. |
Your installation is never written to. The launcher builds a
runtime directory inside the bottle's drive_c, symlinking
everything from the installation and copying in the modified jar. The two
directories the client writes into — its download cache and
tables — are made real directories rather than links, so
the client's own writes cannot travel back through them into your original
copy.
Your configuration is kept. The installer edits your
worlds.ini rather than generating a new one, and changes only the
server addresses and the dead upgrade check. Your avatar, your VIP flags, your
installed worlds and every server section you already had are left alone
— and the server selector is seeded from the servers your client already
knew about.
3. The settings window
It opens before the client starts. That is deliberate: the
choice of server can only take effect before the client exists. Once you are
running, there is also a WorldsClient menu on the client's own
menu bar, beside File, with Settings..., Mods...,
Tools... and Reload mods — because most of what
is in the window, the Tools tab especially, can only tell you anything once
you are actually in a world.
.ini files. Picking a server sets its world, content and script addresses together, because they have to travel as a unit.The first tab
Servers, and the master switch. At the bottom is a checkbox, Disable every modification. With it ticked the client behaves as the stock one, because every hook asks whether the modification is disabled before doing anything.
That checkbox is the first thing to know about this software. It means you can always get back to the original behaviour without uninstalling anything, and it means the modification has to justify itself rather than being something you are stuck with.
The server selector
A list of servers instead of hand-editing .ini files. Picking one
sets three things together: the world server, the content server and the
script server. That grouping matters — asking your own server for
another server's avatars returns nothing, and the client then quietly
substitutes the default figure. Servers travel as a unit for that reason.
Downloaded code policy
This is the security fix. Unmodified, the client hands
WorldScript*.class files it downloaded straight to
defineClass, and they run with your full rights.
Full explanation here.
| Mode | Behaviour |
|---|---|
| Refuse everything | No downloaded code runs, ever. Some worlds lose scripted behaviour. |
| Trusted hosts only (default) | The server you deliberately connected to counts as trusted. Anything else must be allowed on purpose. |
| Ask each time | You get a prompt naming the host. |
| Original behaviour | Kept for compatibility, and labelled for what it is. |
Chat encryption
Optional. People sharing a passphrase can read each other; the server and the network see ciphertext. It travels as ordinary chat, so it passes through any server, including one that knows nothing about it.
Mods
Install from a zip, tick to run. A mod you have just installed is off — copying a file onto your machine is not the same act as agreeing to run it. Two buttons write working examples to disk. Full guide to writing them.
Two more checkboxes live here, both off by default: letting mods reach the web, and letting mods log bots into a server. Each is a thing that should not follow from unzipping a file.
The Tools tab
The debugging half. Everything here reads; nothing changes the world.
| Button | Answers |
|---|---|
| Where am I | Your name and position, the world, the room, how many objects are in it. |
| Avatars | The state of the avatar filter, the content server in use, and everyone here with whether the client actually has geometry for them. |
| Room contents | What the room is made of, counted by kind, and how many objects have no geometry. |
| State | What the modification is currently doing: https, code policy, cache, mods, bots, every module and whether it is on. |
| Log | The end of worldsmod.log, where faults are explained. |
Below the buttons is a line of Lua, run against the live client with the same interface a mod gets. It is the fastest way to try one call:
return worlds.me().name
for i, p in ipairs(worlds.people()) do print(p.name) end
4. Protocol modules
A protocol module answers one question: given an address the client is about to fetch, what should it fetch instead? Installed like a mod, from a zip, in the Modules tab.
function rewrite(url)
if url:sub(1, 20) == 'http://www.3dcd.com/' then
return 'http://mirror.example/' .. url:sub(21)
end
return nil -- none of my business
end
Return the address to fetch instead, or nil. With every module off
or returning nil, the client fetches exactly as it always did.
This is how you point dead hosts at mirrors without patching hundreds of world
files.
The honest limit: a module rewrites an address, it does not fetch anything. So it can put an existing transport in front of an address that would otherwise fail — a mirror, a redirect, an https host in place of a dead http one — but it cannot invent a transport the client has no code for.
5. What to do when it misbehaves
| Symptom | Do |
|---|---|
| Not sure if the modification is even active | Tools → State. It lists everything and whether it is on. |
| A mod is broken | The Mods tab names the mod and the line. So does worldsmod.log. A mod that throws is stopped rather than left half running. |
| Something changed and you want the stock client back | Tick Disable every modification. No uninstall needed. |
| Assertion failure dialog while building | See the Shaper guide. The file and line in the dialog are the useful part. |
| A world's scripts do nothing | Expected, if your code policy is set to refuse. Check the policy before assuming the world is broken. |