From 644f2f0f95e168606b26734836d6c1288a50e520 Mon Sep 17 00:00:00 2001 From: god Date: Sun, 5 Jul 2026 12:47:47 +0800 Subject: [PATCH] chore: clean stale docs, add lua server reference, update steamworks guide --- docs/VERSIONING.md | 157 -- docs/gauntlet-technical-docs.html | 1366 ------------- docs/gauntlet-technical-implementation.md | 793 -------- docs/gauntlet-visualization.html | 1209 ------------ docs/{gauntlet.md => old-gauntlet.md} | 0 docs/rpc_migration_audit.md | 89 - docs/tekton-dash-knowledge-base.html | 2191 --------------------- server/nakama/lua/README.md | 1639 +++++++++++++++ 8 files changed, 1639 insertions(+), 5805 deletions(-) delete mode 100644 docs/VERSIONING.md delete mode 100644 docs/gauntlet-technical-docs.html delete mode 100644 docs/gauntlet-technical-implementation.md delete mode 100644 docs/gauntlet-visualization.html rename docs/{gauntlet.md => old-gauntlet.md} (100%) delete mode 100644 docs/rpc_migration_audit.md delete mode 100644 docs/tekton-dash-knowledge-base.html create mode 100644 server/nakama/lua/README.md diff --git a/docs/VERSIONING.md b/docs/VERSIONING.md deleted file mode 100644 index f8dab1c..0000000 --- a/docs/VERSIONING.md +++ /dev/null @@ -1,157 +0,0 @@ -# Versioning & Artifact Storage Strategy - -## Version Format - -Tekton Armageddon uses semantic versioning: `MAJOR.MINOR.PATCH` - -- **MAJOR**: Breaking changes, incompatible API changes -- **MINOR**: New features, backward-compatible -- **PATCH**: Bug fixes, content updates, backward-compatible - -Current version source: `project.godot` → `config/version` - -## Artifact Types - -### 1. Patch PCK Files -- **Purpose**: Hot-patch content updates without full client reinstall -- **Storage**: GitHub Actions artifacts (90 days) + tekton-updates repo -- **Naming**: `patch.pck` (latest), versioned in tekton-updates repo -- **Integrity**: SHA256 checksums generated and stored alongside - -### 2. Platform Builds -- **Platforms**: Windows, Linux, Android -- **Storage**: GitHub Actions artifacts (90 days) + GitHub Releases (permanent) -- **Naming**: `tekton_armageddon_{Platform}_v{VERSION}.{ext}` -- **Trigger**: Git tags (`v*.*.*`) or manual workflow dispatch -- **Integrity**: SHA256 checksums for each platform build - -### 3. Version Manifest -- **File**: `assets/data/version.json` -- **Purpose**: Client version checking, changelog delivery, patch URLs -- **Storage**: Embedded in builds + tekton-updates repo -- **Integrity**: SHA256 checksum - -## Workflows - -### Patch Deployment (`deploy_patch.yml`) -**Trigger**: Push to `patch-release` branch or manual dispatch - -**Process**: -1. Auto-bump patch version from `project.godot` -2. Extract changelog from `CHANGELOG_DRAFT.md` [NEXT] section -3. Generate `version.json` with new release entry -4. Commit version bump back to repo -5. Build `patch.pck` from changed files -6. Generate SHA256 checksums -7. Upload artifacts to GitHub Actions (90-day retention) -8. Push to `tekton-updates` public repo (`latest/` folder) - -**Artifacts**: -- `patch-pck-{SHA}`: patch.pck + checksum -- `version-manifest-{SHA}`: version.json + checksum - -### Platform Builds (`build_artifacts.yml`) -**Trigger**: Git tag push (`v*.*.*`) or manual dispatch with version input - -**Process**: -1. Matrix build for Windows, Linux, Android -2. Export using Godot export presets -3. Generate SHA256 checksums per platform -4. Upload artifacts to GitHub Actions (90-day retention) -5. Create GitHub Release with all platform builds + checksums - -**Artifacts**: -- `tekton-{Platform}-v{VERSION}`: platform binary + checksum - -## Artifact Retention - -| Artifact Type | Storage Location | Retention | Purpose | -|--------------|------------------|-----------|---------| -| Patch PCK | GitHub Actions | 90 days | CI/CD history, rollback | -| Patch PCK | tekton-updates repo | Permanent | Client downloads | -| Platform Builds | GitHub Actions | 90 days | CI/CD history | -| Platform Builds | GitHub Releases | Permanent | Distribution | -| Version Manifest | tekton-updates repo | Permanent | Client version checks | - -## Checksum Verification - -All artifacts include SHA256 checksums for integrity verification: - -```bash -# Verify patch.pck -sha256sum -c patch.pck.sha256 - -# Verify platform build -sha256sum -c tekton_armageddon_Windows_v2.3.8.sha256 -``` - -## Version Compatibility - -`version.json` includes `minimum_app_version` field: -- Clients below this version must reinstall full build -- Clients at or above can use patch system - -## Changelog Management - -**Source**: `CHANGELOG_DRAFT.md` - -**Format**: -```markdown -## [NEXT] -- Feature or fix description -- Another change - -## [2.3.7] — 2026-05-15 -- Archived release notes -``` - -**Process**: -1. Developers add entries under `[NEXT]` -2. CI extracts `[NEXT]` entries on patch deployment -3. CI archives to versioned section -4. CI clears `[NEXT]` for next cycle - -## Manual Version Bump - -For local testing without CI: - -```bash -# Update versions but keep changelog -python3 tools/generate_version_json.py --local - -# Skip changelog update entirely -python3 tools/generate_version_json.py --skip-changelog -``` - -## Release Process - -### Patch Release -1. Merge changes to `patch-release` branch -2. CI auto-bumps version, builds, deploys -3. Clients auto-download on next launch - -### Full Release -1. Tag commit: `git tag v2.4.0` -2. Push tag: `git push origin v2.4.0` -3. CI builds all platforms, creates GitHub Release -4. Distribute via Steam, Google Play, etc. - -## Rollback Strategy - -**Patch Rollback**: -1. Locate previous patch in GitHub Actions artifacts or tekton-updates repo -2. Manually push to `tekton-updates/latest/` -3. Update `version.json` to point to previous version - -**Full Build Rollback**: -1. Download previous release from GitHub Releases -2. Re-tag or create hotfix branch -3. Redeploy via standard release process - -## Future Enhancements - -- [ ] Automated rollback on failed health checks -- [ ] Delta patching for bandwidth optimization -- [ ] Multi-region CDN distribution -- [ ] Staged rollout (canary deployments) -- [ ] Automated compatibility testing matrix diff --git a/docs/gauntlet-technical-docs.html b/docs/gauntlet-technical-docs.html deleted file mode 100644 index f95f1aa..0000000 --- a/docs/gauntlet-technical-docs.html +++ /dev/null @@ -1,1366 +0,0 @@ - - - - - -Candy Cannon Survival — Technical Documentation - - - - - - - - -
-
-
-
-
- -
- - -
-
Technical Documentation
-

Candy Cannon Survival

-

Gauntlet Mode — Implementation blueprint mapping GDD mechanics to existing Tekton Dash systems

-
-
70%
Code Reuse
-
4
New Files
-
7
Modified Files
-
12
New Terms
-
10
Reused Terms
-
-
- - - - - -
-
-

📖 Glossary

-

All terms used in Gauntlet mode — categorized by whether they're new, adapted, or already implemented in Tekton Dash.

-
- -
-
New — unique to Gauntlet
-
Adapted — modified from existing mechanic
-
Existing — already in game, reused as-is
-
- - - -
- - - - -
- -
- - -
-
🍬
-
-

Sticky Cell New

-

A grid cell hit by the Candy Cannon that becomes impassable. Players stepping onto or pushed into a sticky cell are trapped. Remains until cleansed or round ends. Rendered as Layer 2 overlay (pink translucent mesh, ID 17).

- TILE_STICKY = 17 → GridMap Layer 2 -
-
- -
-
-
-

Telegraph New

-

1-second warning before cannon impact. Target cell glows pink/candy color with a shadow preview and charge-up sound. Uses temporary overlay tile (ID 18) on Layer 2, animated alpha 0→1 over 0.8s, then replaced by Sticky Cell on impact.

- TILE_TELEGRAPH = 18 → rpc("sync_telegraph") -
-
- -
-
💥
-
-

Candy Cannon New

-

Central NPC occupying a permanent 3×3 zone at arena center. Fires volleys of 5 candy shots every 5 seconds, creating sticky cells. Static body — cannot be grabbed, thrown, or interacted with. Not a Tekton — it's a dedicated hazard entity.

- CandyCannonController → candy_cannon.tscn -
-
- -
-
🎯
-
-

Volley New

-

A batch of 5 simultaneous cannon shots fired at different target cells. One volley fires every 5 seconds (36 total over 3 minutes = 180 impacts). Each shot in a volley has an independent impact size roll (1×1, 1×2, or 2×2).

- _fire_volley() → cannon_interval = 5.0 -
-
- -
-
📐
-
-

Impact Size New

-

The footprint of each cannon shot. Three sizes: 1×1 (single cell), 1×2 (two adjacent), 2×2 (four cells square). Distribution changes per phase — early favors 1×1, endgame favors 2×2.

- phase_weights[phase_idx]["2x2"] -
-
- -
-
🪤
-
-

Trapped New

-

Player state when standing on a sticky cell. Cannot move normally. Escape only via Cleanser power-up. Players can be trapped by stepping onto sticky, being pushed into sticky, or direct cannon hit. Trapped players keep their score but are out of active play.

- trapped_players: Dict → rpc("sync_trapped") -
-
- -
-
-
-

Cleanser New

-

Power-up earned by completing 2 missions. Allows 5 cells of movement through sticky candy, cleansing traversed cells back to walkable. Inventory limit: 1. Cannot activate while stunned. 0.3s activation delay.

- player_cleansers[peer_id] → GoalsCycleManager signal -
-
- -
-
💫
-
-

Clash New

-

When two players activate Smack simultaneously (within 0.5s) and are in range of each other. Both get stunned for 1.0s, no push occurs, both smack bars are consumed. Server-authoritative timestamp comparison.

- clash detection → 0.5s window, server authority -
-
- -
-
🔋
-
-

Charged State New

-

3-second window after Smack activation where the player model turns pink. If a target enters range during this window, the smack triggers. If no target is hit within 3s, energy is consumed with no effect.

- smack_charged[player_id] → 3.0s window -
-
- -
-
⚖️
-
-

Anti-Unfairness New

-

Targeting rules preventing the cannon from feeling random/cheap. No same-player twice in a row, 2×2 never directly on player, path validation ensures escape routes exist (except final 30s). Uses AStar pathfinding.

- last_targeted_player_id → EnhancedGridMap.initialize_astar() -
-
- -
-
🚧
-
-

Route Blocking New

-

Cannon targeting strategy (25% chance) that places sticky cells on pathfinding bottlenecks — narrow corridors between sticky regions. Forces players to reroute. Calculated using EnhancedGridMap neighbor analysis.

- _get_route_blocking_target() → 25% weight -
-
- -
-
🏟️
-
-

Gauntlet Arena New

-

20×20 cell arena with 391 playable cells (400 minus 3×3 NPC zone). Players spawn at outer edges/corners. Target: 80% sticky coverage by round end (313 cells), leaving ~78 safe cells.

- ARENA_SIZE = 20 → gauntlet.tscn -
-
- - -
-
👊
-
-

Smack Adapted

-

Gauntlet-specific melee push. Adapts existing try_push() from Attack Mode but replaces boost-meter gating with 8s auto-refill cooldown, adds 3s charged window, sticky landing trap, and clash detection. Push distance: 3 cells.

- PlayerMovementManager.try_push() → smack_cooldowns -
-
- -
-
⏱️
-
-

Phase Adapted

-

Three escalation phases in Gauntlet: Open Arena (0–60s), Route Pressure (60–120s), Survival Endgame (120–180s). Adapts StopNGoManager's Go/Stop phase pattern but uses time-elapsed triggers instead of cycle signals.

- enum Phase { OPEN_ARENA, ROUTE_PRESSURE, SURVIVAL_ENDGAME } -
-
- -
-
🤖
-
-

Bot AI — Cannon Avoidance Adapted

-

Extends BotStrategicPlanner with Gauntlet-specific logic: telegraph awareness, sticky path planning, safe-zone pathfinding. Adapts existing bot movement heuristics to factor in shrinking arena.

- BotStrategicPlanner → new evaluate_gauntlet() -
-
- - -
-
⚔️
-
-

Attack Mode Existing

-

Existing player state toggled via PowerUpManager when boost bar is full. In Gauntlet, not used directly — replaced by Smack mechanic. The push physics from try_push() are reused but the activation logic differs.

- PowerUpManager.is_attack_mode → NOT used in Gauntlet -
-
- -
-
😵
-
-

Stagger Existing

-

Existing 1.5s movement disable after being push-attacked. Gauntlet's Smack uses a shorter 1.0s stun, but the underlying apply_stagger() function is reused with a duration parameter.

- PlayerMovementManager.apply_stagger(duration) -
-
- -
-
🎯
-
-

Mission / Goals Existing

