Jump to content

Logic Update Scripts

From OpenBOR
(Redirected from Updatedlogic)

Logical update scripts are recurring event hooks tied directly to OpenBOR's logical clock. They provide paired entry points immediately before and after each logical tick processed by the engine.

OpenBOR supplies both project-wide global files and level-specific commands:

Hook Scope Execution point
data/scripts/updatelogic.c Project-wide Before native processing for each logical tick
updatelogicscript Current level After global updatelogic.c, before native tick processing
data/scripts/updatedlogic.c Project-wide After native processing for each logical tick
updatedlogicscript Current level After global updatedlogic.c, before the logical clock advances

These hooks differ from the outer-cycle Update and Updated events. Outer update frequency may follow display synchronization, platform timing, and runtime workload. Logical update scripts instead execute once for every simulation tick OpenBOR actually processes, including each tick of a multi-tick catch-up cycle.

This direct relationship to the logical clock makes the series useful for deterministic state machines, tick-accurate controllers, custom simulation systems, pre-simulation preparation, post-simulation reconciliation, replay instrumentation, synchronized hazards, and other features that should advance with engine logic rather than display frequency.

Global Usage

Create either or both global files in data/scripts:

  • data/scripts/updatelogic.c
  • data/scripts/updatedlogic.c

OpenBOR loads them automatically. Both use a main() entry point and receive no automatic event variables.

void main()
{
    long logical_tick = openborvariant("elapsed_time");

    setglobalvar("logical_tick_begin", logical_tick);
}

updatelogic.c is the first script in the logical-tick sequence. updatedlogic.c is the first script after native tick processing.

Projects may use either hook independently or use both as a matched pair.

Level Usage

Add either command to a level definition and provide the path to its script file:

updatelogicscript data/scripts/levels/foundry_updatelogic.c
updatedlogicscript data/scripts/levels/foundry_updatedlogic.c

Each referenced script uses a main() entry point and receives no automatic event variables.

void main()
{
    // Perform level-specific logical-tick work here.
}

OpenBOR loads these scripts with the level and executes them only while that level remains available. One updatelogicscript and one updatedlogicscript may be assigned to a level.

Inline Level Scripts

Both level commands support inline script blocks:

updatelogicscript @script
void main()
{
    setglobalvar("foundry_tick_active", 1);
}
@end_script

updatedlogicscript @script
void main()
{
    setglobalvar("foundry_tick_complete", 1);
}
@end_script

Inline form is convenient for concise level-only behavior. External files are easier to reuse and organize when logical controllers contain several systems or helper functions.

Execution Order

The relevant recurring sequence is:

  1. OpenBOR begins an outer update cycle and refreshes timing and input state.
  2. Global data/scripts/update.c executes when eligible.
  3. The current level's updatescript executes when declared.
  4. OpenBOR processes applicable key events and determines how many logical ticks are due.
  5. For each logical tick:
    1. Global data/scripts/updatelogic.c executes.
    2. The current level's updatelogicscript executes.
    3. OpenBOR processes native logical work, including applicable scrolling, timer, status, and entity updates.
    4. Global data/scripts/updatedlogic.c executes.
    5. The current level's updatedlogicscript executes.
    6. OpenBOR advances the logical clock to the next tick.
  6. After all due ticks finish, OpenBOR prepares the display queue.
  7. Global data/scripts/updated.c executes.
  8. The current level's updatedscript executes when declared.

Global logic always executes before the matching level logic. Level scripts can therefore specialize or consume project-wide state established by their global counterparts during the same phase.

When an outer update cycle processes several logical ticks, the complete pre-tick and post-tick chain repeats for each one. When no logical tick is due, none of the four logical hooks executes during that outer cycle.

Logical Clock Value

Both sides of the pair observe the same logical clock value. OpenBOR executes updatedlogic.c and updatedlogicscript after native tick processing but before incrementing the clock.

void main()
{
    long logical_tick = openborvariant("elapsed_time");

    setglobalvar("logical_tick_finished", logical_tick);
}

