What's done & what's coming.
The live development roadmap of the whole Garden ecosystem β plugin, website, Discord bot. Checked boxes are already on the server.
Garden β CS2 Modding Ecosystem Roadmap
Purpose of this file: single source of truth for any human or AI model resuming work on this project. If you are an AI picking this up with no other context: read this file top to bottom, then read the README/Docs of the repo you are about to touch. Update this file whenever a phase lands.
Owner: Evan (pro.evan.dev@gmail.com) β private retakes server "Garden Retakes" (retakes.fr).
Everything lives in sibling folders under CS2 Mod Dev/.
1. Ecosystem overview (current state β all SHIPPED and working)
| Repo | What it is | Stack |
|---|---|---|
Garden-allocator | Fork of yonilerner/cs2-retakes-allocator. Weapon/utility allocator, heavily customized. | CounterStrikeSharp (C#, net8.0) |
Garden-rankings | Built from scratch. Seasons, ELO (Premier-like, start 5000), HLTV-like per-round rating, Ranked Retakes (auto β₯4 humans), Competitive Retakes (/cr, 2v2/3v3 MR12), clutch rounds, mode cvar profiles, AFK handling, per-round raw stats. | CounterStrikeSharp + EF Core (MySQL/SQLite) |
Garden-website | Ladder, HLTV-style player pages, /compare, CR team ladder, seasons, inventory simulator (per-side loadouts, knives/gloves, 2D sticker placement, Steam OpenID). Deployed on Vercel. | Next.js 14 + Prisma 6 + Aiven MySQL |
Garden-inventory | Fork of ianlucas/cs2-inventory-simulator-plugin. Added css_loadout <name> (switch active loadout via website API), URL sanitizing. | CounterStrikeSharp |
Garden-discord | NEW (Phase D) Discord bot: live presence + auto-updating status embed; future stats commands. | Node.js + discord.js + gamedig + mysql2 |
Garden-retakes | PLANNED (Phase R) Fork of B3none/cs2-retakes. Will absorb allocator + rankings into ONE signature plugin + all new game modes. | CounterStrikeSharp |
Shared infra:
- DB: Aiven MySQL (
defaultdb), shared by rankings plugin, website, and Discord bot. Schema bootstrap:Garden-website/sql/blank-schema.sql(idempotent). Plugin also auto-creates via EFEnsureCreated+ raw-SQLSchemaUpgradesfor post-v1 tables. - Cross-plugin API:
RetakesAllocatorSharedβPluginCapability<IRetakesAllocatorApi>("retakes_allocator:api")(round type, force-buy team, overrides). Rankings consumes it. - Websiteβplugin contract:
GET /api/equipped/v4/{steamid}.json(EquippedV4Response),POST /api/select-loadout(INVSIM_API_KEY==invsim_apikey),POST /api/increment-item-stattrak.
2. Hard-won gotchas (DO NOT re-learn these the crash way)
- Never write
m_hActiveWeaponRaw directly and never remove a held weapon mid-frame. Safe removal:NativeAPI.IssueClientCommand(userid, "slot3")thenweaponEntity.AddEntityIOEvent("Kill", weaponEntity, null, "", 0.1f). (Two crashes came from this.) - Game-event hook ordering across plugins (same event, Post) is load-order dependent.
To run after another plugin's
round_prestartlogic but before itsround_poststartlogic, hookEventRoundPoststartwithHookMode.Pre. This is how clutch/CR team enforcement beats the retakes plugin's team balancer (fixed 2026-07: "1v4 announced, 2v3 played"). - The retakes plugin rebalances teams to its T-ratio every round. Any custom team layout must be applied in the window described in (2), and will be auto-reverted next round (desired for clutch).
- Native buy menu can't render server-side CanAcquire β grey-out is faked with per-round-type
money (
MoneyByRoundType) + post-purchase top-ups. CanAcquire stays the hard block. - Premier scoreboard rating: write
CompetitiveRankType=11,CompetitiveRanking=elo,CompetitiveWins>=10every tick +Utilities.SetStateChanged. Others' ratings need the FakeRanks-RevealAll metamod addon. (K4-System MMR technique.) - FakeConVar string values can keep literal quotes from cfg parsing β sanitize
(see
Garden-inventory Api.GetUrl), and prefer unquoted values in cfg files for URLs/keys. - GetCSWeaponDataFromKey gamedata signature breaks on CS2 updates β static def-indexβCsItem table is primary, native is guarded fallback (try/catch on load).
- EF
EnsureCreatednever upgrades an existing DB β additive tables go inSchemaUpgrades(idempotentCREATE TABLE IF NOT EXISTS, MySQL + SQLite dialects). - Prisma: pinned to v6 (v7 broke
db execute --url). Aiven self-signed CA β connection string?sslaccept=accept_invalid_certs(or shipca.pem+sslaccept=strict+outputFileTracingIncludes). - Workshop/community skins are impossible (client-side assets). Official catalog only.
- Server cfg for plugin convars:
game/csgo/cfg/garden-inventory.cfg+execfromserver.cfg(executes after plugin load). Secrets never in git. - CSS API changes between 1.0.329 β 1.0.367 (hit while porting the donors):
EventPlayerChatremoved β hook chat withAddCommandListener("say"/"say_team", handler, HookMode.Post)and read the message fromcommandInfo.GetArg(1);PlayerConnectedState.PlayerConnectedrenamed toPlayerConnectedState.Connected.
3. Conventions
- C# plugins: net8.0, CounterStrikeSharp; config = JSON file in plugin dir, validated on load;
all player-facing text through
Localizer/lang/*.json(en at minimum); test project per repo (NUnit); every feature toggleable via config. - Commands: chat
/x== consolecss_x. Admin-only via flags (until Garden-retakes admin system lands). - DB writes from game thread:
Task.Run+ fresh DbContext per op; back to game thread withServer.NextFrame. - Website: app router, server components for data pages, purple-rose white theme in
globals.css(respect it).
4. Phases
Phase D β Garden-discord bot ("rich presence") β IN PROGRESS
True per-user Discord Rich Presence is client-side only; chosen design: bot presence + live status embed.
- D1. Bot skeleton: discord.js v14,
.envconfig, graceful shutdown. - D2. Live presence: A2S query (gamedig) of the game server β "Watching 7/10 Β· de_mirage", refresh ~60s.
- D3. Status embed: one auto-edited message in a configured channel (map, players+names, connect link, top-5 ladder from MySQL).
- D4. Stats commands β DONE 2026-07-09:
/ladder [count],/stats <player> [ranked](name or SteamID64, partial names resolve to most recently seen),/compare <p1> <p2>,/seasonsβ purple embeds linking to the website, shared MySQL via a pooled db helper. OptionalGUILD_IDenv for instant slash-command registration. - D5. (Later) Round/match event posts (CR results, records broken) β either poll DB or a tiny webhook sender in the plugin.
Phase R β Garden-retakes: THE merged signature plugin
Base: fork of B3none/cs2-retakes added by Evan as Garden-retakes/ (not yet present).
End state: retakes core + allocator + rankings in one plugin, plus new modes. Old
Garden-allocator/Garden-rankings become read-only donors (copy code in, keep history there).
R0. Skeleton & merge foundation β scaffold landed 2026-07-09
- Solution layout:
GardenRetakes.sln=RetakesPlugin(fork of B3none cs2-retakes v3.0.4, now "Garden Retakes"),RetakesPluginShared,GardenRetakesCore(pure logic, no CSS deps),GardenRetakesTest(NUnit; mode manager + admin registry covered). - Module system:
RetakesPlugin/Garden/βIGardenModule+GardenHost(constructed in RetakesPlugin.Load; lifecycle Load/OnMapStart/Unload; per-module enable flags in theGardenSettingsconfig section of BaseConfigs, ConfigVersion bumped to 3). Modules: Admin, InstantDefuse, GameMode, SmallServer (state basis), Duels/Executes/FastStrat (skeletons). - Central
GameModeManager(Core): exclusive modes Retakes/Duels/Executes/FastStrat + SmallServer overlay (Auto/On/Off, auto at 1..MaxHumans);css_gamemodefront-end refuses unimplemented modes. Still TODO: absorb rankings' ModeCvars profiles into mode switches. - Instant defuse in retakes core (
InstantDefuseModule): last T dies + bomb planted + CT alive β instant round end as defuse, with HE/molotov/inferno danger block + 0.25s recheck. (Instant/auto bomb plant already existed upstream:Bomb.IsAutoPlantEnabled.) - Admin system basis (R3 pulled forward):
AdminRegistryin Core (Owner/Admin/Moderator, config-owner bootstrap, JSON persistence to garden_admins.json, @css/root = Owner fallback). Commands:css_gadmin add|remove|list,css_gkick(Mod+),css_gmap(Mod+),css_gslay(Admin+),css_grcon(Owner). g-prefixed until the legacy plugins retire. TODO R3: DB-backed storage, action log. - Port allocator (donor: Garden-allocator) β landed 2026-07-09:
RetakesAllocatorCore/Shared/Testcopied into the solution unchanged (Shared keeps its assembly identity so Garden-rankings'IRetakesAllocatorApicapability still resolves; core's CSS package bumped to 1.0.367). Plugin layer lives inRetakesPlugin/Garden/Modules/Allocator/keeping the originalRetakesAllocator.*namespaces: Helpers/CustomGameData/menus copied verbatim (onlyRetakesAllocator.InstanceβAllocatorModule.Instance),AllocatorModuleis the transformed main class (attributes β explicit registrations,_plugin.*surface, direct subscription toRetakesPlugin.EventSenderinstead of the capability lookup). Allocator lang keys merged into the fork's en/pt-BR/pt-PT/zh-Hans lang files. Allocator's own config staysconfig.jsonin the plugin dir (copy from the Garden-allocator install, incl.data.dbandgamedata/). Deployment notes: setGameSettings.EnableFallbackAllocation=false(module warns if not);CopyLocalLockFileAssemblies=trueships EF/SQLite/MySQL deps. - Port rankings (donor: Garden-rankings) β landed 2026-07-09:
GardenRankingsCore/Testcopied into the solution (CSS bumped to 1.0.367; config file renamed toconfig/rankings.jsonbecause the allocator core ownsconfig/config.jsonin the same plugin folder). Plugin layer inRetakesPlugin/Garden/Modules/Rankings/keeping namespaceGardenRankings: Helpers/ScoreboardManager/AfkTracker/RoundDataCollector verbatim;RankingsModule+RankingsModule.Modesare the transformed partial class (attributes β explicit registrations,_plugin.*, module lifecycle; clutch/CR enforcement stays on round_poststart PRE). Round types still flow throughIRetakesAllocatorApiβ registered by AllocatorModule in-process. Rankings lang keys merged into the fork's en.json. Deployment (merged plugin replaces ALL THREE legacy plugins): remove standalone Garden-allocator AND Garden-rankings (duplicate commands/hooks otherwise); copy rankingsconfig/config.jsonβ mergedconfig/rankings.json; FakeRanks-RevealAll metamod addon still required for others' scoreboard ratings.
R0 is COMPLETE β Garden-retakes now contains retakes core + allocator + rankings + admin + instant defuse + game-mode/small-server scaffolding. Next: R1 spawn editor.
R1. Spawn editor (visual, multi-user, fast test) β landed 2026-07-09
- Spawn data: base per-map JSON (
map_config/<map>.json) extended withFlags(duel/smallserver/executeβ consumed by R4/R5/R6) andAddedByattribution. Backward compatible with existing B3none/splewis-style JSONs (import = drop the file in). - Visual editing (
Garden/Modules/SpawnEditorModule, Admin level, on top of the base agent-model markers + labels):!gspawns <a|b|all|flag <name>|off>renders one site, all sites, or only flagged spawns; labels show flags + who placed them.!gspawn add <t|ct> <a|b> [flags...]places at your feet,!gspawn del,!gspawn move,!gspawn flag <name>(toggle, incl. planter),!gspawn infoβ all save instantly. - Multi-editor: all edits flow through the single in-memory MapConfigService
(
MutateSpawns) and save immediately; every edit is announced with the editor's name and persisted asAddedBy. Base editor commands (css_showspawns/css_add/...) still work. - Quick test:
!gspawn test [a|b]teleports you through spawns (repeat = next, per-player cursor);!gspawn round <a|b>forces dry-run rounds on one site (ends warmup + skips to a fresh round),!gspawn round offrestores random sites.
R2. In-game config commands β completed 2026-07-09
-
!gconfig <target> <path> <value>β reflection engine (GardenRetakesCore/Config/ ConfigReflection, unit tested): dotted case-insensitive paths, type validation (int/double/bool/enum/string incl. on/off), oldβnew echo, live apply + save. Collections stay file-only by design. - Per-config targets:
retakes(CSS config incl. GardenSettings β written back to configs/plugins/RetakesPlugin/RetakesPlugin.json),garden(shortcut),allocator(config/config.json via new Configs.Save()),rankings(config/rankings.json via new Configs.Save()). Browse with!gconfig <target> [path](Admin), set = Owner, every set goes to the GardenAdminLog audit trail. Construction-time settings announce "applies next map"; SmallServer runtime values re-synced immediately.
R3. Admin system β completed 2026-07-09
- DB-backed admins:
GardenAdminstable in the shared DB (entities + SchemaUpgrades + Queries in GardenRankingsCore; also added to websitesql/blank-schema.sql). AdminModule syncs from DB once the rankings module has it ready (5s retry timer);garden_admins.jsonstays as bootstrap/fallback; configOwnerSteamIdsalways win and are never persisted. - Commands:
!gadmin add|remove|list,!gkick,!gslay,!gmap,!grcon(R0) β now with DB persistence. - Levels: Owner > Admin > Moderator (rcon Owner-only). All actions logged to
GardenAdminLog(actor, action, target, detail, timestamp) β future website admin-log page. - Map-change commands: rankings'
!<alias>commands + BlockMapChangeDuringMatch came along with the R0 rankings port;!gmapcovers arbitrary maps.
R4. Duels mode (1v1 arenas) β completed 2026-07-09
- Arenas: pairs of
duel-flagged spawns (place with!gspawn add ... duel), greedily paired by proximity withinDuels.MaxPairDistance(pure logic in Core, unit tested). - Flow:
!gamemode duels(Admin) β retakes machinery fully gated viaRetakesPlugin.RetakesGameplayActive(round/queue/allocation handlers skipped; rankings collection, clutch/CR/scramble, ranked auto-activate and instant defuse all guarded),mp_ignore_round_win_conditions 1so the module owns the round. On death both fighters respawn instantly at another random arena (no immediate repeats), full heal, configured weapon preset (Duels.Weapons) + kevlar/helmet; per-player win counters announced each duel,!duelscoreboard. - Queue for 3+ players: FIFO in Core
DuelSessionβ loser rotates to the back, next steps in; joiners mid-mode are queued; disconnects promote from the queue. (Parallel duels on distinct arenas = possible later refinement.)
R5. Small-server mode (2β3 players) β completed 2026-07-09
- Activation: auto at 1..MaxHumans humans (default 3) or forced via
!smallserver on/off/auto(Mod+); evaluated every round PRESTART so effects land before nade allocation and spawn assignment; transitions announced. - Closer spawns:
SpawnManager.GardenSpawnFilterhook β while active only spawns flaggedsmallserverare used (place with!gspawn add ... smallserver), with automatic fallback to all spawns when the flagged set is too small or has no planter spawn. - Reduced utility:
NadeHelpers.GardenMaxTotalNadesOverridehard-caps each team's nade pool (SmallServer.MaxTeamNades, default 2); never persisted to config. - Last CT dies β instant round switch (T win, no bomb-timer wait; toggleable); last T dies β instant defuse (was already global via InstantDefuseModule).
R6. Executes mode β completed 2026-07-09
- Strategy definitions per map (
executes/<map>.json, CoreExecuteStore+ models, unit tested; same format feeds R7): name, site, T-start positions, CT-setup positions, utility throws (projectile spawn pos + velocity + delay). Edited fully in game with!gexec: new/edit,tstart/ctsetupcapture where you stand,nadecaptures your last actually thrown grenade (projectile pos+velocity recorded via OnEntitySpawned while editing). - Round flow:
!gamemode executesβ retakes machinery gated; each round picks a random playable strategy (or!gexec play <name>forces one); Ts teleported to starts with configured weapons + C4, CTs to setups; utilities auto-thrown after freeze end with per-nade delays (smokes/mollies use the InitializeSpawnFromWorld nade-practice technique β re-check ThrowUtility first if a CS2 update breaks a type). Normal win conditions (plant/defuse/elimination), rounds cycle naturally.
R7. Fast-strat mode β completed 2026-07-09
-
!gamemode faststrat: every round CTs vote a SETUP (!setup <name|list>) and Ts vote a STRATEGY (!strat <name|list>) β majority per side, ties/no-votes random; both teams spawn in the chosen situation facing off (T strat's TStarts + auto-thrown utility vs CT setup's CtSetups), C4 to a T, normal win conditions, votes reset each round. - Definitions fully shared with Executes: same
executes/<map>.jsonvia the ExecutesModule store;PlaceGroup/ThrowUtilityreused (ThrowUtility now allows both modes). The original Phase R roadmap is COMPLETE β next up: R8 Duels v2, R9 inventory loadout UX, Phase W website pages, Discord D4/D5.
Suggested order: R0 β R1 β R3 β R2 β R5 β R4 β R6 β R7 (spawn editor early because Duels/SmallServer/Executes/FastStrat all consume its data; Executes/FastStrat last β utility replay is the hardest single piece).
R8. Duels v2 (requested 2026-07-09) β completed 2026-07-09
- Named arena pairs:
!garena new <name>(end A = where you stand) +!garena setb <name>,seta/del/list; stored per map induels/<map>.json(CoreDuelArenaStore, tested); the versus announce shows the arena name. Fallback: when a map has no named arenas, the R4 proximity auto-pairing ofduel-flagged spawns still applies ("Arena N"). - Parallel duels: Core
DuelManagerruns up to min(arenas,Duels.MaxParallelDuels=3) lanes at once with one shared FIFO queue and a merged win scoreboard; lanes get non-colliding arenas and rotate arenas between duels; disconnects re-slot the abandoned partner. - Challenges:
!duel <player> [firstTo]invites (30s);!duel accept/decline. A challenge reserves a private lane β no queue rotation, fixed arena, own score line β ends at first-to-X with a winner announce, or runs infinite until!duel stop. Other players keep rotating on the remaining lanes; abandoned partners are re-paired automatically.
R9. Inventory loadout UX (requested 2026-07-09) β completed 2026-07-09
-
GET /api/loadouts/{steamid}.jsonon Garden-website (names + active flag) andApi.FetchLoadoutsAsyncin Garden-inventory;!loadoutslists them with the active one marked green. - Case-insensitivity: verified β the website's
findLoadoutlower-cases names, so!loadout GREENalready works; no fix needed. - Center menu:
!loadoutwith no args opens it β W/S navigate, D select, A/TAB exit, player frozen while open (same UX as the gun menu), active loadout pre-highlighted, "π² Random" entry at the bottom. Uses the fork's pinned CSS.NextFrame wrapper. -
!loadout random(and the menu entry): picks any loadout except the active one. (Auto-random each map = possible later toggle.)
R10. Consolidated backlog β every "(for later)" suggestion scattered across this file, collected 2026-07-09 so nothing gets lost:
- Production cutover β build/test DONE 2026-07-09 (
dotnet buildclean, all 142 tests green across the three suites after two fixes: CSS 1.0.367 API renames + test localizer replacement). REMAINING: rebuild Garden-inventory, deploy the merged plugin, retire standalone Garden-allocator/Garden-rankings, migrate configs (allocatorconfig.json+data.db+gamedata/as-is; rankings config βconfig/rankings.json), setEnableFallbackAllocation=false, redeploy website (react-markdown deps + new endpoints/pages),npx prisma generate. - ModeCvars unification β DONE 2026-07-09 (pragmatic form): every mode transition now applies the right profile β RankingsModule re-applies its Classic/Ranked/Competitive cvars whenever the server returns to Retakes mode (0.5s after ModeChanged), Duels/Executes/FastStrat apply their own Start/StopCommands on their transitions. Config locations intentionally unchanged.
- Short command names β DONE 2026-07-09:
GardenSettings.Admin.EnableShortAliases(default false) additionally registers!admin,!kick,!slay,!map,!rcon. Turn on after the legacy plugins are retired. - Auto-random loadout each map β DONE 2026-07-09:
invsim_random_loadout_each_mapconvar (default off) in Garden-inventory equips a random loadout for every player on each connect/map change (3s after connect so the initial inventory fetch settles). - Parallel-duels polish β DONE 2026-07-09: dead/queued players auto-spectate the duel that
just started (in-eye; guarded observer-handle writes, disable via
Duels.SpectatorAutoFollowif a CS2 update misbehaves);!duelscore arenasshows per-arena duels played + best fighter. - Executes polish β DONE 2026-07-09:
ExecuteStrategy.Weight(0β100, weighted random pick, 0 = manual-only,!gexec weight <n>, tested);UtilityThrow.Teamrecords the thrower's side (old JSONs default to T) β projectiles spawn with the right TeamNum, Executes replays both sides' lineups, Fast-strat replays the T strat's T utility + the CT setup's CT utility.
Phase W β website/bot follow-ups (opportunistic)
- Commands reference page (
/commands) β DONE 2026-07-09: renderscontent/commands.md(mirror ofGarden-retakes/COMMANDS.mdβ keep BOTH in sync whenever commands change), same markdown pipeline/styles as /roadmap, NavBar link. - Roadmap page (
/roadmap) β DONE 2026-07-09: renders this file (mirrored toGarden-website/content/roadmap.mdβ keep the mirror in sync on every roadmap change). - Admin log page β DONE 2026-07-09: hidden key-protected URL
/admin-log?key=<INVSIM_API_KEY>(not in the nav), last 200GardenAdminLogentries; Prisma modelsGardenAdmin/GardenAdminLogEntryadded (runnpx prisma generate). - Duels ladder β DONE 2026-07-09: plugin persists every completed 1v1 to
DuelRecords(season, map, arena, winner/loser, challenge flag + final score; best-effort like the admin log; schema in SchemaUpgrades + blank-schema.sql + Prisma). Website/duelspage: season ladder (wins/losses/winrate/challenges won) + last 20 duels; NavBar link. - Discord D4/D5.
5. Working agreement with the AI
- One major change at a time; after finishing it, STOP and wait for Evan to say "Continue".
- Evan builds/tests locally (
dotnet build/dotnet test) β the sandbox has no .NET SDK and no network. - Keep this file updated: tick checkboxes, add gotchas, note donor-code locations when porting.
- Mirror: copy this file to
Garden-website/content/roadmap.mdafter every update (it feeds /roadmap). - Never commit/print secrets (DB password, INVSIM_API_KEY, Discord token).
6. Changelog (roadmap-level)
- 2026-07-08: invsim URL-quotes crash fixed (Api.GetUrl sanitize); cfg location documented.
- 2026-07-09: Clutch team enforcement moved to
EventRoundPoststartPre (gotcha #2). Roadmap created. Phase D started (Garden-discord skeleton: presence + status embed). - 2026-07-09 (2): B3none fork added as Garden-retakes. R0 scaffold landed: solution + GardenRetakesCore + GardenRetakesTest, Garden module system (GardenHost/IGardenModule + GardenSettings config section), GameModeManager + css_gamemode, SmallServer overlay state, InstantDefuseModule (working), admin system basis (registry + gadmin/gkick/gslay/gmap/grcon), lang keys en+fr.
- 2026-07-09 (3): Allocator ported into the merged plugin (AllocatorModule + copied Core/Shared/Test).
- 2026-07-09 (4): Rankings ported into the merged plugin (RankingsModule + copied Core/Test; rankings config renamed to config/rankings.json). R0 complete β the merged plugin now fully replaces cs2-retakes + Garden-allocator + Garden-rankings.
- 2026-07-09 (5): R1 spawn editor landed (Spawn.Flags/AddedBy, all-sites render with flag labels, gspawns/gspawn command set, MutateSpawns, teleport-test, dry-run rounds).
- 2026-07-09 (6): R3 completed β GardenAdmins + GardenAdminLog tables (shared DB, schema also in
website blank-schema.sql), AdminModule DB sync + full action audit log. Commands reference
created:
Garden-retakes/COMMANDS.md+ Garden section atop the README; website/commandspage queued in Phase W (after mode commands stabilize). - 2026-07-09 (7): R2 completed β ConfigReflection engine + !gconfig over all four configs with live apply, save-back (incl. CSS config write-back) and audit logging; COMMANDS.md updated.
- 2026-07-09 (8): R5 completed β small-server overlay now changes gameplay: flagged closer spawns (with safe fallback), team nade cap, instant round switch on last CT death. New config knobs: SmallServer.MaxTeamNades / UseFlaggedSpawns / InstantRoundSwitchOnLastCtDeath.
- 2026-07-09 (9): R4 Duels completed (Core DuelSession/DuelArenas + tests, DuelsModule, retakes gating via RetakesGameplayActive, ranked auto-activate guard, duels lang keys). Lang files trimmed to en + fr only (user request β others deleted from Garden-retakes).
- 2026-07-09 (10): R6 Executes completed (ExecuteStore + models + tests, ExecutesModule with in-game strategy editor, real-throw lineup capture, auto-throw at freeze end). Roadmap gained R8 Duels v2 (named arena pairs, parallel duels, challenges) and R9 inventory loadout UX (!loadouts, case-insensitive !loadout, center menu, random) β both per Evan's requests. Next (on "Continue"): R7 Fast-strat (CT setup vote + T strat vote, both teams spawn facing off β reuses the executes data format), which finishes the original roadmap.
- 2026-07-09 (11): Website /roadmap page added (renders this mirrored file, react-markdown + remark-gfm, NavBar link).
- 2026-07-09 (12): R7 Fast-strat completed (per-side !strat/!setup votes, shared executes data, face-off spawning + utility replay). All four game modes now switchable via !gamemode. Original Phase R roadmap complete.
- 2026-07-09 (13): R8 Duels v2 completed β DuelArenaStore (named pairs, duels/<map>.json) + DuelManager (parallel lanes, shared queue, challenge lanes) in Core with tests; DuelsModule v2 (!garena editor, per-lane arena assignment, !duel challenges first-to-X/infinite). Next (on "Continue"): R9 inventory loadout UX (!loadouts, center menu, random β touches Garden-inventory + Garden-website).
- 2026-07-09 (14): R9 completed β /api/loadouts endpoint, !loadouts, !loadout center menu + random in Garden-inventory (case-insensitivity confirmed already working). Remaining backlog: Phase W (/commands page, admin log page, duels ladder), Discord D4/D5.
- 2026-07-09 (15): Phase W /commands page + hidden /admin-log page shipped (Prisma admin models, content/commands.md mirror). New R10 section consolidates every inline "(for later)" suggestion β production cutover first, then ModeCvars unification, short command aliases, auto-random loadout, duels/executes polish. Remaining: R10, W duels ladder, Discord D4/D5.
- 2026-07-09 (16): First full build + test pass GREEN (47+56+39 tests) after fixing CSS 1.0.367 renames (gotcha #12) and swapping the allocator tests' JsonStringLocalizer for a self-contained TestJsonLocalizer. R10 sweep landed: mode-cvar reapply on return to Retakes, Admin.EnableShortAliases, invsim_random_loadout_each_map. Remaining backlog: cutover deploy steps, duels/executes polish, W duels ladder, Discord D4/D5.
- 2026-07-09 (17): R10 finale β duels polish (spectator auto-follow, !duelscore arenas) + executes polish (strategy weights, per-side utility capture/replay). R10 done except the deploy half of the cutover. Remaining: cutover deploy, W duels ladder, Discord D4/D5.
- 2026-07-09 (18): Discord D4 shipped β /ladder /stats /compare /seasons slash commands (db.js pooled helper, stats.js aggregates matching the website's summaries). Remaining: cutover deploy (Evan), W duels ladder (needs duel-stat persistence), Discord D5.
- 2026-07-09 (19): Duel persistence + /duels ladder shipped (DuelRecords table end to end: plugin β DB β website page). Remaining: cutover deploy (Evan), Discord D5.
- 2026-07-09 (20): New skin collections (today's CS2 update): @ianlucas/cs2-lib bumped to 8.0.3
(published today with the regenerated item catalog) β run
npm update @ianlucas/cs2-libto refresh the lockfile before redeploying. The inventory simulator picks the new collections up automatically (runtime catalog + cdn.cstrike.app images). Same recipe for future drops.