-

3×3 pattern-matching tile collection system. Reused as-is from GoalManager + GoalsCycleManager. In Gauntlet, completing every 2 missions also triggers Cleanser unlock (new hook on existing signal).

- GoalManager → GoalsCycleManager.goal_count_updated -
-
- -
-
🗂️
-
-

Layer 2 Overlay Existing

-

GridMap's Y=2 layer used for visual overlays (safe zones in Stop N Go, freeze in Freemode, highlights). Gauntlet uses it for Sticky Cell and Telegraph meshes. No conflict — modes are mutually exclusive.

- GridMap.set_cell_item(Vector3i(x, 2, z), id) -
-
- -
-
🫸
-
-

try_push() Existing

-

Player push mechanic in PlayerMovementManager. Pushes target 3 cells backward. Gauntlet's Smack wraps this with direction-based push, sticky landing detection, and clash rules.

- PlayerMovementManager.try_push(target, direction) -
-
- -
-
📳
-
-

Screen Shake Existing

-

Camera shake effect triggered via RPC. Used on cannon impact with "medium" intensity. Already implemented system-wide.

- player.rpc("trigger_screen_shake", "medium") -
-
- -
-
🎪
-
-

Tekton Projectile Existing

-

Arc-tween projectile from Tekton NPC. Candy Cannon reuses this exact visual pattern (spawn_projectile_rpc) — creating a mesh, arc-tweening position, then freeing on arrival.

- tekton.gd → spawn_projectile_rpc(target, duration) -
-
- -
-
📡
-
-

RPC Sync Pattern Existing

-

Server-authoritative state sync via @rpc("authority", "call_local", "reliable"). All Gauntlet state changes (sticky, phase, trap, cleanser) use this identical pattern.

- @rpc("authority", "call_local", "reliable") -
-
- -
-
-
-

Timed Match Existing

-

Global match timer from GoalsCycleManager. Gauntlet passes 180s duration. System handles countdown, HUD timer, and match-end trigger.

- goals_cycle_manager.start_match(180.0) -
-
- -
-
💎
-
-

SpecialTilesManager Existing

-

Handles power-up tiles, inventory, and effects. Gauntlet restricts certain powerups (like Stop N Go restrictions) and adds Cleanser as a new inventory slot via the existing signal/slot system.

- SpecialTilesManager.inventory → mode-based restrictions -
-
- -
-
- - -
-
-

🏗️ Architecture

-

How GauntletManager slots into the existing manager tree, following the StopNGoManager pattern exactly.

-
-
-
-main.gd
-├── _init_managers() ← instantiate GauntletManager
-├── _setup_host_game() ← arena setup branch
-├── _start_game() ← start_game_mode() call
-
-GauntletManager NEW
-├── _setup_arena() ← 20×20 grid, center 3×3 NPC zone
-├── _setup_hud() ← mission label, cleanser indicator
-├── start_game_mode() ← start cannon timer, spawn tiles
-├── _process() ← cannon volley timer, phase escalation
-├── CandyCannonController NEW ← targeting, volley fire
-├── StickyCell system NEW ← Layer 2 overlay, trap logic
-├── Cleanser system NEW ← powerup via missions
-├── Smack system NEW ← modified push with charge/cooldown
-└── Win condition ← highest score at timer end
-
-
-
- - -
-
-

♻️ Reuse Map

-

How each GDD feature maps to existing systems — showing what's reused vs what's new.

-
-
- - - - - - - - - - - - - - - - - - - - - -
GDD FeatureExisting SystemReuseNew Work
Game Mode RegistrationGameMode.gd + LobbyManagerDirectAdd enum + strings
20×20 ArenaStopNGoManager._setup_arena()HeavyCustom layout, same API
Tile Collection / ScoringGoalsCycleManagerDirectReuse as-is
Mission SystemGoalManager + goals_cycle_managerDirectSame 3×3 pattern matching
Timed MatchGoalsCycleManager.start_match()DirectPass 180s duration
Player MovementPlayerMovementManagerDirectNo changes
Powerup SystemSpecialTilesManagerPartialCleanser = new type
Smack MechanicPlayerMovementManager.try_push()AdaptModified push rules
Candy Cannon NPCtekton.gd + TektonControllerPatternNew NPC, reuses projectile
Sticky CellsStopNGoManager safe zone overlayPatternNew tile type, same layer
Telegraph VFXVFXManager / animation.gdPatternNew animations, same system
HUDStopNGoManager._setup_hud()DirectMode-specific labels
Network SyncRPC patternsDirectSame patterns
Lobby SettingsLobbyManager signal/syncDirectGauntlet settings
Bot AIBotController + BotStrategicPlannerAdaptCannon avoidance strategy
-
-
- - -
-
-

🌊 Phase Timeline

-

Three escalation phases that control cannon intensity and impact size distribution.

-
-
-
-
0:00 — 1:00
-

Open Arena

-
    -
  • Collect tiles, learn the mission
  • -
  • Slow candy pressure
  • -
  • 1×1 shots: 60%
  • -
  • 1×2 shots: 40%
  • -
  • 2×2 shots: 0%
  • -
  • ~60 impacts total
  • -
-
-
-
1:00 — 2:00
-

Route Pressure

-
    -
  • Candy shapes arena topology
  • -
  • Smack becomes dangerous
  • -
  • 1×1 shots: 30%
  • -
  • 1×2 shots: 55%
  • -
  • 2×2 shots: 15%
  • -
  • Cleanser used strategically
  • -
-
-
-
2:00 — 3:00
-

Survival Endgame

-
    -
  • ~80% arena is sticky
  • -
  • Safe zones limited, high tension
  • -
  • 1×1 shots: 15%
  • -
  • 1×2 shots: 55%
  • -
  • 2×2 shots: 30%
  • -
  • Aggressive route-blocking allowed
  • -
-
-
-
- - -
-
-

⚙️ Core Systems

-

Deep-dive into the four new systems and how they integrate.

-
- - -
-

🍬 Sticky Cell System

-
- - - - - - - - - - -
FeatureImplementation
VisualLayer 2 overlay — transparent candy-pink mesh (ID 17)
Movement BlockPlayerMovementManager.simple_move_to() — add sticky check alongside wall check
Trap on StepGauntletManager._check_player_on_sticky() in _process()
Trap on PushPlayerMovementManager.try_push() — check landing cell
Cleanser BypassTemporary flag (like is_invisible wall bypass)
Network Syncmain.rpc("sync_grid_item", x, 2, z, 17)
-
-
- - -
-

👊 Smack vs Attack Mode

-
- - - - - - - - - - -
PropertyCurrent Attack ModeGauntlet Smack
Charge SourceBoost bar fills to 1008s auto-refill cooldown
ActivationToggle is_attack_mode3s charged window (pink model)
Push Distance3 cells backward3 cells in push direction
Stagger Duration1.5s apply_stagger()1.0s stun
Sticky LandingN/ATrapped on first sticky cell
Clash RuleN/ABoth stunned, no push, bars consumed
-
-
- - -
-

✨ Cleanser Power-Up

-
- - - - - - - - - - -
PropertyValue
Unlock TriggerGoalsCycleManager.goal_count_updated → count % 2 == 0
StorageGauntletManager.player_cleansers[peer_id] = 1
Effect5 cells movement through sticky — crossed cells become passable
Syncrpc("sync_cleanser_state", peer_id, count)
Clear Stickymain.rpc("sync_grid_item", x, 2, z, -1)
Inventory Limit1 per player
-
-
- - -
-

🎯 Cannon Targeting Intelligence

-
- - - - - - - - -
Roll %Target StrategyPurpose
60%Near a player (not same as last)Direct pressure
25%Route-blocking bottleneckCut escape paths
10%Random non-sticky areaSpread coverage
5%Previously sticky / chaosUnpredictability
-
-
-
- - -
-
-

📁 File Changes

-
- -

New Files

-
-
-
📜
-

gauntlet_manager.gd

Core mode logic, phases, sticky cells, cleanser, smack

-
-
-
📜
-

candy_cannon_controller.gd

Cannon targeting, volley fire, telegraph

-
-
-
🎬
-

gauntlet.tscn

3D arena environment scene

-
-
-
🎬
-

candy_cannon.tscn

Candy Cannon NPC (3×3, static)

-
-
- -

Modified Files

-
-
-
✏️
-

game_mode.gd

Add GAUNTLET = 3 enum, string mappings

-
-
-
✏️
-

lobby_manager.gd

Mode list, gauntlet settings, area mapping

-
-
-
✏️
-

main.gd

Manager init, arena setup branch, start branch

-
-
-
✏️
-

player_movement_manager.gd

Sticky check in move + push

-
-
-
✏️
-

goals_cycle_manager.gd

Cleanser grant on 2nd goal

-
-
-
✏️
-

special_tiles_manager.gd

Gauntlet powerup restrictions

-
-
-
✏️
-

MeshLibrary .tres

Add TILE_STICKY (17) and TILE_TELEGRAPH (18)

-
-
-
- - -
-
-

📡 Network Sync

-

All sync follows existing RPC patterns — no new networking paradigms needed.

-
-
- - - - - - - - - - - -
DataSync MethodExisting Pattern
Sticky Cellsmain.rpc("sync_grid_item", x, 2, z, 17)Safe zone / freeze overlay
Telegraphrpc("sync_telegraph", targets_array)StopNGoManager.sync_phase()
Phase Changesrpc("sync_gauntlet_phase", idx, elapsed)StopNGoManager.sync_phase()
Trap Stateplayer.rpc("sync_trapped", true)player.rpc("sync_stop_freeze")
Cleanser Grantrpc("sync_cleanser", peer_id, count)goals_cycle_manager.sync_goal_count()
Smack Stateplayer.rpc("sync_smack_state", charged)player.rpc("sync_modulate")
Cannon NPCStatic scene — no movement sync needed
-
-
- - -
-
-

📋 Implementation Priority

-
-
-
-
1

Game Mode Registration

game_mode.gd, lobby_manager.gd, main.gd

-
2

Arena Setup

gauntlet_manager._setup_arena(), 20×20 grid

-
3

Tile Spawning

StopNGoManager._spawn_mission_tiles() pattern

-
4

Cannon Timer + Volley

5s interval, 5 shots, 1×1 only

-
5

Sticky Cell System

Layer 2 overlay, movement block, trap detection

-
6

Telegraph VFX

Warning glow → impact transition

-
7

Impact Sizes

1×2 and 2×2 shapes, phase weights

-
8

Smack Mechanic

Modified push with cooldown/charge

-
9

Cleanser

Unlock tracking, sticky bypass

-
10

Targeting Intelligence

Player proximity, route blocking, anti-unfairness

-
11

Bot AI

Cannon avoidance, sticky path planning

-
12

Polish

VFX, SFX, HUD animations, 3D scene

-
-
-
- - -
-
-

⚠️ Risk Assessment

-
-
-
-
🗂️
-
-

Layer 2 Conflict

-

GridMap Layer 2 used by freeze/safe overlays. Mitigated: Gauntlet mode is exclusive — no freeze/safe tiles exist.

-
-
-
-
📊
-
-

20×20 Grid Performance

-

400 cells + overlays. Mitigated: Existing 23×12 and 14×14 arenas work fine; 20×20 comparable.

-
-
-
-
🚫
-
-

Impossible Arenas

-

Cannon could seal all paths. Mitigated: AStar pathfinding check before each volley.

-
-
-
-
🔢
-
-

MeshLibrary ID Collision

-

IDs 17–18 might exist. Mitigated: Verify max ID in .tres before adding.

-
-
-
-
⏱️
-
-

Smack Clash Timing

-

Network latency affects clash detection. Mitigated: Server-authoritative timestamp, 0.5s window.

