Gateway (PackGateway proxy plugin + PackGatewayBackend)¶
Checks every player's modpack version at the proxy and sends them to the right first server:
modded players to the modded hub, unmodded players to the info hub, and outdated packs to an
update screen. Its main half can send brand-new players to a tutorial spot with a Pokégear.
What it does¶
One jar holds two plugins. The same build is installed in two places:
| Part | Runs on | Live file name | Plugin name |
|---|---|---|---|
| Proxy plugin | Velocity | plugins/GatewayVelocity-<version>.jar |
PackGateway (id packgateway) |
| Backend plugin | main |
plugins/PackGatewayBackend-<version>.jar |
PackGatewayBackend |
Proxy part (PackGateway):
- When a player logs in, asks their client for its PackGateway version over the login channel
packgateway:version_check. It also reads the NeoForge mod list as a backup. - If the pack is too old, disconnects them with an "update your modpack" screen that has a clickable CurseForge download link (and an optional Discord link).
- Picks the first server for every connection, so players never fall through to Velocity's
trylist: - Modded client with a good pack:
modded-hub-server(hub). - Unmodded client:
vanilla-info-server(info). - Brand-new modded player, when first-join routing is on:
new-player-main-server(main), with a one-time tutorial marker. - If a player is kicked from the modded hub, sends them to the info hub with "You need the modpack to play on this server!"
- Only speaks when it ends a connection. It does not message players who are connected.
Backend part (PackGatewayBackend, on main):
- When a player arrives with the tutorial marker, waits half a second, then teleports them to the saved tutorial spot and gives them a Pokégear item.
- A natural first join also needs Bukkit to agree the player has never played on
main. A staff test (see below) skips that check. - Right-clicking the Pokégear runs
/pokegear(the Poke Gear menu). Only the tagged item does this. Plain Camping Gear is not affected.
How players use it¶
Players do not run any commands.
- With the right modpack, they land on the modded hub as normal.
- Without the modpack, they land on the info hub.
- With an old pack, they see "Modpack Update Required", their version, the required version, and
an
[Open Modpack Download]button. - Brand-new players (if first-join routing is on) start on
mainat the tutorial spot, holding a Pokégear. Right-click it to open the menu. It is a normal item: it can be dropped or lost on death. Staff can give a new one.
Staff guide¶
Proxy commands (run in-game on any server, or in the Velocity console). Alias /pgateway.
| Task | Command | Permission |
|---|---|---|
| Reload the proxy config | /packgateway reload |
packgateway.reload |
| Queue a one-time first-join test for a player | /packgateway firstjoin reset <player-or-uuid> |
packgateway.firstjoin.reset |
| Cancel a queued test | /packgateway firstjoin cancel <player-or-uuid> |
packgateway.firstjoin.reset |
A queued test sends that player through the new-player path once, on their next login, then clears itself. It never removes them from the real join history. The target must be online or already in the join history.
These are proxy permissions, so grant them with lpv (LuckPerms on Velocity), not lp. Staff
ranks do not have them by default. The proxy console always works.
Backend commands (on main). All need packgateway.firstjoin.set (op by default).
| Task | Command |
|---|---|
| Save the tutorial spot where you stand, facing the way you face | /firstjoin set |
| Show the saved tutorial spot | /firstjoin show |
| Teleport yourself to the tutorial spot | /firstjoin test |
| Give yourself a Pokégear | /firstjoin gear |
| Give a player a Pokégear | /firstjoin gear Steve |
/firstjoin set takes effect at once. Command replies show in the action bar. Test the Pokégear
as a non-op, because ops skip CommandWhitelist.
Configuration¶
Proxy: plugins/packgateway/config.yml on Velocity. /packgateway reload re-reads it. If the
file fails to parse, the proxy refuses new logins with "PackGateway is not configured correctly"
until it is fixed and reloaded.
| Key | What it is for |
|---|---|
current-version |
Lowest accepted pack version. "1.1" also accepts 1.1.0, 1.1.5, and so on |
allow-newer-versions |
Keep true. current-version is bumped only for breaking pack changes, so it is usually behind the shipped pack. false would kick everyone on a newer pack |
modded-hub-server, vanilla-info-server |
Velocity server names for modded and unmodded players |
version-mod-ids |
Mod id that carries the pack version. Must be packgateway |
routing-enabled |
Kill switch. false hands all routing back to velocity.toml. Use only to triage a broken gate |
ignored-hosts |
Hostnames the gate leaves alone, so Velocity's [forced-hosts] picks the server. Exact match only, no wildcards. A wrong entry silently turns off the gate for real players. Must be a top-level key |
outdated-message-* |
Text for the update screen. {required_version} and {client_version} work. -link-line and -discord-line are URLs, not text; https:// is added if missing |
first-join-routing-enabled |
Turns on the new-player route to main. Default false |
first-join-history-file |
NetworkJoinMessages' joined.txt (needs FirstJoinStorageType: TEXT) |
protected-existing-users-file |
Frozen list of known players, always treated as returning |
first-join-history-minimum-records |
If the history file has fewer rows than this, first-join routing turns itself off |
new-player-main-server |
Where new players go (main) |
The download link must be the CurseForge project page, not the website, and it must match the
client mod's own error screen link in
scripts/update_staging_mods/modpack-overrides/config/packgateway-client.properties (ops repo).
Old keys scanned-hosts and host-filter-enabled are ignored and log a warning.
Backend: plugins/PackGatewayBackend/tutorial-location.properties on main holds the
tutorial spot (world UUID, world name, x, y, z, yaw, pitch). Change it with /firstjoin set, not
by hand. Hand edits need a restart.
A new jar on either side needs a restart of that server. When rolling out a first-join change,
restart main first, check its boot, then restart Velocity.
Data and storage¶
| File | Where | What it holds |
|---|---|---|
config.yml |
Velocity plugins/packgateway/ |
Proxy settings |
first-join-test-overrides.txt |
Velocity plugins/packgateway/ |
Queued one-time tests (UUID and name). Safe to edit while no test is wanted |
protected-existing-users.txt |
Velocity plugins/packgateway/ |
Frozen list of returning players. Do not delete: a missing or broken file turns first-join routing off |
joined.txt |
Velocity plugins/networkjoinmessages/ |
Owned by NetworkJoinMessages. Read only |
tutorial-location.properties |
main plugins/PackGatewayBackend/ |
Tutorial spot |
The tutorial marker travels in the forwarded login profile as property packgateway_first_join.
The proxy removes it after the first server connection, so it never carries over to a later server
switch. The Pokégear is tagged with the item data key packgatewaybackend:pokegear.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
Modded players land on info, or get kicked and crash |
The gate is not seeing the client's version | Check the Velocity log for Sent version query and responded to version query. Check routing-enabled and ignored-hosts |
| Everyone with the current pack is kicked as outdated | allow-newer-versions is false, or current-version is too high |
Set allow-newer-versions: true and reload |
| "PackGateway is not configured correctly" | Proxy config.yml did not parse |
Read Could not read config.yml in the Velocity log. A common cause is a nested ignored-hosts key |
| "Modded server is unavailable" | modded-hub-server or new-player-main-server is not in velocity.toml |
Fix the name |
New players go to hub instead of main |
First-join routing off, or history unavailable | Look for First-join registries are unavailable. Check the history and protected files exist and pass the minimum row count |
New player reached main but was not teleported |
Tutorial spot not set, or they had played on main before |
/firstjoin show. Look for Ignored natural first-join marker |
| No Pokégear given | pixelmon:camping_gear did not resolve |
Look for did not resolve in the main boot log |
| Right-click does nothing | Item is not the tagged one, or /pokegear is blocked |
Give a fresh one with /firstjoin gear; test as a non-op |
Good boot lines:
Velocity: PackGateway plugin loaded. (then a config summary, including "Routing enabled: true")
main: Pokégear item: pixelmon:camping_gear -> PIXELMON_CAMPING_GEAR
main: First-join tutorial location: world <x> <y> <z> yaw=... pitch=...
main: Reading PackGateway first-join markers from the Velocity-forwarded profile.
Maintainer notes¶
- The repo is named GatewayVelocity, but the Velocity plugin id is
packgateway. The proxy part must not use Bukkit APIs, and the backend part must not use Velocity APIs. Shared code is incom.snowbird.gateway.common. velocity-plugin.jsonis generated from the@Pluginannotation. Keep the annotation version andbuild.gradleversion in sync.plugin.ymltakes its version from Gradle.- Host matching strips the FML null-byte marker and anything after a TCPShield
///, then compares exactly. Earlier versions used an allow-list (scanned-hosts) that missed the live domain and turned the gate off. 1.1.0 switched to the deny-listignored-hosts. - The login query is the main signal because TCPShield can drop the FML host marker. The mod-list check is a backup.
- If a natural first-join handoff lands on the wrong server, the proxy queues a one-time test for that player so the tutorial still happens on their next login.
- Older ops notes say the backend runs
/warp tutorial. Since 1.2.2 it uses its own saved spot instead, seeded from that warp. - Deploy history and bundles:
manual_uploads/*packgateway*andmanual_uploads/2026-09-14-first-join-*in the ops repo. Server notes:documentation/servers/velocity.md.
Building¶
./gradlew clean test build
Java 21. Output: build/libs/GatewayVelocity-<version>.jar. Install that one file on Velocity as
GatewayVelocity-<version>.jar and on main as PackGatewayBackend-<version>.jar. Twelve unit
tests cover the signal codec, join history, test overrides, and the location store.
Full command, permission and config tables: Reference.