Releases / v0.88.0

v0.88.0 Hold The Line

2026-09-10 Engine

A host whose connection blips for a moment no longer ends everybody's match, and a lobby code can now survive a server restart. Plus a way to see where your bandwidth is actually going.

Your match survives a moment of bad connection

Two players fought over a relay for ten minutes. Three times, the host's connection dropped for a few seconds — and each time the lobby was destroyed and the other player was quietly cut off. For them, everything in the world vanished except the sky and the HUD, and nothing said why.

A lobby now outlives its host's connection by twenty seconds. The players stay in it, and a host that comes back is put straight back into the match it was already in, with the same people. A rebinding router or a Wi-Fi hiccup costs a stutter instead of the game.

If the host really is gone, the lobby still ends and everybody is told what happened — with a sentence that names it rather than a bare disconnect.

If you host a relay yourself, its log now names lobbies and says why each one ended, instead of printing a count. That count was the reason the problem above went unnoticed for so long: it could not say which lobby died, or what killed it.

A lobby code can outlive the server behind it

floptle serve --lobby-code ABCDEF asks to reclaim a code rather than be handed a new one. Managed servers get this automatically. It means a server that restarts, or reconnects to a relay that restarted, comes back on the same six characters your players already wrote down.

A relay only honours the request when the code genuinely belongs to that game, so asking for somebody else's does nothing but get you an ordinary code.

Joining a server that is still starting up

net.joinState() gained "starting" in the last release. It is now wired through: a join to a server that is still coming up waits instead of failing, the engine keeps trying on its own, and the second return carries something you can put on screen.

lua
local state, why = net.joinState()
if state == "starting" then
  find("Status").text = why          -- "about 30 seconds"
end

Give up sooner if you want to: net.join(addr, {timeout = 30}). The default is 90 seconds, after which it becomes a normal refusal. This only ever applies to a server that is waking — an ordinary join is answered immediately, as before.

See where your bandwidth goes

lua
for _, r in ipairs(net.traffic()) do
  log(r.kind .. ": " .. r.count .. " msgs, " .. r.bytes .. " bytes")
end

net.traffic() breaks down what your game has sent by message kind, biggest first, and resets each time you read it — so calling it once a second shows you the last second.

It is worth a look if you have a fast-paced multiplayer game. A rollback match should be almost all Input, at a few bytes per player per frame. A large Snapshot row means full state is going out where inputs would do, which is the single biggest thing you can fix about a game's bandwidth.

Measuring a relay you run

New alongside the relay: floptle-relay-bench, which drives synthetic players through a relay and reports what one player costs, where packet loss starts, and the round-trip time your players would feel. See relay-bench.md.

Also

  • floptle serve documents --lobby-code alongside the flags added last release.
  • A dedicated server now reports the player cap it is enforcing, so a hosting dashboard can show the real number rather than the one it asked for.

Upgrading

Nothing to do. If you run a region box, take the new fleet agent and relay — the match-survival fix is in the relay.

Floptle Hub

Linux x86_64 7.5 MB Download → sha256 7006de198aca9db82163f851a3dd4b8fa5ac40037b0846a6842fe56b2d949182
macOS Apple Silicon 5.7 MB Download → sha256 201e37c7911d51279ca5d1f989ba607134456e9adba2d5deabdfb80c0a3e1b25
macOS Intel 6.2 MB Download → sha256 83ff324f0513b91c8cbbbbe4a79e8b0df2254947c941e8be0755dc795b0f1c30
Windows x86_64 7.1 MB Download → sha256 d5b654a74568b50debef3de9fadf03ddeb1e9a99c2dee1461327200da4c89a86

Engine

Linux x86_64 33.5 MB Download → sha256 6c4143cf06bd2a6e7808ead719522847039e04eba51259a0933ca6b4955b5aa4
macOS Apple Silicon 27.2 MB Download → sha256 452e936d011c1ff22676f380db67a34bec4c81cd5cdbd41a1ada34315aaedfb9
macOS Intel 29.3 MB Download → sha256 cfb9e14821577c20d10697fc4435878282a6a974579bcf4287db28919e878782
web 4.2 MB Download → sha256 6740b227926325006ad6050a6bddc69338584577bfff5e27440c03cda49ebe81
Windows x86_64 33.1 MB Download → sha256 df5db57644c09a33a864eea4977ff656ac6b666e8805ac2491c36d544aac643b

The binaries are not signed yet. On macOS, right-click the app and pick Open the first time. On Windows, if SmartScreen appears, pick More info then Run anyway.