mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 15:09:18 -07:00
[2026] split 2026 to tachy hosts
This commit is contained in:
+10
-1
@@ -159,6 +159,14 @@ RECFLARE_DOMAIN=rec.example.com
|
|||||||
# out. A sandbox account on that build still gets the sandbox pool. Unset, rollout players
|
# out. A sandbox account on that build still gets the sandbox pool. Unset, rollout players
|
||||||
# use the pool above.
|
# use the pool above.
|
||||||
# RECFLARE_TACHYON_HOST_PORT_ROLLOUT=198.51.100.13:7777
|
# RECFLARE_TACHYON_HOST_PORT_ROLLOUT=198.51.100.13:7777
|
||||||
|
#
|
||||||
|
# WHICH builds are served that rollout pool, when it should not simply follow the sign-in
|
||||||
|
# gate: same format as RECFLARE_ROLLOUT_GAME_VERSIONS (builds, commas; `off` for none;
|
||||||
|
# `20260323` and `20260323.14` name the same build). Unset, the rollout pool goes to the
|
||||||
|
# builds under staged rollout, so ending the rollout (ROLLOUT_GAME_VERSIONS=off) also moves
|
||||||
|
# those players onto the live pool. Set this to keep the 2026 build on its own voice
|
||||||
|
# servers after it opens to everyone — the 2025 and 2023 builds stay on the pool above.
|
||||||
|
# RECFLARE_TACHYON_ROLLOUT_GAME_VERSIONS=20260323
|
||||||
|
|
||||||
# The Photon region every session is pinned to (`match`). Unlike the app ids above this
|
# The Photon region every session is pinned to (`match`). Unlike the app ids above this
|
||||||
# does default, to `us` (us-east1) — an instance stamped with an empty region is one the
|
# does default, to `us` (us-east1) — an instance stamped with an empty region is one the
|
||||||
@@ -245,7 +253,8 @@ RECFLARE_DOMAIN=rec.example.com
|
|||||||
# made to upgrade. A build in ROLLOUT is served too, whether or not SUPPORTED names it, but
|
# made to upgrade. A build in ROLLOUT is served too, whether or not SUPPORTED names it, but
|
||||||
# only to staff and the Discord roles below. The lifecycle these exist for:
|
# only to staff and the Discord roles below. The lifecycle these exist for:
|
||||||
# 1. Rollout (both unset): the 2026 build is for staff and supporters only.
|
# 1. Rollout (both unset): the 2026 build is for staff and supporters only.
|
||||||
# 2. General availability: ROLLOUT_GAME_VERSIONS=off — the 2026 build opens to everyone.
|
# 2. General availability: ROLLOUT_GAME_VERSIONS=off — the 2026 build opens to everyone
|
||||||
|
# (and joins the live voice pool unless TACHYON_ROLLOUT_GAME_VERSIONS above keeps it).
|
||||||
# 3. Forced upgrade: SUPPORTED_GAME_VERSIONS=20230414,20260323 (with `off` above) — the
|
# 3. Forced upgrade: SUPPORTED_GAME_VERSIONS=20230414,20260323 (with `off` above) — the
|
||||||
# 2025 build is refused and has to update.
|
# 2025 build is refused and has to update.
|
||||||
# To pull a rollout back instead, list neither: SUPPORTED_GAME_VERSIONS=20230414,20250718
|
# To pull a rollout back instead, list neither: SUPPORTED_GAME_VERSIONS=20230414,20250718
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ vars (`RECFLARE_*` in `.env`, reaching every worker) override them — read thro
|
|||||||
that gates on "is this build served / under rollout" (`auth` tokens, `api` versioncheck,
|
that gates on "is this build served / under rollout" (`auth` tokens, `api` versioncheck,
|
||||||
`match` rollout voice pool). A build outside the supported set is told to update by `api`
|
`match` rollout voice pool). A build outside the supported set is told to update by `api`
|
||||||
AND refused a token by `auth`; a rollout build is always served, but gated to staff and
|
AND refused a token by `auth`; a rollout build is always served, but gated to staff and
|
||||||
Discord roles until `ROLLOUT_GAME_VERSIONS=off`.
|
Discord roles until `ROLLOUT_GAME_VERSIONS=off`. The rollout VOICE pool
|
||||||
|
(`TACHYON_HOST_PORT_ROLLOUT`) follows that list by default but `TACHYON_ROLLOUT_GAME_VERSIONS`
|
||||||
|
overrides it (`tachyonRolloutGameBuilds` in `match`), so a build can stay on its own Tachyon
|
||||||
|
servers after its rollout ends.
|
||||||
|
|
||||||
- `20230414` — default, official. Manifest `7859140924515540835` (2023).
|
- `20230414` — default, official. Manifest `7859140924515540835` (2023).
|
||||||
- `20250718` — beta, official (the `20250718.01` client). Manifest `1151455856673601091`;
|
- `20250718` — beta, official (the `20250718.01` client). Manifest `1151455856673601091`;
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ The table is the default. An operator narrows it with `RECFLARE_SUPPORTED_GAME_V
|
|||||||
in `.env` (a build left out is told to update and refused a sign-in, which is how players
|
in `.env` (a build left out is told to update and refused a sign-in, which is how players
|
||||||
are moved to a newer build), and `RECFLARE_ROLLOUT_GAME_VERSIONS` names the builds under
|
are moved to a newer build), and `RECFLARE_ROLLOUT_GAME_VERSIONS` names the builds under
|
||||||
staged rollout to Discord supporters — `20260323` by default, `off` to open it to
|
staged rollout to Discord supporters — `20260323` by default, `off` to open it to
|
||||||
everyone. See `.env.example`.
|
everyone (`RECFLARE_TACHYON_ROLLOUT_GAME_VERSIONS` keeps a build on the rollout voice
|
||||||
|
servers past that). See `.env.example`.
|
||||||
|
|
||||||
Generally speaking any client that effectively rewrites the nameserver with the
|
Generally speaking any client that effectively rewrites the nameserver with the
|
||||||
right mods (see [RecNet Plugin](https://github.com/djdevin/recnet-plugin), [2025 patch](https://github.com/recflare/patch-2025)) can be used with this server.
|
right mods (see [RecNet Plugin](https://github.com/djdevin/recnet-plugin), [2025 patch](https://github.com/recflare/patch-2025)) can be used with this server.
|
||||||
|
|||||||
@@ -87,11 +87,23 @@ export type Env = SharedHonoEnv & {
|
|||||||
* The builds under staged rollout, overriding `ROLLOUT_GAME_VERSIONS` in @repo/domain:
|
* The builds under staged rollout, overriding `ROLLOUT_GAME_VERSIONS` in @repo/domain:
|
||||||
* a comma-separated list of builds, or `off` for none. Set for every worker from the
|
* a comma-separated list of builds, or `off` for none. Set for every worker from the
|
||||||
* root `.env` (`RECFLARE_ROLLOUT_GAME_VERSIONS`), so this agrees with the gate `auth`
|
* root `.env` (`RECFLARE_ROLLOUT_GAME_VERSIONS`), so this agrees with the gate `auth`
|
||||||
* applies by construction. Read through `rolloutGameBuilds`; decides who is served the
|
* applies by construction. Read through `rolloutGameBuilds`; the DEFAULT for which
|
||||||
* rollout Tachyon pool above. Ending the rollout (`off`) moves those callers to the
|
* builds are served the rollout Tachyon pool above (see `TACHYON_ROLLOUT_GAME_VERSIONS`),
|
||||||
* live pool.
|
* so ending the rollout (`off`) moves those callers to the live pool unless that var
|
||||||
|
* keeps them.
|
||||||
*/
|
*/
|
||||||
ROLLOUT_GAME_VERSIONS?: string
|
ROLLOUT_GAME_VERSIONS?: string
|
||||||
|
/**
|
||||||
|
* The builds served the rollout Tachyon pool, decoupled from the sign-in gate
|
||||||
|
* (`RECFLARE_TACHYON_ROLLOUT_GAME_VERSIONS` in `.env`): the same comma-separated list of
|
||||||
|
* builds, or `off` for none. Unset means `ROLLOUT_GAME_VERSIONS` decides, as above. Set,
|
||||||
|
* it is the only thing that does — so a build opened to everyone (`ROLLOUT_GAME_VERSIONS=off`)
|
||||||
|
* can stay on its own voice servers by naming it here. Matched as a BUILD: a listed
|
||||||
|
* `20260323` (or `20260323.14` — the point release is stripped) covers every point
|
||||||
|
* release of it, the way every other version gate compares. Read through
|
||||||
|
* `tachyonRolloutGameBuilds` in match.app.ts.
|
||||||
|
*/
|
||||||
|
TACHYON_ROLLOUT_GAME_VERSIONS?: string
|
||||||
/**
|
/**
|
||||||
* The Photon region every session is pinned to — both the region named in the connection
|
* The Photon region every session is pinned to — both the region named in the connection
|
||||||
* info and the one stamped on every room instance, which must agree. Optional; unlike the
|
* info and the one stamped on every room instance, which must agree. Optional; unlike the
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import {
|
|||||||
MatchmakingErrorCode,
|
MatchmakingErrorCode,
|
||||||
MessageType,
|
MessageType,
|
||||||
MOST_ACTIVE_CLUBHOUSE_LIMIT,
|
MOST_ACTIVE_CLUBHOUSE_LIMIT,
|
||||||
|
parseGameBuilds,
|
||||||
putPlayerSettingsIfChanged,
|
putPlayerSettingsIfChanged,
|
||||||
readPlayerSettings,
|
readPlayerSettings,
|
||||||
recordRoomVisit,
|
recordRoomVisit,
|
||||||
@@ -254,10 +255,24 @@ const ROLLOUT_TACHYON_SERVER_ID_PREFIX = 'rollout-'
|
|||||||
interface TachyonCaller {
|
interface TachyonCaller {
|
||||||
/** The account's `isSandbox` flag. */
|
/** The account's `isSandbox` flag. */
|
||||||
isSandbox: boolean
|
isSandbox: boolean
|
||||||
/** Running a build under staged rollout (`rolloutGameBuilds`, off the token's `rn.ver`). */
|
/** Running a build served the rollout pool (`tachyonRolloutGameBuilds`, off the token's `rn.ver`). */
|
||||||
isRollout: boolean
|
isRollout: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The builds served the rollout Tachyon pool: the operator's `TACHYON_ROLLOUT_GAME_VERSIONS`
|
||||||
|
* when set (`off` for none), otherwise the builds under staged rollout
|
||||||
|
* ({@link rolloutGameBuilds}, the same list `auth` gates sign-in on). The override exists
|
||||||
|
* because the two lists part ways at general availability: `ROLLOUT_GAME_VERSIONS=off`
|
||||||
|
* opens the new build to everyone, and without this it would also move every player on it
|
||||||
|
* onto the live voice servers. Entries are reduced to builds by {@link parseGameBuilds},
|
||||||
|
* so `20260323.14` lists the same build as `20260323`, and the caller's version is
|
||||||
|
* compared the same way by {@link isRolloutGameVersion}.
|
||||||
|
*/
|
||||||
|
function tachyonRolloutGameBuilds(env: Env): string[] {
|
||||||
|
return parseGameBuilds(env.TACHYON_ROLLOUT_GAME_VERSIONS) ?? rolloutGameBuilds(env)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pool the caller's instance is picked out of — one of three, each the same
|
* The pool the caller's instance is picked out of — one of three, each the same
|
||||||
* comma-separated list, with selection inside it the same instance-id round-robin
|
* comma-separated list, with selection inside it the same instance-id round-robin
|
||||||
@@ -265,10 +280,12 @@ interface TachyonCaller {
|
|||||||
*
|
*
|
||||||
* - `TACHYON_HOST_PORT_SANDBOX` for a SANDBOX account, which runs against its own servers
|
* - `TACHYON_HOST_PORT_SANDBOX` for a SANDBOX account, which runs against its own servers
|
||||||
* apart from the live pool;
|
* apart from the live pool;
|
||||||
* - `TACHYON_HOST_PORT_ROLLOUT` for a caller on a build under staged rollout — the build
|
* - `TACHYON_HOST_PORT_ROLLOUT` for a caller on a build in {@link tachyonRolloutGameBuilds}
|
||||||
* `auth` only lets Discord supporters sign in on, which gets its own voice servers while
|
* — by default the build under staged rollout, which `auth` only lets Discord supporters
|
||||||
* it rolls out. Sessions are already split by build ({@link callerGameVersion}), so a
|
* sign in on and which gets its own voice servers while it rolls out;
|
||||||
* rollout instance's players are all on that build and all land here together;
|
* `TACHYON_ROLLOUT_GAME_VERSIONS` keeps a build there after the gate opens. Sessions are
|
||||||
|
* already split by build ({@link callerGameVersion}), so a rollout instance's players
|
||||||
|
* are all on that build and all land here together;
|
||||||
* - `TACHYON_HOST_PORT`, the live pool, for everyone else.
|
* - `TACHYON_HOST_PORT`, the live pool, for everyone else.
|
||||||
*
|
*
|
||||||
* Sandbox wins when both apply: a sandbox account on the rollout build is still a sandbox
|
* Sandbox wins when both apply: a sandbox account on the rollout build is still a sandbox
|
||||||
@@ -3217,8 +3234,10 @@ const app = new Hono<App>()
|
|||||||
'…). Both are empty when the pool is unset or the caller is in no instance. A',
|
'…). Both are empty when the pool is unset or the caller is in no instance. A',
|
||||||
'sandbox account is picked out of the `TACHYON_HOST_PORT_SANDBOX` pool instead, the',
|
'sandbox account is picked out of the `TACHYON_HOST_PORT_SANDBOX` pool instead, the',
|
||||||
'same way, with ids `dev-1`, `dev-2`, …; a caller whose token’s `rn.ver` is a build',
|
'same way, with ids `dev-1`, `dev-2`, …; a caller whose token’s `rn.ver` is a build',
|
||||||
'under staged rollout (`ROLLOUT_GAME_VERSIONS`, by default `20260323`) out of',
|
'under staged rollout (`ROLLOUT_GAME_VERSIONS`, by default `20260323`; or whatever',
|
||||||
'`TACHYON_HOST_PORT_ROLLOUT`, with ids `rollout-1`, `rollout-2`, …. Sandbox wins when',
|
'`TACHYON_ROLLOUT_GAME_VERSIONS` names instead, so a build can keep its own voice',
|
||||||
|
'servers after its rollout ends) out of `TACHYON_HOST_PORT_ROLLOUT`, with ids',
|
||||||
|
'`rollout-1`, `rollout-2`, …. Sandbox wins when',
|
||||||
'both apply, and either special pool left unset falls back to the live one.',
|
'both apply, and either special pool left unset falls back to the live one.',
|
||||||
'`experiments` carries the client’s networking flags.',
|
'`experiments` carries the client’s networking flags.',
|
||||||
].join(' '),
|
].join(' '),
|
||||||
@@ -3261,11 +3280,11 @@ const app = new Hono<App>()
|
|||||||
const account = await getAccount(c.env.DB, id)
|
const account = await getAccount(c.env.DB, id)
|
||||||
// The instance's server, the same one every other player in it is handed — except
|
// The instance's server, the same one every other player in it is handed — except
|
||||||
// that a SANDBOX account is picked out of the sandbox pool, and a caller on a
|
// that a SANDBOX account is picked out of the sandbox pool, and a caller on a
|
||||||
// rollout build out of the rollout pool, instead of the live one.
|
// rollout-pool build out of the rollout pool, instead of the live one.
|
||||||
const tachyon = tachyonServerFor(
|
const tachyon = tachyonServerFor(
|
||||||
tachyonPoolFor(c.env, {
|
tachyonPoolFor(c.env, {
|
||||||
isSandbox: account?.isSandbox === true,
|
isSandbox: account?.isSandbox === true,
|
||||||
isRollout: isRolloutGameVersion(await callerVersion(c), rolloutGameBuilds(c.env)),
|
isRollout: isRolloutGameVersion(await callerVersion(c), tachyonRolloutGameBuilds(c.env)),
|
||||||
}),
|
}),
|
||||||
roomInstanceId
|
roomInstanceId
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1815,8 +1815,10 @@ describe('auth-gated endpoints', () => {
|
|||||||
const originalSandbox = env.TACHYON_HOST_PORT_SANDBOX
|
const originalSandbox = env.TACHYON_HOST_PORT_SANDBOX
|
||||||
const originalRollout = env.TACHYON_HOST_PORT_ROLLOUT
|
const originalRollout = env.TACHYON_HOST_PORT_ROLLOUT
|
||||||
const originalRolloutVersions = env.ROLLOUT_GAME_VERSIONS
|
const originalRolloutVersions = env.ROLLOUT_GAME_VERSIONS
|
||||||
|
const originalTachyonRolloutVersions = env.TACHYON_ROLLOUT_GAME_VERSIONS
|
||||||
try {
|
try {
|
||||||
env.TACHYON_HOST_PORT = '198.51.100.12:7777'
|
env.TACHYON_HOST_PORT = '198.51.100.12:7777'
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = undefined
|
||||||
env.TACHYON_HOST_PORT_SANDBOX = '203.0.113.30:7777, 203.0.113.30:7778'
|
env.TACHYON_HOST_PORT_SANDBOX = '203.0.113.30:7777, 203.0.113.30:7778'
|
||||||
|
|
||||||
const voiceFor = async (player: string, roomInstanceId?: number) => {
|
const voiceFor = async (player: string, roomInstanceId?: number) => {
|
||||||
@@ -1928,6 +1930,50 @@ describe('auth-gated endpoints', () => {
|
|||||||
voiceConnectionInfo: '198.51.100.12:7777',
|
voiceConnectionInfo: '198.51.100.12:7777',
|
||||||
voiceServerId: 'tachyon-1',
|
voiceServerId: 'tachyon-1',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TACHYON_ROLLOUT_GAME_VERSIONS decouples the pool from the gate: set, it alone
|
||||||
|
// decides which builds get the rollout pool, so a build opened to everyone
|
||||||
|
// (ROLLOUT_GAME_VERSIONS=off) keeps its own voice servers while the 2025 build
|
||||||
|
// stays live. Matched as a build — a listed point release covers every point
|
||||||
|
// release of it, and a token carrying the bare build matches a listed one.
|
||||||
|
env.ROLLOUT_GAME_VERSIONS = 'off'
|
||||||
|
const keptRollout = await createRoomInstance(env.DB, {
|
||||||
|
ownerAccountId: 976,
|
||||||
|
roomId: 2,
|
||||||
|
photonRoomId: 'tachyon-rollout-kept',
|
||||||
|
maxCapacity: 12,
|
||||||
|
})
|
||||||
|
for (const listed of ['20260323', '20260323.14', ' 20260323.2 ,20231207']) {
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = listed
|
||||||
|
for (const version of ['20260323', '20260323.14', '20260323.99']) {
|
||||||
|
expect(await rolloutVoiceFor('976', version, keptRollout.roomInstanceId)).toEqual(
|
||||||
|
rolloutPool[keptRollout.roomInstanceId % rolloutPool.length]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expect(await rolloutVoiceFor('976', '20250718.01', keptRollout.roomInstanceId)).toEqual({
|
||||||
|
voiceConnectionInfo: '198.51.100.12:7777',
|
||||||
|
voiceServerId: 'tachyon-1',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Sandbox still wins over it.
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = '20260323'
|
||||||
|
expect(await rolloutVoiceFor('974', '20260323.14', keptRollout.roomInstanceId)).toEqual(
|
||||||
|
sandboxPool[keptRollout.roomInstanceId % sandboxPool.length]
|
||||||
|
)
|
||||||
|
// Set, it overrides the gate's list in BOTH directions: `off` sends a build still
|
||||||
|
// under rollout to the live pool.
|
||||||
|
env.ROLLOUT_GAME_VERSIONS = '20260323'
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = 'off'
|
||||||
|
expect(await rolloutVoiceFor('976', '20260323.14', keptRollout.roomInstanceId)).toEqual({
|
||||||
|
voiceConnectionInfo: '198.51.100.12:7777',
|
||||||
|
voiceServerId: 'tachyon-1',
|
||||||
|
})
|
||||||
|
// Blank is unset: the gate's list decides again.
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = ''
|
||||||
|
expect(await rolloutVoiceFor('976', '20260323.14', keptRollout.roomInstanceId)).toEqual(
|
||||||
|
rolloutPool[keptRollout.roomInstanceId % rolloutPool.length]
|
||||||
|
)
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = undefined
|
||||||
env.ROLLOUT_GAME_VERSIONS = originalRolloutVersions
|
env.ROLLOUT_GAME_VERSIONS = originalRolloutVersions
|
||||||
|
|
||||||
// With no rollout pool configured, a rollout build falls back to the live one, not
|
// With no rollout pool configured, a rollout build falls back to the live one, not
|
||||||
@@ -1961,6 +2007,7 @@ describe('auth-gated endpoints', () => {
|
|||||||
env.TACHYON_HOST_PORT_SANDBOX = originalSandbox
|
env.TACHYON_HOST_PORT_SANDBOX = originalSandbox
|
||||||
env.TACHYON_HOST_PORT_ROLLOUT = originalRollout
|
env.TACHYON_HOST_PORT_ROLLOUT = originalRollout
|
||||||
env.ROLLOUT_GAME_VERSIONS = originalRolloutVersions
|
env.ROLLOUT_GAME_VERSIONS = originalRolloutVersions
|
||||||
|
env.TACHYON_ROLLOUT_GAME_VERSIONS = originalTachyonRolloutVersions
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user