From d3b332a62a41adb6c8c960e36c66913a677b232a Mon Sep 17 00:00:00 2001 From: Cyd Date: Tue, 28 Jul 2026 22:20:11 -0500 Subject: [PATCH] BT410 5.3.58: the load gate is blocked by a REFILL loop on priority 0, not starvation Per-priority occupancy, measured every second until the fault: [queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 That single line kills the two leading theories. p3/p4 empty means nothing above priority 0 is competing, so the pump is free to serve it -- no starvation. nextReady=1 means PeekAtNextEvent always has a READY event, so priority 0 is not holding a timed event whose alarm never comes due -- no deadlock. What remains is refill: priority 0 is replenished as fast as the pump drains it, ~143 events per simulated second. It is also fully deterministic. Two runs of the same binary reported pump=352/1499 at frame 1001 and 495/2643 at frame 2002, identical to the byte. So 'crashes about half the time' was never a race -- it was comparing runs that differed in binary or conf. Only InterestManager::PostRendererEvent posts at priority 0 (it maps every renderer event there while the app is not RunningMission), so naming the message names the flooder. Application::Post now tallies priority-0 posts by message ID and reports the busiest three. Notably, all three producers of NotifyOfNewInterestingEntity are entity-CREATION paths, so if that ID dominates then something is creating entities in a loop -- which would explain the unbounded growth behind the fault as well as the hang. pod_render_noskl.conf now carries the same probes, so one instrumented binary can be run with the skeleton walk on and off. Walk-off runs are the only configuration of ours known to reach 'Turning Plasma Score Display On'. Co-Authored-By: Claude Fable 5 --- emulator/render-bridge/dosboxlog.txt | 55 + emulator/render-bridge/logs/002-CRASH.txt | 79 + emulator/render-bridge/logs/003-CRASH.txt | 79 + emulator/render-bridge/logs/003-dosbox.txt | 99 + emulator/render-bridge/logs/005-CRASH.txt | 103 + emulator/render-bridge/logs/005-dosbox.txt | 123 + emulator/render-bridge/pod_render_norio.conf | 82 + emulator/render-bridge/pod_render_noskl.conf | 147 +- emulator/render-bridge/podlog.txt | 95 +- emulator/render-bridge/podrun.sh | 8 +- restoration/source410/MUNGA/APP.CPP | 3813 +++++++++-------- restoration/source410/MUNGA/APP.HPP | 28 + restoration/source410/RENDER-ROADMAP.NOTES.md | 102 + 13 files changed, 2848 insertions(+), 1965 deletions(-) create mode 100644 emulator/render-bridge/dosboxlog.txt create mode 100644 emulator/render-bridge/logs/002-CRASH.txt create mode 100644 emulator/render-bridge/logs/003-CRASH.txt create mode 100644 emulator/render-bridge/logs/003-dosbox.txt create mode 100644 emulator/render-bridge/logs/005-CRASH.txt create mode 100644 emulator/render-bridge/logs/005-dosbox.txt create mode 100644 emulator/render-bridge/pod_render_norio.conf diff --git a/emulator/render-bridge/dosboxlog.txt b/emulator/render-bridge/dosboxlog.txt new file mode 100644 index 00000000..484e260c --- /dev/null +++ b/emulator/render-bridge/dosboxlog.txt @@ -0,0 +1,55 @@ +LOG: Early LOG Init complete +LOG: CONFIG: Loaded config file: C:/VWE/TeslaRel410/emulator/render-bridge/pod_render_rec.conf +LOG: DOSBox-X's working directory: C:\VWE\TeslaRel410\emulator\src\src +LOG: Logging init: beginning logging proper. This is the end of the early init logging +LOG: Logging: No logfile was given. All further logging will be discarded. +LOG: DOSBox-X version 2026.06.02 Commit d75a57c (MinGW SDL2 64-bit) +LOG: Prevent capture: 0 +LOG: SDL: version 2.32.10, Video windows, Audio directsound +LOG: EXPERIMENTAL: Debugger enabled for MinGW build, DOSBox-X crashes depending on the terminal software you use. Launching from command prompt (cmd.exe) is recommended. +LOG: Windows keyboard layout ID is 0x0409 +LOG: Host keyboard layout is now us (US English) +LOG: Mapper keyboard layout is now us (US English) +LOG: SDL2 reports desktop display mode 3440 x 1440 +LOG: OpenGL Version : 4.6.0 NVIDIA 610.47 +LOG: Configured windowposition: - +LOG: SDL: Current window pixel format: SDL_PIXELFORMAT_RGB888 +LOG: SDL: You are running in 24 bpp mode, this will slow down things! +LOG: Releasing keyboard +LOG: SDL: Windows 11 round corners will be disabled. +LOG: Screen report: Method 'Win98base' (3440.000 x 1440.000 pixels) at (0.000 x 0.000) (910.167 x 381.000 mm) (35.833 x 15.000 in) (96.000 x 96.000 DPI) +LOG: ISA BCLK: 8333333.333Hz (25000000/3) +LOG: monopal: green, +LOG: Active save slot: 1 [Empty] +LOG: USING AVI+ZMBV +LOG: Pentium CMPXCHG8B emulation is enabled +LOG: Max 1073741824 sz 32768 +LOG: Final 32768 +LOG: Final arrangement: Below 4GB = 32768KB, Above 4GB = 0KB +LOG: VPXLOG: live render backend started +LOG: VPXLOG: base 0x150 respond=1 handshakes=3 log='C:\Users\cyd\AppData\Local/Temp/vwe-pod/vpxresp.txt' +LOG: MIDI:Opened device:win32 +LOG: mic_input: Selected microphone device: Microphone (Steam Streaming Microphone) +LOG: One joystick reported, initializing with 4axis +LOG: Using joystick Xbox One Controller with 6 axes, 16 buttons and 1 hat(s) +LOG: VOODOO LFB now at d0000000 +LOG: Serial1: BASE 3f8h +LOG: Serial1: namedpipe client of \\.\pipe\vrio (poll 0.10ms burst x16) +LOG: Serial1: namedpipe connected to \\.\pipe\vrio +LOG: Serial2: BASE 2f8h +LOG: Serial2: namedpipe client of \\.\pipe\vplasma (poll 1.00ms burst x1) +LOG: Serial2: namedpipe connected to \\.\pipe\vplasma +LOG: Serial3: BASE 3e8h +LOG: Serial: port 3 will write to file C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt +LOG: Parallel1: BASE 378h +LOG: MPU-401 Registering I/O ports as if IBM PC MPU-401 at base 330h +LOG: Memory I/O complexity optimization enabled aka option 'memory io optimization 1'. If the game or demo is unable to draw to the screen properly, set the option to false. +LOG: The 'scanline render on demand' option is available and may provide a modest boost in video render performance if set to true. +LOG: Allocated APM BIOS pm entry point at f000:ce40 +LOG: Writing code to fce40 +LOG: Writing code to fce60 +LOG: Windows: IID_ITaskbarList3 is available +LOG: ISA Plug & Play BIOS enabled +LOG: VGA ROM BIOS init callback +LOG: pixratio 1.000, dw false, dh false +LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750 diff --git a/emulator/render-bridge/logs/002-CRASH.txt b/emulator/render-bridge/logs/002-CRASH.txt new file mode 100644 index 00000000..9822b4bd --- /dev/null +++ b/emulator/render-bridge/logs/002-CRASH.txt @@ -0,0 +1,79 @@ +BattleTech v4.10 +BTL4Application::BTL4Application +GaugeInterpreter: undefined label 'Initialization' +[exp] master player experience=2 simLive=1 heatModelOn=1 advDamage=1 +[role] 'Role::Default' lives=1000 killBonus=500 dmgMod=1 +[torso] horizJoint 'jointtorso' -> 0x00a3fe78 type=1 enabled=1 +[weap] 'AFC100' seg=13 mount=sitelgunport rear=0 +[proj] 'AFC100' ammoBinIndex=21 -> AmmoBinAFC100 (rounds=20) +[weap] 'ERLLaser' seg=7 mount=siterugunport rear=0 +[weap] 'ERSLaser_1' seg=13 mount=sitelgunport rear=0 +[weap] 'ERSLaser_2' seg=8 mount=siterdgunport rear=0 +[weap] 'LRM15_1' seg=18 mount=sitelmissleport rear=0 +[proj] 'LRM15_1' ammoBinIndex=26 -> AmmoBinLRM15_1 (rounds=16) +[weap] 'LRM15_2' seg=19 mount=sitermissleport rear=0 +[proj] 'LRM15_2' ammoBinIndex=28 -> AmmoBinLRM15_2 (rounds=16) +[mech] segment walk done: subsystemCount=32 weaponCount=6 +[skel] jointSubsystem=0x009e6d84 jointCount=25 +[mech] model params: walkTR=65 runTR=40 maxAcc=20 throttleAdj=0.8 (deg,deg,u/s^2,scale) +[mech] look angles: L=50 R=-50 F=-15 B=0 (deg) +[mech] damage zones streamed: 22 [dz_dtorso,dz_hip,dz_larm,dz_ldleg,dz_ldoor,dz_lfoot,dz_lgun,dz_ltorso,dz_luleg,dz_rarm,dz_rdleg,dz_rdoor,dz_reardtorso,dz_rearltorso,dz_rearrtorso,dz_rearutorso,dz_rfoot,dz_rgun,dz_rtorso,dz_ruleg,dz_searchlight,dz_utorso] +[cyl] table 'madcat' LOADED rows=7 +[valve] 'Condenser1' valve=1 flow=0.166667 +[valve] 'Condenser2' valve=1 flow=0.166667 +[valve] 'Condenser3' valve=1 flow=0.166667 +[valve] 'Condenser4' valve=1 flow=0.166667 +[valve] 'Condenser5' valve=1 flow=0.166667 +[valve] 'Condenser6' valve=1 flow=0.166667 +[mapper] ctor id=0 throttleAttr=0x002c30f8 controlModeAttr=0x002c3134 +[gau2] cluster slot=14 classID=3011 auxScreen=5 name=Avionics +[gau2] cluster slot=15 classID=3014 auxScreen=6 name=Myomers +[gau2] cluster slot=22 classID=3021 auxScreen=1 name=AFC100 +[gau2] cluster slot=23 classID=3016 auxScreen=10 name=ERLLaser +[gau2] cluster slot=24 classID=3016 auxScreen=3 name=ERSLaser_1 +[gau2] cluster slot=25 classID=3016 auxScreen=12 name=ERSLaser_2 +[gau2] cluster slot=27 classID=3024 auxScreen=2 name=LRM15_1 +[gau2] cluster slot=29 classID=3024 auxScreen=9 name=LRM15_2 +ColorMapperArmor warning: damage zone dz_missle not found +ColorMapperArmor warning: damage zone dz_missle not found +ColorMapperArmor warning: damage zone dz_lmissle not found +ColorMapperArmor warning: damage zone dz_rmissle not found +ColorMapperArmor warning: damage zone dz_ftorso not found +ColorMapperArmor warning: damage zone dz_btorso not found +[link] InitializePlayerLink -> vehicle playerLink=0x00ac513c +[launch] state=11 minPriorityEmpty=0 +[launch] state=11 minPriorityEmpty=0 +[launch] state=11 minPriorityEmpty=0 +[launch] state=11 minPriorityEmpty=0 +[launch] state=11 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[stack] frame at 0x12dee7 drift=0 over 1001 frames +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[launch] state=2 minPriorityEmpty=0 +[stack] frame at 0x12dee7 drift=0 over 2002 frames +[launch] state=2 minPriorityEmpty=0 +Reference to a page you don't own +PF cr2= 7000 FA64 at 00FF 0000 66D9 +Unhandled exception 000E at 00FF 66D9 ErrCode 0004 diff --git a/emulator/render-bridge/logs/003-CRASH.txt b/emulator/render-bridge/logs/003-CRASH.txt new file mode 100644 index 00000000..e08d711a --- /dev/null +++ b/emulator/render-bridge/logs/003-CRASH.txt @@ -0,0 +1,79 @@ +BattleTech v4.10 +BTL4Application::BTL4Application +GaugeInterpreter: undefined label 'Initialization' +[exp] master player experience=2 simLive=1 heatModelOn=1 advDamage=1 +[role] 'Role::Default' lives=1000 killBonus=500 dmgMod=1 +[torso] horizJoint 'jointtorso' -> 0x00a3fe78 type=1 enabled=1 +[weap] 'AFC100' seg=13 mount=sitelgunport rear=0 +[proj] 'AFC100' ammoBinIndex=21 -> AmmoBinAFC100 (rounds=20) +[weap] 'ERLLaser' seg=7 mount=siterugunport rear=0 +[weap] 'ERSLaser_1' seg=13 mount=sitelgunport rear=0 +[weap] 'ERSLaser_2' seg=8 mount=siterdgunport rear=0 +[weap] 'LRM15_1' seg=18 mount=sitelmissleport rear=0 +[proj] 'LRM15_1' ammoBinIndex=26 -> AmmoBinLRM15_1 (rounds=16) +[weap] 'LRM15_2' seg=19 mount=sitermissleport rear=0 +[proj] 'LRM15_2' ammoBinIndex=28 -> AmmoBinLRM15_2 (rounds=16) +[mech] segment walk done: subsystemCount=32 weaponCount=6 +[skel] jointSubsystem=0x009e6d84 jointCount=25 +[mech] model params: walkTR=65 runTR=40 maxAcc=20 throttleAdj=0.8 (deg,deg,u/s^2,scale) +[mech] look angles: L=50 R=-50 F=-15 B=0 (deg) +[mech] damage zones streamed: 22 [dz_dtorso,dz_hip,dz_larm,dz_ldleg,dz_ldoor,dz_lfoot,dz_lgun,dz_ltorso,dz_luleg,dz_rarm,dz_rdleg,dz_rdoor,dz_reardtorso,dz_rearltorso,dz_rearrtorso,dz_rearutorso,dz_rfoot,dz_rgun,dz_rtorso,dz_ruleg,dz_searchlight,dz_utorso] +[cyl] table 'madcat' LOADED rows=7 +[valve] 'Condenser1' valve=1 flow=0.166667 +[valve] 'Condenser2' valve=1 flow=0.166667 +[valve] 'Condenser3' valve=1 flow=0.166667 +[valve] 'Condenser4' valve=1 flow=0.166667 +[valve] 'Condenser5' valve=1 flow=0.166667 +[valve] 'Condenser6' valve=1 flow=0.166667 +[mapper] ctor id=0 throttleAttr=0x002c30f8 controlModeAttr=0x002c3134 +[gau2] cluster slot=14 classID=3011 auxScreen=5 name=Avionics +[gau2] cluster slot=15 classID=3014 auxScreen=6 name=Myomers +[gau2] cluster slot=22 classID=3021 auxScreen=1 name=AFC100 +[gau2] cluster slot=23 classID=3016 auxScreen=10 name=ERLLaser +[gau2] cluster slot=24 classID=3016 auxScreen=3 name=ERSLaser_1 +[gau2] cluster slot=25 classID=3016 auxScreen=12 name=ERSLaser_2 +[gau2] cluster slot=27 classID=3024 auxScreen=2 name=LRM15_1 +[gau2] cluster slot=29 classID=3024 auxScreen=9 name=LRM15_2 +ColorMapperArmor warning: damage zone dz_missle not found +ColorMapperArmor warning: damage zone dz_missle not found +ColorMapperArmor warning: damage zone dz_lmissle not found +ColorMapperArmor warning: damage zone dz_rmissle not found +ColorMapperArmor warning: damage zone dz_ftorso not found +ColorMapperArmor warning: damage zone dz_btorso not found +[link] InitializePlayerLink -> vehicle playerLink=0x00ac513c +[launch] state=11 minPriorityEmpty=0 events=0 ticks=10873 +[launch] state=11 minPriorityEmpty=0 events=0 ticks=11884 +[launch] state=11 minPriorityEmpty=0 events=0 ticks=12892 +[launch] state=11 minPriorityEmpty=0 events=0 ticks=13900 +[launch] state=11 minPriorityEmpty=0 events=0 ticks=14908 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=16006 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=17032 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=18092 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=19153 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=20214 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=21275 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=22336 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=23396 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=24457 +[stack] frame at 0x12dee7 drift=0 over 1001 frames ticks=24729 pump=352/1499 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=25518 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=26579 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=27640 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=28701 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=29762 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=30821 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=31881 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=32942 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=34003 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=35063 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=36126 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=37187 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=38248 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=39309 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=40370 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=41431 +[stack] frame at 0x12dee7 drift=0 over 2002 frames ticks=41586 pump=495/2643 +[launch] state=2 minPriorityEmpty=0 events=0 ticks=42493 +Reference to a page you don't own +PF cr2= 7000 FA64 at 00FF 0000 66D9 +Unhandled exception 000E at 00FF 66D9 ErrCode 0004 diff --git a/emulator/render-bridge/logs/003-dosbox.txt b/emulator/render-bridge/logs/003-dosbox.txt new file mode 100644 index 00000000..b52b314e --- /dev/null +++ b/emulator/render-bridge/logs/003-dosbox.txt @@ -0,0 +1,99 @@ +LOG: Early LOG Init complete +LOG: CONFIG: Loaded config file: C:/VWE/TeslaRel410/emulator/render-bridge/pod_render_rec.conf +LOG: DOSBox-X's working directory: C:\VWE\TeslaRel410\emulator\src\src +LOG: Logging init: beginning logging proper. This is the end of the early init logging +LOG: Logging: No logfile was given. All further logging will be discarded. +LOG: DOSBox-X version 2026.06.02 Commit d75a57c (MinGW SDL2 64-bit) +LOG: Prevent capture: 0 +LOG: SDL: version 2.32.10, Video windows, Audio directsound +LOG: EXPERIMENTAL: Debugger enabled for MinGW build, DOSBox-X crashes depending on the terminal software you use. Launching from command prompt (cmd.exe) is recommended. +LOG: Windows keyboard layout ID is 0x0409 +LOG: Host keyboard layout is now us (US English) +LOG: Mapper keyboard layout is now us (US English) +LOG: SDL2 reports desktop display mode 3440 x 1440 +LOG: OpenGL Version : 4.6.0 NVIDIA 610.47 +LOG: Configured windowposition: - +LOG: SDL: Current window pixel format: SDL_PIXELFORMAT_RGB888 +LOG: SDL: You are running in 24 bpp mode, this will slow down things! +LOG: Releasing keyboard +LOG: SDL: Windows 11 round corners will be disabled. +LOG: Screen report: Method 'Win98base' (3440.000 x 1440.000 pixels) at (0.000 x 0.000) (910.167 x 381.000 mm) (35.833 x 15.000 in) (96.000 x 96.000 DPI) +LOG: ISA BCLK: 8333333.333Hz (25000000/3) +LOG: monopal: green, +LOG: Active save slot: 1 [Empty] +LOG: USING AVI+ZMBV +LOG: Pentium CMPXCHG8B emulation is enabled +LOG: Max 1073741824 sz 32768 +LOG: Final 32768 +LOG: Final arrangement: Below 4GB = 32768KB, Above 4GB = 0KB +LOG: VPXLOG: live render backend started +LOG: VPXLOG: base 0x150 respond=1 handshakes=3 log='C:\Users\cyd\AppData\Local/Temp/vwe-pod/vpxresp.txt' +LOG: MIDI:Opened device:win32 +LOG: mic_input: Selected microphone device: Microphone (Steam Streaming Microphone) +LOG: One joystick reported, initializing with 4axis +LOG: Using joystick Xbox One Controller with 6 axes, 16 buttons and 1 hat(s) +LOG: VOODOO LFB now at d0000000 +LOG: Serial1: BASE 3f8h +LOG: Serial1: namedpipe client of \\.\pipe\vrio (poll 0.10ms burst x16) +LOG: Serial1: namedpipe connected to \\.\pipe\vrio +LOG: Serial2: BASE 2f8h +LOG: Serial2: namedpipe client of \\.\pipe\vplasma (poll 1.00ms burst x1) +LOG: Serial2: namedpipe connected to \\.\pipe\vplasma +LOG: Serial3: BASE 3e8h +LOG: Serial: port 3 will write to file C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt +LOG: Parallel1: BASE 378h +LOG: MPU-401 Registering I/O ports as if IBM PC MPU-401 at base 330h +LOG: Memory I/O complexity optimization enabled aka option 'memory io optimization 1'. If the game or demo is unable to draw to the screen properly, set the option to false. +LOG: The 'scanline render on demand' option is available and may provide a modest boost in video render performance if set to true. +LOG: Allocated APM BIOS pm entry point at f000:ce40 +LOG: Writing code to fce40 +LOG: Writing code to fce60 +LOG: Windows: IID_ITaskbarList3 is available +LOG: ISA Plug & Play BIOS enabled +LOG: VGA ROM BIOS init callback +LOG: pixratio 1.000, dw false, dh false +LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750 +LOG: WARNING: No translation support (to host) for code page 0 +LOG: DOS keyboard layout loaded with main language code US for layout us +LOG: XMS: 50 handles allocated for use by the DOS environment +LOG: EMS page frame at 0xe000-0xefff +LOG: COMMAND.COM env size: 720 bytes +LOG: COMMAND.COM environment block: 0x0701 sz=0x002d +LOG: COMMAND.COM main body (PSP): 0x072f sz=0x009a +LOG: COMMAND.COM stack: 0x0749 +LOG: pixratio 1.350, dw false, dh false +LOG: Aspect ratio: 720 x 540 xToY=1.333 yToX=0.750 +LOG: SHELL:Redirect output to COM3 +VPX lights@f1: type6=0 typeE=0 decoded=0 dir=0 amb(0.00,0.00,0.00) +LOG: 184935189 ERROR MOUSE:Unhandled videomode 79 on reset +LOG: pixratio 1.000, dw false, dh false +LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750 +VPX lightnode t=6 nb=52: [8]00000000/0 [12]00000000/0 [16]00000001/1.4013e-45 [20]3f800000/1 [24]3f800000/1 [28]3f800000/1 [32]64078678/1e+22 [36]64078678/1e+22 [40]00000000/0 [44]bf6ae9ee/-0.917632 [48]ffffffff/nan +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:3), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: 619106628 ERROR INT10:PutPixel unhandled mode type 7 +LOG: SHELL:Redirect output to RC.TXT +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:471 (IF0:5), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:503 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:503 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:528 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:210 (IF0:0), TX:0, Break 0 diff --git a/emulator/render-bridge/logs/005-CRASH.txt b/emulator/render-bridge/logs/005-CRASH.txt new file mode 100644 index 00000000..463f5abd --- /dev/null +++ b/emulator/render-bridge/logs/005-CRASH.txt @@ -0,0 +1,103 @@ +BattleTech v4.10 +BTL4Application::BTL4Application +GaugeInterpreter: undefined label 'Initialization' +[exp] master player experience=2 simLive=1 heatModelOn=1 advDamage=1 +[role] 'Role::Default' lives=1000 killBonus=500 dmgMod=1 +[torso] horizJoint 'jointtorso' -> 0x00a3fe78 type=1 enabled=1 +[weap] 'AFC100' seg=13 mount=sitelgunport rear=0 +[proj] 'AFC100' ammoBinIndex=21 -> AmmoBinAFC100 (rounds=20) +[weap] 'ERLLaser' seg=7 mount=siterugunport rear=0 +[weap] 'ERSLaser_1' seg=13 mount=sitelgunport rear=0 +[weap] 'ERSLaser_2' seg=8 mount=siterdgunport rear=0 +[weap] 'LRM15_1' seg=18 mount=sitelmissleport rear=0 +[proj] 'LRM15_1' ammoBinIndex=26 -> AmmoBinLRM15_1 (rounds=16) +[weap] 'LRM15_2' seg=19 mount=sitermissleport rear=0 +[proj] 'LRM15_2' ammoBinIndex=28 -> AmmoBinLRM15_2 (rounds=16) +[mech] segment walk done: subsystemCount=32 weaponCount=6 +[skel] jointSubsystem=0x009e6d84 jointCount=25 +[mech] model params: walkTR=65 runTR=40 maxAcc=20 throttleAdj=0.8 (deg,deg,u/s^2,scale) +[mech] look angles: L=50 R=-50 F=-15 B=0 (deg) +[mech] damage zones streamed: 22 [dz_dtorso,dz_hip,dz_larm,dz_ldleg,dz_ldoor,dz_lfoot,dz_lgun,dz_ltorso,dz_luleg,dz_rarm,dz_rdleg,dz_rdoor,dz_reardtorso,dz_rearltorso,dz_rearrtorso,dz_rearutorso,dz_rfoot,dz_rgun,dz_rtorso,dz_ruleg,dz_searchlight,dz_utorso] +[cyl] table 'madcat' LOADED rows=7 +[valve] 'Condenser1' valve=1 flow=0.166667 +[valve] 'Condenser2' valve=1 flow=0.166667 +[valve] 'Condenser3' valve=1 flow=0.166667 +[valve] 'Condenser4' valve=1 flow=0.166667 +[valve] 'Condenser5' valve=1 flow=0.166667 +[valve] 'Condenser6' valve=1 flow=0.166667 +[mapper] ctor id=0 throttleAttr=0x002c30f8 controlModeAttr=0x002c3134 +[gau2] cluster slot=14 classID=3011 auxScreen=5 name=Avionics +[gau2] cluster slot=15 classID=3014 auxScreen=6 name=Myomers +[gau2] cluster slot=22 classID=3021 auxScreen=1 name=AFC100 +[gau2] cluster slot=23 classID=3016 auxScreen=10 name=ERLLaser +[gau2] cluster slot=24 classID=3016 auxScreen=3 name=ERSLaser_1 +[gau2] cluster slot=25 classID=3016 auxScreen=12 name=ERSLaser_2 +[gau2] cluster slot=27 classID=3024 auxScreen=2 name=LRM15_1 +[gau2] cluster slot=29 classID=3024 auxScreen=9 name=LRM15_2 +ColorMapperArmor warning: damage zone dz_missle not found +ColorMapperArmor warning: damage zone dz_missle not found +ColorMapperArmor warning: damage zone dz_lmissle not found +ColorMapperArmor warning: damage zone dz_rmissle not found +ColorMapperArmor warning: damage zone dz_ftorso not found +ColorMapperArmor warning: damage zone dz_btorso not found +[link] InitializePlayerLink -> vehicle playerLink=0x00ac513c +[launch] state=11 minPriorityEmpty=0 ticks=10859 +[queues] p0=BUSY p1=BUSY p2=BUSY p3=BUSY p4=- +[launch] state=11 minPriorityEmpty=0 ticks=11870 +[queues] p0=BUSY p1=BUSY p2=BUSY p3=BUSY p4=- +[launch] state=11 minPriorityEmpty=0 ticks=12878 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=11 minPriorityEmpty=0 ticks=13886 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=11 minPriorityEmpty=0 ticks=14894 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=15992 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=17021 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=18082 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=19142 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=20203 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=21264 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=22324 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=23385 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=24446 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[stack] frame at 0x12dee7 drift=0 over 1001 frames ticks=24718 pump=352/1499 +[launch] state=2 minPriorityEmpty=0 ticks=25508 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=26569 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=27630 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=28691 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=29752 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=30812 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=31873 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=32933 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=33994 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=35055 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=36118 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=37178 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=38239 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +[launch] state=2 minPriorityEmpty=0 ticks=39300 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- +Reference to a page you don't own +PF cr2= 7000 FA64 at 00FF 0000 66D9 +Unhandled exception 000E at 00FF 66D9 ErrCode 0004 diff --git a/emulator/render-bridge/logs/005-dosbox.txt b/emulator/render-bridge/logs/005-dosbox.txt new file mode 100644 index 00000000..bc4a0690 --- /dev/null +++ b/emulator/render-bridge/logs/005-dosbox.txt @@ -0,0 +1,123 @@ +LOG: Early LOG Init complete +LOG: CONFIG: Loaded config file: C:/VWE/TeslaRel410/emulator/render-bridge/pod_render_rec.conf +LOG: DOSBox-X's working directory: C:\VWE\TeslaRel410\emulator\src\src +LOG: Logging init: beginning logging proper. This is the end of the early init logging +LOG: Logging: No logfile was given. All further logging will be discarded. +LOG: DOSBox-X version 2026.06.02 Commit d75a57c (MinGW SDL2 64-bit) +LOG: Prevent capture: 0 +LOG: SDL: version 2.32.10, Video windows, Audio directsound +LOG: EXPERIMENTAL: Debugger enabled for MinGW build, DOSBox-X crashes depending on the terminal software you use. Launching from command prompt (cmd.exe) is recommended. +LOG: Windows keyboard layout ID is 0x0409 +LOG: Host keyboard layout is now us (US English) +LOG: Mapper keyboard layout is now us (US English) +LOG: SDL2 reports desktop display mode 3440 x 1440 +LOG: OpenGL Version : 4.6.0 NVIDIA 610.47 +LOG: Configured windowposition: - +LOG: SDL: Current window pixel format: SDL_PIXELFORMAT_RGB888 +LOG: SDL: You are running in 24 bpp mode, this will slow down things! +LOG: Releasing keyboard +LOG: SDL: Windows 11 round corners will be disabled. +LOG: Screen report: Method 'Win98base' (3440.000 x 1440.000 pixels) at (0.000 x 0.000) (910.167 x 381.000 mm) (35.833 x 15.000 in) (96.000 x 96.000 DPI) +LOG: ISA BCLK: 8333333.333Hz (25000000/3) +LOG: monopal: green, +LOG: Active save slot: 1 [Empty] +LOG: USING AVI+ZMBV +LOG: Pentium CMPXCHG8B emulation is enabled +LOG: Max 1073741824 sz 32768 +LOG: Final 32768 +LOG: Final arrangement: Below 4GB = 32768KB, Above 4GB = 0KB +LOG: VPXLOG: live render backend started +LOG: VPXLOG: base 0x150 respond=1 handshakes=3 log='C:\Users\cyd\AppData\Local/Temp/vwe-pod/vpxresp.txt' +LOG: MIDI:Opened device:win32 +LOG: mic_input: Selected microphone device: Microphone (Steam Streaming Microphone) +LOG: One joystick reported, initializing with 4axis +LOG: Using joystick Xbox One Controller with 6 axes, 16 buttons and 1 hat(s) +LOG: VOODOO LFB now at d0000000 +LOG: Serial1: BASE 3f8h +LOG: Serial1: namedpipe client of \\.\pipe\vrio (poll 0.10ms burst x16) +LOG: Serial1: namedpipe connected to \\.\pipe\vrio +LOG: Serial2: BASE 2f8h +LOG: Serial2: namedpipe client of \\.\pipe\vplasma (poll 1.00ms burst x1) +LOG: Serial2: namedpipe connected to \\.\pipe\vplasma +LOG: Serial3: BASE 3e8h +LOG: Serial: port 3 will write to file C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt +LOG: Parallel1: BASE 378h +LOG: MPU-401 Registering I/O ports as if IBM PC MPU-401 at base 330h +LOG: Memory I/O complexity optimization enabled aka option 'memory io optimization 1'. If the game or demo is unable to draw to the screen properly, set the option to false. +LOG: The 'scanline render on demand' option is available and may provide a modest boost in video render performance if set to true. +LOG: Allocated APM BIOS pm entry point at f000:ce40 +LOG: Writing code to fce40 +LOG: Writing code to fce60 +LOG: Windows: IID_ITaskbarList3 is available +LOG: ISA Plug & Play BIOS enabled +LOG: VGA ROM BIOS init callback +LOG: pixratio 1.000, dw false, dh false +LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750 +LOG: WARNING: No translation support (to host) for code page 0 +LOG: DOS keyboard layout loaded with main language code US for layout us +LOG: XMS: 50 handles allocated for use by the DOS environment +LOG: EMS page frame at 0xe000-0xefff +LOG: COMMAND.COM env size: 720 bytes +LOG: COMMAND.COM environment block: 0x0701 sz=0x002d +LOG: COMMAND.COM main body (PSP): 0x072f sz=0x009a +LOG: COMMAND.COM stack: 0x0749 +LOG: pixratio 1.350, dw false, dh false +LOG: Aspect ratio: 720 x 540 xToY=1.333 yToX=0.750 +LOG: SHELL:Redirect output to COM3 +VPX lights@f1: type6=0 typeE=0 decoded=0 dir=0 amb(0.00,0.00,0.00) +LOG: 184697104 ERROR MOUSE:Unhandled videomode 79 on reset +LOG: pixratio 1.000, dw false, dh false +LOG: Aspect ratio: 640 x 480 xToY=1.333 yToX=0.750 +VPX lightnode t=6 nb=52: [8]00000000/0 [12]00000000/0 [16]00000001/1.4013e-45 [20]3f800000/1 [24]3f800000/1 [28]3f800000/1 [32]64078678/1e+22 [36]64078678/1e+22 [40]00000000/0 [44]bf6ae9ee/-0.917632 [48]ffffffff/nan +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:3 (IF0:3), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:1 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:2 (IF0:2), TX:0, Break 0 +LOG: 601774926 ERROR INT10:PutPixel unhandled mode type 7 +LOG: SHELL:Redirect output to RC.TXT +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:516 (IF0:6), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:522 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:506 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:513 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:511 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:510 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:508 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:510 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:508 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:514 (IF0:1), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:510 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:516 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:512 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:512 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:515 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:521 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:508 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:509 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:522 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:506 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:514 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:514 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:508 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:511 (IF0:0), TX:0, Break 0 +LOG: Serial1: Errors: Framing 0, Parity 0, Overrun RX:331 (IF0:0), TX:0, Break 0 diff --git a/emulator/render-bridge/pod_render_norio.conf b/emulator/render-bridge/pod_render_norio.conf new file mode 100644 index 00000000..9a0ae0f5 --- /dev/null +++ b/emulator/render-bridge/pod_render_norio.conf @@ -0,0 +1,82 @@ +# 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_MECH_LOG=1 +set BT_LAUNCH_LOG=1 +set BT_STACK_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/emulator/render-bridge/pod_render_noskl.conf b/emulator/render-bridge/pod_render_noskl.conf index 02a180bf..ac2df516 100644 --- a/emulator/render-bridge/pod_render_noskl.conf +++ b/emulator/render-bridge/pod_render_noskl.conf @@ -1,70 +1,77 @@ -[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=namedpipe pipe:vrio rxpollus:100 rxburst:16 -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_NO_SKL=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 - +[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=namedpipe pipe:vrio rxpollus:100 rxburst:16 +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_NO_SKL=1 +set BT_STACK_LOG=1 +set BT_LAUNCH_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/emulator/render-bridge/podlog.txt b/emulator/render-bridge/podlog.txt index 9822b4bd..f70ed31a 100644 --- a/emulator/render-bridge/podlog.txt +++ b/emulator/render-bridge/podlog.txt @@ -41,39 +41,68 @@ ColorMapperArmor warning: damage zone dz_rmissle not found ColorMapperArmor warning: damage zone dz_ftorso not found ColorMapperArmor warning: damage zone dz_btorso not found [link] InitializePlayerLink -> vehicle playerLink=0x00ac513c -[launch] state=11 minPriorityEmpty=0 -[launch] state=11 minPriorityEmpty=0 -[launch] state=11 minPriorityEmpty=0 -[launch] state=11 minPriorityEmpty=0 -[launch] state=11 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[stack] frame at 0x12dee7 drift=0 over 1001 frames -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[launch] state=2 minPriorityEmpty=0 -[stack] frame at 0x12dee7 drift=0 over 2002 frames -[launch] state=2 minPriorityEmpty=0 +[launch] state=11 minPriorityEmpty=0 ticks=10820 +[queues] p0=BUSY p1=BUSY p2=BUSY p3=BUSY p4=- nextReady=1 +[launch] state=11 minPriorityEmpty=0 ticks=11832 +[queues] p0=BUSY p1=BUSY p2=BUSY p3=BUSY p4=- nextReady=1 +[launch] state=11 minPriorityEmpty=0 ticks=12842 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=11 minPriorityEmpty=0 ticks=13851 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=11 minPriorityEmpty=0 ticks=14860 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=15958 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=17008 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=18070 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=19132 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=20193 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=21253 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=22362 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=23473 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[stack] frame at 0x12dee7 drift=0 over 1001 frames ticks=24504 pump=352/1499 +[launch] state=2 minPriorityEmpty=0 ticks=24583 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=25692 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=26803 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=27912 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=29021 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=30132 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=31241 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=32350 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=33461 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=34572 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=35681 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +LBE4ControlsManager::Execute, lost RIO analog request +iThinkIRQIsOn =1, actual state =1, characters sent =5012 +[launch] state=2 minPriorityEmpty=0 ticks=36694 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=37738 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=38800 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[launch] state=2 minPriorityEmpty=0 ticks=39861 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 +[stack] frame at 0x12dee7 drift=0 over 2002 frames ticks=40606 pump=495/2643 +[launch] state=2 minPriorityEmpty=0 ticks=40922 +[queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 Reference to a page you don't own PF cr2= 7000 FA64 at 00FF 0000 66D9 Unhandled exception 000E at 00FF 66D9 ErrCode 0004 diff --git a/emulator/render-bridge/podrun.sh b/emulator/render-bridge/podrun.sh index e80d8c1f..521e6141 100644 --- a/emulator/render-bridge/podrun.sh +++ b/emulator/render-bridge/podrun.sh @@ -46,6 +46,7 @@ if [ -s "$BR/podlog.txt" ]; then else echo "archived previous run -> $(basename "$PREV")" fi + [ -s "$BR/dosboxlog.txt" ] && cp "$BR/dosboxlog.txt" "$BR/logs/$(printf '%03d' "$N")-dosbox.txt" fi if tasklist //FI "IMAGENAME eq dosbox-x.exe" 2>/dev/null | grep -qi dosbox-x; then @@ -84,6 +85,11 @@ export RIO_TAP="$WORK/riotap.txt" rm -f "$BR/podlog.txt" cd /c/VWE/TeslaRel410/emulator/src/src -./dosbox-x.exe -conf "$BR/$CONF.conf" > /dev/null 2>&1 & +# +# Keep the emulator's own stdout/stderr. The game's stdout comes back over +# COM3, but the DPMI host's exception report and any device-side complaint go +# to the emulator, and those were being thrown away. +# +./dosbox-x.exe -conf "$BR/$CONF.conf" > "$BR/dosboxlog.txt" 2>&1 & echo "launched $CONF (tag=$TAG) -- pid $!" echo "watch: tail -f $BR/podlog.txt" diff --git a/restoration/source410/MUNGA/APP.CPP b/restoration/source410/MUNGA/APP.CPP index 4a2f0f54..b8ce5386 100644 --- a/restoration/source410/MUNGA/APP.CPP +++ b/restoration/source410/MUNGA/APP.CPP @@ -1,1861 +1,1952 @@ -# 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; - -//############################################################################# -//########################### 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) - << " events=" << eventQueue->GetEventCount() - << " ticks=" << (long)Now().ticks - << "\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; +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 diff --git a/restoration/source410/MUNGA/APP.HPP b/restoration/source410/MUNGA/APP.HPP index a9707edc..05f83726 100644 --- a/restoration/source410/MUNGA/APP.HPP +++ b/restoration/source410/MUNGA/APP.HPP @@ -575,6 +575,17 @@ inline void ) { Check(this); + if (priority == MinEventPriority && message != NULL) + { + Check(message); + if ( + (int)message->messageID >= 0 && + (int)message->messageID < POST_TALLY_IDS + ) + { + postTally[(int)message->messageID]++; + } + } eventQueue->Post(priority,target,message,when); } @@ -677,6 +688,23 @@ extern long extern long eventPumpHits; +// +// POST TALLY, by message ID, for priority 0 only. +// +// Measured: priority 0 is never empty, nothing above it is busy, and +// PeekAtNextEvent always reports a ready event while the pump drains ~143 per +// simulated second. That is a REFILL loop -- something re-posts priority-0 +// work as fast as it is consumed -- and the counters are identical to the byte +// across runs, so it is deterministic rather than a race. +// +// Only the interest manager posts at priority 0 (it maps every renderer event +// there while the application is not RunningMission), so naming the message +// names the flooder. +// +#define POST_TALLY_IDS 64 +extern long + postTally[POST_TALLY_IDS]; + inline Logical Application::ProcessOneEvent(int min_priority) { diff --git a/restoration/source410/RENDER-ROADMAP.NOTES.md b/restoration/source410/RENDER-ROADMAP.NOTES.md index 27d01481..bdac314e 100644 --- a/restoration/source410/RENDER-ROADMAP.NOTES.md +++ b/restoration/source410/RENDER-ROADMAP.NOTES.md @@ -908,3 +908,105 @@ BTL4REC.EXE, exports the host-side VPX board env (without which the run dies at the iserver handshake, not merely unrendered), and archives each run's log -- marking the file -CRASH when it faulted. The only preserved dump before this was an accident, on a rig where every run costs four minutes. + +-------------------------------------------------------------------------------- +THE LOAD GATE: WHAT THE EVENT-PUMP COUNTERS SHOWED +-------------------------------------------------------------------------------- +Two runs of the instrumented build, both ending in the same fault: + + [stack] ... over 1001 frames ticks=24729 pump=352/1499 + [stack] ... over 2002 frames ticks=41586 pump=495/2643 + [launch] state=2 minPriorityEmpty=0 ... (31 reposts, then the fault) + +Reading those numbers rules out most of what "the queue never drains" could +mean: + + * The pump RUNS. 1144 ProcessOneEvent calls over 1001 frames is 1.14 per + frame -- one from the HighEventPriority check at the top of + ExecuteBackgroundTask, plus one every seventh frame from + BackgroundTasks::Execute, which round-robins ONE task per call across our + seven tasks. That 1-in-7 is exactly the rotation, so the early-return path + is NOT starving the background list. + + * The pump FINDS WORK. 143 hits per 1001 frames is ~1 per 7 -- essentially + every ProcessEventTask call succeeds, while the HighEventPriority calls + find nothing. So events are being processed continuously, at about 8 per + real second, and priority 0 still never empties after 30+ seconds. + + * events=0 in the trace is a red herring: ++eventCount lives inside + #if defined(TRACE_EVENT_COUNT), which this build does not define, so + GetEventCount() reads 0 no matter what is queued. The probe now prints + per-priority occupancy instead, which needs no counter. + +That leaves two candidates, and they want opposite fixes: + + 1. VOLUME. There really are hundreds of priority-0 renderer events and we + drain them at 8/sec, so the load simply has not finished when the fault + arrives. Our MakeEntityRenderables loads 19 .bgf objects per mech through + the EMULATED board, which is not a cheap operation. + 2. DEADLOCK. Priority 0's head is a TIMED event whose alarm never comes due. + PeekAtNextEvent only returns a timed event when alarmTime <= Now(), but + IsPriorityEmpty counts it regardless -- so such an event is invisible to + the pump and permanently visible to the gate. + +The [queues] probe prints every priority's occupancy each second, which +separates them: only p0 busy means the deadlock, higher priorities busy means +we are just watching a long load. + +RULED OUT ALONG THE WAY. The queue is allocated correctly +(GeneralEventQueue::Make does new GeneralEventQueue[5], EVENT_PRIORITIES_COUNT +is 5, MaxEventPriority is 4, so this[priority] indexing is in range). +Application::Post passes its priority through untouched. The renderer's +NotifyOfNewInterestingEntity handler consumes its message without re-posting, +so there is no refill loop from that path. + +A SEPARATE THREAD WORTH PULLING. The emulator log -- newly captured by +podrun.sh -- shows a steady trickle of serial1 RX OVERRUN errors on the RIO +named pipe, and ControlsInstanceEventOf posts at HighEventPriority +UNCONDITIONALLY (CONTROLS.HPP:250), with no application-state guard. Our log +also carries "RIO never came back from test mode!" where the shipped binary +does not. pod_render_norio.conf disables serial1 and changes nothing else, so +a launch there against a hang here isolates the RIO in one run. + +-------------------------------------------------------------------------------- +IT IS A REFILL LOOP, AND IT IS DETERMINISTIC +-------------------------------------------------------------------------------- +Per-priority occupancy, steady state, every second until the fault: + + [queues] p0=BUSY p1=- p2=BUSY p3=- p4=- nextReady=1 + + * p3 and p4 EMPTY kills the starvation theory outright. Nothing above + priority 0 is competing, so the pump is free to serve it. + * p2 busy is this handler's own once-a-second repost. Expected. + * nextReady=1 means PeekAtNextEvent always has a READY event to hand out -- + so priority 0 is not holding a timed event that never comes due either. + That kills the deadlock theory. + +What is left is the only remaining shape: priority 0 is being REFILLED as fast +as the pump drains it, at ~143 events per simulated second. + +And it is deterministic. Two runs of the same binary reported pump=352/1499 at +frame 1001 and 495/2643 at frame 2002 -- identical to the byte. So this was +never a race, and "crashes about half the time" was always an artifact of +comparing runs that differed in binary or conf, not in luck. + +Only InterestManager::PostRendererEvent posts at priority 0 -- it maps every +renderer event there while the application is not RunningMission -- so naming +the message names the flooder. Application::Post now tallies priority-0 posts +by message ID and the [posts] line reports the busiest three. The IDs to +expect, from RENDERER.HPP against Receiver::NextMessageID = 3: + + 3 NotifyOfNewInterestingEntity 6 StopAllEntityEffects + 4 NotifyOfBecomingUninteresting 7 StartEntityAlarm + 5 StartEntityEffect 8 StopEntityAlarm + +3 and 4 climbing together would mean entities are oscillating in and out of the +interest set; 3 alone means something re-adds them; 5 or 7 would point at +effects or alarms instead. + +The controlled A/B to run alongside it: pod_render_noskl.conf now carries the +same probes, so the SAME instrumented binary can be run with the skeleton walk +on and off. Earlier walk-off runs reached "Turning Plasma Score Display On", +which is the only configuration of ours known to launch -- if walk-off also +stops the refill, the walk is implicated in the hang even though it is NOT the +cause of the fault signature (which predates it).