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.

No Worlds code is distributed here. The modification is a set of patches and new code that apply to a client you already have. Building it starts by decompiling your own copy. That is also what makes it checkable: every patch is published with the exact text before and after, so anyone can decompile their own client and confirm the "before" matches.

1. What you need

RequirementWhy
A WorldsPlayer installationyours; never modified, only read
JDK 8the 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.6the 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:

StepWhat it does
vineflower.jarDecompiles lib/worlds.jar. Fetched once, checksum verified.
fix-diamonds.pyRemoves Java 7 syntax the decompiler emits, which Java 6 cannot compile.
fix-artifacts.pyRepairs the handful of things javac rejects.
fix-redeclared.pyTurns duplicate declarations into assignments.
fix-unreachable.pyDeletes dead code left after a return.
apply-hooks.pyApplies the patches listed in tests/hook-sites.txt.
compile.shCompiles against a real JRE 6, targeting Java 6.
package.shRepackages worlds.jar.
install-runtime.pyInstalls 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.

The WorldsClient menu inside the running client
The WorldsClient menu, beside File on the client's own menu bar. Settings, mods, tools and reload — reachable while you are actually standing in a world, which is when most of it becomes useful.
The server selector in the WorldsClient settings window
The server selector. A list instead of hand-edited .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.

ModeBehaviour
Refuse everythingNo 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 timeYou get a prompt naming the host.
Original behaviourKept 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.

The chat encryption settings
Chat encryption. Everyone sharing the passphrase reads each other; the server and the network see ciphertext. It does nothing for your password, which is sent before any of this exists.
What it does not do. It does not protect your password — the protocol sends that in clear during login and no client change can alter it. It is not forward secret: anyone with the passphrase can read everything, including messages recorded earlier. It authenticates nothing, so anyone holding the passphrase can forge messages. It is a shared-secret channel among people who already trust each other, and people on unmodified clients will see the encoded text rather than words.

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 Mods tab of the WorldsClient settings window
The Mods tab. Install from a zip, then tick it yourself — a mod you have just installed is off, because copying a file onto your machine is not the same act as agreeing to run it.

The Tools tab

The debugging half. Everything here reads; nothing changes the world.

ButtonAnswers
Where am IYour name and position, the world, the room, how many objects are in it.
AvatarsThe state of the avatar filter, the content server in use, and everyone here with whether the client actually has geometry for them.
Room contentsWhat the room is made of, counted by kind, and how many objects have no geometry.
StateWhat the modification is currently doing: https, code policy, cache, mods, bots, every module and whether it is on.
LogThe end of worldsmod.log, where faults are explained.
Avatars is the button that saves the most time. When everybody in a room turns into the same figure it is almost never a broken avatar — it is the permitted-avatar list failing to fetch, and the client silently replacing everything. That has a state, and this reads it and says so in a sentence.

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

SymptomDo
Not sure if the modification is even activeTools → State. It lists everything and whether it is on.
A mod is brokenThe 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 backTick Disable every modification. No uninstall needed.
Assertion failure dialog while buildingSee the Shaper guide. The file and line in the dialog are the useful part.
A world's scripts do nothingExpected, if your code policy is set to refuse. Check the policy before assuming the world is broken.