feat: integrate GeneralsX Beta 13#17
Open
Dbochman wants to merge 108 commits into
Open
Conversation
… buffer limit (#2783)
Remove all [GX-ISSUE144] tagged stderr instrumentation added during issue #144 investigation. 55 log statements removed across 8 files. Where removing a log left an empty if/else block, the entire block was removed. Unused log_buffer variables and single-use debug flags (_fetchLogged, _constructDrawLogged) were also cleaned up. Files changed: - W3DGameFont.cpp (LoadUnicodeFallbackFont, loadFontData) - InGameUI.cpp (init) - GameWindowManager.cpp (winCreate, assignDefaultGadgetLook) - ControlBarPopupDescription.cpp (show/repopulate/populateBuildTooltipLayout) - ControlBarUnderConstruction.cpp (updateConstructionTextDisplay) - Drawable.cpp (ResolveDrawableCaptionFont, ctor, drawConstructPercent, setCaptionText) - render2dsentence.cpp (Get_Char_Data, Init_FreeType, Store_Freetype_Char) - GlobalLanguage.cpp (init) Builds verified: GeneralsXZH + GeneralsX (macos-vulkan)
…oManager::killAudioEventImmediately() (#2784)
… BitFlags.h (#2759)
…n Pathfinder::adjustDestination() (#2790)
…eckForAdjust() (#2791)
…159) * feat(audio): add MiniAudio backend as alternative to OpenAL+FFmpeg Implement miniaudio (miniaud.io) as a single-header audio backend that replaces both OpenAL and FFmpeg for audio decoding/playback. FFmpeg is still used for video frame decoding (FFmpegVideoPlayer). Key changes: - MiniAudioManager: full AudioManager implementation using miniaudio engine with custom VFS for game file system - MiniAudioStream: video audio streaming support - cmake/miniaudio.cmake: FetchContent dependency (single-header) - SAGE_USE_MINIAUDIO build flag (mutually exclusive with SAGE_USE_OPENAL) - Updated MilesStub.h guards to work with MiniAudio backend - linux64-deploy and macos-vulkan presets now default to MiniAudio - linux64-openal and macos-openal presets added as legacy fallback Based on implementation by Stephan Vedder (feliwir): https://github.com/feliwir/CnC_Generals_Zero_Hour Copyright 2026 Stephan Vedder (MiniAudioManager derived code) Copyright 2025 Electronic Arts Inc. * fix(audio): propagate SAGE_USE_MINIAUDIO via core_config to all targets The SAGE_USE_MINIAUDIO define was not reaching WW3D2 and other Core libraries because it was only added to miniaudio_lib target. Add it to core_config (same as SAGE_USE_OPENAL) so it propagates globally. * fix(audio): resolve compilation errors in MiniAudio implementation - Move MiniAudioStream.h to Include/ (matches OpenALAudioStream pattern) - Fix UnsignedIntPtr -> UnsignedInt in MiniAudioManager signatures - Fix BitTest -> BitIsSet macro name - Fix getUninterruptable -> getUninterruptible method name - Fix getFileLengthMS to use ma_decoder_init_vfs (API compat) - Disable CoreAudio backend on macOS to avoid Byte typedef conflict between MacTypes.h (UInt8) and BaseTypeCore.h (char) * fix(audio): resolve Byte typedef conflict with CoreAudio on macOS Match game's Byte typedef to macOS MacTypes.h (unsigned char instead of char) on Apple platforms to avoid redefinition error when miniaudio includes CoreAudio headers. Also add AUDIO log line on backend load showing version, device, sample rate, channels and playback device count. * fix(audio): remove m_engine.channels/sampleRate from log line ma_engine struct does not expose channels/sampleRate members in this miniaudio version. Log only version, device name, and playback count. * fix(audio): implement MiniAudioStream video audio playback Replace placeholder MiniAudioStream with working implementation: - Accumulate decoded audio in buffer via bufferData() - On play(), decode from memory via ma_decoder_init_memory() - Create ma_sound from decoder for actual audio output Also add diagnostic logging to playAudioEvent for troubleshooting. * fix(audio): remove orphaned ifdef causing unterminated conditional directive * debug(audio): add instrumentation to pinpoint hang location in playAudioEvent * fix(audio): use FFmpeg for audio decoding instead of miniaudio decoders miniaudio's built-in MP3 decoder hangs when streaming through the VFS. Use FFmpeg (already linked for video) to decode all audio files into PCM, then feed to miniaudio via ma_decoder_init_memory. This matches the OpenAL implementation pattern (OpenALAudioCache) and guarantees compatibility with all game audio formats (WAV, MP3, etc). * fix(audio): use ma_audio_buffer instead of ma_decoder for PCM data ma_sound_init_from_data_source does NOT copy data - it holds a reference. Using ma_decoder + uninit destroyed the data source before playback. Fix: use ma_audio_buffer_init which copies PCM data into internal storage. The sound then owns its own copy and plays correctly after decoder cleanup. Also store audioBuffer pointer in PlayingAudio for proper cleanup. * fix(audio): use ma_audio_buffer_init_copy to properly copy PCM data ma_audio_buffer_init() does NOT copy data - it only references external memory. When the pcmData vector goes out of scope, the audio callback reads from freed memory causing SIGSEGV on the CoreAudio IOThread. Fix: use ma_audio_buffer_init_copy() which allocates and copies data into internal storage. Also set sampleRate on the buffer ref. Also: remove debug fprintf statements from playAudioEvent. * fix(audio): rewrite MiniAudioStream with proper video audio playback Replace placeholder MiniAudioStream with working implementation: - Use ma_audio_buffer_init_copy to properly copy PCM data - rebuildSound() recreates sound when new audio data arrives - update() detects new data and triggers rebuild - Proper cleanup of sound and audio buffer in reset() This fixes video audio being silent (decoder data was freed before playback completed). * debug(audio): add diagnostic logging to playAudioEvent Add fprintf logs at each step of sound creation: - FFmpeg decode result - Audio buffer creation result - Sound init result - Sound start result with volume value This will help diagnose why sounds are created but not heard. * fix(audio): fix video audio format detection after float→s16 conversion bytesPerSample was from the original format (e.g. 4 for float32) but after float→s16 conversion the actual data is 16-bit. This caused miniaudio to interpret PCM16 data as float32, producing extreme volume and distortion. Use outputBitsPerSample which tracks the actual output format. * fix(audio): defer video sound creation until data accumulates play() now only sets a flag - actual sound creation is deferred to update() which waits until enough audio data has accumulated (buffer stops growing or ~30 frames elapsed). This prevents playing only the first second of audio before the rest has been decoded. * fix(audio): call MiniAudioStream::update() from FFmpegVideoStream The video player's update() was a no-op for miniaudio - MiniAudioStream:: update() was never called, so the deferred sound creation never happened. Also fix static variable issue in update() and simplify data detection. * refactor(audio): comprehensive MiniAudio implementation review MiniAudioManager.cpp: - Fix stopAllAudioImmediately: erase before release to avoid iterator invalidation when releasing NULL elements - Fix processFadingList: same iterator fix - Fix processPlayingList: now respects m_requestStop flag and updates 3D position every frame for PAT_3DSample - Fix releasePlayingAudio: only notify SoundManager for PAT_Sample and PAT_3DSample, not PAT_INVALID or PAT_Stream - Fix closeDevice: stop all audio first to prevent use-after-free - Fix playAudioEvent: always delete ffmpegFile after decode (memory leak) - Fix playAudioEvent: guard against ma_format_unknown (unsupported bps) - Fix playAudioEvent: assign audio->m_sound/m_audioBuffer before switch to avoid null if early return inside switch (indentation/scope bug) - Fix playAudioEvent: guard null pos before calling set_position - Remove all fprintf debug spam from production code paths - getHandleForBink: provide engine to MiniAudioStream via setEngine() so stream doesn't need to call TheAudio->getDevice() each play() MiniAudioStream.h/.cpp: - Add setEngine() to receive engine reference from MiniAudioManager - Add m_soundCreated flag to prevent double-init - Add m_stableFrameCount: wait 2 frames of no new data before creating sound (more robust than single-frame check) - Rename rebuildSound() to createSound() - called only once per play() - Remove TheAudio dependency from stream (engine now injected) - Cleaner reset() that preserves m_engine (externally owned) * fix(audio-miniaudio): implement thread-safe custom data source for video streaming Refactor MiniAudioStream to use a ma_data_source instead of accumulating all PCM data before playing. This resolves video audio silence on both base Generals and Zero Hour because video audio is decoded frame-by-frame and the buffer is constantly growing. Added std::mutex for thread-safe FIFO buffer access, and automatic memory reclamation after 256 KB has been read. Declared the SAGE_USE_MINIAUDIO option in config-build.cmake. Co-authored-by: Feliwir <stephan.vedder@gmail.com>
Merge upstream changes including Pathfinder optimizations, Zero Hour message translators merge, and sorting renderer 16-bit index limit fix. Reconcile %I64X vs %llX format specifiers in BitFlags.h to prevent compiler warnings on macOS/Linux under GCC/Clang.
…h Alternate Mouse (#2798) Toggle with UseRightMouseScrollWithAlternateMouse=yes/no in Options.ini
…d required code (#2765)
Merge upstream changes including CommandXlat and AcademyStats unification into Core, and implementing right mouse scroll toggle for alternate mouse. Wrap Zero Hour-specific CommandXlat cases in base game with RTS_ZEROHOUR checks to prevent compilation errors.
Merged and reorganized build/deploy/packaging commands under 'Build Commands' and removed duplicate 'Important Commands' section
…lict (#164) * fix(input): map Command key to Control on macOS and resolve SagePatch hotkey collision * docs: update build commands and context loading hints in AGENTS.md
…165) * fix(shroud): resolve black terrain fog of war bug in base game Backport Zero Hour's static map-wide shroud texture behavior to the Generals base game. This prevents DXVK dynamic offset matrix sampling out-of-bounds under Vulkan/MoltenVK. Also make SDL3GameEngine audio inclusions mutually exclusive to avoid type redefinition conflicts when both OpenAL and MiniAudio options are present in the cache. Fixes #88 * build(audio): prioritize miniaudio backend over openal Resolve redefinition conflicts in FFmpegVideoPlayer when both SAGE_USE_MINIAUDIO and SAGE_USE_OPENAL are defined in the CMake cache. This ensures the primary MiniAudio backend takes precedence. * docs: update June 2026 dev blog with video player prioritization details * fix(shadow): skip volumetric shadows for skin meshes in base game Backport Zero Hour check to W3DVolumetricShadow::initFromHLOD to skip volumetric shadow generation for skin meshes. Infantries are skin meshes, and generating stencil shadow volumes on the CPU for animated skeletal models is incorrect and causes infantry to render as solid black silhouettes under MoltenVK/DXVK. * docs: document base game infantry black silhouette rendering fix * fix(graphics): skip volumetric shadows for HLODs with skinned meshes Skeletal animated hierarchies (like infantry) cannot accurately project CPU shadow volumes. In the base game under MoltenVK/Vulkan/DXVK, attempting to cast volumetric shadows on bone-attached rigid sub-meshes of a skinned model (like the GLA Worker's torso/vest) leaves the stencil/GPU state broken, rendering them as solid black silhouettes. Checking the entire HLOD for skinned meshes and skipping volumetric shadow geometry generation for the whole hierarchy resolves this rendering bug, allowing them to fallback to projected/decal shadows exactly as in Zero Hour. Fixes #88 * fix(input): fix mouse edge scrolling and right-click scroll cursor direction * docs: add infantry shadow fix summary report * fix(generals-base): remove double-present call causing uncapped FPS TheDisplay->step() and TheDisplay->draw() were being called redundantly in GameEngine::execute() in addition to the call already dispatched via TheGameClient->update(). This caused a double-presentation per frame loop iteration, resulting in: - Incorrect FPS calculations (e.g. 170 FPS on 170Hz monitor) - Game logic and animations running at twice normal speed Removing the extra draw call aligns the base game main loop with Zero Hour (GeneralsMD), which did not have this duplicate call. Correct 30 FPS cap and game speed are now restored. // GeneralsX @BugFix MrMeeseeks 17/06/2026 * fix(zh-shadow): skip volumetric shadow for standalone skinned meshes in initFromMesh initFromHLOD already skipped skinned sub-meshes (CNC3/gth comment at line 651). initFromMesh had no equivalent guard, meaning a standalone MeshClass with the SKIN flag could still enter the CPU shadow volume pipeline, potentially corrupting the stencil state under DXVK/Vulkan. Add SKIN flag check to initFromMesh, mirroring the existing HLOD behavior, matching the fix already applied to the Generals base game in W3DVolumetricShadow.cpp. // GeneralsX @BugFix Mr. Meeseeks 17/06/2026 * docs: update dev blog and infantry shadow summary with ZH audit results - Record ZH initFromMesh SKIN guard fix in 2026-06-DIARY.md - Document diffuse=0 audit findings (BaseHeightMap is intentional) - Update INFANTRY_SHADOW_FIX_SUMMARY.md with completed follow-up actions, current clean state, and updated smoke test checklist * GeneralsX @BugFix Meeseeks 17/06/2026 Fix shadowType bitmask override bug in W3DProjectedShadow.cpp causing black square building shadows and black infantry in Generals base game. * fix(shadows): use bitwise type matching for combined shadow flags The building shadow alpha decal combined with the SHADOW_DECAL flag produced bitmask values (e.g. 0x21) that failed exact enum switch/equality matching in flushDecals, updateShadowNumbers, renderShadows, and updateTexture. Replace exact equality checks with bitwise AND checks to correctly route and render alpha-blended/additive-blended decals and projection updates. * fix(shadows): implement infantry light clamping and decal shadow fixes - Clamps scaled ambient and diffuse colors for infantry to 1.0f in W3DScene.cpp - Fixes initialization of lastShadowDecalTexture and lastShadowType in W3DProjectedShadow.cpp loops - Flag animated caster shadows as dynamic projected shadows in addShadow - Support SHADOW_ALPHA_DECAL and SHADOW_ADDITIVE_DECAL type checks in addShadow Closes #88
Integrate fog of war, black infantry, and building shadows fixes from commit 91d8e27 into thesuperhackers sync branch. Resolved LookAtXlat.cpp conflict by keeping upstream RMB scroll option (PR #2798) over older code without this feature.
…true Default m_useRightMouseScrollWithAlternateMouse to TRUE in both Generals and Zero Hour to allow right-click drag scrolling under alternate controls by default.
…er by 90% (#2761)
…:8 to allow loading all textures with modern gpu (#2788)
…TextureLoadTaskClass::Begin_Uncompressed_Load() (#2789)
…when unobstructed (#2296)
…rk commands in chronological order when overflowing (#2736)
…CRC intervals (#2796)
chore: sync with thesuperhackers/main upstream
…eHeightMapRenderObjClass::Cast_Ray (#2836)
…e reduction (2) (#2814)
…#2838) Reduces busy frame performance cost by 8 % and fixes ground aligned particles clipping over billboard particles
Fixed a bug in OpenALAudioManager and MiniAudioManager where the engines would continuously allocate new hardware sources without terminating lower priority sounds, eventually exhausting all available physical sources. Also corrected the sample count logic in MiniAudioManager to only evaluate 2D and 3D sample types rather than all playing sounds.
- Set OpenAL as the default audio backend and MiniAudio as WIP. - Establish Generals base game as stable and require backports. - Rename DEV_BLOG to WORKLOG to better reflect automated nature. - Remove Worklog link from README.md project goals.
* Fix macOS fullscreen Spaces and disable manual window resize * Pass nullptr to SDL_SetWindowFullscreenMode to request Desktop Fullscreen (Spaces) instead of Exclusive mode, allowing trackpad gestures to work. * Remove SDL_WINDOW_RESIZABLE to prevent live window resizing (which distorts the DXVK swapchain) and to disable the native macOS green maximize button in windowed mode. * Restore SDL_WINDOW_RESIZABLE * Allows live window resizing and native maximize/minimize buttons to work as expected in windowed mode (-win). * Fullscreen Spaces support remains active due to the nullptr argument in W3DDisplay.
…speed (#2840) Set GameWindowTransitionSpeedMultiplier=1..1000 in Options.ini to customize the speed
* feat(math): integrate deterministic GameMath Replaces standard CRT trigonometric functions with deterministic fdlibm equivalents from GameMath via FetchContent. Ensures multiplayer cross-platform replay determinism. Resolves cross-platform mismatch disconnects. * Enforce cross-platform math determinism (GameMath) & Fix Linux build * Fix remaining non-deterministic native math calls in Core libraries This commit addresses remaining naked CRT calls (ceilf, floorf, sqrt) in BaseType.h, Point.h, INI.cpp, and AIPathfind.cpp that were previously missed, enforcing determinism via WWMath and fast_float wrappers to prevent cross-platform replay desyncs. * docs(devblog): sanitize user path in dev blog * fix(audio): resolve FPU precision state leaks and disable retail macros - Implement ScopedFPUGuard to restore FPU precision mode upon scope exit in MiniAudio and OpenAL audio managers. - Disable legacy RETAIL_COMPATIBLE_* macros (CRC, PATHFINDING, AIGroup, Networking, etc.) in GameDefines.h to avoid dynamic RNG/memory inconsistencies. * ci(replay-tests): fetch assets/replays from gitlab with caching Add GitLab cloning with GitHub fallback for replays. Implement branch matching for assets and replays repositories. Enable caching for the replay files repository to optimize CI runs. * chore(engine): enable retail compatibility modes and update resolution Enable retail compatibility macros (scripted camera, CRC, pathfinding, allocation, AI group) and set default display resolution to 1024x768. * disable all retail compatibility
WaterRenderObjClass::Render() dereferences m_skyBox whenever
TheGlobalData->m_drawSkyBox is set, but Create_Render_Obj("new_skybox")
can return null when the skybox asset is unavailable (e.g. the base
Generals archives are not found at init). On macOS this segfaults
(SIGSEGV in WaterRenderObjClass::Render via
Render_And_Clear_Static_Sort_Lists) as soon as the shell map or any
water-bearing map renders.
- Guard the m_skyBox use in Render()
- Early-return in replaceSkyboxTexture(), since
replaceAssetTexture() dereferences the render object unconditionally
- Add [TEX_MISSING] stderr logging in Apply_Missing_Texture()
(non-Windows only), as proposed in Session 44 notes - this is what
made the missing-asset root cause diagnosable
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… in SortingRenderer (#2854)
…gget::parseDebrisObjectNames (#2597)
Fix segfault in WaterRenderObjClass when skybox asset fails to load
… render frame rate (#2835)
…n (#189) * fix(network): correctly map PlayerIndex to SlotIndex in CRC validation A previous commit (14/06/2026) introduced a regression where TheNetwork->isPlayerConnected was incorrectly passed a PlayerIndex instead of a SlotIndex. This caused the loop to skip CRC validation for all players, allowing matches to desync silently without triggering the Mismatch UI. This fix resolves the slot index dynamically by matching the player's display name, restoring the intended behavior. * refactor(network): extract slot index lookup to PlayerList * fix(build): remove non-existent GameSlot.h include * fix(network): prevent null dereference of TheGameInfo in resolveSlotIndices * fix(network): resolve PR #2857 code review comments
… camera scrolling (#2847)
…to thesuperhackers-sync-07-10-2026
Sync upstream thesuperhackers/main (07-10-2026)
Merge the verified GeneralsX-Beta-13 tag while preserving the OpenAL-first macOS architecture and retail compatibility defaults. Harden replay asset caching, keep deterministic GameMath opt-in, fix audited slot, config, radar, and math regressions, and retain the existing Apple and iOS work. Validated with clean macOS ARM64 builds for Zero Hour and Generals plus a full-match playtest from the side-by-side runtime.
Dbochman
marked this pull request as ready for review
July 11, 2026 03:06
|
Beta13 has two significant issues: one inherited from the superhackers and another introduced by me. I’ve already written the fix and plan to release beta14 with it tomorrow. My recommendation is to skip beta13 and directly move to beta14. |
|
Note: I also reverted the default audio backend from Miniaudio to OpenAL on GeneralsX due to Miniaudio bugs. So, you don’t need to worry about Miniaudio for now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Integrates the exact
GeneralsX-Beta-13tag (d24b2aad40a485edfcde85a1de8b8aa60db49503) while preserving this fork's OpenAL-first architecture, retail-compatible gameplay/network defaults, Apple platform work, and documentation workflow.The integration keeps deterministic GameMath disabled in shipping presets until replay-corpus validation is complete and retains the existing macOS runtime as a rollback candidate.
What changed
Byte, and ceil/floor behavior.Bytedeclaration without changing the engine's signed retail type.d00070157d1e8cd911927eca7499aed29dda0118.SagePatch.inimigration.LAN86debug scaffolding while retaining activeDEBUG_LOGdiagnostics.Fable pre-PR review
The first read-only Fable review found no P0/P1 issues and identified three P2 and five P3 findings. All eight were addressed before publishing.
A second full review of amended merge commit
ece02827ef20bdfd5cbbd69eca25759b21841275verified the fixes and concluded that there are no actionable blockers for a draft PR.Validation
macos-vulkanrebuild linkedGeneralsXZHandGeneralsXafter 1,577 steps.macos-miniaudioconfigured with the pinned dependency and linked both games after 2,138 steps.actionlint, YAML parsing, patch whitespace checks, GameMath OFF/ON dispatch probes, and the pinned GameMath standalone build passed.Remaining validation and follow-ups
Impact
This brings the fork to the Beta 13 feature baseline while keeping the currently tested OpenAL/macOS path and retail compatibility as the production defaults.