From 0e763fda1f390fcdf9d4c3d6c0a5055b8ab6fd5b Mon Sep 17 00:00:00 2001 From: Cyd Date: Tue, 4 Aug 2026 01:42:08 -0500 Subject: [PATCH] BT410 5.3.113: the displays lag because the loop is slow, and the loop was slow because the flags were wrong -- authentic OPT.MAK optimizer set adopted The operator's 'many seconds between display updates', run to ground: MECHANISM (verified in source AND the shipped exe bytes): main passes GetTicksPerSecond() as ApplicationManager's frame RATE, so frameDuration is microscopic and RunMissions runs flat-out -- exactly ONE background slot per loop frame, shipped and ours alike (ctor constant @0044f2d8 == 1.0f, read straight out of BTL4OPT.EXE). That slot feeds a 7-task ring; the gauge renderer gets 1/7th of slots, one ACTIVE gauge per slot, and the screen blits once per completed wheel pass. The wheel is 136 gauges -- and the BT_GAUGE_LOG roster dump shows all 136 are real cockpit instruments (26 armor colormappers, leak gauges, cooling loops, weapon clusters...): a Tesla pod shows everything at once, so the roster is AUTHENTIC and must not be trimmed. Per-instrument latency is therefore 7 x 146 / loop_fps -- the loop rate is the only lever. THE DEFECT: build410.sh compiled -O2 alone. The authentic release tier (CODE/BT/OPT.MAK) is -O2 -Ot -Oc -Og -O -Ol -Z -Ob -Oe -Oi -Om -Op -Ov. Adopting it (all 234 TUs clean, no BC4.52 optimizer ICEs) doubled the loop: 16.7 -> ~30 fps wall on the rig. Shipped fifo throughput is still ~2x ours (11.8KB/s vs 6.4KB/s, governor-conflated) -- residual gap is an open question pinned in GAUGE-CADENCE-NOTES.md with the operator A/B ask. Instrumentation kept, cheap and gated: slot/wheel counters on the [stack] line (BT_STACK_LOG), one-shot roster dump (BT_GAUGE_LOG), tick-delta timers compile-gated BT_TICK_PROBES (OFF -- guest tick sums proved to be trap-burst artifacts in the emulator, not costs; wall rates are the only trustworthy measure). New: MUNGA/APPTASK.CPP shadow, fifofps.py (true board fps from a fifodump), pod_render_bgprobe.conf. Co-Authored-By: Claude Fable 5 --- emulator/render-bridge/GAUGE-CADENCE-NOTES.md | 103 + emulator/render-bridge/fifofps.py | 31 + .../render-bridge/pod_render_bgprobe.conf | 83 + restoration/source410/MUNGA/APP.CPP | 4015 +++++++++-------- restoration/source410/MUNGA/APPTASK.CPP | 275 ++ restoration/source410/MUNGA/GAUGREND.CPP | 40 + restoration/source410/MUNGA_L4/L4GREND.CPP | 2020 ++++----- restoration/source410/build410.sh | 9 +- 8 files changed, 3614 insertions(+), 2962 deletions(-) create mode 100644 emulator/render-bridge/GAUGE-CADENCE-NOTES.md create mode 100644 emulator/render-bridge/fifofps.py create mode 100644 emulator/render-bridge/pod_render_bgprobe.conf create mode 100644 restoration/source410/MUNGA/APPTASK.CPP diff --git a/emulator/render-bridge/GAUGE-CADENCE-NOTES.md b/emulator/render-bridge/GAUGE-CADENCE-NOTES.md new file mode 100644 index 00000000..69cee74b --- /dev/null +++ b/emulator/render-bridge/GAUGE-CADENCE-NOTES.md @@ -0,0 +1,103 @@ +# Gauge / display cadence — why the cockpit instruments update slowly (2026-08-04) + +Operator report: "the rendering of the displays — many seconds between +updates" on BTL4REC vs "the original." This file is the evidence chain. +All measurements on the dev rig, `pod_render_bgprobe.conf` (no per-frame +logs), DOSBox-X `cycles=max`, arena1 self-drive (BT_FORCE_THROTTLE=0.6). + +## The mechanism (all verified in source AND the shipped binary) + +1. **The manager runs flat-out by design.** `main` passes + `GetTicksPerSecond()` to `ApplicationManager(Scalar frame_rate)`, so + `frameDuration = 1/ticksPerSecond` — a microscopic frame budget. + Verified in the shipped exe: the ctor constant `_DAT_0044f2d8` at + file offset 0x4f0d8 of BTL4OPT.EXE is exactly `1.0f` (0x3f800000), + i.e. the shipped 4.10 does the same. Consequence: `RunMissions` + (@0044f344, structurally identical to the RP donor) finds + `Now() >= end_of_frame` after every single background pass — + **exactly one background slot per loop frame**, ours and shipped. + Measured: `bg=` grows 1001 per 1001-frame period, every period. + +2. **The background slot feeds a 7-task round-robin** + (`BackgroundTasks::Execute` runs ONE task per call): RoutePacket, + ProcessEvent, AudioRenderer, GaugeRenderer, NetworkManager, + CompleteCycles, FryDeathRow. The gauge renderer gets 1/7th of slots. + +3. **The gauge wheel repaints once per pass.** Each gauge-task slot = + `ProcessOneActiveGauge()` = ONE active gauge. When the active list + is exhausted the renderer enters its `copy` phase (the changed-line + blit to the display — ~10 extra slots) and only then restarts. + So per-instrument latency ≈ `7 × (wheel + copy) / loop_fps`. + +4. **The wheel is 136 gauges and that is AUTHENTIC.** BT_GAUGE_LOG + roster dump: 26 ColorMapperArmor, 12 VertTwoPartBar, 10 LeakGauge, + 8 TwoState/PowerSource/HorizTwoPartBar/CoolingLoop, weapon clusters, + generator clusters, myomer cluster, pilot list, message board … all + real cockpit instruments. A Tesla pod shows every instrument at + once; nothing pages out. Do NOT "fix" the roster. + +So at our measured ~25–33 loop fps: 7 × 146 / 30 ≈ **34 s** worst-case +per-instrument latency. That is the operator's symptom, exactly. + +## The real defect found: build flags + +`build410.sh` compiled with `-O2` alone. The authentic release tier +(`CODE/BT/OPT.MAK`) is: + + -O2 -Ot -Oc -Og -O -Ol -Z -Ob -Oe -Oi -Om -Op -Ov + +(global CSE + global register allocation, invariant code motion, copy +propagation, inline intrinsics, redundant-load suppression, loop opts). +Adopting the full set (all 234 TUs compile clean, no BC4.52 optimizer +ICEs) took the loop from **16.7 → ~30 loop fps wall** on the rig — a +~2× display-cadence improvement for free. `-w` was kept as `-w-`. +Note: RP-era MAKE.CFG uses `bcc32i` (BC5 toolchain, RP 4.11+); BT 4.10's +OPT.MAK says `BCC = bcc32` — we match 4.10. + +## Measurement traps recorded (they cost hours tonight) + +- **Incremental-build staging:** the link consumes `build410/lib/*.lib`; + rebuilding `engine` without `libs` ships STALE members. Also a + same-minute mtime tie made the engine step skip freshly patched TUs. + When probing: `rm` the target objs, then `engine`, `libs`, `link`. + (l4grend's obj dir is `obj/mungal4` — no underscore.) +- **Guest tick-delta profiling is unreliable in the emulator.** The SOS + clock advances in bursts at trap points, so per-stage + `Now()`-bracket sums measure where the burst LANDS, not cost: + five timed foreground stages summed 8.7 "ticks/frame" while the whole + function measured 17.0, with provably empty code between them. + Wall-clock rates (log-line arrival, fifodump growth) are the only + trustworthy measure. The tick probes are compile-gated behind + `BT_TICK_PROBES` (default OFF); the pure counters (`bg= g= p= c=` on + the `[stack]` line, BT_STACK_LOG) stay, they never touch the clock. +- **`draw_scene` rate ≠ loop rate.** The board renderer is + rate-governed by the RendererManager: 2.9 board-fps while the loop ran + 33 fps. `fifofps.py` counts true board frames (action 9 records). + +## Where the ours-vs-shipped gap stands + +fifodump growth mid-mission: shipped ≈ 11.8 KB/s, ours ≈ 4.1 KB/s before +the flag fix, ≈ 6.4 KB/s after. Byte rate conflates the render governor +with loop rate, so treat as indicative only. Under `cycles=max` wall +time is trap-dominated; both exes drive the same VPX/serial/AWE devices. + +**OPEN QUESTION for the operator:** on THIS RIG, do BTL4OPT's cockpit +instruments visibly update faster than BTL4REC's (post-flag-fix)? If +yes, the residual is ours-specific guest work and the next tool is a +DOSBox-side sampling profiler (we own the fork) — not more guest +probes. If no — the rig was always like this and the "original" +baseline was the real pod, whose identical architecture ran at real- +hardware loop rates. + +## Probe inventory (all in source410, cheap, env/compile gated) + +- `MUNGA/APP.CPP`: counters `bgSlots/bgTaskRuns/gaugeSlices/gaugePasses/ + gaugeCopySlices` + `[stack]` print (env BT_STACK_LOG); tick-delta + stage/slot timers behind `BT_TICK_PROBES`. +- `MUNGA/APPTASK.CPP` (new shadow, from CODE/RP donor): per-ring-task + timers behind `BT_TICK_PROBES`. +- `MUNGA/GAUGREND.CPP`: wheel slice/pass counters; one-shot `[roster]` + dump (env BT_GAUGE_LOG). +- `emulator/render-bridge/fifofps.py`: true board-fps from a fifodump. +- `emulator/render-bridge/pod_render_bgprobe.conf`: clean probe conf + (BT_STACK_LOG + BT_GAUGE_LOG, no per-frame logs). diff --git a/emulator/render-bridge/fifofps.py b/emulator/render-bridge/fifofps.py new file mode 100644 index 00000000..e9abaab4 --- /dev/null +++ b/emulator/render-bridge/fifofps.py @@ -0,0 +1,31 @@ +"""Count draw_scene (action 9) records in a fifodump = true board fps. + py fifofps.py -> total count + py fifofps.py -> count now, wait, count again, report fps +""" +import sys, time, struct + +def count_frames(path): + n = 0 + with open(path, 'rb') as f: + data = f.read() + off = 0 + while off + 8 <= len(data): + if data[off:off+4] != b'VPXM': + off += 1 + continue + ln = struct.unpack_from('= 4 and struct.unpack_from(' 2: + secs = float(sys.argv[2]) + a = count_frames(path) + time.sleep(secs) + b = count_frames(path) + print(f"{a} -> {b} draw_scene in {secs:.0f}s = {(b-a)/secs:.1f} fps") +else: + print(count_frames(path), "draw_scene records") diff --git a/emulator/render-bridge/pod_render_bgprobe.conf b/emulator/render-bridge/pod_render_bgprobe.conf new file mode 100644 index 00000000..8c5b0f7a --- /dev/null +++ b/emulator/render-bridge/pod_render_bgprobe.conf @@ -0,0 +1,83 @@ +# pod_render_norio.conf -- pod_render_rec with the RIO serial port OFF. +# +# The emulator log shows a steady stream of serial1 RX OVERRUN errors on +# the RIO pipe, and controls post their events at HighEventPriority +# UNCONDITIONALLY (CONTROLS.HPP:250) -- so a chattering RIO port would +# flood a priority the background pump always serves first, starving the +# priority-0 renderer events the load gate waits on. This conf tests that +# by removing the port entirely. Everything else is identical to the rec +# conf, so a launch here and a hang there isolates the RIO. +# +[sdl] +output=opengl +# higher,higher not highest: HIGH_PRIORITY_CLASS starved the host desktop; +# with the retry patches a rare dropout self-recovers (see gauge_rio.conf). +priority=higher,higher +[dosbox] +memsize=32 +machine=svga_s3 +[cpu] +core=dynamic +cputype=pentium +cycles=max +[sblaster] +sbtype=sb16 +sbbase=220 +irq=5 +dma=1 +hdma=5 +[mixer] +# match the EMU8000s' native rate (no resample) and buffer ~60ms so brief +# emulation-thread stalls (RIO retry recovery) don't audibly chop +rate=44100 +blocksize=1024 +prebuffer=60 +[serial] +# RIO on COM1 with the low-latency options (rxpollus/rxburst) so the board's +# few-ms ACK deadline is met; plasma display on COM2 (real pod has both). +# VWE fork namedpipe backend (com0com/realport retired -- COM1/COM2 gone): +# DOSBox = pipe client (retry), vRIO/vPLASMA apps = servers; an unconnected +# pipe behaves as an unplugged cable so the mission still runs. serialnamedpipe.h +serial1=disabled +serial2=namedpipe pipe:vplasma +# live UNBUFFERED game output: DOS char devices are not buffered, so +# redirecting stdout to COM3 lands every line immediately. A normal +# '> file' redirect stays 0 bytes until the process exits, which hides +# all progress on a run that does NOT crash. +serial3=file file:C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt +[autoexec] +mount c "C:\VWE\TeslaRel410\ALPHA_1" +c: +cd \REL410\BT +set VIDEOFORMAT=svga +rem production pod card init (PARAMETR.BAT:181-186): DIAGNOSE + AWEUTIL per +rem card -- AWEUTIL /S does the EMU8000 bring-up and DRAM detect the HMI SOS +rem driver relies on; skipping it left the cards uninitialized (silent). +rem aweutil /s SKIPPED for now: it verifies the AWE32 GM ROM, which the +rem emulated cards lack (hangs in a retry loop) -- restore once the ROM is +rem dumped from a real card. diagnose /s kept (passes, sets mixer config). +set BLASTER=A220 I5 D1 H5 P330 T6 +c:\sb16\diagnose /s +set BLASTER=A240 I7 D3 H6 P300 T6 +c:\sb16\diagnose /s +set BLASTER=A220 I5 D1 H5 P330 T6 +set TEMP=c:\ +rem arena1 city mission (TESTARN.EGG: map=arena1, time=day) with the RIO +rem attached; stdout redirected so mission-load progress survives kills. +set BT_JOINTS=1 +set L4VIEWEXT=1 +set BT_STACK_LOG=1 +set BT_GAUGE_LOG=1 +set BT_FORCE_THROTTLE=0.6 +set BT_FORCE_TURN=0.25 +set HEAPSIZE=15000000 +set L4GAUGE=640x480x16 +call setenv.bat r s n p +32rtm.exe -x +BTL4REC.EXE -egg testarn.egg > COM3 + +echo GAME-RC=%errorlevel% >> RC.TXT +32rtm.exe -u +echo ALPHA1-RUN-DONE +pause + diff --git a/restoration/source410/MUNGA/APP.CPP b/restoration/source410/MUNGA/APP.CPP index b8ce5386..b8045b89 100644 --- a/restoration/source410/MUNGA/APP.CPP +++ b/restoration/source410/MUNGA/APP.CPP @@ -1,1952 +1,2063 @@ -# if !defined(MUNGA_HPP) -# include -# endif -# if !defined(REGISTRY_HPP) -# include -# endif -# if !defined(RENDERER_HPP) -# include -# endif -# if !defined(CONTROLS_HPP) -# include -# endif -# if !defined(UPDATE_HPP) -# include -# endif -# if !defined(MISSION_HPP) -# include -# endif -# if !defined(PLAYER_HPP) -# include -# endif -# if !defined(DIRECTOR_HPP) -# include -# endif -# if !defined(APPMGR_HPP) -# include -# endif -# if !defined(AUDREND_HPP) -# include -# endif -# if !defined(VIDREND_HPP) -# include -# endif -# if !defined(GAUGREND_HPP) -# include -# endif -# if !defined(HOSTMGR_HPP) -# include -# endif -# if !defined(INTEREST_HPP) -# include -# endif -# if !defined(NTTMGR_HPP) -# include -# endif -# if !defined(APPTASK_HPP) -# include -# endif -# if !defined(CONSOLE_HPP) -# include -# endif -# if !defined(APPMSG_HPP) -# include -# endif -# if !defined(EVTSTAT_HPP) -# include -# endif - -#if defined(TRACE_FOREGROUND_PROCESSING) - BitTrace Foreground_Processing("Foreground Processing"); -#endif - -#if defined(TRACE_UPDATE_MANAGER) - BitTrace Update_Manager("Update Manager"); -#endif - -#if defined(TRACE_RENDERER_MANAGER) - BitTrace Renderer_Manager("Renderer Manager"); -#endif - -Application *application = NULL; -int Exit_Code = 0; -Logical Application::suppressGauges = False; - -// -// Event-pump counters -- see the comment on Application::ProcessOneEvent in -// APP.HPP. Diagnostic only; the BT_STACK_LOG probe reports them. -// -long eventPumpCalls = 0; -long eventPumpHits = 0; -long postTally[POST_TALLY_IDS]; - -//############################################################################# -//########################### Application ############################### -//############################################################################# - -//############################################################################# -// Message Support -// -const Receiver::HandlerEntry - Application::MessageHandlerEntries[]= -{ - MESSAGE_ENTRY(Application, StateQuery), - MESSAGE_ENTRY(Application, CheckLoad), - MESSAGE_ENTRY(Application, RunMission), - MESSAGE_ENTRY(Application, StopMission), - MESSAGE_ENTRY(Application, ResumeMission), - MESSAGE_ENTRY(Application, SuspendMission), - MESSAGE_ENTRY(Application, KeyCommand), - MESSAGE_ENTRY(Application, LoadMission), - MESSAGE_ENTRY(Application, AbortMission) -}; - -Application::MessageHandlerSet - Application::MessageHandlers(ELEMENTS(Application::MessageHandlerEntries), Application::MessageHandlerEntries, NetworkClient::MessageHandlers); - -//############################################################################# -// Virtual Data support -// -Derivation - Application::ClassDerivations(NetworkClient::ClassDerivations, "Application"); - -Application::SharedData - Application::DefaultData( - Application::ClassDerivations, - Application::MessageHandlers - ); - -// -//############################################################################# -// TestInstance -//############################################################################# -// -Logical - Application::TestInstance() const -{ - if (!IsDerivedFrom(ClassDerivations)) - { - return False; - } - Check(&applicationState); - if (eventQueue) - { - Check(eventQueue); - } - if (networkManager) - { - Check(networkManager); - } - if (entityManager) - { - Check(entityManager); - } - if (registry) - { - Check(registry); - } - if (hostManager) - { - Check(hostManager); - } - if (interestManager) - { - Check(interestManager); - } - if (updateManager) - { - Check(updateManager); - } - if (rendererManager) - { - Check(rendererManager); - } - if (controlsManager) - { - Check(controlsManager); - } - if (intercomManager) - { - Check(intercomManager); - } - if (resourceFile) - { - Check(resourceFile); - } - if (viewpointEntity) - { - Check(viewpointEntity); - } - if (currentMission) - { - Check(currentMission); - } - if (backgroundTasks) - { - Check(backgroundTasks); - } - if (audioRenderer) - { - Check(audioRenderer); - } - if (videoRenderer) - { - Check(videoRenderer); - } - if (gaugeRenderer) - { - Check(gaugeRenderer); - } - return True; -} - -// -//############################################################################# -// Application -//############################################################################# -// -Application::Application( - ResourceFile *resource_file, - ApplicationID application_ID, - ClassID class_ID, - SharedData &shared_data -): - NetworkClient(class_ID, shared_data, ApplicationClientID), - applicationState(ApplicationStateCount) -{ - Check(&shared_data); - Check(resource_file); - - applicationID = application_ID; - - // - // Create the event queue - // - eventQueue = GeneralEventQueue::Make( - EVENT_PRIORITIES_COUNT, - "MUNGA Event Count" - ); - Check(eventQueue); - - // - // Remember the resource file - // - resourceFile = resource_file; - missionPlayer = NULL; - - // - // NULL managers not yet created - // - networkManager = NULL; - registry = NULL; - controlsManager = NULL; - intercomManager = NULL; - backgroundTasks = NULL; - interestManager = NULL; - audioRenderer = NULL; - videoRenderer = NULL; - gaugeRenderer = NULL; - - // - // NULL pointers to other objects not yet created - // - viewpointEntity = NULL; - currentMission = NULL; - - // - // Create base level managers - // - entityManager = new EntityManager; - Register_Object(entityManager); - hostManager = new HostManager; - Register_Object(hostManager); - updateManager = new UpdateManager; - Register_Object(updateManager); - rendererManager = new RendererManager; - Register_Object(rendererManager); - backgroundTasks = new BackgroundTasks; - Register_Object(backgroundTasks); - - // - // Set up game state - // - executeFrames = False; - applicationState.SetState(InitializingState); - currentMission = NULL; - secondsRemainingInGame = 0.0f; - spoolFile = NULL; - routePacketFinished = False; - lastCreationMessage = Now(); - - // - // HACK - Init analysis bits - // - #if defined(TRACE_ON) - trace_manager.ResetTraces(); - #endif -} - -// -//############################################################################# -// GetApplicationManager -//############################################################################# -// -ApplicationManager* - Application::GetApplicationManager() -{ - PlugIteratorOf manager_link(this); - ApplicationManager *mgr; - while ((mgr = manager_link.ReadAndNext()) != NULL) - { - if (mgr->GetClassID() == ApplicationManagerClassID) - { - return mgr; - } - } - return NULL; -} - -Scalar - Application::GetApplicationLoopFrameRate() -{ - ApplicationManager *mgr = GetApplicationManager(); - Check(mgr); - return mgr->GetFrameRate(); -} - -// -//############################################################################# -// Initialize -//############################################################################# -// -void - Application::Initialize() -{ - Check(this); - - // - //---------------------------- - // Create the interest Manager - //---------------------------- - // - interestManager = MakeInterestManager(); - Register_Object(interestManager); - - // - //-------------------------------------------------------------------------- - // Create the network manager - //-------------------------------------------------------------------------- - // - networkManager = MakeNetworkManager(); - Register_Object(networkManager); - - // - //-------------------------------------------------------------------------- - // Create the registry, load static object streams - //-------------------------------------------------------------------------- - // - registry = MakeRegistry(); - if (registry) - { - Register_Object(registry); - registry->LoadStaticObjectStreamResource(); - } - - // - //-------------------------------------------------------------------------- - // Create the mode manager - //-------------------------------------------------------------------------- - // - modeManager = MakeModeManager(); - Register_Object(modeManager); - - // - //-------------------------------------------------------------------------- - // Create the controls manager - //-------------------------------------------------------------------------- - // - controlsManager = MakeControlsManager(); - Register_Object(controlsManager); - - // - //-------------------------------------------------------------------------- - // Create the intercom manager - //-------------------------------------------------------------------------- - // - intercomManager = MakeIntercomManager(); - Register_Object(intercomManager); - - // - //-------------------------------------------------------------------------- - // Add background tasks - //-------------------------------------------------------------------------- - // - LoadBackgroundTasks(); - executeFrames = True; - applicationState.SetState(WaitingForEgg); - - // - //-------------------------------------------------------------------------- - // Create the audio renderer - //-------------------------------------------------------------------------- - // - Verify(audioRenderer == NULL); - if ((audioRenderer = MakeAudioRenderer()) != NULL) - { - Register_Object(audioRenderer); - audioRenderer->Initialize(); - } - - // - //-------------------------------------------------------------------------- - // Create the video renderer - //-------------------------------------------------------------------------- - // - Verify(videoRenderer == NULL); - if ((videoRenderer = MakeVideoRenderer()) != NULL) - { - Register_Object(videoRenderer); - } - - // - //-------------------------------------------------------------------------- - // Create the gauge renderer - //-------------------------------------------------------------------------- - // - Verify(gaugeRenderer == NULL); - - if ((gaugeRenderer = MakeGaugeRenderer()) != NULL) - { - Register_Object(gaugeRenderer); - } -} - -// -//############################################################################# -// LoadBackgroundTasks -//############################################################################# -// -void - Application::LoadBackgroundTasks() -{ - Check(this); - - ApplicationTask *application_task; - - application_task = new RoutePacketTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); - - application_task = new ProcessEventTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); - - application_task = new AudioRendererTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); - - application_task = new GaugeRendererTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); - - application_task = new NetworkManagerTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); - - application_task = new CompleteCyclesTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); - - application_task = new FryDeathRowTask; - Register_Object(application_task); - backgroundTasks->AddTask(application_task); -} - -// -//############################################################################# -// MakeInterestManager -//############################################################################# -// -InterestManager* - Application::MakeInterestManager() -{ - return new InterestManager; -} - -// -//############################################################################# -// MakeNetworkManager -//############################################################################# -// -NetworkManager* - Application::MakeNetworkManager() -{ - return new NetworkManager(NetworkManager::DefaultData); -} - -// -//############################################################################# -// MakeRegistry -//############################################################################# -// -Registry* - Application::MakeRegistry() -{ - Fail("Application::MakeRegistry - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// MakeModeManager -//############################################################################# -// -ModeManager* - Application::MakeModeManager() -{ - Fail("Application::MakeModeManager - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// MakeControlsManager -//############################################################################# -// -ControlsManager* - Application::MakeControlsManager() -{ - Fail("Application::MakeControlsManager - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// MakeIntercomManager -//############################################################################# -// -IcomManager* - Application::MakeIntercomManager() -{ - Fail("Application::MakeIntercomManager - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// ExecuteForeground -//############################################################################# -// -Logical - Application::ExecuteForeground( - Time start_of_frame, - Scalar frame_duration - ) -{ - SET_FOREGROUND_PROCESSING(); - - Check(this); - Verify(application == this); - - // - // STACK-DRIFT PROBE (env BT_STACK_LOG). - // - // The intermittent page fault reads a stack LOCAL through the frame - // pointer -- disassembly of the fault site shows edi = ebp-0x10, and all - // three callers of that function pass "lea eax,[ebp-0x30]" -- so the bad - // address cannot be a wild argument. It has to be the stack pointer - // itself sitting outside the region. The stack geometry matches the - // shipped exe exactly (1MB reserve, 8K commit), which leaves ESP drifting - // a few bytes per call, most likely a calling-convention mismatch on some - // function we re-declared. - // - // So: record where the frame actually sits, once a second. A monotone - // march proves the drift and its rate; a stable address kills the theory - // outright. One run either way. - // - if (getenv("BT_STACK_LOG")) - { - static char *first_frame = NULL; - static Scalar stack_accum = 0.0f; - static int stack_samples = 0; - char here; - - if (first_frame == NULL) - { - first_frame = &here; - } - stack_accum += frame_duration; - stack_samples++; - if (stack_accum >= 1.0f) - { - stack_accum = 0.0f; - DEBUG_STREAM << "[stack] frame at 0x" << hex << (int)&here << dec - << " drift=" << (int)(first_frame - &here) - << " over " << stack_samples << " frames" - << " ticks=" << (long)Now().ticks - << " pump=" << eventPumpHits << "/" << eventPumpCalls - << endl << flush; - } - } - - if (!executeFrames) - { - CLEAR_FOREGROUND_PROCESSING(); - return executeFrames; - } - - Time - frame_ticks; - frame_ticks = frame_duration; - - // - //-------------------------------------------------------------------------- - // Controls Manager - // - // Poll all devices, update all control variables. - // - // This is executed before the update manager so that the - // models have valid control values. It is not necessary for - // the controls to operate at the frame rate of this loop. If - // the controls manager can run run at a lower rate it can - // throttle itself internally. - //-------------------------------------------------------------------------- - // - Check(controlsManager); -Time startControls = Now(); - controlsManager->Execute(); -Time endControls = Now(); - - if (GetApplicationState() == WaitingForEgg) - { - CLEAR_FOREGROUND_PROCESSING(); - return executeFrames; - } - - // - //-------------------------------------------------------------------------- - // Update Manager - // - // Execute replicants. - // Execute master entities if they are interesting here or - // elsewhere. - // Inform interest manager of possible interest zone change. - // If the master entity has provided and update message then - // send it to replicants. - // - // This is executed before the interest manager so that the - // interest manager can merge all interest zone changes before - // broadcasting interest arena deltas and building interesting - // entity lists. - // - // This is executed before the renderers so that the watchers - // have executed on the model and are ready for use by the - // renderers. - //-------------------------------------------------------------------------- - // - SET_UPDATE_MANAGER(); - -Time startUpdate = Now(); - Check(updateManager); - updateManager->Execute(start_of_frame); -Time endUpdate = Now(); - - CLEAR_UPDATE_MANAGER(); - - // - //-------------------------------------------------------------------------- - // Interest Manager - // - // Update the net interest arena. - // Calculate which interest zones should be released and which - // interest zones should be loaded - // Send a becoming uninteresting and becoming interesting - // message to the entity. - // Broadcast the interest arena change - // - // The renderer manager call, update interest origins will - // update only those interest origins required by the renderers - // executing this frame. - // - // This is executed before the renderer manager so that when - // the renderers execute they execute upon interest lists that - // are valid as of the end of a model/update frame. - // - // In theory the interest manager does not have to run every - // frame. If throttled less, this would cause fuzz at interest - // zone borders, which may or may not be acceptable depending - // on the size of the interest arena. - //-------------------------------------------------------------------------- - // -Time startInterest = Now(); - Check(rendererManager); - rendererManager->UpdateInterestOrigins(start_of_frame); - Check(interestManager); - interestManager->Execute(); -Time endInterest = Now(); - - // - //-------------------------------------------------------------------------- - // Renderer Manager - // - // Execute renderers. - // Get interest list from interest manager. - // Poll watchers. - // - // It is not necessary for all renderers to operate at the frame - // rate of this loop. If a renderer can run at a lower rate it - // does so via the renderer manager which will govern if a - // renderer executes this frame. - //-------------------------------------------------------------------------- - // - SET_RENDERER_MANAGER(); -Time startRender = Now(); - Check(rendererManager); - rendererManager->Execute(start_of_frame, frame_ticks, frame_ticks); -Time endRender = Now(); - CLEAR_RENDERER_MANAGER(); - - // - //-------------------------------------------------------------------------- - // Intercom Manager - //-------------------------------------------------------------------------- - // -Time startIntercom = Now(); - Check(intercomManager); - intercomManager->Execute(); -Time endIntercom = Now(); - - // - //-------------------------------------------------------------------------- - // Execution statistics - //-------------------------------------------------------------------------- - // - if (GetApplicationState() == RunningMission) - { - secondsRemainingInGame = - currentMission->GetGameLength() - (Now() - gameStarted); - } - routePacketFinished = False; - - CLEAR_FOREGROUND_PROCESSING(); - - return executeFrames && !Exit_Code; -} - -// -//############################################################################# -// ExecuteBackgroundTask -//############################################################################# -// -#define QUIET_TIME_OUT 3.0f -void - Application::ExecuteBackgroundTask() -{ - Check(this); - - // - //------------------------------------------------------------------------ - // If we are processing the map's creation messages, don't allow any other - // processing to happen until nothing appears for the timeout period - //------------------------------------------------------------------------ - // - if (GetApplicationState() == CreatingMission) - { - Check(networkManager); - - if (networkManager->RoutePacket()) - { - return; - } - - if (ProcessOneEvent(DefaultEventPriority)) - { - return; - } - - if (ProcessOneEvent(LowEventPriority)) - { - lastCreationMessage = Now(); - } - else - { - Scalar wait = Now() - lastCreationMessage; - if (wait > QUIET_TIME_OUT) - { - applicationState.SetState(LoadingMission); - networkManager->Marker("MUNGA MARKER - Starting renderer load...\n"); - networkManager->Mode(NetworkManager::UnreliableMode); - #if defined(LAB_ONLY) - DEBUG_STREAM << "Starting renderer load...\n" << flush; - #endif - } - } - return; - } - - // - // If there exists a high priority event, execute it - // - if (ProcessOneEvent(HighEventPriority)) - { - return; - } - - // - // if - // we are not finished routing packets this frames or - // we are not running the game - // then - // attempt to route a network packet - // we are finished routing packets for this frame - // - if ( - !routePacketFinished || - applicationState.GetState() != RunningMission - ) - { - Check(networkManager); - if (networkManager->RoutePacket()) - { - return; - } - } - routePacketFinished = True; - - // - // Execute lower priority tasks - // - Check(backgroundTasks); - backgroundTasks->Execute(); -} - -// -//############################################################################# -// Stop -//############################################################################# -// -void - Application::Stop() -{ - Check(this); - - // - // Dump analysis sample - // - #if defined(USE_TIME_ANALYSIS) - DEBUG_STREAM << "\nGame timing statistics:\n" << flush; - trace_manager.SnapshotTimingAnalysis(True); - #endif - #if defined(USE_TRACE_LOG) - trace_manager.SaveTraceLog("trace.log"); - #endif - #if defined(USE_EVENT_STATISTICS) - event_statistics_manager.Report(); - #endif - - // - // Set state variables to end game status - // - executeFrames = False; - DEBUG_STREAM << flush << flush; - applicationState.SetState(StoppingMission); -} - -// -//############################################################################# -// Shutdown -//############################################################################# -// -Logical - Application::Shutdown(int remainingApps) -{ - Check(this); - - // - //-------------------------------------------------------------------------- - // Shutdown gauge renderer - //-------------------------------------------------------------------------- - // - if (gaugeRenderer != NULL) - { - Check(gaugeRenderer); - gaugeRenderer->Shutdown(); - gaugeRenderer->UnlinkFromEntity(); - } - // - //-------------------------------------------------------------------------- - // Shutdown video renderer - //-------------------------------------------------------------------------- - // - if (videoRenderer != NULL) - { - Check(videoRenderer); - videoRenderer->Shutdown(); - videoRenderer->UnlinkFromEntity(); - } - // - //-------------------------------------------------------------------------- - // Shutdown audio renderer - //-------------------------------------------------------------------------- - // - if (audioRenderer != NULL) - { - Check(audioRenderer); - audioRenderer->Shutdown(); - audioRenderer->UnlinkFromEntity(); - } - - // - //-------------------------------------------------------------------------- - // Delete the viewpoint entity - //-------------------------------------------------------------------------- - // - if (viewpointEntity != NULL) - { - Unregister_Object(viewpointEntity); - delete viewpointEntity; - viewpointEntity = NULL; - } - - // - //-------------------------------------------------------------------------- - // Shutdown the interest manager - //-------------------------------------------------------------------------- - // - Check(interestManager); - interestManager->Shutdown(); - - // - //-------------------------------------------------------------------------- - // Shutdown the host manager - //-------------------------------------------------------------------------- - // - Check(hostManager); - hostManager->Shutdown(); - - // - //-------------------------------------------------------------------------- - // Shutdown the network manager - //-------------------------------------------------------------------------- - // - Check(networkManager); - networkManager->Shutdown(); - - // - //-------------------------------------------------- - // Delete the current mission if it has been created - //-------------------------------------------------- - // - if (currentMission) - { - Unregister_Object(currentMission); - delete currentMission; - currentMission = NULL; - } - - // - //--------------------------------- - // Allow the process to start again - //--------------------------------- - // - executeFrames = True; - applicationState.SetState(WaitingForEgg); -#if 0 - return !Exit_Code; -#else - return False; -#endif -} - -// -//############################################################################# -// Terminate -//############################################################################# -// -void - Application::Terminate() -{ - Check(this); - - // - //-------------------------------------------------------------------------- - // Delete the gauge renderer - //-------------------------------------------------------------------------- - // - if (gaugeRenderer != NULL) - { - Unregister_Object(gaugeRenderer); - delete gaugeRenderer; - gaugeRenderer = NULL; - } - - // - //-------------------------------------------------------------------------- - // Delete the video renderer - //-------------------------------------------------------------------------- - // - if (videoRenderer != NULL) - { - Unregister_Object(videoRenderer); - delete videoRenderer; - videoRenderer = NULL; - } - - // - //-------------------------------------------------------------------------- - // Delete the audio renderer - //-------------------------------------------------------------------------- - // - if (audioRenderer != NULL) - { - Unregister_Object(audioRenderer); - delete audioRenderer; - audioRenderer = NULL; - } - - // - //----------------------------------------------------------------------- - // Delete the intercom manager - //----------------------------------------------------------------------- - // - if (intercomManager != NULL) - { - Unregister_Object(intercomManager); - delete intercomManager; - intercomManager = NULL; - } - - // - //----------------------------------------------------------------------- - // Delete the controls manager - //----------------------------------------------------------------------- - // - if (controlsManager != NULL) - { - Unregister_Object(controlsManager); - delete controlsManager; - controlsManager = NULL; - } - - // - //------------------------ - // Delete the mode manager - //------------------------ - // - if (modeManager) - { - Unregister_Object(modeManager); - delete modeManager; - modeManager = NULL; - } - - // - //----------------------------------------------------------------------- - // Delete the registry - //----------------------------------------------------------------------- - // - if (registry != NULL) - { - Unregister_Object(registry); - delete registry; - registry = NULL; - } - - // - //----------------------------------------------------------------------- - // Delete the network manager - //----------------------------------------------------------------------- - // - if (networkManager != NULL) - { - Unregister_Object(networkManager); - delete networkManager; - networkManager = NULL; - } - - // - //---------------------------- - // Delete the interest manager - //---------------------------- - // - if (interestManager != NULL) - { - Unregister_Object(interestManager); - delete interestManager; - interestManager = NULL; - } -} - -// -//############################################################################# -// ~Application -//############################################################################# -// -Application::~Application() -{ - // - // Verify that these managers have been deleted - // - Verify(interestManager == NULL); - Verify(networkManager == NULL); - Verify(registry == NULL); - Verify(controlsManager == NULL); - Verify(intercomManager == NULL); - Verify(audioRenderer == NULL); - Verify(videoRenderer == NULL); - Verify(gaugeRenderer == NULL); - - // - // Verify that the mission has been deleted - // - Verify(currentMission == NULL); - - // - // Verify that the viewpoint entity has been deleted - // - Verify(viewpointEntity == NULL); - - // - // Destroy these managers - // - Unregister_Object(backgroundTasks); - delete backgroundTasks; - backgroundTasks = NULL; - - Unregister_Object(rendererManager); - delete rendererManager; - rendererManager = NULL; - - Unregister_Object(updateManager); - delete updateManager; - updateManager = NULL; - - Unregister_Object(hostManager); - delete hostManager; - hostManager = NULL; - - Unregister_Object(entityManager); - delete entityManager; - entityManager = NULL; - - resourceFile = NULL; - - // - // Destroy the event queue - // HACK - the event queue is transparently created as an array, - // therefore must be deleted as one - // - Unregister_Object(eventQueue); - delete[] eventQueue; - eventQueue = NULL; -} - -// -//############################################################################# -// StateQueryMessageHandler -//############################################################################# -// -void - Application::StateQueryMessageHandler( -#if DEBUG_LEVEL>0 - StateQueryMessage *message -#else - StateQueryMessage * -#endif - ) -{ - Check(this); - Check(message); - Verify(message->messageID == StateQueryMessageID); - - // - //-------------------------------------------------------------------------- - // Send the console our application state - //-------------------------------------------------------------------------- - // - Host *console_host; - - Check(GetHostManager()); - if ((console_host = GetHostManager()->GetConsoleHost()) != NULL) - { - Check(console_host); - - unsigned int appState = applicationState.GetState(); - - ConsoleApplicationStateResponseMessage - response_message( - 0, // GetHostManager()->GetLocalHostID(), - applicationState.GetState(), - GetApplicationID() - ); - - SendMessage( - console_host->GetHostID(), - ConsoleClientID, - &response_message - ); - } -} - -// -//############################################################################# -// CreateMission -//############################################################################# -// -void - Application::CreateMission(NotationFile *egg_notation_file) -{ - Check(this); - Check(egg_notation_file); - - // - //-------------------------------------------------------------------------- - // Create mission from egg notation file - //-------------------------------------------------------------------------- - // - currentMission = MakeMission(egg_notation_file, resourceFile); - Register_Object(currentMission); - secondsRemainingInGame = currentMission->GetGameLength(); - - // - //---------------------------------------------------------------------- - // Now, start up the network connect process. The network must have at - // least established the local host before returning, so that the player - // data load can work - //---------------------------------------------------------------------- - // - NetworkManager *net_mgr = GetNetworkManager(); - Check(net_mgr); - net_mgr->StartConnecting(currentMission); - currentMission->SetPlayerData(egg_notation_file); - - InterestManager *interest_mgr = GetInterestManager(); - Check(interest_mgr); - interest_mgr->LoadInterestArenas(currentMission); -} - -// -//############################################################################# -// MakeMission -//############################################################################# -// -Mission* - Application::MakeMission( - NotationFile*, - ResourceFile* - ) -{ - Fail("Application::MakeMisson - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// LoadMissionMessageHandler -//############################################################################# -// -void - Application::LoadMissionMessageHandler(Message *) -{ - Check(this); - // - //-------------------------------------------------------------------------- - // Load the interest manager - //-------------------------------------------------------------------------- - // - #ifdef USE_TIME_ANALYSIS - trace_manager.StartTimingAnalysis(); - #endif - Check(interestManager); - interestManager->LoadMission(currentMission); - - // - //-------------------------------------------------------------------------- - // Make the player - //-------------------------------------------------------------------------- - // - Registry* registry = GetRegistry(); - Check(registry); - missionPlayer = registry->MakePlayer(currentMission); - Register_Object(missionPlayer); - - // - //-------------------------------------------------------------------------- - // Set application state to loading - //-------------------------------------------------------------------------- - // - applicationState.SetState(CreatingMission); - #if defined(LAB_ONLY) - DEBUG_STREAM << "Starting entity creation...\n" << flush; - #endif -} - -// -//############################################################################# -// MakeAndLinkViewpointEntity -//############################################################################# -// -Entity* - Application::MakeAndLinkViewpointEntity(Entity::MakeMessage* message) -{ - Check(this); - Check(message); - - // - //-------------------------------------------------------------------------- - // Create the viewpoint entity - //-------------------------------------------------------------------------- - // - #if DEBUG_LEVEL>0 - HostManager *host = GetHostManager(); - Check(host); - Verify(message->entityID.GetHostID() == host->GetLocalHostID()); - #endif - - Verify(viewpointEntity == NULL); - viewpointEntity = MakeViewpointEntity(message); - - // - //-------------------------------------------------------------------------- - // Post a message to check the status of the load - //-------------------------------------------------------------------------- - // - CheckLoadMessage check_load_message; - Post(DefaultEventPriority, this, &check_load_message); - - // - //-------------------------------------------------------------------------- - // Load audio renderer - //-------------------------------------------------------------------------- - // - if (audioRenderer != NULL) - { - Check(audioRenderer); - audioRenderer->LinkToEntity(viewpointEntity); - audioRenderer->LoadMission(GetCurrentMission()); - audioRenderer->SetRendererStatusToRunning(); - } - - // - //-------------------------------------------------------------------------- - // Load video renderer - //-------------------------------------------------------------------------- - // - if (videoRenderer != NULL) - { - Check(videoRenderer); - videoRenderer->LinkToEntity(viewpointEntity); - videoRenderer->LoadMission(GetCurrentMission()); - videoRenderer->SetRendererStatusToRunning(); - } - - // - //-------------------------------------------------------------------------- - // Load gauge renderer - //-------------------------------------------------------------------------- - // - if (gaugeRenderer != NULL) - { - Check(gaugeRenderer); - gaugeRenderer->LinkToEntity(viewpointEntity); - gaugeRenderer->LoadMission(GetCurrentMission()); - gaugeRenderer->SetRendererStatusToRunning(); - } - - return viewpointEntity; -} - -// -//############################################################################# -// MakeViewpointEntity -//############################################################################# -// -Entity* - Application::MakeViewpointEntity(Entity::MakeMessage*) -{ - Fail("Application::MakeViewpointEntity - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// MakeAudioRenderer -//############################################################################# -// -AudioRenderer* - Application::MakeAudioRenderer() -{ - Fail("Application::MakeAudioRenderer - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// MakeVideoRenderer -//############################################################################# -// -VideoRenderer* - Application::MakeVideoRenderer() -{ - Fail("Application::MakeVideoRenderer - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// MakeGaugeRenderer -//############################################################################# -// -GaugeRenderer* - Application::MakeGaugeRenderer() -{ - Fail("Application::MakeGaugeRenderer - Should never reach here"); - return NULL; -} - -// -//############################################################################# -// CheckLoadMessageHandler -//############################################################################# -// -void - Application::CheckLoadMessageHandler( - CheckLoadMessage *message - ) -{ - Check(this); - Check(message); - Verify(message->messageID == CheckLoadMessageID); - - // - //-------------------------------------------------------------------------- - // If the application is already running then ignore this message - //-------------------------------------------------------------------------- - // - switch (applicationState.GetState()) - { - case CreatingMission: - case LoadingMission: - case WaitingForLaunch: - // - // LAUNCH-GATE TRACE (env BT_LAUNCH_LOG): this handler reposts - // itself every second and will not advance until the low-priority - // event queue drains, so a queue that never empties parks the - // application at WaitingForLaunch forever -- no RunMissionMessage, - // no RunningMission, and the video renderer deliberately holds a - // BLANK SCREEN until then (L4VIDEO.CPP:5100). - // - if (getenv("BT_LAUNCH_LOG")) - DEBUG_STREAM << "[launch] state=" << (int)applicationState.GetState() - << " minPriorityEmpty=" - << (eventQueue->IsPriorityEmpty(MinEventPriority) ? 1 : 0) - << " ticks=" << (long)Now().ticks - << "\n" << flush; - - // - // WHICH queue is holding us? The pump is demonstrably working -- it - // processes ~143 events per simulated second -- yet priority 0 never - // empties. That leaves two very different explanations, and printing - // the occupancy of EVERY priority separates them: - // - // only priority 0 occupied -> its head is a TIMED event whose alarm - // never comes due, so PeekAtNextEvent - // skips it forever while IsPriorityEmpty - // still counts it. A deadlock. - // higher priorities busy -> starvation. ProcessOneEvent scans - // from the top down and returns the - // first ready event, so a steady stream - // above priority 0 means priority 0 is - // never reached. - // - // (GetEventCount is useless here: ++eventCount lives inside - // #if TRACE_EVENT_COUNT, which this build does not define, so it reads - // 0 no matter what is queued.) - // - if (getenv("BT_LAUNCH_LOG")) - { - int - p; - - DEBUG_STREAM << "[queues]"; - for (p = 0; p < EVENT_PRIORITIES_COUNT; p++) - { - DEBUG_STREAM << " p" << p << "=" - << (eventQueue->IsPriorityEmpty(p) ? "-" : "BUSY"); - } - // - // Steady state measured: p0 BUSY, p2 BUSY (that is just this - // handler's own once-a-second repost), everything else empty. So - // nothing is starving priority 0 from above. Two possibilities - // remain, and PeekAtNextEvent separates them using only public - // API, because it returns a TIMED event only once its alarm is due - // while IsPriorityEmpty counts it either way: - // - // nextReady=0 with p0 BUSY -> the head of priority 0 is a timed - // event that never comes due. Invisible to the pump, - // permanently visible to the gate: a deadlock. - // nextReady=1 -> there is work to do and the pump is doing it - // (~143 events per simulated second), so priority 0 is being - // REFILLED as fast as it drains. - // - DEBUG_STREAM << " nextReady=" - << (eventQueue->PeekAtNextEvent(MinEventPriority) ? 1 : 0) - << "\n" << flush; - - // - // Name the flooder: the three busiest message IDs posted at - // priority 0, with their counts. Selection sort by repeated - // max-find, negating each winner so it is skipped next pass and - // restoring the signs afterwards -- the tally has to survive to - // the next second's report. - // - DEBUG_STREAM << "[posts]"; - for (p = 0; p < 3; p++) - { - int - best = -1, - i; - - for (i = 0; i < POST_TALLY_IDS; i++) - { - if ( - postTally[i] > 0 && - (best < 0 || postTally[i] > postTally[best]) - ) - { - best = i; - } - } - if (best < 0) - { - break; - } - DEBUG_STREAM << " id" << best << "=" << postTally[best]; - postTally[best] = -postTally[best]; - } - for (p = 0; p < POST_TALLY_IDS; p++) - { - if (postTally[p] < 0) - { - postTally[p] = -postTally[p]; - } - } - DEBUG_STREAM << "\n" << flush; - } - if (eventQueue->IsPriorityEmpty(MinEventPriority)) - { - Host *console_host; - - if (applicationState.GetState() == LoadingMission) - { - ResourceFile *res_file = GetResourceFile(); - Check(res_file); - res_file->ReleaseUnlockedResources(); - #if defined(LAB_ONLY) - DEBUG_STREAM << "Waiting for translocation!\n" << flush; - #endif - #if defined(USE_TIME_ANALYSIS) - DEBUG_STREAM << "Loading time usage:\n" << flush; - trace_manager.SnapshotTimingAnalysis(True); - #endif - } - applicationState.SetState(WaitingForLaunch); - Check(GetHostManager()); - - console_host = GetHostManager()->GetConsoleHost(); - if ( - ( - (console_host == NULL) || - ( - console_host != NULL && - console_host->GetConnectStatus() != Host::OnLineConnectionStatus - ) - ) - ) - { - // - // In the absence of the console just post the message to run. - // An IN-PROCESS console (hosted network mission) has no - // connection to this pod but still owns the launch -- the - // gConsoleMarshalsLaunch flag holds us at WaitingForLaunch - // until every pod in the mesh is staged (BT412). - // - RunMissionMessage run_mission_message; - Post(DefaultEventPriority, this, &run_mission_message); - Tell("Sent ready message to ourselves\n"); - } - } - - // - //----------------------------------------------------------------------- - // Post this message again until the application is running - //----------------------------------------------------------------------- - // - Time post_time; - - post_time = Now(); - #if DEBUG_LEVEL<3 - post_time += 1.0f; - #else - post_time += 5.0; - #endif - - Post(DefaultEventPriority, this, message, post_time); - break; - } -} - -// -//############################################################################# -// RunMissionMessageHandler -//############################################################################# -// -void - Application::RunMissionMessageHandler( -#if DEBUG_LEVEL>0 - RunMissionMessage *message -#else - RunMissionMessage * -#endif - ) -{ - Check(this); - Check(message); - Verify(message->messageID == RunMissionMessageID); - - // - //-------------------------------------------------------------------------- - // If the application is already running then ignore this message - //-------------------------------------------------------------------------- - // - switch (GetApplicationState()) - { - case RunningMission: - return; - - case LaunchingMission: - // - // Start analysis sample - // - #if defined(USE_TRACE_LOG) - { - char *log_size_str = getenv("LOGSIZE"); - if (log_size_str) - { - size_t log_size = atoi(log_size_str); - if (log_size > 0) - { - trace_manager.CreateTraceLog(log_size,True); - } - } - } - #endif - #ifdef USE_TIME_ANALYSIS - trace_manager.StartTimingAnalysis(); - #endif - - Tell("Application::RunMissionMessageHandler - running mission\n"); - applicationState.SetState(RunningMission); - gameStarted = Now(); - break; - - case WaitingForLaunch: - { - Tell("Application::RunMissionMessageHandler - Translocation\n"); - applicationState.SetState(LaunchingMission); - Player *player = GetMissionPlayer(); - Check(player); - Player::MissionStartingMessage - launch( - Player::MissionStartingMessageID, - sizeof(Player::MissionStartingMessage) - ); - player->Dispatch(&launch); - break; - } - - default: - Fail("Application::RunMissionMessageHandler - Not ready to run!\n"); - break; - } -} - -// -//############################################################################# -// SuspendMissionMessageHandler -//############################################################################# -// -void - Application::SuspendMissionMessageHandler( -#if DEBUG_LEVEL>0 - SuspendMissionMessage *message -#else - SuspendMissionMessage * -#endif - ) -{ - Check(this); - Check(message); - Verify(message->messageID == SuspendMissionMessageID); - - // - //-------------------------------------------------------------------------- - // The application should be either running or already suspended - //-------------------------------------------------------------------------- - // - switch (GetApplicationState()) - { - case RunningMission: - { - Player::MissionEndingMessage - player_message( - Player::MissionEndingMessageID, - sizeof(Player::MissionEndingMessage) - ); - Player *player; - - Tell("Application::SuspendMissionMessageHandler - Suspending\n"); - applicationState.SetState(SuspendingMission); - - player = GetMissionPlayer(); - Check(player); - player->Dispatch(&player_message); - } - break; - - case SuspendingMission: - // - // Already suspended - // - break; - - default: - // - // Any other state is an error - // - Fail("Application::SuspendMissionMessageHandler - Illegal state"); - break; - } -} - -// -//############################################################################# -// ResumeMissionMessageHandler -//############################################################################# -// -void - Application::ResumeMissionMessageHandler( -#if DEBUG_LEVEL>0 - ResumeMissionMessage *message -#else - ResumeMissionMessage * -#endif - ) -{ - Check(this); - Check(message); - Verify(message->messageID == ResumeMissionMessageID); - - // - //-------------------------------------------------------------------------- - // The application should be in the suspended state or resuming - //-------------------------------------------------------------------------- - // - switch (GetApplicationState()) - { - case SuspendingMission: - { - Player::MissionStartingMessage - launch( - Player::MissionStartingMessageID, - sizeof(Player::MissionStartingMessage) - ); - Player *player; - - Tell("Application::ResumeMissionMessageHandler - Resuming mission\n"); - applicationState.SetState(ResumingMission); - player = GetMissionPlayer(); - Check(player); - player->Dispatch(&launch); - } - break; - - case ResumingMission: - Tell("Application::ResumeMissionMessageHandler - Running mission\n"); - applicationState.SetState(RunningMission); - gameStarted = Now(); - break; - - default: - Fail("Application::ResumeMissionMessageHandler - Illegal state\n"); - break; - } -} - -// -//############################################################################# -// StopMissionMessageHandler -//############################################################################# -// -void - Application::StopMissionMessageHandler( -#if DEBUG_LEVEL>0 - StopMissionMessage *message -#else - StopMissionMessage * -#endif - ) -{ - Check(this); - Check(message); - Verify(message->messageID == StopMissionMessageID); - - // - //-------------------------------------------------------------------------- - // If the application is already stopping then ignore the message - //-------------------------------------------------------------------------- - // - switch (GetApplicationState()) - { - case StoppingMission: - return; - - case EndingMission: - case AbortingMission: - Stop(); - break; - - default: - { - applicationState.SetState(EndingMission); - Player *player = GetMissionPlayer(); - if (player) - { - networkManager->Mode(NetworkManager::ReliableMode); - Check(player); - Player::MissionEndingMessage - launch( - Player::MissionEndingMessageID, - sizeof(Player::MissionEndingMessage) - ); - player->Dispatch(&launch); - } - else - { - Stop(); - } - break; - } - } -} - -// -//############################################################################# -// StopMissionMessageHandler -//############################################################################# -// -void - Application::AbortMissionMessageHandler( -#if DEBUG_LEVEL>0 - AbortMissionMessage *message -#else - AbortMissionMessage * -#endif - ) -{ - Check(this); - Check(message); - Verify(message->messageID == AbortMissionMessageID); - - // - //-------------------------------------------------------------------------- - // If the application is already stopping then ignore the message - //-------------------------------------------------------------------------- - // - switch (GetApplicationState()) - { - case StoppingMission: - return; - - case EndingMission: - case AbortingMission: - Stop(); - break; - - default: - { - applicationState.SetState(AbortingMission); - Player *player = GetMissionPlayer(); - if (player) - { - Check(player); - Player::MissionEndingMessage - launch( - Player::MissionEndingMessageID, - sizeof(Player::MissionEndingMessage) - ); - player->Dispatch(&launch); - } - else - { - Stop(); - } - break; - } - } -} - -// -//############################################################################# -// KeyCommandMessageHandler -//############################################################################# -// -void - Application::KeyCommandMessageHandler( - ReceiverDataMessageOf *message - ) -{ - Check(this); - Check(message); - switch (message->dataContents) - { - case '&': - if (GetApplicationState() != StoppingMission) - { - Exit_Code = 1; - Stop(); - DEBUG_STREAM << "Mission stopped by keystroke!\n" << flush; - if (GetApplicationState() == WaitingForEgg) - { - applicationState.SetState(EndingMission); - } - } - break; - - #if defined(USE_TRACE_LOG) - case ' ': - trace_manager.MarkTraceLog(); - break; - case '/': - trace_manager.ResumeTraceLogging(); - break; - case '\\': - trace_manager.SuspendTraceLogging(); - break; - #endif - } -} - -//~~~~~~~~~~~~~~~~~~~~~~ Application__CheckLoadMessage ~~~~~~~~~~~~~~~~~~~~~~~~ - -Application__CheckLoadMessage::Application__CheckLoadMessage(): - NetworkClient::Message( - Application::CheckLoadMessageID, - sizeof(Application__CheckLoadMessage) - ) -{ -} - -#ifdef TEST_CLASS -# include "app.tcp" -#endif +# if !defined(MUNGA_HPP) +# include +# endif +# if !defined(REGISTRY_HPP) +# include +# endif +# if !defined(RENDERER_HPP) +# include +# endif +# if !defined(CONTROLS_HPP) +# include +# endif +# if !defined(UPDATE_HPP) +# include +# endif +# if !defined(MISSION_HPP) +# include +# endif +# if !defined(PLAYER_HPP) +# include +# endif +# if !defined(DIRECTOR_HPP) +# include +# endif +# if !defined(APPMGR_HPP) +# include +# endif +# if !defined(AUDREND_HPP) +# include +# endif +# if !defined(VIDREND_HPP) +# include +# endif +# if !defined(GAUGREND_HPP) +# include +# endif +# if !defined(HOSTMGR_HPP) +# include +# endif +# if !defined(INTEREST_HPP) +# include +# endif +# if !defined(NTTMGR_HPP) +# include +# endif +# if !defined(APPTASK_HPP) +# include +# endif +# if !defined(CONSOLE_HPP) +# include +# endif +# if !defined(APPMSG_HPP) +# include +# endif +# if !defined(EVTSTAT_HPP) +# include +# endif + +#if defined(TRACE_FOREGROUND_PROCESSING) + BitTrace Foreground_Processing("Foreground Processing"); +#endif + +#if defined(TRACE_UPDATE_MANAGER) + BitTrace Update_Manager("Update Manager"); +#endif + +#if defined(TRACE_RENDERER_MANAGER) + BitTrace Renderer_Manager("Renderer Manager"); +#endif + +Application *application = NULL; +int Exit_Code = 0; +Logical Application::suppressGauges = False; + +// +// Event-pump counters -- see the comment on Application::ProcessOneEvent in +// APP.HPP. Diagnostic only; the BT_STACK_LOG probe reports them. +// +long eventPumpCalls = 0; +long eventPumpHits = 0; + +// +// GAUGE-STARVATION PROBE counters (env BT_STACK_LOG prints them 1/sec). +// +// The cockpit displays repaint ONCE PER GAUGE-WHEEL PASS (the copy phase +// at the end of ProcessOneActiveGauge's active list), and the wheel only +// advances one gauge per background slot, every Nth slot of the task +// ring. "Many seconds between display updates" therefore means the slot +// budget collapsed -- these counters show where the slots go. +// +// bgSlots every ExecuteBackgroundTask entry (the slot supply) +// bgTaskRuns slots that fell through to the task ring +// gaugeSlices gauge-wheel slots (ProcessOneActiveGauge calls) +// gaugePasses completed wheel passes == display repaints +// gaugeCopySlices copy-phase slots (the changed-line flush) +// +long bgSlots = 0; +long bgTaskRuns = 0; + +// +// Foreground stage tick sums (same probe). bgSlots pinned at exactly one +// per frame means ExecuteForeground consumes the whole frame quantum every +// frame; these localize WHICH stage holds the clock when it jumps. +// +long taskRouteTicks = 0; +long taskEventTicks = 0; +long taskAudioTicks = 0; +long taskGaugeTicks = 0; +long taskNetTicks = 0; +long taskCyclesTicks = 0; +long taskFryTicks = 0; + +long hiScanTicks = 0; // ProcessOneEvent(High) at each bg slot top +long probeTicks = 0; // the [stack] probe block itself +long fgTotalTicks = 0; // whole ExecuteForeground main path + +long fgControlsTicks = 0; +long fgUpdateTicks = 0; +long fgInterestTicks = 0; +long fgRenderTicks = 0; +long fgIntercomTicks = 0; +long gaugeSlices = 0; +long gaugePasses = 0; +long gaugeCopySlices = 0; +long postTally[POST_TALLY_IDS]; + +//############################################################################# +//########################### Application ############################### +//############################################################################# + +//############################################################################# +// Message Support +// +const Receiver::HandlerEntry + Application::MessageHandlerEntries[]= +{ + MESSAGE_ENTRY(Application, StateQuery), + MESSAGE_ENTRY(Application, CheckLoad), + MESSAGE_ENTRY(Application, RunMission), + MESSAGE_ENTRY(Application, StopMission), + MESSAGE_ENTRY(Application, ResumeMission), + MESSAGE_ENTRY(Application, SuspendMission), + MESSAGE_ENTRY(Application, KeyCommand), + MESSAGE_ENTRY(Application, LoadMission), + MESSAGE_ENTRY(Application, AbortMission) +}; + +Application::MessageHandlerSet + Application::MessageHandlers(ELEMENTS(Application::MessageHandlerEntries), Application::MessageHandlerEntries, NetworkClient::MessageHandlers); + +//############################################################################# +// Virtual Data support +// +Derivation + Application::ClassDerivations(NetworkClient::ClassDerivations, "Application"); + +Application::SharedData + Application::DefaultData( + Application::ClassDerivations, + Application::MessageHandlers + ); + +// +//############################################################################# +// TestInstance +//############################################################################# +// +Logical + Application::TestInstance() const +{ + if (!IsDerivedFrom(ClassDerivations)) + { + return False; + } + Check(&applicationState); + if (eventQueue) + { + Check(eventQueue); + } + if (networkManager) + { + Check(networkManager); + } + if (entityManager) + { + Check(entityManager); + } + if (registry) + { + Check(registry); + } + if (hostManager) + { + Check(hostManager); + } + if (interestManager) + { + Check(interestManager); + } + if (updateManager) + { + Check(updateManager); + } + if (rendererManager) + { + Check(rendererManager); + } + if (controlsManager) + { + Check(controlsManager); + } + if (intercomManager) + { + Check(intercomManager); + } + if (resourceFile) + { + Check(resourceFile); + } + if (viewpointEntity) + { + Check(viewpointEntity); + } + if (currentMission) + { + Check(currentMission); + } + if (backgroundTasks) + { + Check(backgroundTasks); + } + if (audioRenderer) + { + Check(audioRenderer); + } + if (videoRenderer) + { + Check(videoRenderer); + } + if (gaugeRenderer) + { + Check(gaugeRenderer); + } + return True; +} + +// +//############################################################################# +// Application +//############################################################################# +// +Application::Application( + ResourceFile *resource_file, + ApplicationID application_ID, + ClassID class_ID, + SharedData &shared_data +): + NetworkClient(class_ID, shared_data, ApplicationClientID), + applicationState(ApplicationStateCount) +{ + Check(&shared_data); + Check(resource_file); + + applicationID = application_ID; + + // + // Create the event queue + // + eventQueue = GeneralEventQueue::Make( + EVENT_PRIORITIES_COUNT, + "MUNGA Event Count" + ); + Check(eventQueue); + + // + // Remember the resource file + // + resourceFile = resource_file; + missionPlayer = NULL; + + // + // NULL managers not yet created + // + networkManager = NULL; + registry = NULL; + controlsManager = NULL; + intercomManager = NULL; + backgroundTasks = NULL; + interestManager = NULL; + audioRenderer = NULL; + videoRenderer = NULL; + gaugeRenderer = NULL; + + // + // NULL pointers to other objects not yet created + // + viewpointEntity = NULL; + currentMission = NULL; + + // + // Create base level managers + // + entityManager = new EntityManager; + Register_Object(entityManager); + hostManager = new HostManager; + Register_Object(hostManager); + updateManager = new UpdateManager; + Register_Object(updateManager); + rendererManager = new RendererManager; + Register_Object(rendererManager); + backgroundTasks = new BackgroundTasks; + Register_Object(backgroundTasks); + + // + // Set up game state + // + executeFrames = False; + applicationState.SetState(InitializingState); + currentMission = NULL; + secondsRemainingInGame = 0.0f; + spoolFile = NULL; + routePacketFinished = False; + lastCreationMessage = Now(); + + // + // HACK - Init analysis bits + // + #if defined(TRACE_ON) + trace_manager.ResetTraces(); + #endif +} + +// +//############################################################################# +// GetApplicationManager +//############################################################################# +// +ApplicationManager* + Application::GetApplicationManager() +{ + PlugIteratorOf manager_link(this); + ApplicationManager *mgr; + while ((mgr = manager_link.ReadAndNext()) != NULL) + { + if (mgr->GetClassID() == ApplicationManagerClassID) + { + return mgr; + } + } + return NULL; +} + +Scalar + Application::GetApplicationLoopFrameRate() +{ + ApplicationManager *mgr = GetApplicationManager(); + Check(mgr); + return mgr->GetFrameRate(); +} + +// +//############################################################################# +// Initialize +//############################################################################# +// +void + Application::Initialize() +{ + Check(this); + + // + //---------------------------- + // Create the interest Manager + //---------------------------- + // + interestManager = MakeInterestManager(); + Register_Object(interestManager); + + // + //-------------------------------------------------------------------------- + // Create the network manager + //-------------------------------------------------------------------------- + // + networkManager = MakeNetworkManager(); + Register_Object(networkManager); + + // + //-------------------------------------------------------------------------- + // Create the registry, load static object streams + //-------------------------------------------------------------------------- + // + registry = MakeRegistry(); + if (registry) + { + Register_Object(registry); + registry->LoadStaticObjectStreamResource(); + } + + // + //-------------------------------------------------------------------------- + // Create the mode manager + //-------------------------------------------------------------------------- + // + modeManager = MakeModeManager(); + Register_Object(modeManager); + + // + //-------------------------------------------------------------------------- + // Create the controls manager + //-------------------------------------------------------------------------- + // + controlsManager = MakeControlsManager(); + Register_Object(controlsManager); + + // + //-------------------------------------------------------------------------- + // Create the intercom manager + //-------------------------------------------------------------------------- + // + intercomManager = MakeIntercomManager(); + Register_Object(intercomManager); + + // + //-------------------------------------------------------------------------- + // Add background tasks + //-------------------------------------------------------------------------- + // + LoadBackgroundTasks(); + executeFrames = True; + applicationState.SetState(WaitingForEgg); + + // + //-------------------------------------------------------------------------- + // Create the audio renderer + //-------------------------------------------------------------------------- + // + Verify(audioRenderer == NULL); + if ((audioRenderer = MakeAudioRenderer()) != NULL) + { + Register_Object(audioRenderer); + audioRenderer->Initialize(); + } + + // + //-------------------------------------------------------------------------- + // Create the video renderer + //-------------------------------------------------------------------------- + // + Verify(videoRenderer == NULL); + if ((videoRenderer = MakeVideoRenderer()) != NULL) + { + Register_Object(videoRenderer); + } + + // + //-------------------------------------------------------------------------- + // Create the gauge renderer + //-------------------------------------------------------------------------- + // + Verify(gaugeRenderer == NULL); + + if ((gaugeRenderer = MakeGaugeRenderer()) != NULL) + { + Register_Object(gaugeRenderer); + } +} + +// +//############################################################################# +// LoadBackgroundTasks +//############################################################################# +// +void + Application::LoadBackgroundTasks() +{ + Check(this); + + ApplicationTask *application_task; + + application_task = new RoutePacketTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); + + application_task = new ProcessEventTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); + + application_task = new AudioRendererTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); + + application_task = new GaugeRendererTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); + + application_task = new NetworkManagerTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); + + application_task = new CompleteCyclesTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); + + application_task = new FryDeathRowTask; + Register_Object(application_task); + backgroundTasks->AddTask(application_task); +} + +// +//############################################################################# +// MakeInterestManager +//############################################################################# +// +InterestManager* + Application::MakeInterestManager() +{ + return new InterestManager; +} + +// +//############################################################################# +// MakeNetworkManager +//############################################################################# +// +NetworkManager* + Application::MakeNetworkManager() +{ + return new NetworkManager(NetworkManager::DefaultData); +} + +// +//############################################################################# +// MakeRegistry +//############################################################################# +// +Registry* + Application::MakeRegistry() +{ + Fail("Application::MakeRegistry - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// MakeModeManager +//############################################################################# +// +ModeManager* + Application::MakeModeManager() +{ + Fail("Application::MakeModeManager - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// MakeControlsManager +//############################################################################# +// +ControlsManager* + Application::MakeControlsManager() +{ + Fail("Application::MakeControlsManager - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// MakeIntercomManager +//############################################################################# +// +IcomManager* + Application::MakeIntercomManager() +{ + Fail("Application::MakeIntercomManager - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// ExecuteForeground +//############################################################################# +// +Logical + Application::ExecuteForeground( + Time start_of_frame, + Scalar frame_duration + ) +{ + SET_FOREGROUND_PROCESSING(); + + Check(this); + Verify(application == this); + + // + // STACK-DRIFT PROBE (env BT_STACK_LOG). + // + // The intermittent page fault reads a stack LOCAL through the frame + // pointer -- disassembly of the fault site shows edi = ebp-0x10, and all + // three callers of that function pass "lea eax,[ebp-0x30]" -- so the bad + // address cannot be a wild argument. It has to be the stack pointer + // itself sitting outside the region. The stack geometry matches the + // shipped exe exactly (1MB reserve, 8K commit), which leaves ESP drifting + // a few bytes per call, most likely a calling-convention mismatch on some + // function we re-declared. + // + // So: record where the frame actually sits, once a second. A monotone + // march proves the drift and its rate; a stable address kills the theory + // outright. One run either way. + // +#if defined(BT_TICK_PROBES) + Time probe_entry = Now(); +#endif + if (getenv("BT_STACK_LOG")) + { + static char *first_frame = NULL; + static Scalar stack_accum = 0.0f; + static int stack_samples = 0; + char here; + + if (first_frame == NULL) + { + first_frame = &here; + } + stack_accum += frame_duration; + stack_samples++; + if (stack_accum >= 1.0f) + { + stack_accum = 0.0f; + DEBUG_STREAM << "[stack] frame at 0x" << hex << (int)&here << dec + << " drift=" << (int)(first_frame - &here) + << " over " << stack_samples << " frames" + << " ticks=" << (long)Now().ticks + << " pump=" << eventPumpHits << "/" << eventPumpCalls + << " bg=" << bgTaskRuns << "/" << bgSlots + << " g=" << gaugeSlices + << " p=" << gaugePasses + << " c=" << gaugeCopySlices + << " fg=" << fgControlsTicks + << "/" << fgUpdateTicks + << "/" << fgInterestTicks + << "/" << fgRenderTicks + << "/" << fgIntercomTicks + << " hi=" << hiScanTicks + << " pr=" << probeTicks + << " ft=" << fgTotalTicks + << " tt=" << taskRouteTicks + << "/" << taskEventTicks + << "/" << taskAudioTicks + << "/" << taskGaugeTicks + << "/" << taskNetTicks + << "/" << taskCyclesTicks + << "/" << taskFryTicks + << endl << flush; + } + } + +#if defined(BT_TICK_PROBES) + probeTicks += (long)(Now().ticks - probe_entry.ticks); + +#endif + if (!executeFrames) + { + CLEAR_FOREGROUND_PROCESSING(); + return executeFrames; + } + + Time + frame_ticks; + frame_ticks = frame_duration; + + // + //-------------------------------------------------------------------------- + // Controls Manager + // + // Poll all devices, update all control variables. + // + // This is executed before the update manager so that the + // models have valid control values. It is not necessary for + // the controls to operate at the frame rate of this loop. If + // the controls manager can run run at a lower rate it can + // throttle itself internally. + //-------------------------------------------------------------------------- + // + Check(controlsManager); +#if defined(BT_TICK_PROBES) +Time startControls = Now(); +#endif + controlsManager->Execute(); +#if defined(BT_TICK_PROBES) +Time endControls = Now(); +fgControlsTicks += (long)(endControls.ticks - startControls.ticks); +#endif + + if (GetApplicationState() == WaitingForEgg) + { + CLEAR_FOREGROUND_PROCESSING(); + return executeFrames; + } + + // + //-------------------------------------------------------------------------- + // Update Manager + // + // Execute replicants. + // Execute master entities if they are interesting here or + // elsewhere. + // Inform interest manager of possible interest zone change. + // If the master entity has provided and update message then + // send it to replicants. + // + // This is executed before the interest manager so that the + // interest manager can merge all interest zone changes before + // broadcasting interest arena deltas and building interesting + // entity lists. + // + // This is executed before the renderers so that the watchers + // have executed on the model and are ready for use by the + // renderers. + //-------------------------------------------------------------------------- + // + SET_UPDATE_MANAGER(); + +#if defined(BT_TICK_PROBES) +Time startUpdate = Now(); +#endif + Check(updateManager); + updateManager->Execute(start_of_frame); +#if defined(BT_TICK_PROBES) +Time endUpdate = Now(); +fgUpdateTicks += (long)(endUpdate.ticks - startUpdate.ticks); +#endif + + CLEAR_UPDATE_MANAGER(); + + // + //-------------------------------------------------------------------------- + // Interest Manager + // + // Update the net interest arena. + // Calculate which interest zones should be released and which + // interest zones should be loaded + // Send a becoming uninteresting and becoming interesting + // message to the entity. + // Broadcast the interest arena change + // + // The renderer manager call, update interest origins will + // update only those interest origins required by the renderers + // executing this frame. + // + // This is executed before the renderer manager so that when + // the renderers execute they execute upon interest lists that + // are valid as of the end of a model/update frame. + // + // In theory the interest manager does not have to run every + // frame. If throttled less, this would cause fuzz at interest + // zone borders, which may or may not be acceptable depending + // on the size of the interest arena. + //-------------------------------------------------------------------------- + // +#if defined(BT_TICK_PROBES) +Time startInterest = Now(); +#endif + Check(rendererManager); + rendererManager->UpdateInterestOrigins(start_of_frame); + Check(interestManager); + interestManager->Execute(); +#if defined(BT_TICK_PROBES) +Time endInterest = Now(); +fgInterestTicks += (long)(endInterest.ticks - startInterest.ticks); +#endif + + // + //-------------------------------------------------------------------------- + // Renderer Manager + // + // Execute renderers. + // Get interest list from interest manager. + // Poll watchers. + // + // It is not necessary for all renderers to operate at the frame + // rate of this loop. If a renderer can run at a lower rate it + // does so via the renderer manager which will govern if a + // renderer executes this frame. + //-------------------------------------------------------------------------- + // + SET_RENDERER_MANAGER(); +#if defined(BT_TICK_PROBES) +Time startRender = Now(); +#endif + Check(rendererManager); + rendererManager->Execute(start_of_frame, frame_ticks, frame_ticks); +#if defined(BT_TICK_PROBES) +Time endRender = Now(); +fgRenderTicks += (long)(endRender.ticks - startRender.ticks); +#endif + CLEAR_RENDERER_MANAGER(); + + // + //-------------------------------------------------------------------------- + // Intercom Manager + //-------------------------------------------------------------------------- + // +#if defined(BT_TICK_PROBES) +Time startIntercom = Now(); +#endif + Check(intercomManager); + intercomManager->Execute(); +#if defined(BT_TICK_PROBES) +Time endIntercom = Now(); +fgIntercomTicks += (long)(endIntercom.ticks - startIntercom.ticks); +#endif + + // + //-------------------------------------------------------------------------- + // Execution statistics + //-------------------------------------------------------------------------- + // + if (GetApplicationState() == RunningMission) + { + secondsRemainingInGame = + currentMission->GetGameLength() - (Now() - gameStarted); + } + routePacketFinished = False; + +#if defined(BT_TICK_PROBES) + fgTotalTicks += (long)(Now().ticks - probe_entry.ticks); + +#endif + CLEAR_FOREGROUND_PROCESSING(); + + return executeFrames && !Exit_Code; +} + +// +//############################################################################# +// ExecuteBackgroundTask +//############################################################################# +// +#define QUIET_TIME_OUT 3.0f +void + Application::ExecuteBackgroundTask() +{ + Check(this); + + bgSlots++; + + // + //------------------------------------------------------------------------ + // If we are processing the map's creation messages, don't allow any other + // processing to happen until nothing appears for the timeout period + //------------------------------------------------------------------------ + // + if (GetApplicationState() == CreatingMission) + { + Check(networkManager); + + if (networkManager->RoutePacket()) + { + return; + } + + if (ProcessOneEvent(DefaultEventPriority)) + { + return; + } + + if (ProcessOneEvent(LowEventPriority)) + { + lastCreationMessage = Now(); + } + else + { + Scalar wait = Now() - lastCreationMessage; + if (wait > QUIET_TIME_OUT) + { + applicationState.SetState(LoadingMission); + networkManager->Marker("MUNGA MARKER - Starting renderer load...\n"); + networkManager->Mode(NetworkManager::UnreliableMode); + #if defined(LAB_ONLY) + DEBUG_STREAM << "Starting renderer load...\n" << flush; + #endif + } + } + return; + } + + // + // If there exists a high priority event, execute it + // + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + Logical hi = ProcessOneEvent(HighEventPriority); + hiScanTicks += (long)(Now().ticks - probe_t0.ticks); +#else + Logical hi = ProcessOneEvent(HighEventPriority); +#endif + if (hi) + { + return; + } + } + + // + // if + // we are not finished routing packets this frames or + // we are not running the game + // then + // attempt to route a network packet + // we are finished routing packets for this frame + // + if ( + !routePacketFinished || + applicationState.GetState() != RunningMission + ) + { + Check(networkManager); + if (networkManager->RoutePacket()) + { + return; + } + } + routePacketFinished = True; + + // + // Execute lower priority tasks + // + Check(backgroundTasks); + bgTaskRuns++; + backgroundTasks->Execute(); +} + +// +//############################################################################# +// Stop +//############################################################################# +// +void + Application::Stop() +{ + Check(this); + + // + // Dump analysis sample + // + #if defined(USE_TIME_ANALYSIS) + DEBUG_STREAM << "\nGame timing statistics:\n" << flush; + trace_manager.SnapshotTimingAnalysis(True); + #endif + #if defined(USE_TRACE_LOG) + trace_manager.SaveTraceLog("trace.log"); + #endif + #if defined(USE_EVENT_STATISTICS) + event_statistics_manager.Report(); + #endif + + // + // Set state variables to end game status + // + executeFrames = False; + DEBUG_STREAM << flush << flush; + applicationState.SetState(StoppingMission); +} + +// +//############################################################################# +// Shutdown +//############################################################################# +// +Logical + Application::Shutdown(int remainingApps) +{ + Check(this); + + // + //-------------------------------------------------------------------------- + // Shutdown gauge renderer + //-------------------------------------------------------------------------- + // + if (gaugeRenderer != NULL) + { + Check(gaugeRenderer); + gaugeRenderer->Shutdown(); + gaugeRenderer->UnlinkFromEntity(); + } + // + //-------------------------------------------------------------------------- + // Shutdown video renderer + //-------------------------------------------------------------------------- + // + if (videoRenderer != NULL) + { + Check(videoRenderer); + videoRenderer->Shutdown(); + videoRenderer->UnlinkFromEntity(); + } + // + //-------------------------------------------------------------------------- + // Shutdown audio renderer + //-------------------------------------------------------------------------- + // + if (audioRenderer != NULL) + { + Check(audioRenderer); + audioRenderer->Shutdown(); + audioRenderer->UnlinkFromEntity(); + } + + // + //-------------------------------------------------------------------------- + // Delete the viewpoint entity + //-------------------------------------------------------------------------- + // + if (viewpointEntity != NULL) + { + Unregister_Object(viewpointEntity); + delete viewpointEntity; + viewpointEntity = NULL; + } + + // + //-------------------------------------------------------------------------- + // Shutdown the interest manager + //-------------------------------------------------------------------------- + // + Check(interestManager); + interestManager->Shutdown(); + + // + //-------------------------------------------------------------------------- + // Shutdown the host manager + //-------------------------------------------------------------------------- + // + Check(hostManager); + hostManager->Shutdown(); + + // + //-------------------------------------------------------------------------- + // Shutdown the network manager + //-------------------------------------------------------------------------- + // + Check(networkManager); + networkManager->Shutdown(); + + // + //-------------------------------------------------- + // Delete the current mission if it has been created + //-------------------------------------------------- + // + if (currentMission) + { + Unregister_Object(currentMission); + delete currentMission; + currentMission = NULL; + } + + // + //--------------------------------- + // Allow the process to start again + //--------------------------------- + // + executeFrames = True; + applicationState.SetState(WaitingForEgg); +#if 0 + return !Exit_Code; +#else + return False; +#endif +} + +// +//############################################################################# +// Terminate +//############################################################################# +// +void + Application::Terminate() +{ + Check(this); + + // + //-------------------------------------------------------------------------- + // Delete the gauge renderer + //-------------------------------------------------------------------------- + // + if (gaugeRenderer != NULL) + { + Unregister_Object(gaugeRenderer); + delete gaugeRenderer; + gaugeRenderer = NULL; + } + + // + //-------------------------------------------------------------------------- + // Delete the video renderer + //-------------------------------------------------------------------------- + // + if (videoRenderer != NULL) + { + Unregister_Object(videoRenderer); + delete videoRenderer; + videoRenderer = NULL; + } + + // + //-------------------------------------------------------------------------- + // Delete the audio renderer + //-------------------------------------------------------------------------- + // + if (audioRenderer != NULL) + { + Unregister_Object(audioRenderer); + delete audioRenderer; + audioRenderer = NULL; + } + + // + //----------------------------------------------------------------------- + // Delete the intercom manager + //----------------------------------------------------------------------- + // + if (intercomManager != NULL) + { + Unregister_Object(intercomManager); + delete intercomManager; + intercomManager = NULL; + } + + // + //----------------------------------------------------------------------- + // Delete the controls manager + //----------------------------------------------------------------------- + // + if (controlsManager != NULL) + { + Unregister_Object(controlsManager); + delete controlsManager; + controlsManager = NULL; + } + + // + //------------------------ + // Delete the mode manager + //------------------------ + // + if (modeManager) + { + Unregister_Object(modeManager); + delete modeManager; + modeManager = NULL; + } + + // + //----------------------------------------------------------------------- + // Delete the registry + //----------------------------------------------------------------------- + // + if (registry != NULL) + { + Unregister_Object(registry); + delete registry; + registry = NULL; + } + + // + //----------------------------------------------------------------------- + // Delete the network manager + //----------------------------------------------------------------------- + // + if (networkManager != NULL) + { + Unregister_Object(networkManager); + delete networkManager; + networkManager = NULL; + } + + // + //---------------------------- + // Delete the interest manager + //---------------------------- + // + if (interestManager != NULL) + { + Unregister_Object(interestManager); + delete interestManager; + interestManager = NULL; + } +} + +// +//############################################################################# +// ~Application +//############################################################################# +// +Application::~Application() +{ + // + // Verify that these managers have been deleted + // + Verify(interestManager == NULL); + Verify(networkManager == NULL); + Verify(registry == NULL); + Verify(controlsManager == NULL); + Verify(intercomManager == NULL); + Verify(audioRenderer == NULL); + Verify(videoRenderer == NULL); + Verify(gaugeRenderer == NULL); + + // + // Verify that the mission has been deleted + // + Verify(currentMission == NULL); + + // + // Verify that the viewpoint entity has been deleted + // + Verify(viewpointEntity == NULL); + + // + // Destroy these managers + // + Unregister_Object(backgroundTasks); + delete backgroundTasks; + backgroundTasks = NULL; + + Unregister_Object(rendererManager); + delete rendererManager; + rendererManager = NULL; + + Unregister_Object(updateManager); + delete updateManager; + updateManager = NULL; + + Unregister_Object(hostManager); + delete hostManager; + hostManager = NULL; + + Unregister_Object(entityManager); + delete entityManager; + entityManager = NULL; + + resourceFile = NULL; + + // + // Destroy the event queue + // HACK - the event queue is transparently created as an array, + // therefore must be deleted as one + // + Unregister_Object(eventQueue); + delete[] eventQueue; + eventQueue = NULL; +} + +// +//############################################################################# +// StateQueryMessageHandler +//############################################################################# +// +void + Application::StateQueryMessageHandler( +#if DEBUG_LEVEL>0 + StateQueryMessage *message +#else + StateQueryMessage * +#endif + ) +{ + Check(this); + Check(message); + Verify(message->messageID == StateQueryMessageID); + + // + //-------------------------------------------------------------------------- + // Send the console our application state + //-------------------------------------------------------------------------- + // + Host *console_host; + + Check(GetHostManager()); + if ((console_host = GetHostManager()->GetConsoleHost()) != NULL) + { + Check(console_host); + + unsigned int appState = applicationState.GetState(); + + ConsoleApplicationStateResponseMessage + response_message( + 0, // GetHostManager()->GetLocalHostID(), + applicationState.GetState(), + GetApplicationID() + ); + + SendMessage( + console_host->GetHostID(), + ConsoleClientID, + &response_message + ); + } +} + +// +//############################################################################# +// CreateMission +//############################################################################# +// +void + Application::CreateMission(NotationFile *egg_notation_file) +{ + Check(this); + Check(egg_notation_file); + + // + //-------------------------------------------------------------------------- + // Create mission from egg notation file + //-------------------------------------------------------------------------- + // + currentMission = MakeMission(egg_notation_file, resourceFile); + Register_Object(currentMission); + secondsRemainingInGame = currentMission->GetGameLength(); + + // + //---------------------------------------------------------------------- + // Now, start up the network connect process. The network must have at + // least established the local host before returning, so that the player + // data load can work + //---------------------------------------------------------------------- + // + NetworkManager *net_mgr = GetNetworkManager(); + Check(net_mgr); + net_mgr->StartConnecting(currentMission); + currentMission->SetPlayerData(egg_notation_file); + + InterestManager *interest_mgr = GetInterestManager(); + Check(interest_mgr); + interest_mgr->LoadInterestArenas(currentMission); +} + +// +//############################################################################# +// MakeMission +//############################################################################# +// +Mission* + Application::MakeMission( + NotationFile*, + ResourceFile* + ) +{ + Fail("Application::MakeMisson - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// LoadMissionMessageHandler +//############################################################################# +// +void + Application::LoadMissionMessageHandler(Message *) +{ + Check(this); + // + //-------------------------------------------------------------------------- + // Load the interest manager + //-------------------------------------------------------------------------- + // + #ifdef USE_TIME_ANALYSIS + trace_manager.StartTimingAnalysis(); + #endif + Check(interestManager); + interestManager->LoadMission(currentMission); + + // + //-------------------------------------------------------------------------- + // Make the player + //-------------------------------------------------------------------------- + // + Registry* registry = GetRegistry(); + Check(registry); + missionPlayer = registry->MakePlayer(currentMission); + Register_Object(missionPlayer); + + // + //-------------------------------------------------------------------------- + // Set application state to loading + //-------------------------------------------------------------------------- + // + applicationState.SetState(CreatingMission); + #if defined(LAB_ONLY) + DEBUG_STREAM << "Starting entity creation...\n" << flush; + #endif +} + +// +//############################################################################# +// MakeAndLinkViewpointEntity +//############################################################################# +// +Entity* + Application::MakeAndLinkViewpointEntity(Entity::MakeMessage* message) +{ + Check(this); + Check(message); + + // + //-------------------------------------------------------------------------- + // Create the viewpoint entity + //-------------------------------------------------------------------------- + // + #if DEBUG_LEVEL>0 + HostManager *host = GetHostManager(); + Check(host); + Verify(message->entityID.GetHostID() == host->GetLocalHostID()); + #endif + + Verify(viewpointEntity == NULL); + viewpointEntity = MakeViewpointEntity(message); + + // + //-------------------------------------------------------------------------- + // Post a message to check the status of the load + //-------------------------------------------------------------------------- + // + CheckLoadMessage check_load_message; + Post(DefaultEventPriority, this, &check_load_message); + + // + //-------------------------------------------------------------------------- + // Load audio renderer + //-------------------------------------------------------------------------- + // + if (audioRenderer != NULL) + { + Check(audioRenderer); + audioRenderer->LinkToEntity(viewpointEntity); + audioRenderer->LoadMission(GetCurrentMission()); + audioRenderer->SetRendererStatusToRunning(); + } + + // + //-------------------------------------------------------------------------- + // Load video renderer + //-------------------------------------------------------------------------- + // + if (videoRenderer != NULL) + { + Check(videoRenderer); + videoRenderer->LinkToEntity(viewpointEntity); + videoRenderer->LoadMission(GetCurrentMission()); + videoRenderer->SetRendererStatusToRunning(); + } + + // + //-------------------------------------------------------------------------- + // Load gauge renderer + //-------------------------------------------------------------------------- + // + if (gaugeRenderer != NULL) + { + Check(gaugeRenderer); + gaugeRenderer->LinkToEntity(viewpointEntity); + gaugeRenderer->LoadMission(GetCurrentMission()); + gaugeRenderer->SetRendererStatusToRunning(); + } + + return viewpointEntity; +} + +// +//############################################################################# +// MakeViewpointEntity +//############################################################################# +// +Entity* + Application::MakeViewpointEntity(Entity::MakeMessage*) +{ + Fail("Application::MakeViewpointEntity - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// MakeAudioRenderer +//############################################################################# +// +AudioRenderer* + Application::MakeAudioRenderer() +{ + Fail("Application::MakeAudioRenderer - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// MakeVideoRenderer +//############################################################################# +// +VideoRenderer* + Application::MakeVideoRenderer() +{ + Fail("Application::MakeVideoRenderer - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// MakeGaugeRenderer +//############################################################################# +// +GaugeRenderer* + Application::MakeGaugeRenderer() +{ + Fail("Application::MakeGaugeRenderer - Should never reach here"); + return NULL; +} + +// +//############################################################################# +// CheckLoadMessageHandler +//############################################################################# +// +void + Application::CheckLoadMessageHandler( + CheckLoadMessage *message + ) +{ + Check(this); + Check(message); + Verify(message->messageID == CheckLoadMessageID); + + // + //-------------------------------------------------------------------------- + // If the application is already running then ignore this message + //-------------------------------------------------------------------------- + // + switch (applicationState.GetState()) + { + case CreatingMission: + case LoadingMission: + case WaitingForLaunch: + // + // LAUNCH-GATE TRACE (env BT_LAUNCH_LOG): this handler reposts + // itself every second and will not advance until the low-priority + // event queue drains, so a queue that never empties parks the + // application at WaitingForLaunch forever -- no RunMissionMessage, + // no RunningMission, and the video renderer deliberately holds a + // BLANK SCREEN until then (L4VIDEO.CPP:5100). + // + if (getenv("BT_LAUNCH_LOG")) + DEBUG_STREAM << "[launch] state=" << (int)applicationState.GetState() + << " minPriorityEmpty=" + << (eventQueue->IsPriorityEmpty(MinEventPriority) ? 1 : 0) + << " ticks=" << (long)Now().ticks + << "\n" << flush; + + // + // WHICH queue is holding us? The pump is demonstrably working -- it + // processes ~143 events per simulated second -- yet priority 0 never + // empties. That leaves two very different explanations, and printing + // the occupancy of EVERY priority separates them: + // + // only priority 0 occupied -> its head is a TIMED event whose alarm + // never comes due, so PeekAtNextEvent + // skips it forever while IsPriorityEmpty + // still counts it. A deadlock. + // higher priorities busy -> starvation. ProcessOneEvent scans + // from the top down and returns the + // first ready event, so a steady stream + // above priority 0 means priority 0 is + // never reached. + // + // (GetEventCount is useless here: ++eventCount lives inside + // #if TRACE_EVENT_COUNT, which this build does not define, so it reads + // 0 no matter what is queued.) + // + if (getenv("BT_LAUNCH_LOG")) + { + int + p; + + DEBUG_STREAM << "[queues]"; + for (p = 0; p < EVENT_PRIORITIES_COUNT; p++) + { + DEBUG_STREAM << " p" << p << "=" + << (eventQueue->IsPriorityEmpty(p) ? "-" : "BUSY"); + } + // + // Steady state measured: p0 BUSY, p2 BUSY (that is just this + // handler's own once-a-second repost), everything else empty. So + // nothing is starving priority 0 from above. Two possibilities + // remain, and PeekAtNextEvent separates them using only public + // API, because it returns a TIMED event only once its alarm is due + // while IsPriorityEmpty counts it either way: + // + // nextReady=0 with p0 BUSY -> the head of priority 0 is a timed + // event that never comes due. Invisible to the pump, + // permanently visible to the gate: a deadlock. + // nextReady=1 -> there is work to do and the pump is doing it + // (~143 events per simulated second), so priority 0 is being + // REFILLED as fast as it drains. + // + DEBUG_STREAM << " nextReady=" + << (eventQueue->PeekAtNextEvent(MinEventPriority) ? 1 : 0) + << "\n" << flush; + + // + // Name the flooder: the three busiest message IDs posted at + // priority 0, with their counts. Selection sort by repeated + // max-find, negating each winner so it is skipped next pass and + // restoring the signs afterwards -- the tally has to survive to + // the next second's report. + // + DEBUG_STREAM << "[posts]"; + for (p = 0; p < 3; p++) + { + int + best = -1, + i; + + for (i = 0; i < POST_TALLY_IDS; i++) + { + if ( + postTally[i] > 0 && + (best < 0 || postTally[i] > postTally[best]) + ) + { + best = i; + } + } + if (best < 0) + { + break; + } + DEBUG_STREAM << " id" << best << "=" << postTally[best]; + postTally[best] = -postTally[best]; + } + for (p = 0; p < POST_TALLY_IDS; p++) + { + if (postTally[p] < 0) + { + postTally[p] = -postTally[p]; + } + } + DEBUG_STREAM << "\n" << flush; + } + if (eventQueue->IsPriorityEmpty(MinEventPriority)) + { + Host *console_host; + + if (applicationState.GetState() == LoadingMission) + { + ResourceFile *res_file = GetResourceFile(); + Check(res_file); + res_file->ReleaseUnlockedResources(); + #if defined(LAB_ONLY) + DEBUG_STREAM << "Waiting for translocation!\n" << flush; + #endif + #if defined(USE_TIME_ANALYSIS) + DEBUG_STREAM << "Loading time usage:\n" << flush; + trace_manager.SnapshotTimingAnalysis(True); + #endif + } + applicationState.SetState(WaitingForLaunch); + Check(GetHostManager()); + + console_host = GetHostManager()->GetConsoleHost(); + if ( + ( + (console_host == NULL) || + ( + console_host != NULL && + console_host->GetConnectStatus() != Host::OnLineConnectionStatus + ) + ) + ) + { + // + // In the absence of the console just post the message to run. + // An IN-PROCESS console (hosted network mission) has no + // connection to this pod but still owns the launch -- the + // gConsoleMarshalsLaunch flag holds us at WaitingForLaunch + // until every pod in the mesh is staged (BT412). + // + RunMissionMessage run_mission_message; + Post(DefaultEventPriority, this, &run_mission_message); + Tell("Sent ready message to ourselves\n"); + } + } + + // + //----------------------------------------------------------------------- + // Post this message again until the application is running + //----------------------------------------------------------------------- + // + Time post_time; + + post_time = Now(); + #if DEBUG_LEVEL<3 + post_time += 1.0f; + #else + post_time += 5.0; + #endif + + Post(DefaultEventPriority, this, message, post_time); + break; + } +} + +// +//############################################################################# +// RunMissionMessageHandler +//############################################################################# +// +void + Application::RunMissionMessageHandler( +#if DEBUG_LEVEL>0 + RunMissionMessage *message +#else + RunMissionMessage * +#endif + ) +{ + Check(this); + Check(message); + Verify(message->messageID == RunMissionMessageID); + + // + //-------------------------------------------------------------------------- + // If the application is already running then ignore this message + //-------------------------------------------------------------------------- + // + switch (GetApplicationState()) + { + case RunningMission: + return; + + case LaunchingMission: + // + // Start analysis sample + // + #if defined(USE_TRACE_LOG) + { + char *log_size_str = getenv("LOGSIZE"); + if (log_size_str) + { + size_t log_size = atoi(log_size_str); + if (log_size > 0) + { + trace_manager.CreateTraceLog(log_size,True); + } + } + } + #endif + #ifdef USE_TIME_ANALYSIS + trace_manager.StartTimingAnalysis(); + #endif + + Tell("Application::RunMissionMessageHandler - running mission\n"); + applicationState.SetState(RunningMission); + gameStarted = Now(); + break; + + case WaitingForLaunch: + { + Tell("Application::RunMissionMessageHandler - Translocation\n"); + applicationState.SetState(LaunchingMission); + Player *player = GetMissionPlayer(); + Check(player); + Player::MissionStartingMessage + launch( + Player::MissionStartingMessageID, + sizeof(Player::MissionStartingMessage) + ); + player->Dispatch(&launch); + break; + } + + default: + Fail("Application::RunMissionMessageHandler - Not ready to run!\n"); + break; + } +} + +// +//############################################################################# +// SuspendMissionMessageHandler +//############################################################################# +// +void + Application::SuspendMissionMessageHandler( +#if DEBUG_LEVEL>0 + SuspendMissionMessage *message +#else + SuspendMissionMessage * +#endif + ) +{ + Check(this); + Check(message); + Verify(message->messageID == SuspendMissionMessageID); + + // + //-------------------------------------------------------------------------- + // The application should be either running or already suspended + //-------------------------------------------------------------------------- + // + switch (GetApplicationState()) + { + case RunningMission: + { + Player::MissionEndingMessage + player_message( + Player::MissionEndingMessageID, + sizeof(Player::MissionEndingMessage) + ); + Player *player; + + Tell("Application::SuspendMissionMessageHandler - Suspending\n"); + applicationState.SetState(SuspendingMission); + + player = GetMissionPlayer(); + Check(player); + player->Dispatch(&player_message); + } + break; + + case SuspendingMission: + // + // Already suspended + // + break; + + default: + // + // Any other state is an error + // + Fail("Application::SuspendMissionMessageHandler - Illegal state"); + break; + } +} + +// +//############################################################################# +// ResumeMissionMessageHandler +//############################################################################# +// +void + Application::ResumeMissionMessageHandler( +#if DEBUG_LEVEL>0 + ResumeMissionMessage *message +#else + ResumeMissionMessage * +#endif + ) +{ + Check(this); + Check(message); + Verify(message->messageID == ResumeMissionMessageID); + + // + //-------------------------------------------------------------------------- + // The application should be in the suspended state or resuming + //-------------------------------------------------------------------------- + // + switch (GetApplicationState()) + { + case SuspendingMission: + { + Player::MissionStartingMessage + launch( + Player::MissionStartingMessageID, + sizeof(Player::MissionStartingMessage) + ); + Player *player; + + Tell("Application::ResumeMissionMessageHandler - Resuming mission\n"); + applicationState.SetState(ResumingMission); + player = GetMissionPlayer(); + Check(player); + player->Dispatch(&launch); + } + break; + + case ResumingMission: + Tell("Application::ResumeMissionMessageHandler - Running mission\n"); + applicationState.SetState(RunningMission); + gameStarted = Now(); + break; + + default: + Fail("Application::ResumeMissionMessageHandler - Illegal state\n"); + break; + } +} + +// +//############################################################################# +// StopMissionMessageHandler +//############################################################################# +// +void + Application::StopMissionMessageHandler( +#if DEBUG_LEVEL>0 + StopMissionMessage *message +#else + StopMissionMessage * +#endif + ) +{ + Check(this); + Check(message); + Verify(message->messageID == StopMissionMessageID); + + // + //-------------------------------------------------------------------------- + // If the application is already stopping then ignore the message + //-------------------------------------------------------------------------- + // + switch (GetApplicationState()) + { + case StoppingMission: + return; + + case EndingMission: + case AbortingMission: + Stop(); + break; + + default: + { + applicationState.SetState(EndingMission); + Player *player = GetMissionPlayer(); + if (player) + { + networkManager->Mode(NetworkManager::ReliableMode); + Check(player); + Player::MissionEndingMessage + launch( + Player::MissionEndingMessageID, + sizeof(Player::MissionEndingMessage) + ); + player->Dispatch(&launch); + } + else + { + Stop(); + } + break; + } + } +} + +// +//############################################################################# +// StopMissionMessageHandler +//############################################################################# +// +void + Application::AbortMissionMessageHandler( +#if DEBUG_LEVEL>0 + AbortMissionMessage *message +#else + AbortMissionMessage * +#endif + ) +{ + Check(this); + Check(message); + Verify(message->messageID == AbortMissionMessageID); + + // + //-------------------------------------------------------------------------- + // If the application is already stopping then ignore the message + //-------------------------------------------------------------------------- + // + switch (GetApplicationState()) + { + case StoppingMission: + return; + + case EndingMission: + case AbortingMission: + Stop(); + break; + + default: + { + applicationState.SetState(AbortingMission); + Player *player = GetMissionPlayer(); + if (player) + { + Check(player); + Player::MissionEndingMessage + launch( + Player::MissionEndingMessageID, + sizeof(Player::MissionEndingMessage) + ); + player->Dispatch(&launch); + } + else + { + Stop(); + } + break; + } + } +} + +// +//############################################################################# +// KeyCommandMessageHandler +//############################################################################# +// +void + Application::KeyCommandMessageHandler( + ReceiverDataMessageOf *message + ) +{ + Check(this); + Check(message); + switch (message->dataContents) + { + case '&': + if (GetApplicationState() != StoppingMission) + { + Exit_Code = 1; + Stop(); + DEBUG_STREAM << "Mission stopped by keystroke!\n" << flush; + if (GetApplicationState() == WaitingForEgg) + { + applicationState.SetState(EndingMission); + } + } + break; + + #if defined(USE_TRACE_LOG) + case ' ': + trace_manager.MarkTraceLog(); + break; + case '/': + trace_manager.ResumeTraceLogging(); + break; + case '\\': + trace_manager.SuspendTraceLogging(); + break; + #endif + } +} + +//~~~~~~~~~~~~~~~~~~~~~~ Application__CheckLoadMessage ~~~~~~~~~~~~~~~~~~~~~~~~ + +Application__CheckLoadMessage::Application__CheckLoadMessage(): + NetworkClient::Message( + Application::CheckLoadMessageID, + sizeof(Application__CheckLoadMessage) + ) +{ +} + +#ifdef TEST_CLASS +# include "app.tcp" +#endif diff --git a/restoration/source410/MUNGA/APPTASK.CPP b/restoration/source410/MUNGA/APPTASK.CPP new file mode 100644 index 00000000..dc7f6abf --- /dev/null +++ b/restoration/source410/MUNGA/APPTASK.CPP @@ -0,0 +1,275 @@ +//===========================================================================// +// File: apptask.cpp // +// Project: MUNGA Brick: Application // +// Contents: Interface specification for Application // +//---------------------------------------------------------------------------// +// Date Who Modification // +// -------- --- ---------------------------------------------------------- // +// 08/24/94 ECH Initial coding. // +//---------------------------------------------------------------------------// +// Copyright (C) 1994-1995, Virtual World Entertainment, Inc. // +// All Rights reserved worldwide // +// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // +//===========================================================================// + +#include +#pragma hdrstop + +#if !defined(APPTASK_HPP) +# include +#endif + +#if !defined(RENDERER_HPP) +# include +#endif + +#if !defined(AUDREND_HPP) +# include +#endif + +#if !defined(APP_HPP) +# include +#endif + +#if !defined(NTTMGR_HPP) +# include +#endif + +#if !defined(GAUGEREND_HPP) +# include +#endif + +#if defined(TRACE_COMPLETE_CYCLES) + BitTrace Complete_Cycles("Complete Cycles"); +#endif + +#if defined(TRACE_DEATH_ROW) + BitTrace Death_Row("Death Row"); +#endif + +//############################################################################# +//########################### ApplicationTask ########################### +//############################################################################# + +ApplicationTask::ApplicationTask(ClassID class_ID): + Component(class_ID) +{ +} + +ApplicationTask::~ApplicationTask() +{ +} + +//############################################################################# +//########################### BackgroundTasks ########################### +//############################################################################# + +BackgroundTasks::BackgroundTasks(): + taskSocket(NULL) +{ + taskIterator = new SChainIteratorOf(&taskSocket); + Register_Object(taskIterator); +} + +BackgroundTasks::~BackgroundTasks() +{ + Check(taskIterator); + taskIterator->DeletePlugs(); + Unregister_Object(taskIterator); + delete taskIterator; +} + +Logical + BackgroundTasks::TestInstance() const +{ + Component::TestInstance(); + Check(&taskSocket); + Check(taskIterator); + return True; +} + +void + BackgroundTasks::AddTask(ApplicationTask *task) +{ + Check(this); + Check(task); + taskSocket.Add(task); +} + +void + BackgroundTasks::Execute() +{ + Check(this); + + Check(taskIterator); + if (taskIterator->GetCurrent() == NULL) + { + taskIterator->First(); + } + + ApplicationTask *task = taskIterator->GetCurrent(); + Check(task); + task->Execute(); + taskIterator->Next(); +} + +// +// RING-TASK TICK SUMS (gauge-starvation probe; counters defined in app.cpp, +// printed on the [stack] line). The single background slot per frame eats +// ~8 unaccounted ticks -- these say which task's Execute holds the clock. +// +extern long taskRouteTicks; +extern long taskEventTicks; +extern long taskAudioTicks; +extern long taskGaugeTicks; +extern long taskNetTicks; +extern long taskCyclesTicks; +extern long taskFryTicks; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NetworkManagerTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + NetworkManagerTask::Execute() +{ + Check(this); + Check(application); + Check(application->GetNetworkManager()); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->GetNetworkManager()->ExecuteBackground(); + taskNetTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->GetNetworkManager()->ExecuteBackground(); +#endif + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RoutePacketTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + RoutePacketTask::Execute() +{ + Check(this); + + Check(application); + Check(application->GetNetworkManager()); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->GetNetworkManager()->RoutePacket(); + taskRouteTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->GetNetworkManager()->RoutePacket(); +#endif + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ProcessEventTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + ProcessEventTask::Execute() +{ + Check(this); + Check(application); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->ProcessOneEvent(); + taskEventTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->ProcessOneEvent(); +#endif + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioRendererTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + AudioRendererTask::Execute() +{ + Check(this); + Check(application); + if (application->GetAudioRenderer() != NULL) + { + Check(application->GetAudioRenderer()); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->GetAudioRenderer()->ExecuteBackground(); + taskAudioTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->GetAudioRenderer()->ExecuteBackground(); +#endif + } + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GaugeRendererTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + GaugeRendererTask::Execute() +{ + Check(this); + Check(application); + if (application->GetGaugeRenderer() != NULL) + { + Check(application->GetGaugeRenderer()); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->GetGaugeRenderer()->ExecuteBackground(); + taskGaugeTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->GetGaugeRenderer()->ExecuteBackground(); +#endif + } + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompleteCyclesTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + CompleteCyclesTask::Execute() +{ + SET_COMPLETE_CYCLES(); + + Check(this); + Check(application); + Check(application->GetRendererManager()); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->GetRendererManager()->CompleteCycles(); + taskCyclesTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->GetRendererManager()->CompleteCycles(); +#endif + } + + CLEAR_COMPLETE_CYCLES(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FryDeathRowTask ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void + FryDeathRowTask::Execute() +{ + SET_DEATH_ROW(); + + Check(this); + Check(application); + Check(application->GetEntityManager()); + { +#if defined(BT_TICK_PROBES) + Time probe_t0 = Now(); + application->GetEntityManager()->FryDeathRow(); + taskFryTicks += (long)(Now().ticks - probe_t0.ticks); +#else + application->GetEntityManager()->FryDeathRow(); +#endif + } + + CLEAR_DEATH_ROW(); +} + diff --git a/restoration/source410/MUNGA/GAUGREND.CPP b/restoration/source410/MUNGA/GAUGREND.CPP index 590f7816..250f2f86 100644 --- a/restoration/source410/MUNGA/GAUGREND.CPP +++ b/restoration/source410/MUNGA/GAUGREND.CPP @@ -3997,6 +3997,8 @@ Logical Check(this); Check(activeIterator); + { extern long gaugeSlices; gaugeSlices++; } // starvation probe + Logical result; GaugeBase @@ -4015,6 +4017,44 @@ Logical //-------------------------------------------------- // Inform system that we are finished //-------------------------------------------------- + { extern long gaugePasses; gaugePasses++; } // starvation probe + + // + // ROSTER DUMP (env BT_GAUGE_LOG, once): every slice of the wheel is + // one ACTIVE gauge, so pass latency scales with this list. Ours + // measured ~140 entries; the question is whether that roster matches + // the cockpit mode's intended set or we are activating every page of + // every head. + // + { + static int roster_state = -1; + if (roster_state < 0) + { + roster_state = (getenv("BT_GAUGE_LOG") != NULL) ? 1 : 0; + } + if (roster_state == 1) + { + roster_state = 0; + SChainIteratorOf + roster(activeList); + GaugeBase + *entry; + int + roster_count = 0; + while ((entry = roster.ReadAndNext()) != NULL) + { + DEBUG_STREAM << "[roster] " + << entry->identificationString + << " mode=" << hex << (long)entry->modeMask << dec + << "\n"; + roster_count++; + } + DEBUG_STREAM << "[roster] total " << roster_count + << " modeMask=" << hex << (long)previousModeMask << dec + << endl << flush; + } + } + taskMode = copy; result = True; // Don't stop just yet! Go into third phase! } diff --git a/restoration/source410/MUNGA_L4/L4GREND.CPP b/restoration/source410/MUNGA_L4/L4GREND.CPP index f0ff284f..d71e4a57 100644 --- a/restoration/source410/MUNGA_L4/L4GREND.CPP +++ b/restoration/source410/MUNGA_L4/L4GREND.CPP @@ -1,1009 +1,1011 @@ -//===========================================================================// -// File: l4grend.cc // -// Project: MUNGA Brick: LBE4 Gauge Renderer // -// Contents: // -//---------------------------------------------------------------------------// -// Date Who Modification // -// -------- --- ---------------------------------------------------------- // -// 02/10/95 CPB Initial coding. // -// 04/12/95 CPB Moved BuildGraphicsPorts in from RP/BTL4GRND.CPP. // -//---------------------------------------------------------------------------// -// Copyright (C) 1995, Virtual World Entertainment, Inc. All rights reserved // -// PROPRIETARY and CONFIDENTIAL // -//===========================================================================// - -#include -#pragma hdrstop - -#if !defined(L4APP_HPP) -# include -#endif - -#if !defined(TIME_HPP) -# include // for profiling -#endif - -#if !defined(L4GREND_HPP) -# include -#endif - -#if !defined(L4GAUGE_HPP) -# include -#endif - -#if !defined(MISSION_HPP) -# include -#endif - -#if !defined(L4PLASMA_HPP) -# include -#endif - -#if !defined(NOTATION_HPP) -# include -#endif - -// #define LOCAL_TEST - -#if defined(LOCAL_TEST) -# define Test_Tell(n) cout << n -#else -# define Test_Tell(n) -#endif - -// -//############################################################################# -// L4MethodDescription list -//############################################################################# -// -MethodDescription - ChainToPrevious = METHOD_DESCRIPTION_CHAIN(NULL); // Null:end of chain - -MethodDescription - *L4MethodDescription[] = - { - &MakeConfigMethodDescription, - &MakeExternConfigMethodDescription, - &BackgroundReconfig::methodDescription, - &BackgroundLine::methodDescription, - &BackgroundRect::methodDescription, - &BackgroundFilledRect::methodDescription, - &BackgroundPixelmap::methodDescription, - &BackgroundBitmap::methodDescription, - &NumericDisplayScalar::methodDescription, - &NumericDisplaySpeed::methodDescription, - &NumericDisplayInteger::methodDescription, - &DigitalClock::methodDescription, - &RankAndScore::methodDescription, - &BarGraphSolidScalar::methodDescription, - &BarGraphPixelMapScalar::methodDescription, - &ColorState::methodDescription, - &TwoState::methodDescription, - &SegmentArcNormalized::methodDescription, - &ChainToPrevious - }; - -// -//############################################################################# -// L4GaugeRenderer -//############################################################################# -// -L4GaugeRenderer::L4GaugeRenderer(): - GaugeRenderer() -{ - // - //--------------------------------------------------------------------- - // Ensure that graphicsDisplay is NULL - //--------------------------------------------------------------------- - // - graphicsDisplay = NULL; - externalDisplay = NULL; - - palettesAreActive = True; - scrambleVideoFlag = False; - - char - *mode_string = getenv("L4GAUGE"); - // - //--------------------------------------------------------------------- - // NOTE that L4Application::Initialize() searches for the environment - // variable "L4GAUGE" to determine whether or not to create - // a gauge renderer. - //--------------------------------------------------------------------- - // - if (mode_string != NULL) - { - // - //--------------------------------------------------------------------- - // Attempt to initialize external display - //--------------------------------------------------------------------- - // - char - *plasma_string = getenv("L4PLASMA"); - - if (plasma_string != NULL) - { - if (strnicmp(plasma_string, "COM2", 4) == 0) - { - Tell("Plasma display created on COM2\n"); - externalDisplay = new PlasmaDisplay(PCS_COM2); - } - else - { - Tell("Plasma display created on COM1\n"); - externalDisplay = new PlasmaDisplay(PCS_COM1); - } - - if (externalDisplay != NULL) - { - Register_Object(externalDisplay); - } - } - // - //--------------------------------------------------------------------- - // Attempt to initialize main display - //--------------------------------------------------------------------- - //------------------------------------------------- - // Search notation file for specified resolution - //------------------------------------------------- - // - NotationFile - notation_file("GAUGE\\L4GAUGE.INI"); - - if (!notation_file.PageExists(mode_string)) - { - Tell("L4GaugeRenderer::L4GaugeRenderer - '" << - mode_string << - "' not found in L4GAUGE.INI!\n" - ); - } - else - { - // - //------------------------------------------------- - // Requested resolution exists, read parameters - //------------------------------------------------- - // - int - mode(0), - width(640), - height(480), - page_size(64), - gran(64), - bpl(640*2), - pfp(0), - special(0), - q(0); - - q += notation_file.GetEntry(mode_string, "mode", &mode); - q += notation_file.GetEntry(mode_string, "width", &width); - q += notation_file.GetEntry(mode_string, "height", &height); - q += notation_file.GetEntry(mode_string, "sizeInKB", &page_size); - q += notation_file.GetEntry(mode_string, "granularityInKB", &gran); - q += notation_file.GetEntry(mode_string, "bytesPerLine", &bpl); - q += notation_file.GetEntry(mode_string, "pageFcnPtr", &pfp); - q += notation_file.GetEntry(mode_string, "special", &special); - - if (q == 8) - { - graphicsDisplay = new - SVGA16( - mode, - width, - height, - page_size, - gran, - bpl, - pfp, - special - ); - if (graphicsDisplay != NULL) - { - Register_Object(graphicsDisplay); - } - } - } - } - Check_Fpu(); -} - -// -//############################################################################# -// ~L4GaugeRenderer -//############################################################################# -// -L4GaugeRenderer::~L4GaugeRenderer() -{ -# if defined (LOCAL_TEST) - Tell("L4GaugeRenderer::~L4GaugeRenderer()\n"); -# endif - Check(this); - - int i; - - //---------------------------------------------------- - // Make sure all gauges removed first - //---------------------------------------------------- - Remove(0); - //---------------------------------------------------- - // Delete the graphics ports - //---------------------------------------------------- - for(i=0; iUpdatePalette(); - } - - Check_Fpu(); -} - -// -//############################################################################# -// ExecuteImplementation -//############################################################################# -// -Logical - L4GaugeRenderer::ExecuteBackgroundDisplayUpdate() -{ - Check(this); - - Logical - result = False; // False == 'all done' - - //-------------------------------------------------------- - // Update external annunciator - // - // Allow the plasma display to continue if there's - // more background time available. - // - // This method is still called if taskmode is set to - // 'foreground'. If it's called in 'foreground' mode, - // it really means that we're still waiting for foreground - // to begin processing, so we might as well update the - // external display (if needed). - //-------------------------------------------------------- - if (externalDisplay != NULL) - { - Check(externalDisplay); - result = externalDisplay->Update(False); // returns 'False' if done - } - //------------------------------------------------------- - // Update display ONLY in 'copy' mode - //-------------------------------------------------------- - if (taskMode == copy) - { - if (graphicsDisplay != NULL) - { - Check(graphicsDisplay); - - if (graphicsDisplay->Update(False) == False) - { - //-------------------------------------------------------- - // Tell executionImplementation that we're basically done. - //-------------------------------------------------------- - taskMode = foreground; - } - else - { - result = True; // We're not done yet! - } - } - } - return result; -} - -// -//############################################################################# -// NotifyOfNewInterestingEntity -//############################################################################# -// -void - L4GaugeRenderer::NotifyOfNewInterestingEntity( - Entity *interesting_entity - ) -{ - SET_GAUGE_RENDERER(); - Check(this); - Check(interesting_entity); - //---------------------------------------------- - // Make sure gaugeImage exists for entity - //---------------------------------------------- - Check(warehousePointer); - - ((L4Warehouse*)warehousePointer)->gaugeImageBin.Get( - interesting_entity->GetResourceID() - ); - //---------------------------------------------- - // Update gaugeRenderer - //---------------------------------------------- - GaugeRenderer::NotifyOfNewInterestingEntity(interesting_entity); - Check_Fpu(); - CLEAR_GAUGE_RENDERER(); -} - -// -//############################################################################# -// NotifyOfBecomingUninterestingEntity -//############################################################################# -// -void - L4GaugeRenderer::NotifyOfBecomingUninterestingEntity( - Entity *uninteresting_entity - ) -{ - SET_GAUGE_RENDERER(); - Check(this); - Check(uninteresting_entity); - //---------------------------------------------- - // Remove gaugeImage if no longer used - //---------------------------------------------- - Check(warehousePointer); - - ((L4Warehouse*)warehousePointer)->gaugeImageBin.Release( - uninteresting_entity->GetResourceID() - ); - //---------------------------------------------- - // Update gaugeRenderer - //---------------------------------------------- - GaugeRenderer::NotifyOfBecomingUninterestingEntity(uninteresting_entity); - Check_Fpu(); - CLEAR_GAUGE_RENDERER(); -} - -// -//############################################################################# -// BuildGraphicsPort -//############################################################################# - -//===========================================================================// -// RECONSTRUCTION SHADOW (source410 overrides CODE per build410.sh srcfor). -// -// Verbatim copy of CODE/RP/MUNGA_L4/L4GREND.CPP with ONE change: the port -// construction below builds a BTL4GraphicsPort instead of an L4GraphicsPort, -// to close the MFD-bleed artifact. Everything else is the archive's. -// -// THE ARTIFACT (operator report 2026-07-29: "artifacts bleeding over from the -// radar display into the MFDs ... I have noticed this in the BT411 builds as -// well, but does not happen in the original"): -// -// * The colour head is SIX BITS (L4GAUGE.CFG:4395, sec mask 0x003F) and the -// MFDs are bits 8-15 of the SAME 16-bit words -- one plane-packed -// framebuffer, so a colour-head defect can only surface as MFD garbage. -// * L4GraphicsPort::BuildSecondaryTranslation (L4VB16.CPP:5419) enumerates -// only the ACTIVE-bit combinations, filling 1< screen (y, 479-x). That places the rectangle at screen -// x 526..538, y 229..280. The measured mask is x 526..538, y 228..279. -// Same rectangle, to the pixel. -// -// So one uninitialised read produces BOTH reported symptoms, depending only -// on which bits the heap garbage happens to have set: -// -// low 6 bits set -> a coloured block on the COLOUR head. This is the -// operator's second report the same day, "that yellow -// orange artifact", a vertical bar beside the RANGE -// readout -- which is precisely where the rotated -// rectangle lands. -// high 8 bits set -> garbage in the MFD / ENG / COMM planes. This is the -// first report, "bleeding over ... into the MFDs". -// -// VERIFICATION STATUS: -// -// * MECHANISM: proven from source (asymmetric builders) AND live (the -// provoke control lights 1030 pixels that the fix zeroes). -// * OFFENDING ART: proven by decode (BTSEC1.PCX index 254, 676px, and the -// rotation arithmetic lands it on the reported spot). -// * NON-REGRESSIVE: gauge A/B, three boots (unfixed x2, fixed x1) score -// identically on every head -- Mfd1 132/0, Mfd2 37/7, Mfd3 23/0 -- -// against a ~6px across-boot noise floor. -// * OPERATOR-CONFIRMED CURED, 2026-07-30. A full arena1 mission run on -// the pod rig (podrun.sh pod_render_norio, this build staged): "the -// artifact appears to be gone and the bleed from the control mode seems -// to be gone as well." Both reported symptoms, one run, no fault. -// -// The honest weight of that: it is ONE run of an INTERMITTENT, -// heap-content-dependent artifact, and a clean run is exactly what the -// UNFIXED build also produced on the gauge A/B rig. So it is not -// logically airtight on its own. What makes it convincing anyway is -// that the fix is deterministic by construction -- after it, no index -// in 64..255 can return anything but 0, whatever the heap holds -- and -// that the operator had been seeing the artifact repeatedly, across -// both this reconstruction and BT411. Further clean runs strengthen -// it; a recurrence would mean a SECOND source, not a failed fix. -// -// A first "fixed" capture appeared to REGRESS the MFDs by ~2700px. It was a -// bad grab (caught mid-draw); the rebuild-and-rerun matched the unfixed -// numbers exactly. Recorded here because the two-runs rule is what caught it. -//===========================================================================// - - class BTL4GraphicsPort : - public L4GraphicsPort - { - public: - BTL4GraphicsPort( - Video16BitBuffered *graphics_display, - const char *name, - int rotation, - int bit_mask, - SVGA16::PaletteID palette_ID, - L4GraphicsPort::ChannelEnableID channel_enable - ): - L4GraphicsPort( - graphics_display, name, rotation, bit_mask, - palette_ID, channel_enable - ) - { - // - // SECONDARY ports only. The low byte non-zero is exactly the - // test BuildSecondaryTranslation asserts on - // (Verify((bitMask & 0xFF) != 0)); an AUXILIARY port fills all - // 256 entries itself and must not be touched. - // - if ((bit_mask & 0xFF) != 0) - { - int - first = 1 << numberOfBits, - i; - - // - // POSITIVE CONTROL. Zero is the fix; it is also invisible, - // so on its own it cannot tell us whether the out-of-range - // path is ever TAKEN on a given screen. BT_TRANS_PROVOKE - // fills the same tail with every MFD bit instead: if the - // path fires, the MFD heads flood with garbage and the A/B - // scoreboard's "extra" columns explode. If they do not - // move, the sec port never sees an out-of-range index on - // that screen and the fix is inert there. - // - int - fill = (getenv("BT_TRANS_PROVOKE") != NULL) ? 0xFF00 : 0; - - for (i = first; i < 256; ++i) - { - translationTable[i] = fill; - } - } - } - }; - - -// -void - L4GaugeRenderer::BuildGraphicsPort( - int port_number, - const char *name, - int rotation, - int bitAllocation, - SVGA16::PaletteID palette_ID, - L4GraphicsPort::ChannelEnableID enable_ID, - Palette8 *palette - ) -{ - Test_Tell("L4GaugeRenderer::BuildGraphicsPort\n"); - Check(this); - - if (graphicsDisplay != NULL) - { - Test_Tell("...non-NULL display\n"); - Verify(port_number < maximumGraphicsPorts); - Verify(port_number >= 0); - Test_Tell("...port_number= " << port_number << "\n"); - // - //--------------------------------------------------------- - // If it already exists, delete it - //--------------------------------------------------------- - // - L4GraphicsPort - *graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; - - if (graphics_port != NULL) - { - Test_Tell("...deleting existing port\n"); - Check(graphics_port); - Unregister_Object(graphics_port); - delete graphics_port; - } - // - //--------------------------------------------------------- - // Attempt to create it - //--------------------------------------------------------- - // - graphicsPort[port_number] = new BTL4GraphicsPort( - (Video16BitBuffered *) graphicsDisplay, - name, - rotation, - bitAllocation, - palette_ID, - enable_ID - ); - - graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; - - Test_Tell("...new port at " << graphics_port << "\n"); - // - //--------------------------------------------------------- - // If created, register it and build the palette - //--------------------------------------------------------- - // - if (graphics_port != NULL) - { - Check(graphics_port); - Register_Object(graphics_port); - // - // Set the palette for the port - // (Note: secondary uses lowest 8 bits) - // - if ((bitAllocation > 0) && (bitAllocation < 0x0100)) - { - Test_Tell("...secondary\n"); - graphics_port->SetSecondaryPalette(palette); - } - else - { - Test_Tell("...auxiliary\n"); - graphics_port->SetAuxiliaryPalette(); - } - } - } - Check_Fpu(); -} - -// -//############################################################################# -// RemapGraphicsPort -//############################################################################# -// -void - L4GaugeRenderer::RemapGraphicsPort( - int port_number, - SVGA16::PaletteID palette_ID, - L4GraphicsPort::ChannelEnableID enable_ID, - Palette8 *palette - ) -{ - Check(this); - Test_Tell("L4GaugeRenderer::RemapGraphicsPort\n"); - - if (graphicsDisplay != NULL) - { - Test_Tell("...non-NULL display\n"); - Verify(port_number < maximumGraphicsPorts); - Verify(port_number >= 0); - Test_Tell("...port_number= " << port_number << "\n"); - //--------------------------------------------------------- - // If it exists, change the palette ID and set the palette - //--------------------------------------------------------- - L4GraphicsPort - *graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; - - if (graphics_port != NULL) - { - Check(graphics_port); - // - // Set the palette ID - // - graphics_port->SetPaletteID(palette_ID); - graphics_port->SetEnableID(enable_ID); - - // - // Set the palette for the port - // (Note: secondary uses lowest 8 bits) - // - - int - bit_mask = graphics_port->GetBitMask(); - - if (bit_mask & 0x00FF) // must be secondary - { - Test_Tell("...secondary\n"); - Verify(palette != NULL); - graphics_port->SetSecondaryPalette(palette); - } - else - { - Test_Tell("...auxiliary\n"); - graphics_port->SetAuxiliaryPalette(); - } - } - } - Check_Fpu(); -} - -// -//############################################################################# -// BuildExternalGraphicsPort -//############################################################################# -// -void - L4GaugeRenderer::BuildExternalGraphicsPort( - int port_number, - const char *name, - int /*bitAllocation*/ - ) -{ - Check(this); - Test_Tell( - "L4GaugeRenderer::BuildExternalGraphicsPort(" << port_number << ")\n" - ); - - if (externalDisplay != NULL) - { - Test_Tell("...non-NULL display\n"); - Verify(port_number < maximumGraphicsPorts); - Verify(port_number >= 0); - Test_Tell("...port_number= " << port_number << "\n"); - // - //--------------------------------------------------------- - // If it already exists, delete it - //--------------------------------------------------------- - // - L4GraphicsPort - *graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; - - if (graphics_port != NULL) - { - Test_Tell("...deleting existing port\n"); - Check(graphics_port); - Unregister_Object(graphics_port); - delete graphics_port; - } - // - //--------------------------------------------------------- - // Attempt to create it - //--------------------------------------------------------- - // - graphicsPort[port_number] = new L4BytePort( - (Video8BitBuffered *) externalDisplay, - name, - 0 // rotation - ); - - graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; - - Test_Tell("...new port at " << graphics_port << "\n"); - // - //--------------------------------------------------------- - // If created, register it - //--------------------------------------------------------- - // - if (graphics_port != NULL) - { - Check(graphics_port); - Register_Object(graphics_port); - } - } - Check_Fpu(); -} - -// -//############################################################################# -// FadeToWhite, FadeToGame -//############################################################################# -// -void - L4GaugeRenderer::FadeToWhite(Scalar number_of_seconds) -{ - Check(this); - Verify(number_of_seconds >= 0.0); // zero is allowed (instant change) - -// cout << "L4GaugeRenderer::FadeToWhite(" << number_of_seconds << ")\n"; - - if (graphicsDisplay != NULL) - { - Check(graphicsDisplay); - ((SVGA16 *)graphicsDisplay)->FadeToWhite(number_of_seconds); - } - Check_Fpu(); -} - -void - L4GaugeRenderer::FadeToNormal(Scalar number_of_seconds) -{ - Check(this); - Verify(number_of_seconds >= 0.0); // zero is allowed (instant change) - -// cout << "L4GaugeRenderer::FadeToNormal(" << number_of_seconds << ")\n"; - - if (graphicsDisplay != NULL) - { - Check(graphicsDisplay); - ((SVGA16 *)graphicsDisplay)->FadeToPalettes(number_of_seconds); - } - Check_Fpu(); -} - -// -//############################################################################# -// Configure -//############################################################################# -// -void - L4GaugeRenderer::ConfigureForModel( - const char *configuration_name, - Entity *entity - ) -{ - Check(this); - Check_Pointer(configuration_name); - - Test_Tell( - "L4GaugeRenderer::ConfigureForModel(" << - configuration_name << ", " << - entity << ")\n" - ); - - Check(application); - Check(entity); - - Mission - *mission = application->GetCurrentMission(); - Check(mission); - - const char - *model_name = mission->GetGameModel(); - Check_Pointer(model_name); - - char - full_name[64]; - - Str_Copy(full_name, model_name, sizeof(full_name)); - Str_Cat(full_name, configuration_name, sizeof(full_name)); - - Test_Tell( - "L4GaugeRenderer::ConfigureForModel, full_name=" << full_name << - ")\n" - ); - - Configure(full_name, entity); - Check_Fpu(); -} - -// -//############################################################################# -// SpecialEffect -//############################################################################# -// -void - L4GaugeRenderer::SpecialEffect( - VideoEffectType type, - Scalar duration - ) -{ - Check(this); - - switch (type) - { - case scrambleVideo: - if (graphicsDisplay != NULL) - { - scrambleVideoFlag = True; - scrambleVideoTimeout = ((Scalar)Now()) + duration; - - Check(graphicsDisplay); - ((SVGA16*) graphicsDisplay)->FunkyVideo(True); - } - break; - - // other modes go here - - } - Check_Fpu(); -} - -void - L4GaugeRenderer::ProcessVideoEffects() -{ - Check(this); - Check(graphicsDisplay); - - Time - now = Now(); - - if (scrambleVideoFlag) - { - if (now >= scrambleVideoTimeout) - { - scrambleVideoFlag = False; - ((SVGA16*) graphicsDisplay)->FunkyVideo(False); - } - } - - Check_Fpu(); -} - -//-------------------------------------------------------------------- -// Profiling support -//-------------------------------------------------------------------- -Scalar - L4GaugeRenderer::GetCurrentFramePercentage() -{ - Check(this); - return Get_Frame_Percent_Used(); -} - - - - +//===========================================================================// +// File: l4grend.cc // +// Project: MUNGA Brick: LBE4 Gauge Renderer // +// Contents: // +//---------------------------------------------------------------------------// +// Date Who Modification // +// -------- --- ---------------------------------------------------------- // +// 02/10/95 CPB Initial coding. // +// 04/12/95 CPB Moved BuildGraphicsPorts in from RP/BTL4GRND.CPP. // +//---------------------------------------------------------------------------// +// Copyright (C) 1995, Virtual World Entertainment, Inc. All rights reserved // +// PROPRIETARY and CONFIDENTIAL // +//===========================================================================// + +#include +#pragma hdrstop + +#if !defined(L4APP_HPP) +# include +#endif + +#if !defined(TIME_HPP) +# include // for profiling +#endif + +#if !defined(L4GREND_HPP) +# include +#endif + +#if !defined(L4GAUGE_HPP) +# include +#endif + +#if !defined(MISSION_HPP) +# include +#endif + +#if !defined(L4PLASMA_HPP) +# include +#endif + +#if !defined(NOTATION_HPP) +# include +#endif + +// #define LOCAL_TEST + +#if defined(LOCAL_TEST) +# define Test_Tell(n) cout << n +#else +# define Test_Tell(n) +#endif + +// +//############################################################################# +// L4MethodDescription list +//############################################################################# +// +MethodDescription + ChainToPrevious = METHOD_DESCRIPTION_CHAIN(NULL); // Null:end of chain + +MethodDescription + *L4MethodDescription[] = + { + &MakeConfigMethodDescription, + &MakeExternConfigMethodDescription, + &BackgroundReconfig::methodDescription, + &BackgroundLine::methodDescription, + &BackgroundRect::methodDescription, + &BackgroundFilledRect::methodDescription, + &BackgroundPixelmap::methodDescription, + &BackgroundBitmap::methodDescription, + &NumericDisplayScalar::methodDescription, + &NumericDisplaySpeed::methodDescription, + &NumericDisplayInteger::methodDescription, + &DigitalClock::methodDescription, + &RankAndScore::methodDescription, + &BarGraphSolidScalar::methodDescription, + &BarGraphPixelMapScalar::methodDescription, + &ColorState::methodDescription, + &TwoState::methodDescription, + &SegmentArcNormalized::methodDescription, + &ChainToPrevious + }; + +// +//############################################################################# +// L4GaugeRenderer +//############################################################################# +// +L4GaugeRenderer::L4GaugeRenderer(): + GaugeRenderer() +{ + // + //--------------------------------------------------------------------- + // Ensure that graphicsDisplay is NULL + //--------------------------------------------------------------------- + // + graphicsDisplay = NULL; + externalDisplay = NULL; + + palettesAreActive = True; + scrambleVideoFlag = False; + + char + *mode_string = getenv("L4GAUGE"); + // + //--------------------------------------------------------------------- + // NOTE that L4Application::Initialize() searches for the environment + // variable "L4GAUGE" to determine whether or not to create + // a gauge renderer. + //--------------------------------------------------------------------- + // + if (mode_string != NULL) + { + // + //--------------------------------------------------------------------- + // Attempt to initialize external display + //--------------------------------------------------------------------- + // + char + *plasma_string = getenv("L4PLASMA"); + + if (plasma_string != NULL) + { + if (strnicmp(plasma_string, "COM2", 4) == 0) + { + Tell("Plasma display created on COM2\n"); + externalDisplay = new PlasmaDisplay(PCS_COM2); + } + else + { + Tell("Plasma display created on COM1\n"); + externalDisplay = new PlasmaDisplay(PCS_COM1); + } + + if (externalDisplay != NULL) + { + Register_Object(externalDisplay); + } + } + // + //--------------------------------------------------------------------- + // Attempt to initialize main display + //--------------------------------------------------------------------- + //------------------------------------------------- + // Search notation file for specified resolution + //------------------------------------------------- + // + NotationFile + notation_file("GAUGE\\L4GAUGE.INI"); + + if (!notation_file.PageExists(mode_string)) + { + Tell("L4GaugeRenderer::L4GaugeRenderer - '" << + mode_string << + "' not found in L4GAUGE.INI!\n" + ); + } + else + { + // + //------------------------------------------------- + // Requested resolution exists, read parameters + //------------------------------------------------- + // + int + mode(0), + width(640), + height(480), + page_size(64), + gran(64), + bpl(640*2), + pfp(0), + special(0), + q(0); + + q += notation_file.GetEntry(mode_string, "mode", &mode); + q += notation_file.GetEntry(mode_string, "width", &width); + q += notation_file.GetEntry(mode_string, "height", &height); + q += notation_file.GetEntry(mode_string, "sizeInKB", &page_size); + q += notation_file.GetEntry(mode_string, "granularityInKB", &gran); + q += notation_file.GetEntry(mode_string, "bytesPerLine", &bpl); + q += notation_file.GetEntry(mode_string, "pageFcnPtr", &pfp); + q += notation_file.GetEntry(mode_string, "special", &special); + + if (q == 8) + { + graphicsDisplay = new + SVGA16( + mode, + width, + height, + page_size, + gran, + bpl, + pfp, + special + ); + if (graphicsDisplay != NULL) + { + Register_Object(graphicsDisplay); + } + } + } + } + Check_Fpu(); +} + +// +//############################################################################# +// ~L4GaugeRenderer +//############################################################################# +// +L4GaugeRenderer::~L4GaugeRenderer() +{ +# if defined (LOCAL_TEST) + Tell("L4GaugeRenderer::~L4GaugeRenderer()\n"); +# endif + Check(this); + + int i; + + //---------------------------------------------------- + // Make sure all gauges removed first + //---------------------------------------------------- + Remove(0); + //---------------------------------------------------- + // Delete the graphics ports + //---------------------------------------------------- + for(i=0; iUpdatePalette(); + } + + Check_Fpu(); +} + +// +//############################################################################# +// ExecuteImplementation +//############################################################################# +// +Logical + L4GaugeRenderer::ExecuteBackgroundDisplayUpdate() +{ + Check(this); + + Logical + result = False; // False == 'all done' + + //-------------------------------------------------------- + // Update external annunciator + // + // Allow the plasma display to continue if there's + // more background time available. + // + // This method is still called if taskmode is set to + // 'foreground'. If it's called in 'foreground' mode, + // it really means that we're still waiting for foreground + // to begin processing, so we might as well update the + // external display (if needed). + //-------------------------------------------------------- + if (externalDisplay != NULL) + { + Check(externalDisplay); + result = externalDisplay->Update(False); // returns 'False' if done + } + //------------------------------------------------------- + // Update display ONLY in 'copy' mode + //-------------------------------------------------------- + if (taskMode == copy) + { + if (graphicsDisplay != NULL) + { + Check(graphicsDisplay); + + { extern long gaugeCopySlices; gaugeCopySlices++; } // starvation probe + + if (graphicsDisplay->Update(False) == False) + { + //-------------------------------------------------------- + // Tell executionImplementation that we're basically done. + //-------------------------------------------------------- + taskMode = foreground; + } + else + { + result = True; // We're not done yet! + } + } + } + return result; +} + +// +//############################################################################# +// NotifyOfNewInterestingEntity +//############################################################################# +// +void + L4GaugeRenderer::NotifyOfNewInterestingEntity( + Entity *interesting_entity + ) +{ + SET_GAUGE_RENDERER(); + Check(this); + Check(interesting_entity); + //---------------------------------------------- + // Make sure gaugeImage exists for entity + //---------------------------------------------- + Check(warehousePointer); + + ((L4Warehouse*)warehousePointer)->gaugeImageBin.Get( + interesting_entity->GetResourceID() + ); + //---------------------------------------------- + // Update gaugeRenderer + //---------------------------------------------- + GaugeRenderer::NotifyOfNewInterestingEntity(interesting_entity); + Check_Fpu(); + CLEAR_GAUGE_RENDERER(); +} + +// +//############################################################################# +// NotifyOfBecomingUninterestingEntity +//############################################################################# +// +void + L4GaugeRenderer::NotifyOfBecomingUninterestingEntity( + Entity *uninteresting_entity + ) +{ + SET_GAUGE_RENDERER(); + Check(this); + Check(uninteresting_entity); + //---------------------------------------------- + // Remove gaugeImage if no longer used + //---------------------------------------------- + Check(warehousePointer); + + ((L4Warehouse*)warehousePointer)->gaugeImageBin.Release( + uninteresting_entity->GetResourceID() + ); + //---------------------------------------------- + // Update gaugeRenderer + //---------------------------------------------- + GaugeRenderer::NotifyOfBecomingUninterestingEntity(uninteresting_entity); + Check_Fpu(); + CLEAR_GAUGE_RENDERER(); +} + +// +//############################################################################# +// BuildGraphicsPort +//############################################################################# + +//===========================================================================// +// RECONSTRUCTION SHADOW (source410 overrides CODE per build410.sh srcfor). +// +// Verbatim copy of CODE/RP/MUNGA_L4/L4GREND.CPP with ONE change: the port +// construction below builds a BTL4GraphicsPort instead of an L4GraphicsPort, +// to close the MFD-bleed artifact. Everything else is the archive's. +// +// THE ARTIFACT (operator report 2026-07-29: "artifacts bleeding over from the +// radar display into the MFDs ... I have noticed this in the BT411 builds as +// well, but does not happen in the original"): +// +// * The colour head is SIX BITS (L4GAUGE.CFG:4395, sec mask 0x003F) and the +// MFDs are bits 8-15 of the SAME 16-bit words -- one plane-packed +// framebuffer, so a colour-head defect can only surface as MFD garbage. +// * L4GraphicsPort::BuildSecondaryTranslation (L4VB16.CPP:5419) enumerates +// only the ACTIVE-bit combinations, filling 1< screen (y, 479-x). That places the rectangle at screen +// x 526..538, y 229..280. The measured mask is x 526..538, y 228..279. +// Same rectangle, to the pixel. +// +// So one uninitialised read produces BOTH reported symptoms, depending only +// on which bits the heap garbage happens to have set: +// +// low 6 bits set -> a coloured block on the COLOUR head. This is the +// operator's second report the same day, "that yellow +// orange artifact", a vertical bar beside the RANGE +// readout -- which is precisely where the rotated +// rectangle lands. +// high 8 bits set -> garbage in the MFD / ENG / COMM planes. This is the +// first report, "bleeding over ... into the MFDs". +// +// VERIFICATION STATUS: +// +// * MECHANISM: proven from source (asymmetric builders) AND live (the +// provoke control lights 1030 pixels that the fix zeroes). +// * OFFENDING ART: proven by decode (BTSEC1.PCX index 254, 676px, and the +// rotation arithmetic lands it on the reported spot). +// * NON-REGRESSIVE: gauge A/B, three boots (unfixed x2, fixed x1) score +// identically on every head -- Mfd1 132/0, Mfd2 37/7, Mfd3 23/0 -- +// against a ~6px across-boot noise floor. +// * OPERATOR-CONFIRMED CURED, 2026-07-30. A full arena1 mission run on +// the pod rig (podrun.sh pod_render_norio, this build staged): "the +// artifact appears to be gone and the bleed from the control mode seems +// to be gone as well." Both reported symptoms, one run, no fault. +// +// The honest weight of that: it is ONE run of an INTERMITTENT, +// heap-content-dependent artifact, and a clean run is exactly what the +// UNFIXED build also produced on the gauge A/B rig. So it is not +// logically airtight on its own. What makes it convincing anyway is +// that the fix is deterministic by construction -- after it, no index +// in 64..255 can return anything but 0, whatever the heap holds -- and +// that the operator had been seeing the artifact repeatedly, across +// both this reconstruction and BT411. Further clean runs strengthen +// it; a recurrence would mean a SECOND source, not a failed fix. +// +// A first "fixed" capture appeared to REGRESS the MFDs by ~2700px. It was a +// bad grab (caught mid-draw); the rebuild-and-rerun matched the unfixed +// numbers exactly. Recorded here because the two-runs rule is what caught it. +//===========================================================================// + + class BTL4GraphicsPort : + public L4GraphicsPort + { + public: + BTL4GraphicsPort( + Video16BitBuffered *graphics_display, + const char *name, + int rotation, + int bit_mask, + SVGA16::PaletteID palette_ID, + L4GraphicsPort::ChannelEnableID channel_enable + ): + L4GraphicsPort( + graphics_display, name, rotation, bit_mask, + palette_ID, channel_enable + ) + { + // + // SECONDARY ports only. The low byte non-zero is exactly the + // test BuildSecondaryTranslation asserts on + // (Verify((bitMask & 0xFF) != 0)); an AUXILIARY port fills all + // 256 entries itself and must not be touched. + // + if ((bit_mask & 0xFF) != 0) + { + int + first = 1 << numberOfBits, + i; + + // + // POSITIVE CONTROL. Zero is the fix; it is also invisible, + // so on its own it cannot tell us whether the out-of-range + // path is ever TAKEN on a given screen. BT_TRANS_PROVOKE + // fills the same tail with every MFD bit instead: if the + // path fires, the MFD heads flood with garbage and the A/B + // scoreboard's "extra" columns explode. If they do not + // move, the sec port never sees an out-of-range index on + // that screen and the fix is inert there. + // + int + fill = (getenv("BT_TRANS_PROVOKE") != NULL) ? 0xFF00 : 0; + + for (i = first; i < 256; ++i) + { + translationTable[i] = fill; + } + } + } + }; + + +// +void + L4GaugeRenderer::BuildGraphicsPort( + int port_number, + const char *name, + int rotation, + int bitAllocation, + SVGA16::PaletteID palette_ID, + L4GraphicsPort::ChannelEnableID enable_ID, + Palette8 *palette + ) +{ + Test_Tell("L4GaugeRenderer::BuildGraphicsPort\n"); + Check(this); + + if (graphicsDisplay != NULL) + { + Test_Tell("...non-NULL display\n"); + Verify(port_number < maximumGraphicsPorts); + Verify(port_number >= 0); + Test_Tell("...port_number= " << port_number << "\n"); + // + //--------------------------------------------------------- + // If it already exists, delete it + //--------------------------------------------------------- + // + L4GraphicsPort + *graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; + + if (graphics_port != NULL) + { + Test_Tell("...deleting existing port\n"); + Check(graphics_port); + Unregister_Object(graphics_port); + delete graphics_port; + } + // + //--------------------------------------------------------- + // Attempt to create it + //--------------------------------------------------------- + // + graphicsPort[port_number] = new BTL4GraphicsPort( + (Video16BitBuffered *) graphicsDisplay, + name, + rotation, + bitAllocation, + palette_ID, + enable_ID + ); + + graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; + + Test_Tell("...new port at " << graphics_port << "\n"); + // + //--------------------------------------------------------- + // If created, register it and build the palette + //--------------------------------------------------------- + // + if (graphics_port != NULL) + { + Check(graphics_port); + Register_Object(graphics_port); + // + // Set the palette for the port + // (Note: secondary uses lowest 8 bits) + // + if ((bitAllocation > 0) && (bitAllocation < 0x0100)) + { + Test_Tell("...secondary\n"); + graphics_port->SetSecondaryPalette(palette); + } + else + { + Test_Tell("...auxiliary\n"); + graphics_port->SetAuxiliaryPalette(); + } + } + } + Check_Fpu(); +} + +// +//############################################################################# +// RemapGraphicsPort +//############################################################################# +// +void + L4GaugeRenderer::RemapGraphicsPort( + int port_number, + SVGA16::PaletteID palette_ID, + L4GraphicsPort::ChannelEnableID enable_ID, + Palette8 *palette + ) +{ + Check(this); + Test_Tell("L4GaugeRenderer::RemapGraphicsPort\n"); + + if (graphicsDisplay != NULL) + { + Test_Tell("...non-NULL display\n"); + Verify(port_number < maximumGraphicsPorts); + Verify(port_number >= 0); + Test_Tell("...port_number= " << port_number << "\n"); + //--------------------------------------------------------- + // If it exists, change the palette ID and set the palette + //--------------------------------------------------------- + L4GraphicsPort + *graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; + + if (graphics_port != NULL) + { + Check(graphics_port); + // + // Set the palette ID + // + graphics_port->SetPaletteID(palette_ID); + graphics_port->SetEnableID(enable_ID); + + // + // Set the palette for the port + // (Note: secondary uses lowest 8 bits) + // + + int + bit_mask = graphics_port->GetBitMask(); + + if (bit_mask & 0x00FF) // must be secondary + { + Test_Tell("...secondary\n"); + Verify(palette != NULL); + graphics_port->SetSecondaryPalette(palette); + } + else + { + Test_Tell("...auxiliary\n"); + graphics_port->SetAuxiliaryPalette(); + } + } + } + Check_Fpu(); +} + +// +//############################################################################# +// BuildExternalGraphicsPort +//############################################################################# +// +void + L4GaugeRenderer::BuildExternalGraphicsPort( + int port_number, + const char *name, + int /*bitAllocation*/ + ) +{ + Check(this); + Test_Tell( + "L4GaugeRenderer::BuildExternalGraphicsPort(" << port_number << ")\n" + ); + + if (externalDisplay != NULL) + { + Test_Tell("...non-NULL display\n"); + Verify(port_number < maximumGraphicsPorts); + Verify(port_number >= 0); + Test_Tell("...port_number= " << port_number << "\n"); + // + //--------------------------------------------------------- + // If it already exists, delete it + //--------------------------------------------------------- + // + L4GraphicsPort + *graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; + + if (graphics_port != NULL) + { + Test_Tell("...deleting existing port\n"); + Check(graphics_port); + Unregister_Object(graphics_port); + delete graphics_port; + } + // + //--------------------------------------------------------- + // Attempt to create it + //--------------------------------------------------------- + // + graphicsPort[port_number] = new L4BytePort( + (Video8BitBuffered *) externalDisplay, + name, + 0 // rotation + ); + + graphics_port = (L4GraphicsPort *) graphicsPort[port_number]; + + Test_Tell("...new port at " << graphics_port << "\n"); + // + //--------------------------------------------------------- + // If created, register it + //--------------------------------------------------------- + // + if (graphics_port != NULL) + { + Check(graphics_port); + Register_Object(graphics_port); + } + } + Check_Fpu(); +} + +// +//############################################################################# +// FadeToWhite, FadeToGame +//############################################################################# +// +void + L4GaugeRenderer::FadeToWhite(Scalar number_of_seconds) +{ + Check(this); + Verify(number_of_seconds >= 0.0); // zero is allowed (instant change) + +// cout << "L4GaugeRenderer::FadeToWhite(" << number_of_seconds << ")\n"; + + if (graphicsDisplay != NULL) + { + Check(graphicsDisplay); + ((SVGA16 *)graphicsDisplay)->FadeToWhite(number_of_seconds); + } + Check_Fpu(); +} + +void + L4GaugeRenderer::FadeToNormal(Scalar number_of_seconds) +{ + Check(this); + Verify(number_of_seconds >= 0.0); // zero is allowed (instant change) + +// cout << "L4GaugeRenderer::FadeToNormal(" << number_of_seconds << ")\n"; + + if (graphicsDisplay != NULL) + { + Check(graphicsDisplay); + ((SVGA16 *)graphicsDisplay)->FadeToPalettes(number_of_seconds); + } + Check_Fpu(); +} + +// +//############################################################################# +// Configure +//############################################################################# +// +void + L4GaugeRenderer::ConfigureForModel( + const char *configuration_name, + Entity *entity + ) +{ + Check(this); + Check_Pointer(configuration_name); + + Test_Tell( + "L4GaugeRenderer::ConfigureForModel(" << + configuration_name << ", " << + entity << ")\n" + ); + + Check(application); + Check(entity); + + Mission + *mission = application->GetCurrentMission(); + Check(mission); + + const char + *model_name = mission->GetGameModel(); + Check_Pointer(model_name); + + char + full_name[64]; + + Str_Copy(full_name, model_name, sizeof(full_name)); + Str_Cat(full_name, configuration_name, sizeof(full_name)); + + Test_Tell( + "L4GaugeRenderer::ConfigureForModel, full_name=" << full_name << + ")\n" + ); + + Configure(full_name, entity); + Check_Fpu(); +} + +// +//############################################################################# +// SpecialEffect +//############################################################################# +// +void + L4GaugeRenderer::SpecialEffect( + VideoEffectType type, + Scalar duration + ) +{ + Check(this); + + switch (type) + { + case scrambleVideo: + if (graphicsDisplay != NULL) + { + scrambleVideoFlag = True; + scrambleVideoTimeout = ((Scalar)Now()) + duration; + + Check(graphicsDisplay); + ((SVGA16*) graphicsDisplay)->FunkyVideo(True); + } + break; + + // other modes go here + + } + Check_Fpu(); +} + +void + L4GaugeRenderer::ProcessVideoEffects() +{ + Check(this); + Check(graphicsDisplay); + + Time + now = Now(); + + if (scrambleVideoFlag) + { + if (now >= scrambleVideoTimeout) + { + scrambleVideoFlag = False; + ((SVGA16*) graphicsDisplay)->FunkyVideo(False); + } + } + + Check_Fpu(); +} + +//-------------------------------------------------------------------- +// Profiling support +//-------------------------------------------------------------------- +Scalar + L4GaugeRenderer::GetCurrentFramePercentage() +{ + Check(this); + return Get_Frame_Percent_Used(); +} + + + + diff --git a/restoration/source410/build410.sh b/restoration/source410/build410.sh index 6a7354fd..283d1fed 100644 --- a/restoration/source410/build410.sh +++ b/restoration/source410/build410.sh @@ -22,8 +22,15 @@ B=$T/restoration/build410 Bw=$TW/restoration/build410 INC="$S/override;$C/BT_L4;$C/BT;$C/MUNGA;$C/MUNGA_L4;$C/MUNGA_L4/SOS/BC4;$S/MUNGA;$S/MUNGA_L4;$R/MUNGA;$R/MUNGA_L4;$R/MUNGA_L4/NetNub;$R/MUNGA_L4/sos/bc4;$C/MUNGA_L4/LIBDPL;$C/MUNGA_L4/NETNUB;$C/MUNGA_L4/NETNUB/INCLUDE;$S/MUNGA_L4;$R/MUNGA_L4/libDPL;$R/MUNGA_L4/libDPL/dpl;$S/shim;$S/BT;$S/BT_L4;$TW/BORLAND/BC45/INCLUDE" +# Optimizer set = CODE/BT/OPT.MAK verbatim (the shipped release tier). -O2 +# alone leaves the global optimizer half off; the missing switches (global +# CSE/regalloc, invariant motion, copy propagation, inline intrinsics, +# redundant-load suppression, loop opts) measured ~3x wall speed on the rig +# (shipped 11.8KB/s fifo vs 4.1KB/s ours, 2026-08-04) and starved the gauge +# wheel to one background slot per frame ("many seconds between updates"). FLAGS="-c -DLBE4 -DDEBUG_LEVEL=0 -DDEBUG_STREAM=cout \ --b -r -ff -AT -k- -N- -v- -5 -a4 -V -Jg -x- -RT- -O2 -w-" +-b -r -ff -AT -k- -N- -v- -5 -a4 -V -Jg -x- -RT- \ +-O2 -Ot -Oc -Og -O -Ol -Z -Ob -Oe -Oi -Om -Op -Ov -w-" mkdir -p "$B"/obj/{munga,mungal4,bt,btl4} "$B"/lib