-
-
-
-
- -
- - - - - - - - - diff --git a/docs/gauntlet-technical-implementation.md b/docs/gauntlet-technical-implementation.md deleted file mode 100644 index 2c637ad..0000000 --- a/docs/gauntlet-technical-implementation.md +++ /dev/null @@ -1,793 +0,0 @@ -# Candy Pump Survival (Gauntlet) — Technical Implementation Plan - -## 1. Feasibility Summary - -**Verdict: Feasible.** The existing codebase provides ~70% of the infrastructure needed. The game mode architecture is modular — each mode has its own manager (`StopNGoManager`, `PortalModeManager`) that handles arena setup, HUD, phase logic, and win conditions. A new `GauntletManager` follows this identical pattern. - -### Reuse Breakdown - -| GDD Feature | Existing System | Reuse Level | New Work | -|---|---|---|---| -| Game Mode registration | `GameMode.gd` enum + `LobbyManager` | **Direct** | Already registered (`GAUNTLET = 3`) | -| 24×24 Arena setup | `StopNGoManager._setup_arena()` pattern | **Heavy** | Custom layout, same GridMap API | -| Tile collection / scoring | `GoalsCycleManager` | **Direct** | Reuse goal completion + scoring | -| Mission system (goals) | `GoalManager` + `goals_cycle_manager.gd` | **Direct** | Same 3×3 pattern matching | -| Timed match (3 min) | `GoalsCycleManager.start_match()` | **Direct** | Pass 180s duration | -| Player movement | `PlayerMovementManager` | **Direct** | Add sticky checks to `simple_move_to()` | -| Sticky cells | `StopNGoManager` safe zone overlay (Layer 2) | **Pattern** | New tile type, same GridMap layer approach | -| Telegraph VFX | Existing GauntletManager telegraph system | **Direct** | Adapt for growth ticks instead of cannon | -| Smack mechanic | Existing GauntletManager smack system | **Direct** | Already implemented | -| Cleanser power-up | Existing GauntletManager cleanser system | **Direct** | Already implemented | -| HUD | `StopNGoManager._setup_hud()` pattern | **Direct** | Mode-specific labels | -| Network sync | RPC patterns throughout codebase | **Direct** | Same `rpc()` / `sync_*` patterns | -| Bot AI | `BotController` + `BotStrategicPlanner` | **Adapt** | New strategy for sticky avoidance | -| Candy bubbles | **NEW** | **New** | Bubble spawn, grow, explode system | -| Candidate scoring | **NEW** | **New** | Cellular-automation growth algorithm | -| Movement buffers | **NEW** | **New** | Hidden safe zone detection/decay | - -### What Changes from Current Implementation - -The current `GauntletManager` uses a **cannon shooting** model (NPC fires projectiles at targets). The new GDD replaces this with a **ground growth** model (candy spreads from the ground via cellular-automation scoring). This requires: - -1. **Remove** `_fire_volley()`, cannon timer, volley size, projectile spawning -2. **Add** growth tick timer, candidate scoring, weighted cell selection -3. **Add** candy bubble system (spawn, grow, explode) -4. **Add** movement buffer detection and decay -5. **Add** layer-based priority logic -6. **Change** arena from 20×20 to 24×24 - ---- - -## 2. Architecture Overview - -``` -main.gd -├── _init_managers() ← GauntletManager instantiation (existing) -├── _setup_host_game() ← GauntletManager._setup_arena() -├── _start_game() ← GauntletManager.start_game_mode() -│ -GauntletManager (MODIFY EXISTING) -├── _setup_arena() ← 24×24 grid, center 3×3 NPC zone -├── _setup_hud() ← Mission label, cleanser indicator -├── start_game_mode() ← Start growth timer, spawn tiles -├── _process() ← Growth tick timer, bubble timer, phase escalation -├── GrowthTick system ← Candidate scoring, weighted selection, telegraph -├── CandyBubble system ← Bubble spawn, grow, explode -├── StickyCell system ← Layer 2 overlay, trap logic -├── MovementBuffer system ← Hidden safe zone detection, decay, camping override -├── Cleanser system ← Existing powerup -├── Smack system ← Existing modified push -└── Win condition ← Highest score at timer end -``` - ---- - -## 3. File-by-File Implementation - -### 3.1 Game Mode Registration — Already Done - -The existing `game_mode.gd` already has: -```gdscript -enum Mode { - FREEMODE = 0, - STOP_N_GO = 1, - TEKTON_DOORS = 2, - GAUNTLET = 3 # Already registered -} -``` - -And `LobbyManager` already has `"Candy Cannon Survival"` in `available_game_modes`. The mode name string can remain as-is or be updated to `"Candy Pump Survival"` if desired. - ---- - -### 3.2 Core Manager — `gauntlet_manager.gd` (MODIFY EXISTING) - -**Location:** `scripts/managers/gauntlet_manager.gd` - -**Major structural changes:** - -#### Remove (cannon-based system): - -``` -var cannon_timer: float -var cannon_interval: float -var volley_size: int -var last_targeted_player_id: int -func _fire_volley() -func _select_targets() -func _get_near_player_target() -func _get_route_blocking_target() -func _get_random_non_sticky_target() -func _get_random_target() -``` - -#### Add (growth-based system): - -```gdscript -class_name GauntletManager -extends Node - -# Signals -signal phase_changed(phase_index: int) -signal growth_tick(targets: Array) -signal player_trapped(player_id: int) -signal cleanser_granted(player_id: int) -signal bubble_spawned(center: Vector2i) -signal bubble_exploded(center: Vector2i, area: Array[Vector2i]) - -# Constants -const ARENA_SIZE = 24 -const NPC_SIZE = 3 -const NPC_CENTER = Vector2i(11, 11) # Center of 24×24 -const TILE_STICKY = 17 -const TILE_TELEGRAPH = 18 -const TILE_WALKABLE = 0 -const TILE_OBSTACLE = 4 - -# Phase timing -enum Phase { OUTER_PRESSURE, MIDDLE_PRESSURE, INNER_SURVIVAL } -var current_phase: Phase = Phase.OUTER_PRESSURE -var elapsed_time: float = 0.0 - -# Growth tick state -var growth_timer: float = 0.0 -var growth_interval: float = 3.0 -var telegraph_duration: float = 1.0 -var sticky_cells: Dictionary = {} # Vector2i -> true -var telegraphed_cells: Dictionary = {} # Vector2i -> true - -# Phase-based growth config -var phase_growth_config: Array = [ - {"cells_per_tick": [4, 6], "distribution": {"outer": 0.75, "middle": 0.10, "inner": 0.00, "near_player": 0.10, "random": 0.05}}, - {"cells_per_tick": [6, 8], "distribution": {"outer": 0.20, "middle": 0.50, "inner": 0.00, "near_player": 0.15, "sticky_expansion": 0.10, "random": 0.05}}, - {"cells_per_tick": [8, 10], "distribution": {"outer": 0.10, "middle": 0.25, "inner": 0.35, "near_player": 0.15, "sticky_expansion": 0.15, "random": 0.10}}, -] - -# Candy bubble state -var bubble_timer: float = 0.0 -var bubbles_this_phase: int = 0 -var max_bubbles_per_phase: Array = [0, 2, 3] -var active_bubbles: Array = [] # [{center, grow_timer, warning_area}] -var recent_bubble_positions: Array = [] # For RepetitionPenalty - -# Movement buffer state -var movement_buffers: Dictionary = {} # Vector2i -> {penalty: float, created_at: float} -var camping_tracker: Dictionary = {} # player_id -> {position: Vector2i, since: float} - -# Smack state (per-player) — unchanged -var smack_cooldowns: Dictionary = {} -var smack_charged: Dictionary = {} - -# Cleanser tracking — unchanged -var player_mission_completions: Dictionary = {} -var player_cleansers: Dictionary = {} - -# Trapped players — unchanged -var trapped_players: Dictionary = {} - -# Arena layer cache -var arena_layers: Dictionary = {} # Vector2i -> "outer"/"middle"/"inner" -``` - ---- - -### 3.3 Arena Setup — `_setup_arena()` - -**Pattern source:** `StopNGoManager._setup_arena()` - -Key changes from 20×20 to 24×24: - -```gdscript -func _setup_arena(): - if not multiplayer.is_server(): - return - # Resize gridmap to 24×24 - enhanced_gridmap.columns = ARENA_SIZE - enhanced_gridmap.rows = ARENA_SIZE - enhanced_gridmap.floors = 3 - # Clear all layers - enhanced_gridmap.clear_floor(0) - enhanced_gridmap.clear_floor(1) - enhanced_gridmap.clear_floor(2) - # Fill Floor 0 with walkable tiles - for x in range(ARENA_SIZE): - for z in range(ARENA_SIZE): - enhanced_gridmap.set_cell_item(Vector3i(x, 0, z), TILE_WALKABLE) - # Block center 3×3 for Candy Pump NPC - for x in range(NPC_CENTER.x - 1, NPC_CENTER.x + 2): - for z in range(NPC_CENTER.y - 1, NPC_CENTER.y + 2): - enhanced_gridmap.set_cell_item(Vector3i(x, 0, z), TILE_OBSTACLE) - # Build arena layer map - _build_arena_layers() - # Sync to clients - rpc("sync_arena_setup", ARENA_SIZE, NPC_CENTER) - enhanced_gridmap.initialize_astar() - enhanced_gridmap.update_astar_costs() -``` - ---- - -### 3.4 Layer Calculation — `_build_arena_layers()` - -**New method.** Precomputes the layer for every cell based on edge distance. - -```gdscript -func _build_arena_layers(): - arena_layers.clear() - for x in range(ARENA_SIZE): - for z in range(ARENA_SIZE): - var edge_dist = mini(x, z, ARENA_SIZE - 1 - x, ARENA_SIZE - 1 - z) - var layer: String - if edge_dist <= 3: - layer = "outer" - elif edge_dist <= 7: - layer = "middle" - else: - layer = "inner" - arena_layers[Vector2i(x, z)] = layer -``` - ---- - -### 3.5 Growth Tick System — `_process_growth_tick()` - -**Replaces** `_fire_volley()`. Called every 3 seconds. - -```gdscript -func _process_growth_tick(): - if not multiplayer.is_server(): - return - var config = phase_growth_config[current_phase] - var cell_count = randi_range(config.cells_per_tick[0], config.cells_per_tick[1]) - var candidates = _generate_candidates() - var selected = _select_cells_weighted(candidates, cell_count) - # Path safety check - selected = _apply_path_safety(selected) - # Movement buffer check - selected = _apply_movement_buffer_check(selected) - # Telegraph - _telegraph_cells(selected) - # After telegraph_duration: apply sticky - get_tree().create_timer(telegraph_duration).timeout.connect(func(): - _apply_sticky_cells(selected) - ) -``` - ---- - -### 3.6 Candidate Generation — `_generate_candidates()` - -**New method.** Builds scored list of all SAFE cells. - -```gdscript -func _generate_candidates() -> Array: - var candidates: Array = [] - var players = get_tree().get_nodes_in_group("Players") - for x in range(ARENA_SIZE): - for z in range(ARENA_SIZE): - var pos = Vector2i(x, z) - if not _is_cell_valid_for_growth(pos): - continue - var score = _calculate_candidate_score(pos, players) - candidates.append({"pos": pos, "score": score}) - return candidates -``` - ---- - -### 3.7 Candidate Scoring — `_calculate_candidate_score()` - -**New method.** Implements the full Candidate Score formula from the GDD. - -```gdscript -func _calculate_candidate_score(pos: Vector2i, players: Array) -> float: - var score: float = 0.0 - - # LayerPriority - var layer = arena_layers.get(pos, "outer") - var layer_scores = { - Phase.OUTER_PRESSURE: {"outer": 60.0, "middle": 15.0, "inner": -40.0}, - Phase.MIDDLE_PRESSURE: {"outer": 20.0, "middle": 60.0, "inner": 5.0}, - Phase.INNER_SURVIVAL: {"outer": 10.0, "middle": 35.0, "inner": 60.0}, - } - score += layer_scores[current_phase].get(layer, 0.0) - - # StickyNeighborScore (+8 per sticky neighbor, max +64) - var neighbors = _get_8_neighbors(pos) - for n in neighbors: - if sticky_cells.has(n): - score += 8.0 - - # InwardPressureScore - var center_dist = pos.distance_to(Vector2(NPC_CENTER)) - var max_dist = Vector2(ARENA_SIZE, ARENA_SIZE).length() / 2.0 - var inward_ratio = 1.0 - (center_dist / max_dist) - match current_phase: - Phase.OUTER_PRESSURE: score += lerpf(0.0, 10.0, inward_ratio) - Phase.MIDDLE_PRESSURE: score += lerpf(5.0, 20.0, inward_ratio) - Phase.INNER_SURVIVAL: score += lerpf(10.0, 30.0, inward_ratio) - - # PlayerPressureScore - var min_player_dist = INF - for p in players: - var p_pos = Vector2i(p.grid_position.x, p.grid_position.z) if p.has_method("get_grid_position") else Vector2i(p.position.x, p.position.z) - var dist = pos.distance_to(p_pos) - min_player_dist = mini(min_player_dist, int(dist)) - if min_player_dist >= 2 and min_player_dist <= 4: - score += 20.0 - elif min_player_dist == 0: - if elapsed_time < 150.0: # Before final 30s - score -= 50.0 - else: - score += 10.0 - - # ClusterGrowthScore - if _connects_sticky_clusters(pos): - score += 25.0 - elif _expands_sticky_cluster(pos): - score += 15.0 - - # RoutePressureScore - if _is_high_traffic_route(pos): - score += randf_range(10.0, 25.0) - - # CampingPressureScore - for pid in camping_tracker: - var camp = camping_tracker[pid] - if pos.distance_to(camp.position) <= 4: - var camp_duration = elapsed_time - camp.since - if camp_duration > 10.0 and player_cleansers.get(pid, 0) > 0: - score += 60.0 - elif camp_duration > 8.0: - score += 40.0 - elif camp_duration > 5.0: - score += 20.0 - - # RandomNoise - score += randf_range(-20.0, 20.0) - - # MovementBufferPenalty - if movement_buffers.has(pos): - var buffer = movement_buffers[pos] - var penalty = _get_buffer_penalty(buffer.penalty) - score += penalty - - # PathSafetyPenalty - if _would_trap_player(pos) and elapsed_time < 150.0: - score -= 100.0 - elif _removes_last_exit(pos): - score -= 60.0 - elif _makes_route_too_narrow(pos): - score -= 20.0 - - # RepetitionPenalty - if _was_recently_targeted(pos): - score -= 30.0 - elif _region_targeted_repeatedly(pos): - score -= 15.0 - - return score -``` - ---- - -### 3.8 Weighted Cell Selection — `_select_cells_weighted()` - -**New method.** Selects cells using weighted randomness from scored candidates. - -```gdscript -func _select_cells_weighted(candidates: Array, count: int) -> Array[Vector2i]: - # Sort by score descending - candidates.sort_custom(func(a, b): return a.score > b.score) - # Build weight array - var weights: Array[float] = [] - var total_weight: float = 0.0 - for c in candidates: - var w = maxf(c.score + 100.0, 1.0) # Offset to ensure positive weights - weights.append(w) - total_weight += w - # Weighted random selection without replacement - var selected: Array[Vector2i] = [] - var available = candidates.duplicate() - var available_weights = weights.duplicate() - for i in range mini(count, available.size()): - var roll = randf() * total_weight - var cumulative = 0.0 - for j in range(available.size()): - cumulative += available_weights[j] - if roll <= cumulative: - selected.append(available[j].pos) - total_weight -= available_weights[j] - available.remove_at(j) - available_weights.remove_at(j) - break - return selected -``` - ---- - -### 3.9 Candy Bubble System - -#### Bubble Spawn Timer - -```gdscript -func _process_bubbles(delta: float): - if not multiplayer.is_server(): - return - # Tick active bubbles - for i in range(active_bubbles.size() - 1, -1, -1): - var bubble = active_bubbles[i] - bubble.grow_timer -= delta - if bubble.grow_timer <= 0: - _explode_bubble(bubble) - active_bubbles.remove_at(i) -``` - -#### Bubble Spawn Logic - -```gdscript -func _try_spawn_bubble(): - var max_bubbles = max_bubbles_per_phase[current_phase] - if bubbles_this_phase >= max_bubbles: - return - var candidates = _generate_bubble_candidates() - if candidates.is_empty(): - return - # Weighted selection - var selected = _select_bubble_target(candidates) - _spawn_bubble(selected) - bubbles_this_phase += 1 -``` - -#### Bubble Candidate Scoring - -```gdscript -func _generate_bubble_candidates() -> Array: - var candidates: Array = [] - var players = get_tree().get_nodes_in_group("Players") - for x in range(ARENA_SIZE): - for z in range(ARENA_SIZE): - var pos = Vector2i(x, z) - if not _is_cell_valid_for_bubble(pos): - continue - var score = _calculate_bubble_score(pos, players) - candidates.append({"pos": pos, "score": score}) - return candidates - -func _calculate_bubble_score(pos: Vector2i, players: Array) -> float: - var score: float = 0.0 - - # CampingScore - for pid in camping_tracker: - var camp = camping_tracker[pid] - if pos.distance_to(camp.position) <= 4: - var camp_duration = elapsed_time - camp.since - if camp_duration > 10.0 and player_cleansers.get(pid, 0) > 0: - score += 80.0 - elif camp_duration > 8.0: - score += 60.0 - elif camp_duration > 5.0: - score += 40.0 - - # UntouchedAreaScore - if _is_near_untouched_cluster(pos): - score += 30.0 - - # PlayerClusterScore - var nearby_players = 0 - for p in players: - var p_pos = Vector2i(p.position.x, p.position.z) - if pos.distance_to(p_pos) <= 5: - nearby_players += 1 - if nearby_players >= 2: - score += 20.0 - - # MissionRouteScore - if _is_important_for_scoring(pos): - score += randf_range(10.0, 20.0) - - # RandomNoise - score += randf_range(-20.0, 20.0) - - # DirectHitPenalty - for p in players: - var p_pos = Vector2i(p.position.x, p.position.z) - if pos == p_pos: - score -= 60.0 - break - - # RecentBubblePenalty - for recent in recent_bubble_positions: - if pos.distance_to(recent) <= 5: - score -= 50.0 - break - - # UnfairTrapPenalty - if _would_create_unfair_trap(pos): - score -= 100.0 - - return score -``` - -#### Bubble Explosion - -```gdscript -func _explode_bubble(bubble: Dictionary): - var center = bubble.center - var explosion_area: Array[Vector2i] = [] - for dx in range(-1, 2): - for dz in range(-1, 2): - var pos = Vector2i(center.x + dx, center.y + dz) - if _is_cell_valid_for_growth(pos): - explosion_area.append(pos) - # Telegraph 3×3 area briefly, then apply sticky - _telegraph_cells(explosion_area) - get_tree().create_timer(0.5).timeout.connect(func(): - _apply_sticky_cells(explosion_area) - rpc("sync_bubble_explode", center, explosion_area) - recent_bubble_positions.append(center) - if recent_bubble_positions.size() > 5: - recent_bubble_positions.remove_at(0) - ) - rpc("sync_bubble_explode_vfx", center) -``` - ---- - -### 3.10 Movement Buffer System - -#### Buffer Detection - -```gdscript -func _detect_movement_buffers(): - # Find all connected clusters of SAFE cells - var visited: Dictionary = {} - var clusters: Array = [] - for x in range(ARENA_SIZE): - for z in range(ARENA_SIZE): - var pos = Vector2i(x, z) - if visited.has(pos) or not _is_cell_safe(pos): - continue - var cluster = _flood_fill_safe_cluster(pos, visited) - clusters.append(cluster) - # Apply buffer penalties to clusters that are critical for movement - for cluster in clusters: - if _is_critical_for_movement(cluster): - for pos in cluster: - if not movement_buffers.has(pos): - movement_buffers[pos] = {"penalty": 1.0, "created_at": elapsed_time} -``` - -#### Buffer Decay - -```gdscript -func _decay_movement_buffers(): - var to_remove: Array = [] - for pos in movement_buffers: - var buffer = movement_buffers[pos] - # Every 5 seconds: reduce penalty by 25% - var age = elapsed_time - buffer.created_at - var decay_cycles = int(age / 5.0) - buffer.penalty *= pow(0.75, decay_cycles) - # Phase change: reduce by 50% - # (Applied once at phase transition, tracked separately) - # Final 30s: remove most - if elapsed_time > 150.0: - buffer.penalty *= 0.1 - if buffer.penalty < 0.05: - to_remove.append(pos) - for pos in to_remove: - movement_buffers.erase(pos) -``` - -#### Camping Detection - -```gdscript -func _update_camping_tracker(): - var players = get_tree().get_nodes_in_group("Players") - for p in players: - var pid = p.get_multiplayer_authority() - var p_pos = Vector2i(p.position.x, p.position.z) - if camping_tracker.has(pid): - var camp = camping_tracker[pid] - if p_pos == camp.position: - pass # Still camping - else: - camping_tracker[pid] = {"position": p_pos, "since": elapsed_time} - else: - camping_tracker[pid] = {"position": p_pos, "since": elapsed_time} -``` - ---- - -### 3.11 Sticky Cell Application - -```gdscript -func _apply_sticky_cells(positions: Array[Vector2i]): - for pos in positions: - if not _is_cell_valid_for_growth(pos): - continue - sticky_cells[pos] = true - telegraphed_cells.erase(pos) - # Set Layer 2 overlay - enhanced_gridmap.set_cell_item(Vector3i(pos.x, 2, pos.y), TILE_STICKY) - # Check if any player is now on sticky - _check_players_on_sticky() - # Update A* costs - enhanced_gridmap.update_astar_costs() - # Sync to clients - rpc("sync_sticky_cells", sticky_cells.keys()) -``` - ---- - -### 3.12 Player Sticky Check - -```gdscript -func _check_players_on_sticky(): - var players = get_tree().get_nodes_in_group("Players") - for p in players: - var p_pos = Vector2i(p.position.x, p.position.z) - if sticky_cells.has(p_pos): - var pid = p.get_multiplayer_authority() - if is_cleanser_active(pid): - clear_sticky_cell(p_pos) - use_cleanser_cell(pid) - else: - _trap_player(p) -``` - ---- - -### 3.13 Path Safety Check - -```gdscript -func _apply_path_safety(selected: Array[Vector2i]) -> Array[Vector2i]: - if elapsed_time > 150.0: # Final 30s: softer rules - return selected - var players = get_tree().get_nodes_in_group("Players") - var result = selected.duplicate() - for p in players: - var pid = p.get_multiplayer_authority() - if trapped_players.has(pid): - continue - var p_pos = Vector2i(p.position.x, p.position.z) - # Temporarily apply selected cells - var temp_sticky = sticky_cells.duplicate() - for pos in result: - temp_sticky[pos] = true - # Check if player has reachable safe cells within 6–8 cells - var has_escape = _has_reachable_safe_cell(p_pos, temp_sticky, 8) - if not has_escape: - # Replace some cells with safer alternatives - result = _replace_with_safer_candidates(result, 2) - return result -``` - ---- - -### 3.14 Telegraph System (Modified) - -The existing telegraph system works but needs adaptation for growth ticks instead of cannon volleys. - -```gdscript -func _telegraph_cells(positions: Array[Vector2i]): - for pos in positions: - telegraphed_cells[pos] = true - enhanced_gridmap.set_cell_item(Vector3i(pos.x, 2, pos.y), TILE_TELEGRAPH) - rpc("sync_growth_telegraph", positions) - # Animate telegraph - _animate_growth_telegraph(positions) -``` - -**Reuse existing** `_animate_telegraph()` tween pattern from current GauntletManager. - ---- - -### 3.15 Network Sync - -| Data | Sync Method | Pattern | -|---|---|---| -| Sticky cells | `rpc("sync_sticky_cells", positions)` | Same as `sync_grid_item` | -| Growth telegraph | `rpc("sync_growth_telegraph", positions)` | Same as `sync_telegraph` | -| Phase changes | `rpc("sync_gauntlet_phase", phase_idx, elapsed)` | Same as `sync_phase` | -| Bubble spawn | `rpc("sync_bubble_spawn", center, grow_duration)` | New RPC | -| Bubble explode | `rpc("sync_bubble_explode", center, area)` | New RPC | -| Trap state | `player.rpc("sync_trapped", true)` | Same as `sync_stop_freeze` | -| Cleanser grant | `rpc("sync_cleanser", peer_id, count)` | Same as `sync_goal_count` | -| Smack state | `player.rpc("sync_smack_state", charged)` | Same as `sync_modulate` | - ---- - -### 3.16 Integration Points in `main.gd` - -The existing integration in `main.gd` already handles GauntletManager. No changes needed unless the mode name string is updated. - ---- - -## 4. New Files Summary - -| File | Type | Purpose | -|---|---|---| -| (none) | — | All changes are modifications to existing `gauntlet_manager.gd` | - -## 5. Modified Files Summary - -| File | Changes | -|---|---| -| `scripts/managers/gauntlet_manager.gd` | **Major rewrite:** Replace cannon system with growth tick system, add candidate scoring, add candy bubble system, add movement buffer system, add layer calculation, change arena to 24×24 | -| `scripts/game_mode.gd` | Optionally rename string to `"Candy Pump Survival"` | -| `scripts/managers/lobby_manager.gd` | Optionally rename mode string; update settings (remove cannon_interval, volley_size; add growth_interval, cells_per_tick) | -| `scripts/mode_config.gd` | Update schema: remove `gauntlet_cannon_interval`, `gauntlet_volley_size`; add `gauntlet_growth_interval`, `gauntlet_cells_per_tick_phase1/2/3` | -| `scenes/main.gd` | Update mode string match if renamed | - ---- - -## 6. Helper Methods Required - -These utility methods need to be added to `gauntlet_manager.gd`: - -```gdscript -# Cell validation -func _is_cell_valid_for_growth(pos: Vector2i) -> bool -func _is_cell_valid_for_bubble(pos: Vector2i) -> bool -func _is_cell_safe(pos: Vector2i) -> bool - -# Neighbor queries -func _get_8_neighbors(pos: Vector2i) -> Array[Vector2i] -func _flood_fill_safe_cluster(start: Vector2i, visited: Dictionary) -> Array[Vector2i] - -# Cluster analysis -func _expands_sticky_cluster(pos: Vector2i) -> bool -func _connects_sticky_clusters(pos: Vector2i) -> bool -func _is_near_untouched_cluster(pos: Vector2i) -> bool -func _is_critical_for_movement(cluster: Array) -> bool - -# Route analysis -func _is_high_traffic_route(pos: Vector2i) -> bool -func _is_important_for_scoring(pos: Vector2i) -> bool -func _would_trap_player(pos: Vector2i) -> bool -func _removes_last_exit(pos: Vector2i) -> bool -func _makes_route_too_narrow(pos: Vector2i) -> bool -func _would_create_unfair_trap(pos: Vector2i) -> bool -func _has_reachable_safe_cell(from: Vector2i, temp_sticky: Dictionary, radius: int) -> bool - -# Repetition tracking -func _was_recently_targeted(pos: Vector2i) -> bool -func _region_targeted_repeatedly(pos: Vector2i) -> bool - -# Bubble helpers -func _select_bubble_target(candidates: Array) -> Vector2i -func _replace_with_safer_candidates(selected: Array[Vector2i], count: int) -> Array[Vector2i] -``` - ---- - -## 7. Implementation Priority (Recommended Order) - -1. **Update arena to 24×24** — Modify `_setup_arena()`, update `NPC_CENTER`, update `_build_arena_layers()` -2. **Replace cannon with growth tick** — Remove `_fire_volley()`, add `_process_growth_tick()`, `_generate_candidates()`, `_calculate_candidate_score()` -3. **Weighted cell selection** — `_select_cells_weighted()`, sticky application, A* cost update -4. **Movement buffer system** — `_detect_movement_buffers()`, `_decay_movement_buffers()`, buffer penalty in scoring -5. **Path safety check** — `_apply_path_safety()`, `_has_reachable_safe_cell()`, replace unsafe selections -6. **Candy bubble system** — Bubble timer, `_try_spawn_bubble()`, bubble scoring, `_explode_bubble()` -7. **Camping detection** — `_update_camping_tracker()`, camping score in candidate and bubble scoring -8. **Update HUD** — Growth tick indicator, bubble warning, phase label -9. **Network sync** — New RPCs for growth telegraph, bubble spawn/explode -10. **Bot AI** — Sticky avoidance, pathfinding through sticky, cleanser usage -11. **Polish** — VFX for growth ticks, bubble animations, screen shake on explosion, sound effects -12. **Update lobby settings** — Replace cannon/volley settings with growth settings in `lobby_manager.gd` and `mode_config.gd` - ---- - -## 8. Risk Assessment - -| Risk | Mitigation | -|---|---| -| GridMap Layer 2 conflict with existing freeze/safe overlays | Gauntlet mode is exclusive — no freeze/safe tiles in this mode | -| 24×24 grid performance (576 cells + scoring every 3s) | Scoring runs on server only; candidate list is max 567 cells; weighted selection is O(n log n) | -| Movement buffer creating invisible safe zones that feel unfair | Buffers decay aggressively; camping override removes them; final 30s removes most; players experience it as "uneven growth" not "protected zones" | -| Path safety check preventing any arena pressure | Only triggers when a player would be fully trapped; final 30s disables strict check | -| Bubble stacking creating unavoidable traps | RecentBubblePenalty (-50) prevents nearby bubbles; max 5 per round; UnfairTrapPenalty (-100) prevents instant failures | -| Candidate scoring feeling too complex to tune | Start with simple weights; each component is independent and tunable; playtest to adjust | -| A* pathfinding cost updates every 3s causing lag | `update_astar_costs()` is lightweight (updates existing AStar2D); only runs on server | diff --git a/docs/gauntlet-visualization.html b/docs/gauntlet-visualization.html deleted file mode 100644 index 934a33a..0000000 --- a/docs/gauntlet-visualization.html +++ /dev/null @@ -1,1209 +0,0 @@ - - - - - -Candy Pump Survival — Gameplay Visualization - - - -

