Game State Management in PHPDTS: How State Transitions Work

The PHPDTS engine uses a global $gamestate variable evaluated on every request to cycle through six distinct phases, from idle preparation to final combo showdown, triggered by time-based checkpoints, player counts, and arena capacity limits.

The amarillonmc/phpdts repository implements a deterministic state machine that drives the battle royale lifecycle. At the heart of this game state management system lies the global $gamestate variable declared in include/common.inc.php, which is checked and updated inside the main request loop to ensure continuous match progression across stateless HTTP requests.

The Global State Variable and Core Architecture

All state persistence relies on the $gamestate global variable. The engine initializes this value at the start of each request via load_gameinfo() in include/init.func.php (line 165), which deserializes the stored value from gamedata/gameinfo.php. When a match ends, gameover() in include/system.func.php (line 260) resets this variable to 0.

The state machine executes inside a guarded block that excludes chat scripts to prevent high-frequency polling from triggering spurious transitions:

if(CURSCRIPT !== 'chat') {
    // State transition logic runs here (lines 64-260)
}

State Values and Their Meanings

The engine defines six discrete integer states that represent distinct match phases, each set at specific lines within include/common.inc.php:

  • 0Idle/Game Over: No active match. Initialized by load_gameinfo() at line 165 and reset by gameover() at line 260.
  • 10Preparation: Countdown window before the match starts. Set at lines 68–70 when $now > $starttime - $startmin*60.
  • 20Active Battle: Normal gameplay phase. Set at lines 82–84 once $now >= $starttime.
  • 30Stop Activation: Entry is closed and the arena stops expanding. Set at lines 102–104 when capacity or player limits are reached.
  • 40Combo Mode: Accelerated "continuous battle" phase. Set at lines 124–131 or 133–140 based on survivor counts or death thresholds.
  • 50End of Combo: Terminal state assigned in gameover() at line 260 immediately before resetting to 0.

State Transition Logic in the Main Loop

The transition logic resides in include/common.inc.php and evaluates conditions sequentially on every non-chat request.

Transition 0 → 10: Entering Preparation

When the system is idle, it checks against the pre-game timer:

if(!$gamestate){
    if(($starttime) && ($now > $starttime - $startmin*60)){
        $gamestate = 10;   // preparation phase (lines 68-70)
    }
}

Transition 10 → 20: Match Start

Once the preparation countdown completes, the battle phase begins:

if($gamestate == 10 && $now >= $starttime){
    $gamestate = 20;       // active battle (lines 82-84)
}

Transition 20 → 30: Stop Activation

During active battle, the engine monitors arena capacity ($areanum) and valid participant counts ($validnum):

if($gamestate == 20){
    if(($validnum <= 0) && $areanum >= $arealimit*$areaadd){
        gameover(...);               // not enough players → end
    } elseif(($areanum >= $arealimit*$areaadd) || ($validnum >= $validlimit)){
        $gamestate = 30;            // stop new entrants (lines 102-104)
    }
}

If valid participants drop to zero while the arena is full, gameover() terminates the match immediately rather than transitioning to state 30.

Transition 30 → 40: Entering Combo Mode

Combo mode activates via two alternative survival checks at lines 124–140:

// Check 1: Few survivors remaining
if($gamestate < 40 && $gamestate > 20 && $alivenum <= $combolimit){
    $gamestate = 40;                // combo because few survivors
}

// Check 2: Death count exceeds threshold
if($gamestate < 40 && $gamestate >= 20 && $combonum && $deathnum >= $combonum){
    $gamestate = 40;                // combo because deaths exceed threshold
}

Transition 40 → 0: Game Conclusion

While in combo mode (state 40) or end-of-combo (state 50), the engine checks for a single survivor:

if($gamestate >= 40){
    $result = $db->query("SELECT pid FROM {$tablepre}players WHERE hp>0 AND type=0");
    $alivenum = $db->num_rows($result);
    if($alivenum <= 1){
        gameover();                 // line 260: resets $gamestate to 0
    }
}

Anti-AFK Protection Within Combo State

State 40 includes automated AFK detection that runs continuously to prevent idle stalling:

if($gamestate >= 40 && $now > $afktime + $antiAFKertime*60){
    antiAFK();                      // resets AFK timer
}

The antiAFK() function in include/system.func.php refreshes timestamps to keep the final showdown moving.

Supporting Functions and Persistence Layer

State persistence and helper logic are modularized across specific files:

Configuration and Custom Rule Sets

Default thresholds governing game state transitions are defined in gamedata/system.php:

  • $startmin – Minutes before starttime to enter preparation (state 10).
  • $arealimit and $areaadd – Arena expansion limits triggering state 30.
  • $validlimit – Minimum valid participants required for stop activation.
  • $combolimit – Alive player threshold entering combo mode.
  • $antiAFKertime – Minutes allowed before AFK protection fires.

The include/ruleset_override.func.php file allows server administrators to inject custom logic, overriding variables like $combolimit or $validlimit without altering the core state machine in include/common.inc.php.

Summary

  • The PHPDTS game state management system relies on a global $gamestate variable persisted in gamedata/gameinfo.php and evaluated on every non-chat request.
  • Six discrete integer states (0, 10, 20, 30, 40, 50) represent the complete match lifecycle from idle preparation through combo finale.
  • Transitions trigger based on time-based checkpoints (start timers), player-count thresholds (valid participants and survivors), and arena capacity limits (arealimit calculations).
  • The gameover() and antiAFK() functions in include/system.func.php handle terminal state cleanup and idle protection.
  • Custom rule sets via include/ruleset_override.func.php allow modification of transition logic without core code changes.

Frequently Asked Questions

How does PHPDTS persist game state between HTTP requests?

The engine saves the $gamestate variable to gamedata/gameinfo.php at the end of each request cycle. At the start of every subsequent request, load_gameinfo() in include/init.func.php (line 165) deserializes this file back into the global scope, ensuring continuity across the stateless HTTP protocol.

What happens if the player count drops to zero during active battle?

If $validnum reaches zero while the arena has expanded beyond its limit ($areanum >= $arealimit*$areaadd), the transition logic in include/common.inc.php (lines 102–104) immediately invokes gameover(). This resets $gamestate to 0 and terminates the match early due to insufficient participants.

Can server administrators modify the state transition thresholds?

Yes. While default values reside in gamedata/system.php, administrators can implement custom logic through include/ruleset_override.func.php. The init_ruleset_override() and load_ruleset_override_functions() hooks allow modification of variables like $combolimit and $validlimit without altering the core state machine in include/common.inc.php.

Why does the state machine exclude the chat script?

The guard clause if(CURSCRIPT !== 'chat') prevents the state transition logic from executing during chat polling requests. This optimization reduces database load and prevents premature state advances caused by high-frequency AJAX chat updates that do not represent actual game actions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →