Releases: luibara2/endlink
Release list
Endlink v0.3.0
A backend can now be a Minecraft: Java server, reached through a
Geyser instance the proxy treats as an ordinary Bedrock backend. Bedrock
players are sent to it alongside the Bedrock ones and keep their identity, their permissions and
their player data. Install
EndlinkGuard's geyser/ build into that Geyser's
extensions/ folder; nothing in Endlink's config needs setting.
Moving to or from a Java backend reconnects the player to the proxy rather than handing them
over, and that cannot be worked around. A Bedrock client reads its block-id scheme from the
StartGame it logs in with and can never be told otherwise while it is playing: Bedrock servers hash
block ids from the block state, Geyser numbers them by palette position. A seamless handoff across
that boundary delivers chunks the client cannot decode, and the player stands in an empty or
scrambled world with nothing in any log to explain it.
So Endlink learns each backend's scheme from its first StartGame, remembers it, and moves players
across that boundary by transferring them to its own address, putting them where they asked.
They never leave the proxy - same listener, same verification, same permissions, and backends stay
unreachable from outside. They see a loading screen. Switches between backends of the same kind are
untouched and stay seamless.
Also fixed
- A palette-indexed backend's block registry is left alone. The cross-backend palette was
merging every other backend's custom blocks into it and zeroing its checksum. Where block ids are
palette indices that renumbers the world out from under the client - wrong blocks everywhere,
unknown blocks past the end - and clearing the checksum removed the client's own mismatch check,
so it corrupted the world silently instead of failing cleanly. It was also pushing such a
backend's blocks into every other backend's registry. - Sub-chunk requests are withheld from a backend that does not implement them. A client taught
to request terrain that way by a Bedrock backend goes on doing it after a switch, and Geyser
treats those requests as a protocol violation and drops the player mid-game. Inferred
automatically;backend.<name>.dropSubChunkRequestsremains for a backend nobody has visited yet.
New setting
publicAddress - the address players are sent back to when a move needs a reconnect. Empty uses
the address each player connected with, which is right for almost every install.
Tested live: a network of two Bedrock backends and two Java ones, switching in every direction,
with the player's real IP reaching the Java server.
Endlink v0.2.0
Custom content — items, entities, blocks and the resource packs behind them — now survives a backend
switch. Before this release it did not, and no amount of correct pack configuration fixed it.
Bedrock reads a client's resource pack stack, item registry, entity identifier list and custom block
definitions once, at level init. A seamless switch deliberately does not re-run level init, so a
client kept whatever the first backend it joined had told it: another backend's custom items drew
arbitrary vanilla textures, and its custom entities and blocks were invisible — solid and clickable,
but not drawn.
Cross-backend registries
Every client is now given the combined item and entity registries of all backends at login, and each
backend's own network ids are translated to and from that union as packets pass through. The
translation is free per packet: it rides on the codec's existing decode step rather than rewriting
item ids packet by packet.
Custom blocks need no id translation — a modern backend hashes a block's runtime id from its state,
so the number is the same everywhere — but they do need their definitions, which are merged in the
same way. A backend that numbers blocks by palette order instead of hashing them is reported, since
that case cannot be corrected by a proxy.
Each backend's registries are learned the first time anyone visits it and cached in
cache/backend-palettes.nbt, so later logins account for backends nobody has been to this run. After
a backend's addons change, the first player to go there sees the old registry until they rejoin; that
is logged, not silent.
Controlled by crossBackendPalette, on by default.
Resource packs
- Unpacked pack folders are loaded, not only
.mcpackfiles. A folder holdingmanifest.jsonis
zipped in memory at startup, reproducibly, so an unchanged folder keeps its hash across restarts
and clients do not redownload it. - Backend packs are cached and served by the proxy (
resourcePacks.cacheBackendPacks). The proxy
keeps a copy of the bytes it sees a client download, and downloads a switched-to backend's packs
itself — nobody else ever asks that backend for them. Later logins include them, which is what
makes a switched-to backend's content render. Newest version wins; a pack placed in
resourcePacks.dirby hand wins a tie. Encrypted packs cannot be cached and are named in the log.
Joining
security.rateLimit.packetLimit now defaults to 500 rather than RakNet's 120. A single Bedrock
login is a burst of MTU-sized fragments carrying the identity chain and skin; over loopback or a LAN
they land inside one 10ms tick and exceed 120. That blocked the address for ten seconds, stalled the
handshake into disconnect.timeout, and repeated on every retry — a player in that state could not
join at all. A blocked address now also names itself and the setting that blocked it in the log.
Resource pack chunks are 100KB rather than 1MB, so the acknowledgements a client sends back while
downloading stay inside that budget.
Upgrading
Drop in the new jar. No config changes are required — the new settings default to on, and an existing
config.properties keeps working. Two things worth doing:
- If your
config.propertiessetssecurity.rateLimit.packetLimit=120, raise it; the new default
only applies to newly generated configs. - The caches start empty, so the first visit to each backend is what teaches the proxy. Visit each
backend once and rejoin, and everything is in place from then on.
Endlink v0.1.1
Security fix release — updating from v0.1.0 is recommended.
Security
- Fixed a pre-auth denial-of-service (zlib decompression bomb). The zlib decompression
size cap was not being enforced, so an unauthenticated client could send a small compressed
batch that inflated without bound and exhausted the proxy's memory before logging in. The
cap (bedrock.maxDecompressedBytes, default 10 MB) is now enforced on the zlib path, matching
the Snappy path, and is covered by a regression test. Reported through responsible disclosure.
Other changes since v0.1.0
- ViaEndlink (the Java Edition addon) is now linked from the sibling projects, marked beta.
- Fuller packet diagnostics: resource-pack response status and item-stack requests/responses
are now traceable.
Endlink.jar is a drop-in replacement for v0.1.0.
Endlink v0.1.0
First tagged build of Endlink.
Work in progress. Tested and working for a Minecraft 1.26.40 client against a 1.26.40 backend — a player joins, plays, and switches between backends. Other version pairings are less certain, and the older-version translation chain is incomplete.
Install
-
Drop
Endlink.jaron the machine that should face players and run it:java -jar Endlink.jarIt writes a documented
config.propertiesand aplugins/folder on first start, then exits ready to configure. -
Set your backends and
backendVerification.sharedSecretinconfig.properties. -
Install EndlinkGuard on every backend and give it the same shared secret:
https://github.com/luibara2/endlinkguardBackends sit in offline mode behind the proxy so it can own authentication. Without EndlinkGuard they are open to anyone who learns their address.
Requirements
- Java 21 or newer
- Endstone backends (recommended), each running EndlinkGuard
Apache 2.0. Built from source in this tag; gradle build reproduces it.