Candy Pump Survival

-
Gauntlet Mode — Gameplay Visualization
- -
-
Timer
3:00
-
Phase
OUTER PRESSURE
-
Coverage
0%
-
Growth Tick
0
-
Bubbles
0
-
- -
- - -
- -
-
-

Event Log

-
-
-
- - - - diff --git a/docs/gauntlet.md b/docs/old-gauntlet.md similarity index 100% rename from docs/gauntlet.md rename to docs/old-gauntlet.md diff --git a/docs/rpc_migration_audit.md b/docs/rpc_migration_audit.md deleted file mode 100644 index 928da7b..0000000 --- a/docs/rpc_migration_audit.md +++ /dev/null @@ -1,89 +0,0 @@ -# Nakama JS → Lua Migration Audit - -## JS RPC Registration (core.js.bak lines 12-72) - -| # | JS RPC Name | JS Function | Lua Module | Lua RPC Name | Status | -|---|---|---|---|---|---| -| 1 | `admin_kick_player` | `rpcAdminKickPlayer` | admin.lua | `lua_admin_kick_player` | ✅ | -| 2 | `admin_ban_player` | `rpcAdminBanPlayer` | admin.lua | `lua_admin_ban_player` | ✅ | -| 3 | `admin_unban_player` | `rpcAdminUnbanPlayer` | admin.lua | `lua_admin_unban_player` | ✅ | -| 4 | `admin_get_ban_list` | `rpcAdminGetBanList` | admin.lua | `lua_admin_get_ban_list` | ✅ | -| 5 | `admin_get_server_stats` | `rpcAdminGetServerStats` | admin.lua | `lua_admin_get_server_stats` | ✅ | -| 6 | `admin_get_player_list` | `rpcAdminGetPlayerList` | admin.lua | `lua_admin_get_player_list` | ✅ Fixed | -| 7 | `admin_end_match` | `rpcAdminEndMatch` | admin.lua | `lua_admin_end_match` | ✅ | -| 8 | `admin_set_user_role` | `rpcAdminSetUserRole` | admin.lua | `lua_admin_set_user_role` | ✅ | -| 9 | `admin_list_users` | `rpcAdminListUsers` | admin.lua | `lua_admin_list_users` | ✅ | -| 10 | `admin_delete_users` | `rpcAdminDeleteUsers` | admin.lua | `lua_admin_delete_users` | ✅ Bug fixed | -| 11 | `admin_topup_gold` | `rpcAdminTopupGold` | admin.lua | `lua_admin_topup_gold` | ✅ | -| 12 | `admin_clear_global_chat` | `rpcAdminClearGlobalChat` | admin.lua | `lua_admin_clear_global_chat` | ✅ | -| 13 | `get_user_profile` | `rpcGetUserProfile` | user.lua | `lua_get_user_profile` | ✅ | -| 14 | `update_user_profile` | `rpcUpdateUserProfile` | user.lua | `lua_update_user_profile` | ✅ | -| 15 | `search_users` | `rpcSearchUsers` | user.lua | `lua_search_users` | ✅ | -| 16 | `get_leaderboard_stats` | `rpcGetLeaderboardStats` | leaderboard.lua | `lua_get_leaderboard_stats` | ✅ | -| 17 | `admin_update_stats` | `rpcAdminUpdateStats` | leaderboard.lua | `lua_admin_update_stats` | ✅ | -| 18 | `admin_delete_stats` | `rpcAdminDeleteStats` | leaderboard.lua | `lua_admin_delete_stats` | ✅ | -| 19 | `admin_sync_leaderboard` | `rpcAdminSyncLeaderboard` | leaderboard.lua | `lua_admin_sync_leaderboard` | ✅ | -| 20 | `submit_score` | `rpcSubmitScore` | leaderboard.lua | `lua_submit_score` | ✅ | -| 21 | `sync_leaderboard` | `rpcSyncLeaderboard` | leaderboard.lua | `lua_sync_leaderboard` | ✅ | -| 22 | `change_credentials` | `rpcChangeCredentials` | user.lua | `lua_change_credentials` | ✅ | -| 23 | `reset_stats` | `rpcResetStats` | leaderboard.lua | `lua_reset_stats` | ✅ | -| 24 | `send_lobby_invite` | `rpcSendLobbyInvite` | user.lua | `lua_send_lobby_invite` | ✅ | -| 25 | `send_friend_request` | `rpcSendFriendRequest` | user.lua | `lua_send_friend_request` | ✅ | -| 26 | `claim_daily_reward` | `rpcClaimDailyReward` | daily_rewards.lua | `lua_claim_daily_reward` | ✅ | -| 27 | `get_daily_reward_state` | `rpcGetDailyRewardState` | daily_rewards.lua | `lua_get_daily_reward_state` | ✅ | -| 28 | `set_daily_reward_config` | `rpcSetDailyRewardConfig` | daily_rewards.lua | `lua_set_daily_reward_config` | ✅ | -| 29 | `get_daily_reward_config_admin` | `rpcGetDailyRewardConfigAdmin` | daily_rewards.lua | `lua_get_daily_reward_config_admin` | ✅ | -| 30 | `admin_send_mail` | `rpcAdminSendMail` | inbox.lua | `lua_admin_send_mail` | ✅ | -| 31 | `admin_list_mail` | `rpcAdminListMail` | inbox.lua | `lua_admin_list_mail` | ✅ | -| 32 | `admin_update_mail` | `rpcAdminUpdateMail` | inbox.lua | `lua_admin_update_mail` | ✅ | -| 33 | `admin_delete_mail_server` | `rpcAdminDeleteMailServer` | inbox.lua | `lua_admin_delete_mail_server` | ✅ | -| 34 | `get_mail` | `rpcGetMail` | inbox.lua | `lua_get_mail` | ✅ | -| 35 | `claim_mail_reward` | `rpcClaimMailReward` | inbox.lua | `lua_claim_mail_reward` | ✅ | -| 36 | `delete_mail` | `rpcDeleteMail` | inbox.lua | `lua_delete_mail` | ✅ | -| 37 | `save_mail_state` | `rpcSaveMailState` | inbox.lua | `lua_save_mail_state` | ✅ | -| 38 | `purchase_item` | `rpcPurchaseItem` | economy.lua | `lua_purchase_item` | ✅ | -| 39 | `get_shop_catalog` | `rpcGetShopCatalog` | economy.lua | `lua_get_shop_catalog` | ✅ | -| 40 | `buy_currency` | `rpcBuyCurrency` | economy.lua | `lua_buy_currency` | ✅ | -| 41 | `admin_set_featured_banners` | `rpcAdminSetFeaturedBanners` | economy.lua | `lua_admin_set_featured_banners` | ✅ | -| 42 | `admin_get_featured_banners` | `rpcAdminGetFeaturedBanners` | economy.lua | `lua_admin_get_featured_banners` | ✅ | - -## Hooks - -| JS Hook | JS Function | Lua Module | Status | -|---|---|---|---| -| `registerAfterAuthenticateSteam` | `afterAuthenticateSteam` | core.lua | ✅ Fixed | - -## Non-RPC Items - -| Item | Lua Module | Status | -|---|---|---| -| `leaderboardCreate("global_high_score")` | core.lua | ✅ | -| `ADMIN_ROLES` definition | utils.lua | ✅ | -| `isAdmin` / `isMatchHost` helpers | utils.lua | ✅ | -| `requireAdmin` | utils.lua | ✅ | -| `requireAdminOrHost` | utils.lua | ✅ | - -## Bugs Fixed This Session - -1. **`admin_get_player_list` RPC was missing** — Added to admin.lua -2. **`afterAuthenticateSteam` hook was missing** — Added to core.lua via `nk.register_req_after` -3. **`admin_delete_users` metadata bug** — `pcall(nk.json_decode, ...)` result was discarded, so the admin-role guard never worked. Fixed to assign decoded result. -4. **`core.lua` had stale duplicate stubs** — Duplicate `rpc_get_user_profile` and `rpc_admin_kick_player` registrations conflicted with user.lua and admin.lua. Removed. - -## Final Module Map - -``` -server/nakama/ -├── main.lua # Entrypoint - requires all modules -├── lua/ -│ ├── utils.lua # Shared helpers (is_admin, require_admin, etc.) -│ ├── core.lua # Steam auth hook + leaderboard creation -│ ├── admin.lua # 12 admin RPCs -│ ├── user.lua # 6 user RPCs -│ ├── daily_rewards.lua # 4 daily reward RPCs -│ ├── leaderboard.lua # 7 leaderboard RPCs -│ ├── inbox.lua # 8 inbox/mail RPCs -│ └── economy.lua # 5 economy/shop RPCs -``` - -**Total: 42 RPCs + 1 auth hook = full 1:1 parity with core.js.bak** ✅ diff --git a/docs/tekton-dash-knowledge-base.html b/docs/tekton-dash-knowledge-base.html deleted file mode 100644 index 979f6a4..0000000 --- a/docs/tekton-dash-knowledge-base.html +++ /dev/null @@ -1,2191 +0,0 @@ - - - - - - - Knowledge Base: Multi-Platform & Regional Production Deployment Blueprint - - - - - - - - - - - - - -
-
-
-
- Godot -
-
- Nakama - x Godot 4 - Production Blueprint -
-
-
- - - Production Ready - - - - -
-
- - - -
- -
- - -
-