This timing allows the early hook to capture or prepare state for tick N, while the late hook examines the results of that same tick N. The following logical iteration then begins with tick N + 1.

OpenBOR uses a default logical rate of 200 ticks per second. Projects may configure a different logical clock rate, so scripts should use the clock value or project timing configuration when converting ticks into real-time units.

Pause and Eligibility

Logical update scripts execute only while OpenBOR is actively processing logical ticks. Pausing prevents the logical loop from advancing, so neither the early nor late logical hook executes during paused outer cycles.

The alwaysupdate setting expands eligibility beyond normal in-game update calls. It does not bypass pause or create a logical tick when the current outer cycle has none to process.

This differs from outer-cycle updated hooks, which may still execute during paused in-game cycles. Systems that must remain active while paused belong in an appropriate outer update or presentation hook rather than the logical series.

Before and After Native Simulation

updatelogic is suited to work that must be ready before the engine updates gameplay state. Quick applications include applying queued decisions, advancing custom input state, preparing movement rules, selecting active hazards, updating custom clocks, or publishing values that entity scripts will consume during the current tick.

updatedlogic is suited to work that depends on the results of native processing. Quick applications include resolving objectives after entity updates, reconciling custom physics, recording resulting state, counting surviving participants, evaluating synchronized conditions, or preparing state for the next logical tick.

void main()
{
    int pending_phase = getglobalvar("pending_phase");

    if (pending_phase)
    {
        setglobalvar("active_phase", pending_phase);
        setglobalvar("pending_phase", 0);
    }
}

The example is appropriate for updatelogic.c when the new phase must be visible to every system during the upcoming native tick.

void main()
{
    int targets_remaining = getglobalvar("targets_remaining");

    if (targets_remaining <= 0)
    {
        setglobalvar("pending_phase", 3);
    }
}

The matching updatedlogic.c can evaluate values produced during native processing and queue a phase change for the next tick.

Choosing an Update Hook

Hook Frequency Phase Typical responsibility
Global update.c and level updatescript Once per eligible outer update cycle Before key events and logical processing Outer-cycle coordination and early preparation
Global updatelogic.c and level updatelogicscript Once per processed logical tick Immediately before native tick work Tick-accurate preparation and custom simulation input
updateentityscript During applicable entity updates Within native logical processing Per-entity recurring behavior
Global updatedlogic.c and level updatedlogicscript Once per processed logical tick After native tick work, before clock advancement Tick-accurate resolution and resulting-state inspection
Global updated.c and level updatedscript Once per eligible outer update cycle After logical processing and display preparation Late-cycle coordination and presentation

Display synchronization does not define logical-script frequency. One outer cycle may process zero, one, or several logical ticks, and the logical hooks follow the tick count exactly.

Other Uses

Quick applications include:

  • Deterministic controllers - Advance custom state machines exactly once per engine logical tick.
  • Custom simulation - Run project-defined movement, resources, hazards, clocks, or world systems beside native logic.
  • Pre-tick preparation - Publish values, apply queued commands, choose rules, or establish state consumed during the current tick.
  • Post-tick reconciliation - Evaluate entity results, resolve simultaneous conditions, or queue changes for the next tick.
  • Synchronized encounters - Coordinate hazards, waves, objectives, formations, or multi-part sequences on a common clock.
  • Replay and diagnostics - Record pre-tick and post-tick state, identify the exact logical time of a change, or compare deterministic results.
  • Level simulation - Keep stage-specific clock logic with the level while common systems remain in the global files.
  • Timing tools - Build profilers, counters, test harnesses, or controlled simulation instrumentation around exact tick boundaries.
Script Relationship
update.c Global early outer-cycle hook that executes before logical processing begins.
updatescript Level-specific early outer-cycle hook.
updateentityscript Model hook executed for individual entities during native logical processing.
updated.c Global late outer-cycle hook that executes after all due logical ticks.
updatedscript Level-specific late outer-cycle hook.

See Also