- Knowledge Base & Production Deployment Blueprint -

-

- A highly descriptive, production-centric strategy for engineering global, multi-store authentication, - transaction validation, and regulatory compliance networks utilizing Godot 4 and Heroic Labs Nakama. -

- -
-
-
-
-
-

Core Jurisdictions -

-
- - - - - - Europe, Mainland China Transit & APAC - Networks -
-
-
-
-
-
-

IAP Validation

-

Asynchronous, Server-to-Server, Ledger-Signed - Receipt Verification

-
-
-
-
-
-

Store Integration

-

Steamworks, Google Play, Apple App Store, TapSDK - (Zero-Commission)

-
-
-
-
- - -
-
- -

1. Regional Infrastructure & Regulatory Compliance

-
-

- Deploying cross-border games requires careful partitioning of databases and game nodes to satisfy - extreme technical boundaries (e.g., latency issues caused by the Great Firewall) and data protection - statutes (e.g., GDPR, local municipal privacy mandates). -

- -
- -
-
-
- - Europe - (EU) - Webdock -
- -
-

GDPR & DMA Pipeline

-
    -
  • Central - Cluster: Host on high-density Webdock.io Ryzen-powered VPS - profiles located in Frankfurt or Vienna.
  • -
  • - Sovereignty: Webdock offers EU-owned infrastructure with strict - hardware insulation and zero overseas sub-processor data leaks.
  • -
  • User - Control: Integrate explicit game-level telemetry opt-out flags which halt - outgoing Nakama analytics scripts instantly.
  • -
-
- - -
-
-
- - - China - Transit - HostHatch -
- -
-

HostHatch HK Edge Gateway

-
    -
  • GFW - Proximity: Deploy regional proxy logic to HostHatch.com - Hong Kong nodes, featuring direct low-latency peering tunnels.
  • -
  • Regulatory - Separation: Standalone DB instances keep Mainland China data segregated - from Western clusters while resolving network hops near the target audience.
  • -
  • NPPA - Integrations: Route localized traffic from HK edge to Chinese validation - and anti-addiction registry backends efficiently.
  • -
-
- - -
-
-
- - - - Asia - (Ex-CN) - Following CN -
- -
-

APAC Edge Arrays

-
    -
  • Transit - Integration: Set up edge nodes in Tokyo, Seoul, and Singapore configured to - capture traffic spills when cross-border HK connections are saturated.
  • -
  • Latency - Optimization: Run high-performance CockroachDB clusters to maintain global - synchronization while serving nearby regional users under 45ms.
  • -
  • Data - Privacy: Satisfy local guidelines (Japan's APPI, South Korea's PIPA) using - explicit user data deletion interfaces inside Nakama profile routes.
  • -
-
-
- - -
- -
- Critical GFW Operational Warning: - Real-time WebSocket and UDP connection signals across the Great Firewall suffer massive packet - losses (≥ 25%). Incorporating HostHatch's Hong Kong node acts as an indispensable entry buffer; - however, complete logical segregation of the Chinese client backend instance is still mandatory. -
-
-
- - -
-
-
- -

2. Storefront Commissions & Licensing Pipeline

-
-
-

- Operating margins rely on optimizing each storefront's fee parameters. Before collecting gross revenue, - publishers must clear the initial platform entry fees. -

- - -
-

- - Initial Publishing Costs & First Fee Settlement Grid -

-

- Below is the required capital breakdown needed to register your identity and prepare storefront - slots before pushing your initial Godot client build to production channels. -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PlatformFirst Fee AmountMarket FlagFee Recurrence TypeRefundability StatusSettlement Methods
Steamworks (PC)$100.00 USD (per product) - - One-time per App SlotYes (Refunded after $1,000 in gross sales) - Credit Card, PayPal, Steam Wallet, Wire Transfer
Itch.io (PC)$0.00 USD (Zero entry fee) - - NoneNot ApplicableNone (Optional tax documentation verification)
Google Play$25.00 USD - - One-time per Developer IdentityNoCredit/Debit Card (requires Google Pay Profile)
Apple App Store$99.00 USD - - Annual Subscription RenewalNoCredit Card (linked to Apple ID Developer Account)
TapTap Developer$0.00 USD (Corporate validation) - - - NoneNot ApplicableRequires legal corporate identity / ICP verification
-
-
- -
- Net Yield Production - Formula -
- R_net = R_gross × (1 - C_store - T_tax) - F_fees -
-

- Where C_store matches the target store platform commission, T_tax matches regional withholding taxes, and F_fees encapsulates external server transaction margins and API - query operations. -

-
- - -
-
- - - - - -
- -
- -
-
-
-

Steamworks Implementation Matrix

- - - - - - - - - - - - - - - - - -
Commission Rate30% (Default)
Volume Scaling25% at $10M | 20% at $50M
First Fee$100 USD (Steam Direct - App Deposit)
Nakama VerificationServer Authenticated - Session Ticket
-
-
-

Technical - Execution

-

- Initialize GodotSteam - dynamically inside your client. The user retrieves their secure hex session ticket - locally, passing it down to Nakama using the native client driver hook to eliminate - credential spoofing risks. -

- Endpoint: - client.authenticate_steam_async(ticket) -
-
-
- - - - - - - - - - - - -
-
-
- - -
-
- -

3. Monetization Architecture & Secure IAP Loop

-
-

- Client-side reporting is inherently untrustworthy. Memory injectors (e.g., Lucky Patcher on Android, - memory editing tools on PC) can manipulate the client runtime to simulate successful purchases. - Implementing a robust, asynchronous verification cycle ensures validation is handled strictly by the - server. -

- - -
-

Asynchronous Verification Topology

- -
- -
-
- 1
-

Purchase & Tokenization

-

The Godot client requests checkout. The player submits payment - to the store network, which issues an encrypted, signed platform transaction token.

-
- -
-
- 2
-

Nakama RPC Ingestion

-

The client forwards the raw transaction token securely to - Nakama via an RPC function call: verify_purchase.

-
- -
-
- 3
-

Server Validation Check

-

Nakama blocks immediate user manipulation. It connects - server-to-server with Google, Apple, or Steam APIs to verify status and signatures.

-
- -
-
- 4
-

Ledger Provisioning

-

Upon verification, Nakama updates the persistent storage - wallet data and broadcasts confirmation back to the Godot client.

-
-
-
-
- - -
-
- -

4. Core Architecture: Unified Identity Manager Decision Flow - Chart

-
-

- The Unified Identity Manager dynamically discovers platforms at runtime, resolving features and - singletons without breaking compilations on platforms lacking those SDK wrappers. Click the platform - modes below to preview the path execution, safety hooks, and token routing. -

- - -
- -
- - - - - -
- - -
- - -
-
-
Game - Initialization
-

_ready() / dispatch_platform_auth()

-

Queries environment architecture and singletons

-
-
- - -
-
-
- - -
- - -
-
- Steam - PC - Steam Target -
-
-
-
Class Check
- ClassDB.has_singleton("Steam") -
-
-
-
Ticket Retrieval
-

Grabs session hex ticket asynchronously -

-
-
-
-
Nakama Endpoint
- authenticate_steam_async() -
-
-
- - -
-
- Google Play - Android - OS Target -
-
-
-
Feature Gating
- OS.has_feature("taptap") -
-
- YesNo
-
-
-
TapSDK
-

Fetch OAuth Token

-
-
-
Google Play
-

Fetch Auth Code

-
-
-
-
-
Nakama Endpoint
- authenticate_custom_async()
OR
authenticate_google_async()
-
-
-
- - -
-
- Apple - iOS - OS Target -
-
-
-
Feature Gating
- OS.has_feature("taptap") -
-
- YesNo
-
-
-
TapSDK
-

Fetch OAuth Token

-
-
-
Apple Auth
-

Fetch Identity Token

-
-
-
-
-
Nakama Endpoint
- authenticate_custom_async()
OR
authenticate_apple_async()
-
-
-
- - -
-
- Itch.io - PC/Itch.io - Standalone -
-
-
-
Device Hardware ID
- OS.get_unique_id() -
-
-
-
Hardware Hashing
-

Generate hardware fingerprint

-
-
-
-
Nakama Endpoint
- authenticate_device_async() -
-
-
- -
- - -
-
-
- - -
-
-
- - - - - Nakama Server Response Node -
-

NakamaSession Established

-

- Session token decoded & validated, persistent profiles resolved, and socket pipelines - opened. Global matching/telemetry gates unlocked. -

-
-
- -
-
-
- - - - - - -
-
-
- -

5. Project Management (PR) Board & AI Checklist

-
-
-

- This section serves as a fully detailed tracking board. It merges production readiness, backend reconstruction, gameflow audit, and Steam depot release tasks. - Every task is fully expanded with checklists and automated testing criteria so you can track AI execution seamlessly. -

- -
-

Priority Rule

-

- Do not spend release time on Steam depot upload, signing polish, or branch promotion until P0 backend authority - is fixed. Current audit found client-authoritative economy, gacha paths, and sync loopholes. - Those are launch blockers because they can corrupt wallet, inventory, match state, and account identity before first public build. -

-
- - -
- -
- -
- P0 - PRD-P0-1 -

Economy Authority

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- server/nakama/tekton_admin.js, user_profile_manager.gd -
-
- -
-

Goal / Risk

-

Stop trusting client prices, categories, package IDs. Reconstruct server-authoritative economy.

-
- -
-

Execution Checklist

-
    -
  • Create server catalog mapping item IDs to category, price, currency type, stack rules.
  • Change purchase request so client sends only item ID, quantity, and optional idempotency key.
  • Validate balance and inventory capacity server-side before mutation.
  • Replace fake currency purchase with receipt verification placeholder interface per platform.
  • Write wallet/inventory mutation audit entry with user ID, request ID, before/after values.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Analyze current Tekton economy flow in server/nakama/tekton_admin.js and Godot callers. Reconstruct shop purchase authority so the client no longer sends trusted price_gold, price_star, category, or reward data. Add a server-side item catalog and update rpcPurchaseItem to accept only item_id, quantity, and idempotency_key. Replace rpcBuyCurrency behavior with a receipt-verification-safe interface that records pending/verified transactions and never grants premium currency from package ID alone. Preserve existing profile/wallet behavior where possible. Add validation, normalized errors, and audit ledger writes. Update Godot callers to match new payload shape. Acceptance: no wallet or inventory mutation depends on client-submitted price/category/package intent; duplicate idempotency key does not duplicate grant; existing shop UI can still request purchases.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Call `rpcPurchaseItem` with modified price/category from client. Assert server rejects or ignores client price and uses catalog price. Assert duplicate idempotency keys return the exact same transaction result without deducting twice.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P0-1]: Economy Authority**
-- **Goal:** Stop trusting client prices, categories, package IDs. Reconstruct server-authoritative economy.
-- **Status:** Integrated & verified. Code changes applied to: server/nakama/tekton_admin.js, user_profile_manager.gd
- -
-
-
-
-
- -
- -
- P0 - PRD-P0-2 -

Gacha Authority

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- gacha_manager.gd, Nakama economy RPCs -
-
- -
-

Goal / Risk

-

Move RNG, pity, cost consume, and rewards server-side.

-
- -
-

Execution Checklist

-
    -
  • Add server RPC for gacha pull with banner ID, pull count, and idempotency key.
  • Store pity and banner state server-side.
  • Server consumes cost, rolls reward, writes item/fragment result, and returns canonical result.
  • Client only animates returned result; no local grant or deduction.
  • Add migration note for existing local pity/fragment data.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Refactor Tekton gacha so authority lives in Nakama. Read scripts/managers/gacha_manager.gd, user_profile_manager.gd, and server/nakama/tekton_admin.js before editing. Add server-side RPCs for gacha_pull and any needed banner/profile state. Server must own RNG, pity counter, cost deduction, reward choice, inventory/fragment writes, and audit/idempotency. Client must become presentation-only: it sends banner_id, pull_count, and idempotency_key, then animates the canonical server response. Remove local reward grant and local currency deduction from gacha_manager.gd. Acceptance: editing client RNG/pity code cannot change real rewards; duplicate pull request cannot duplicate rewards; profile refresh after pull shows server state.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Trigger `rpcGachaPull`. Assert client currency deduction happens only upon server response. Assert client cannot specify reward or manipulate RNG seed.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P0-2]: Gacha Authority**
-- **Goal:** Move RNG, pity, cost consume, and rewards server-side.
-- **Status:** Integrated & verified. Code changes applied to: gacha_manager.gd, Nakama economy RPCs
- -
-
-
-
-
- -
- -
- P0 - PRD-P0-3 -

Auth & Secrets Lock

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- auth_manager.gd, nakama_manager.gd, project.godot -
-
- -
-

Goal / Risk

-

Remove insecure Steam fallback, default App ID 480, hardcoded release secrets.

-
- -
-

Execution Checklist

-
    -
  • Replace production Steam App ID placeholder only when real ID exists.
  • Fail hard in Steam build if Steam ticket cannot be acquired.
  • Remove fallback email/custom auth from Steam release path.
  • Externalize server host, scheme, key, encryption key, and secrets.
  • Delete or environment-gate admin topup RPC and admin UI entry points.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Audit and harden Tekton authentication and admin mutation paths. Read project.godot, scripts/services/steamworks_manager.gd, scripts/managers/auth_manager.gd, scripts/nakama_manager.gd, scripts/ui/admin_panel.gd, and server/nakama/tekton_admin.js. Remove insecure Steam release fallback behavior so Steam builds authenticate only with valid Steam tickets. Add clear release guards for Steam App ID 480 so production export fails or warns loudly if still using test ID. Externalize backend config and local encryption material away from hardcoded production defaults. Remove or environment-gate rpcAdminTopupGold and ensure admin panel scenes/scripts are not included in player exports unless explicitly feature-flagged. Acceptance: Steam build cannot silently fall back to insecure auth; test App ID 480 is blocked for production; admin mint path is unavailable in production runtime.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Export project with Steam features. Disconnect Steam client. Assert game fails to authenticate and does NOT fallback to custom/device auth. Assert admin UI is entirely hidden in release builds.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P0-3]: Auth & Secrets Lock**
-- **Goal:** Remove insecure Steam fallback, default App ID 480, hardcoded release secrets.
-- **Status:** Integrated & verified. Code changes applied to: auth_manager.gd, nakama_manager.gd, project.godot
- -
-
-
-
-
- -
- -
- P0 - PRD-P0-4 -

Backend Deploy Safety

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- server/, Nakama runtime module -
-
- -
-

Goal / Risk

-

Replace manual module copy/restart with staging/prod deploy, health check, rollback.

-
- -
-

Execution Checklist

-
    -
  • Separate dev/staging/prod Nakama config and secrets.
  • Script module package/copy/restart with version label.
  • Add health check RPC after deploy.
  • Keep previous module artifact for rollback.
  • Add smoke checklist: auth, profile, shop, mail, gacha, friends, leaderboard.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Create a production-safe Nakama deployment workflow for Tekton. Review server/docker-compose.yaml, server/nakama/README.md, and current runtime module layout. Replace manual docker cp guidance with scripts or documented commands for staging and production deploys. Include environment-specific config/secrets, module version labeling, restart procedure, health check, smoke test commands, and rollback to previous module. Do not commit real secrets. Acceptance: a developer can deploy to staging, verify health, promote to production, and rollback using documented repeatable steps without manually editing containers.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Run deploy script. Assert Nakama server restarts without losing data. Trigger health check RPC to verify new module loaded successfully.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P0-4]: Backend Deploy Safety**
-- **Goal:** Replace manual module copy/restart with staging/prod deploy, health check, rollback.
-- **Status:** Integrated & verified. Code changes applied to: server/, Nakama runtime module
- -
-
-
-
-
- -
- -
- P0 - PRD-GF-P0-1 -

Spawn/Sync Authority Lock

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- main.gd, player.gd -
-
- -
-

Goal / Risk

-

Retain: deterministic pre-spawn sync. Remove: client-trusted teleport/update paths.

-
- -
-

Execution Checklist

-
    -
  • Keep deterministic pre-spawn strategy (client pre-creates lobby roster).
  • Remove client-trusted position mutation path that can move authoritative state without server validation.
  • Introduce server-owned spawn_revision and state_revision integers.
  • Reject stale updates on client and server.
  • Ensure reconnect flow requests full player sync, then grid sync, then mode-specific sync.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Audit and harden player spawn/sync authority in scenes/main.gd and scenes/player.gd. Keep deterministic pre-spawn strategy and existing server-authoritative item randomization pattern, but remove any client-trusted position mutation path that can move authoritative state without server validation. Introduce server-owned spawn_revision and state_revision integers sent with spawn and full-sync payloads. Reject stale updates on client and server. Ensure reconnect flow requests full player sync first, then full grid sync, then mode-specific sync (Stop n Go / Tekton Doors) with explicit ack step. Acceptance: client cannot force authoritative teleport; reconnecting client converges to identical player positions/goals/playerboards after one sync cycle; stale packets no longer overwrite newer state.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Simulate client sending stale spawn_revision. Assert server rejects. Reconnect mid-match, assert player converges to exact same grid position as before.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P0-1]: Spawn/Sync Authority Lock**
-- **Goal:** Retain: deterministic pre-spawn sync. Remove: client-trusted teleport/update paths.
-- **Status:** Integrated & verified. Code changes applied to: main.gd, player.gd
- -
-
-
-
-
- -
- -
- P0 - PRD-GF-P0-2 -

Lobby Start Gate Hardening

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- lobby.gd, lobby_manager.gd -
-
- -
-

Goal / Risk

-

Add preflight checklist RPC, check ready-state and host authority.

-
- -
-

Execution Checklist

-
    -
  • Preserve LAN/Nakama dual-mode behavior and tutorial fast path.
  • Add preflight readiness checks before _on_game_starting transition.
  • Verify session valid, host authority true, all player records present, mode config validated.
  • Add one typed preflight result object and render actionable errors.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Rework lobby game-start gating in scenes/lobby.gd and scripts/managers/lobby_manager.gd. Preserve LAN/Nakama dual-mode behavior and tutorial fast path, but add preflight readiness checks before _on_game_starting transition: session valid (or explicit guest/LAN mode), host authority true, all required player records present, mode config validated, and scene dependencies reachable. Add one typed preflight result object and render actionable errors in connection_status/status_label. Acceptance: start button cannot trigger broken scene load with partial state; host and clients see same preflight result; loading screen transition only occurs after preflight pass.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Attempt to start match without full player records. Assert UI blocks start and shows specific error string from preflight check.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P0-2]: Lobby Start Gate Hardening**
-- **Goal:** Add preflight checklist RPC, check ready-state and host authority.
-- **Status:** Integrated & verified. Code changes applied to: lobby.gd, lobby_manager.gd
- -
-
-
-
-
- -
- -
- P0 - PRD-GF-P0-3 -

RPC Sender Identity & Contract Clamp

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- main.gd, player.gd, lobby_manager.gd -
-
- -
-

Goal / Risk

-

Remove payload fields that claim identity. Validate sender natively.

-
- -
-

Execution Checklist

-
    -
  • Read all any_peer RPC entry points.
  • Remove payload fields that pretend to identify requester/authority (use get_remote_sender_id).
  • Verify sender identity and authority explicitly for state-mutation RPCs.
  • Normalize RPC contracts to carry stable error codes.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Clamp multiplayer RPC trust boundaries across scenes/main.gd, scenes/player.gd, and scripts/managers/lobby_manager.gd. Read all any_peer RPC entry points before editing. Keep fast RPC update flow, but remove payload fields that pretend to identify requester/authority when sender can be derived from multiplayer.get_remote_sender_id(). For room info, start flow, rematch, and state-mutation RPCs, verify sender identity and authority explicitly. Normalize RPC contracts so request payloads contain only data the caller is allowed to propose, and response payloads carry canonical server state plus stable error codes. Acceptance: spoofed requester IDs are ignored; unauthorized peers cannot mutate host/server-owned state; RPC errors are debuggable and consistent.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Call state-mutation RPC pretending to be another peer ID in payload. Assert server overrides payload with actual `get_remote_sender_id()` and blocks if unauthorized.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P0-3]: RPC Sender Identity & Contract Clamp**
-- **Goal:** Remove payload fields that claim identity. Validate sender natively.
-- **Status:** Integrated & verified. Code changes applied to: main.gd, player.gd, lobby_manager.gd
- -
-
-
-
-
- -
- -
- P0 - PRD-GF-P0-4 -

Chat/DM Abuse Control

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- lobby.gd, Nakama chat -
-
- -
-

Goal / Risk

-

Add moderation, throttling, sanitation, flood guard, and permission matrix.

-
- -
-

Execution Checklist

-
    -
  • Keep current channel UX, DM tabs, and history pull.
  • Add per-user send cooldown and max payload length limits.
  • Add command permission matrix (/clear admin only, all other slash commands explicit).
  • Mark unsent/failed messages in UI with retry policy.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Harden global chat and DM flow in scenes/lobby.gd and related Nakama chat policy. Keep current channel UX, DM tabs, and history pull, but add abuse controls: per-user send cooldown, max payload length, profanity/moderation hook placeholder, and command permission matrix (/clear admin only, all other slash commands explicit). Fix any DM append/state bug found during read-through. Prevent silent local-only divergence by marking unsent/failed messages in UI and retry policy. Acceptance: flood attempts are throttled; unauthorized command execution blocked server-side; message rendering sanitized and bounded; chat remains responsive under burst traffic.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Send 50 chat messages in 1 second. Assert Nakama throttles request and UI shows 'failed to send/cooldown' UI marker. Attempt `/clear` as non-admin, assert blocked.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P0-4]: Chat/DM Abuse Control**
-- **Goal:** Add moderation, throttling, sanitation, flood guard, and permission matrix.
-- **Status:** Integrated & verified. Code changes applied to: lobby.gd, Nakama chat
- -
-
-
-
-
- -
- -
- P1 - PRD-P1-1 -

Module Split & RPC Validation

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- tekton_admin.js -
-
- -
-

Goal / Risk

-

Split monolith into auth, economy, admin, mail, social, leaderboard, validation helpers.

-
- -
-

Execution Checklist

-
    -
  • Refactor tekton_admin.js into domain modules without changing external RPC names.
  • Create modules for auth, economy, admin, mail, social, leaderboard, storage, validation.
  • Add central validators for payload shape, types, limits.
  • Add normalized error responses with stable error codes.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Refactor server/nakama/tekton_admin.js into maintainable domain modules without changing external RPC names unless necessary. Create or plan modules for auth, economy, admin, mail, social, leaderboard, storage, and validation helpers. Add central validators for payload shape, types, limits, and allowed enum values. Add normalized error responses with stable error codes. Keep behavior compatible while moving code in small steps. Acceptance: RPC registration remains clear; each RPC validates payload before mutation; error responses are consistent; module split does not break existing smoke tests.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Call split RPC with missing payload fields. Assert central validator catches it and returns `INVALID_ARGUMENT` standard error code.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P1-1]: Module Split & RPC Validation**
-- **Goal:** Split monolith into auth, economy, admin, mail, social, leaderboard, validation helpers.
-- **Status:** Integrated & verified. Code changes applied to: tekton_admin.js
- -
-
-
-
-
- -
- -
- P1 - PRD-P1-2 -

Ledger, Idempotency & Storage Model

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- Wallet, inventory, fragments, mail rewards -
-
- -
-

Goal / Risk

-

Add mutation audit ledger, idempotency keys, and canonical fragment storage path.

-
- -
-

Execution Checklist

-
    -
  • Define one canonical fragment storage location and migration path.
  • Add idempotency keys for mail claim, daily reward, purchase, gacha, and admin adjustments.
  • Add audit records with source, user_id, mutation type, request_id.
  • Make mail claim transactional (claim, mark, return canonical state).
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Implement a canonical mutation ledger and idempotency policy for Tekton live-service rewards. Read server/nakama/tekton_admin.js, scripts/managers/user_profile_manager.gd, scripts/managers/mail_manager.gd, and gacha/profile storage code. Define one canonical fragment storage location and migration path. Add idempotency keys for mail claim, daily reward, purchase, gacha, and admin adjustments. Add audit records with source, user_id, mutation type, request_id, before/after summary, and timestamp. Make mail claim transactional: claim rewards, mark claimed, and return canonical updated state in one server response. Acceptance: repeated claim/purchase/reward requests do not duplicate grants; fragments read/write from one canonical path; mail UI refreshes from server-returned state.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Send identical mail claim RPC twice simultaneously. Assert only one processes successfully and the second returns 'already claimed'.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P1-2]: Ledger, Idempotency & Storage Model**
-- **Goal:** Add mutation audit ledger, idempotency keys, and canonical fragment storage path.
-- **Status:** Integrated & verified. Code changes applied to: Wallet, inventory, fragments, mail rewards
- -
-
-
-
-
- -
- -
- P1 - PRD-P1-3 -

Client Backend Facade

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- nakama_manager.gd, auth_manager.gd, backend_service.gd -
-
- -
-

Goal / Risk

-

Make one typed backend owner for session, socket, RPC calls, and central errors.

-
- -
-

Execution Checklist

-
    -
  • Decide whether BackendService becomes the sole typed backend facade or is deleted.
  • Implement one owner for client/session/socket.
  • Add typed methods for RPCs, central error handling.
  • Remove direct UI RPC scatter for economy/auth/mail/gacha/social flows.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Clean up Tekton client backend ownership. Read scripts/nakama_manager.gd, scripts/managers/auth_manager.gd, scripts/services/backend_service.gd, and UI/manager scripts that call NakamaManager.client.rpc_async directly. Decide whether BackendService becomes the sole typed backend facade or is deleted. Implement chosen direction in small steps: one owner for client/session/socket, typed methods for RPCs, central error handling, and no direct UI RPC scatter for economy/auth/mail/gacha/social flows. Acceptance: UI calls typed service/manager methods, not raw client.rpc_async; session/socket ownership is clear; duplicate auth/bootstrap code is removed or delegated.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Global search for `client.rpc_async` in `scripts/ui/`. Assert 0 results found (all go through facade).

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P1-3]: Client Backend Facade**
-- **Goal:** Make one typed backend owner for session, socket, RPC calls, and central errors.
-- **Status:** Integrated & verified. Code changes applied to: nakama_manager.gd, auth_manager.gd, backend_service.gd
- -
-
-
-
-
- -
- -
- P1 - PRD-GF-P1-1 -

Tutorial Isolation Contract

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- tutorial_manager.gd -
-
- -
-

Goal / Risk

-

Remove multiplayer-side effects during pause/freeze phases. Isolate tutorial boundaries.

-
- -
-

Execution Checklist

-
    -
  • Keep onboarding sequence and camera storytelling.
  • Enforce contract: no persistent wallet/profile mutation during tutorial.
  • Ensure no shared lobby state leakage.
  • Ensure clean bot/timer restore on exit, deterministic return-to-lobby handshake.
  • Replace broad pause/freeze side effects with scoped tutorial-state toggles.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Isolate tutorial runtime from multiplayer/session side effects. Review scripts/managers/tutorial_manager.gd and match lifecycle hooks. Keep onboarding sequence and camera storytelling, but enforce tutorial contract: no persistent wallet/profile mutation, no shared lobby state leakage, clean bot/timer restore on exit, deterministic return-to-lobby handshake. Replace broad pause/freeze side effects with scoped tutorial-state toggles where possible. Acceptance: exiting tutorial leaves no stale bot freeze, no leaked paused systems, and no corrupted room/session flags.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Abort tutorial midway. Assert main game tree is fully unpaused, bots are reset, and no 'tutorial_active' flags leak into lobby.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P1-1]: Tutorial Isolation Contract**
-- **Goal:** Remove multiplayer-side effects during pause/freeze phases. Isolate tutorial boundaries.
-- **Status:** Integrated & verified. Code changes applied to: tutorial_manager.gd
- -
-
-
-
-
- -
- -
- P1 - PRD-GF-P1-2 -

Mode Config Completeness

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- main.gd, lobby mode configs -
-
- -
-

Goal / Risk

-

Remove duplicated/inconsistent option toggles. Add schema-driven validation.

-
- -
-

Execution Checklist

-
    -
  • Keep existing Stop n Go custom UI.
  • Remove duplicated/fragile control toggles.
  • Implement Tekton Doors options with same host-authoritative lock and sync callbacks.
  • Introduce schema-driven config validation shared by host, client, and bootstrap.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Complete mode-configuration parity between Stop n Go and Tekton Doors in lobby and match bootstrap flow. Keep existing Stop n Go custom UI, but remove duplicated/fragile control toggles and implement Tekton Doors options with same host-authoritative lock and sync callbacks. Introduce schema-driven config validation shared by host, client display logic, and match bootstrap. Acceptance: both modes expose full validated config; non-host clients always mirror host values; invalid config rejected before match start.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Join as client, attempt to spoof mode config RPC. Assert host rejects invalid mode config changes and overrides client.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P1-2]: Mode Config Completeness**
-- **Goal:** Remove duplicated/inconsistent option toggles. Add schema-driven validation.
-- **Status:** Integrated & verified. Code changes applied to: main.gd, lobby mode configs
- -
-
-
-
-
- -
- -
- P1 - PRD-GF-P1-3 -

Backend Facade & Flow Decoupling

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- backend_service.gd, UI panels -
-
- -
-

Goal / Risk

-

Improve service ownership and typed errors. Add one backend facade.

-
- -
-

Execution Checklist

-
    -
  • Identify remaining UI/manager scripts calling client.rpc_async.
  • Migrate calls to the central BackendService or unified manager.
  • Implement central error mapping and retry policy.
  • Verify all gameflow-adjacent UI uses new typed methods.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Finish client backend decoupling for gameflow-adjacent live-service features. Read scripts/services/backend_service.gd plus UI/manager scripts that still call NakamaManager.client.rpc_async directly (profile, social, leaderboard, daily reward, mail, admin, friend flows). Decide whether BackendService becomes real facade or is removed. Implement one typed backend owner for auth/session/socket/RPC calls, central error mapping, and retry policy. Acceptance: gameflow-adjacent UI does not call raw client.rpc_async directly for production paths; backend ownership is obvious; future auth/RPC changes touch one service layer first, not many UI panels.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Trigger network failure during profile fetch. Assert BackendService retry policy handles it gracefully without UI hard-crashing.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P1-3]: Backend Facade & Flow Decoupling**
-- **Goal:** Improve service ownership and typed errors. Add one backend facade.
-- **Status:** Integrated & verified. Code changes applied to: backend_service.gd, UI panels
- -
-
-
-
-
- -
- -
- P1 - PRD-P1-4 -

Versioning & Patch Integrity

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- tools/, export_presets.cfg, version.json -
-
- -
-

Goal / Risk

-

Single release version source, checksums, compatibility rules, changelog archive.

-
- -
-

Execution Checklist

-
    -
  • Create one release version source (version.json or python script).
  • Update project version, export versions, Android version deterministically.
  • Update patch manifest and changelog archive.
  • Add patch integrity fields: checksum, size, minimum compatible app version.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Rebuild Tekton versioning workflow. Review tools/generate_version_json.py, tools/build_patch.gd, export_presets.cfg, project.godot, assets/data/version.json, README.md, and CHANGELOG_DRAFT.md. Create one release version source and update all platform metadata deterministically: project version, export versions, Android version/code, patch manifest, changelog archive, and Git tag instructions. Add patch integrity fields such as checksum, size, minimum compatible app version, and signature placeholder if signing is not available yet. Acceptance: one command or documented flow bumps release version; generated metadata matches across files; patch manifest can reject incompatible or corrupted patch.pck.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Run version bump script. Assert export_presets.cfg Android version code increments correctly and patch manifest checksum is updated.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P1-4]: Versioning & Patch Integrity**
-- **Goal:** Single release version source, checksums, compatibility rules, changelog archive.
-- **Status:** Integrated & verified. Code changes applied to: tools/, export_presets.cfg, version.json
- -
-
-
-
-
- -
- -
- P2 - PRD-P2-1 -

Steam Depot & Store Packaging

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- tools/steam/, export presets -
-
- -
-

Goal / Risk

-

Create SteamPipe VDFs, branch SOP, signing/notarization, platform filters.

-
- -
-

Execution Checklist

-
    -
  • Create tools/steam/app_build_.vdf and per-platform depot templates.
  • Document steamcmd upload command, branch promotion path.
  • Add guidance for Windows signing, macOS notarization, Android package name.
  • Configure store-specific export filters.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Add Steam and storefront release packaging workflow for Tekton after P0/P1 backend gates are complete. Review export_presets.cfg, docs/STEAMWORKS_SETUP.md, README.md, and current build output conventions. Create tools/steam/app_build_.vdf and per-platform depot VDF templates using placeholders only. Document steamcmd upload command, branch promotion path internal -> beta -> default, and smoke tests required before promotion. Add guidance for Windows signing, macOS bundle/team/notarization, Android final package name/version code, and store-specific export filters so Steam libraries are not shipped in non-Steam builds. Acceptance: no real IDs/secrets committed; SteamPipe templates exist; release checklist blocks default branch promotion until smoke tests pass.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Trigger dry-run of SteamPipe VDF. Assert paths resolve to output directory without committing real credentials.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-P2-1]: Steam Depot & Store Packaging**
-- **Goal:** Create SteamPipe VDFs, branch SOP, signing/notarization, platform filters.
-- **Status:** Integrated & verified. Code changes applied to: tools/steam/, export presets
- -
-
-
-
-
- -
- -
- P2 - PRD-GF-P2-1 -

Dead Path, Debug Gate & Telemetry Cleanup

-
-
- - -
-
-
- -
-
-

Files / Areas

-
- main.gd, player.gd, placeholders -
-
- -
-

Goal / Risk

-

Remove release-noisy debug hooks. Add safe-remove candidate matrix + SLO dashboard.

-
- -
-

Execution Checklist

-
    -
  • Build matrix: keep, safe-remove, needs-runtime-proof, feature-flag.
  • Remove or feature-gate release-only noise (e.g., debug key hooks, excessive prints).
  • Instrument events: room_joined, preflight_pass, loading_screen, match_sync.
  • Do not delete autoload/runtime-loaded scripts without proof.
  • -
-
-
- - -
-
-

AI Execution Prompt

-
-
Create dead-path/debug-path cleanup and telemetry gates for lobby-to-match lifecycle. Review main.gd, player.gd, login_screen.gd, backend_service.gd, and other placeholders/debug hooks. Build matrix with columns: keep, safe-remove, needs-runtime-proof, feature-flag. Remove or feature-gate release-only noise such as debug key hooks and excessive prints, but do not delete autoload/runtime-loaded scripts without proof. Instrument events: room_joined, preflight_pass/fail, loading_screen_start/finish, match_sync_complete, reconnect_success/fail, match_end_summary. Acceptance: safe-remove candidates are evidence-backed; release export excludes debug-only hooks; branch promotion can check match-start and reconnect SLO metrics.
- -
-
- -
-

Testing / Auto-Check

-

AI AUTO-CHECK: Search codebase for `Input.is_key_pressed(KEY_F9)`. Assert wrapped in `OS.has_feature("debug")` or completely removed.

-
- -
-

MS Teams Daily Report

-
-
**Completed [PRD-GF-P2-1]: Dead Path, Debug Gate & Telemetry Cleanup**
-- **Goal:** Remove release-noisy debug hooks. Add safe-remove candidate matrix + SLO dashboard.
-- **Status:** Integrated & verified. Code changes applied to: main.gd, player.gd, placeholders
- -
-
-
-
-
- -
-
-
- - - - - - - - - - - - \ No newline at end of file diff --git a/server/nakama/lua/README.md b/server/nakama/lua/README.md new file mode 100644 index 0000000..560b569 --- /dev/null +++ b/server/nakama/lua/README.md @@ -0,0 +1,1639 @@ +# Tekton Nakama Lua Server API Reference + +> Auto-generated from source code. For AI agents — call these RPCs without reading Lua. + +## Table of Contents + +1. [Authentication & Core (core.lua)](#1-authentication--core-coreluau) +2. [Utilities (utils.lua)](#2-utilities-utilsluau) +3. [User Module (user.lua)](#3-user-module-userluau) +4. [Economy Module (economy.lua)](#4-economy-module-economyluau) +5. [Gacha Module (gacha.lua)](#5-gacha-module-gachaluau) +6. [Leaderboard Module (leaderboard.lua)](#6-leaderboard-module-leaderboardluau) +7. [Inbox/Mail Module (inbox.lua)](#7-inboxmail-module-inboxluau) +8. [Daily Rewards Module (daily_rewards.lua)](#8-daily-rewards-module-daily_rewardsluau) +9. [Admin Module (admin.lua)](#9-admin-module-adminluau) +10. [Storage Collections Reference](#10-storage-collections-reference) +11. [Leaderboard Config](#11-leaderboard-config) +12. [Error Strings Reference](#12-error-strings-reference) +13. [Permission Levels](#13-permission-levels) + +--- + +## 1. Authentication & Core (`core.lua`) + +### After-Hook: `after_authenticate_steam` + +| Field | Value | +|---|---| +| **Trigger** | `AuthenticateSteam` | +| **Purpose** | On first Steam login: sets display_name from Steam username, default role to `"player"` | +| **Auth** | None (internal hook) | + +**Behavior:** +- If user has no `display_name`, sets it from `input.username` (fallback: `"SteamPlayer"`) +- If user metadata has no `role`, sets `metadata.role = "player"` + +### Startup (not an RPC) + +``` +nk.leaderboard_create("global_high_score", true, "desc", "best", nil, {}) +``` + +Creates native leaderboard `"global_high_score"` on module load (also created in `leaderboard.lua` — pcall wraps both so duplicate is safe). + +--- + +## 2. Utilities (`utils.lua`) + +Shared helpers used by other modules. + +### Constants + +| Constant | Value | Purpose | +|---|---|---| +| `ADMIN_ROLES` | `{admin=true, moderator=true, owner=true}` | Roles with admin privileges | +| `SYSTEM_USER_ID` | `"00000000-0000-0000-0000-000000000000"` | System/nil user for global storage | +| `CHANNEL_TYPE_ROOM` | `1` | Nakama channel type for rooms | +| `CHANNEL_TYPE_DIRECT` | `2` | Nakama channel type for DMs | +| `CHANNEL_TYPE_GROUP` | `3` | Nakama channel type for groups | + +### `utils.is_admin(context)` + +Checks if caller has admin/moderator/owner role. + +| Param | Type | Description | +|---|---|---| +| `context` | table | Nakama RPC context | + +**Returns:** `boolean` + +### `utils.is_match_host(context, match_id)` + +Checks if caller is the host of a match. + +| Param | Type | Description | +|---|---|---| +| `context` | table | Nakama RPC context | +| `match_id` | string | Match ID to check | + +**Returns:** `boolean` +**Note:** Reads `match.state` and checks `state.hostUserId` against `context.user_id`. + +### `utils.require_admin(context)` + +Errors with `"Admin privileges required"` if caller is not admin/moderator/owner. + +### `utils.require_admin_or_host(context, match_id)` + +Errors with `"Admin or host privileges required"` if caller is neither admin nor match host. + +### `utils.resolve_channel_id(channel_id)` + +Resolves a friendly room name (e.g. `"social_global"`) to a hashed Nakama channel ID. + +| Param | Type | Description | +|---|---|---| +| `channel_id` | string | Raw channel ID or friendly room name | + +**Returns:** `string` — resolved channel ID (passes through if already hashed, i.e. contains `"."`) +**Logic:** If no `"."` in value, calls `nk.channel_id_build("", name, CHANNEL_TYPE_ROOM)`. + +--- + +## 3. User Module (`user.lua`) + +### RPC: `get_user_profile` + +| Field | Value | +|---|---| +| **Function** | `user.rpc_get_user_profile` | +| **Auth** | None required (can view any profile) | +| **Purpose** | Get a user's public profile | + +**Params (JSON):** + +| Field | Type | Required | Default | Description | +|---|---|---|---|---| +| `user_id` | string | No | `context.user_id` | Target user ID | + +**Returns (JSON):** + +| Field | Type | Description | +|---|---|---| +| `user_id` | string | User ID | +| `username` | string | Username | +| `display_name` | string | Display name | +| `avatar_url` | string | Avatar URL | +| `create_time` | string | Account creation time | +| `role` | string | Role from metadata (`"player"` default) | + +**Errors:** +- `"Account not found"` — user doesn't exist +- `"Account banned until