Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/commit-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
tools/validation/openq4_validate.py \
tools/validation/stock_asset_baseline.py \
tools/tests/renderer_validation_matrix.py \
tools/tests/aas_settings_contract.py \
tools/tests/arena_campaign.py \
tools/tests/async_drop_client_contract.py \
tools/tests/campaign_split_state_transition.py \
Expand Down Expand Up @@ -240,6 +241,7 @@ jobs:
bash -n tools/validation/validate_push.sh
bash -n tools/validation/validate_pr.sh
python tools/validation/audit_source_provenance.py --check
python tools/tests/aas_settings_contract.py
python tools/tests/arena_campaign.py
python tools/tests/async_drop_client_contract.py
python tools/tests/campaign_split_state_transition.py
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/push-verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
tools/validation/openq4_validate.py \
tools/validation/stock_asset_baseline.py \
tools/tests/renderer_validation_matrix.py \
tools/tests/aas_settings_contract.py \
tools/tests/arena_campaign.py \
tools/tests/async_drop_client_contract.py \
tools/tests/campaign_split_state_transition.py \
Expand Down Expand Up @@ -240,6 +241,7 @@ jobs:
bash -n tools/validation/validate_push.sh
bash -n tools/validation/validate_pr.sh
python tools/validation/audit_source_provenance.py --check
python tools/tests/aas_settings_contract.py
python tools/tests/arena_campaign.py
python tools/tests/async_drop_client_contract.py
python tools/tests/campaign_split_state_transition.py
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/release-completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ is `docs/dev/macos-moltenvk-decision.md`.
- [x] Ragdoll activation quality improved without changing the fixed 60 Hz simulation cadence: startup now keeps owner/world motion, handles initial penetrations more cleanly, and preserves slightly richer contact support for grounded corpses.
- [x] Rigid-body physics timing-safe quality pass applied without changing the fixed simulation cadence: angular velocity handoff now respects world inertia, water is handled as drag instead of a one-time collision-state hack, rigid-body contacts keep richer deduplicated support points, and impacts now preserve time-of-impact momentum while consuming a small bounded amount of leftover fixed-step time.
- [x] Script compiler x64 pointer-temp parity fix ported from OpenD3: right-associative indirect-expression retagging now guards 4-byte object-ref temp vs 8-byte pointer temp storage mismatch by allocating pointer-sized result defs when needed, preventing trigger/door script chain corruption.
- [x] Retail AAS placeholder parity restored: stock dummy `.aas` files now load/discard like retail instead of warning-spamming and failing stock map init, and AAS tactical data is cleared correctly between loads.
- [x] Retail and rebuilt AAS compatibility is restored: stock dummy `.aas` files load/discard like retail instead of warning-spamming and failing stock map init, AAS tactical data is cleared correctly between loads, and the AAS 1.08 settings reader now accepts the `generateAllFaces` key emitted by its own writer. The face/tactical builder flags are initialized and round-trip their selected values instead of being hardcoded on output. Issue #137's supplied `airdefense1` archive (`CED6E9D92E67D8FF71E1F2E93B68D7F6DD7C961D7E759E90E3AA243408AED187`) exposed the mismatch consistently in `.aas32`, `.aas48`, `.aas96`, `.aas128`, and `.aas250`. The focused source/CI contract passes, and a current 1280x720 windowed OpenGL run loaded the archive's exact `.map`, `.proc`, `.cm`, and all five AAS files from the isolated loose path, initialized gameplay, wrote the engine capture (`E62BD7DE995E16D31890CE9E44625FB829B20541DD7B7CB31729251214548F38`), and exited without an invalid-token, stale-AAS, navigation-load, engine error, or fatal diagnostic; the retained log is `74E5C6775C75CEA391E9E6D9E16443A026EFFAFDAC9291072F2966F50A73B196` under `.tmp/issue137-exact-map/`.
- [x] Bloom stability and quality improved: live `r_bloom`/`r_bloom*` changes now use the offscreen scene-target path immediately, scratch render targets rebuild their FBO attachments safely after runtime reallocations, map handoffs no longer rely on the fragile back-buffer bloom path, and bright-pass extraction now keeps only highlight energy so broad lights stop producing solid white bloom discs.
- [x] Manual release packaging builds are healthy again: the engine restored the shared `idCommon` timing/error/tool accessors expected by the SP/MP game libs, fixing the cross-platform release-build break that stopped the `v0.1.011` workflow before artifacts were produced.
- [x] Retail trigger compatibility improved for `game/process1 second`: the intro lift now uses retail-style precise clipping for hurt triggers while keeping the x64 bounds fallback for non-damaging player triggers, preventing a stock shaft kill volume from falsely killing the player during the downward ride.
Expand Down
1 change: 1 addition & 0 deletions docs/dev/releases/v0.12.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
- Avoided constructing bake/debug-only fallback light-grid layouts during ordinary play on maps without baked grids; bake commands still generate the required layout on demand.
- Corrected OpenGL and Vulkan shadow-map caster coverage with topology-aware face culling, near-shell depth for sealed geometry, two-sided depth for open geometry, bounded huge-light point bias, tighter 9-tap balanced filters, consistent PCSS blocker sampling, exact world/pass/storage cache identity, bind-time atlas and point-cube provenance, live point-format and projected-atlas generation validation, newest-compatible stale reuse that rejects moved or rescaled point lights, dynamic and partial-map stencil supplements, translucent-only GLOBAL ownership, hardware atlas bounds, and safely nested OpenGL GPU timing.
- Added fail-after-primary compatibility fallbacks for the three stock brown-fluid images and large water-splash sample omitted from the retail PK4s, while preserving loose-file and mod overrides; generated TrueType atlas materials now use their uploaded intrinsic image identity from first parse and reinstall the full stage after renderer restart; generated wide loading backgrounds stage through a short CSPRNG-named root path before atomic publication; generated image caches retain their legacy identity first and use a deterministic compact identity only when a long save path cannot be opened, with every header, mip record, and payload write checked; expected rigid-body rest and inertia recovery remains developer-visible but no longer enters the warning stream.
- Rebuilt Quake 4 AAS 1.08 navigation files now load without `generateAllFaces` token warnings. The loader accepts both generated face/tactical-feature settings, initializes them deterministically, and the writer preserves their selected values.
- Kept view-model client entities, attached animated models, and the lightning gun beam on the exact rendered skeletal pose.
- Prevented remote-client prediction replays from advancing projectile visuals twice or replacing the viewweapon's authoritative interpolation endpoint, and restored impact/fizzle visuals when a short-lived projectile is first received in a terminal state.
- Fixed weapon swaps that could inherit firing state or leave stale effects behind.
Expand Down
12 changes: 8 additions & 4 deletions src/aas/AASFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ idAASSettings::idAASSettings( void ) {
boundingBoxes[0] = idBounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 72 ) );
usePatches = false;
// jmarshall - aas 1.08
generateAllFaces = false;
generateTacticalFeatures = false;
// jmarshall end
writeBrushMap = false;
Expand Down Expand Up @@ -296,8 +297,11 @@ bool idAASSettings::FromParser( idLexer &src ) {
if ( !ParseBool( src, usePatches ) ) { return false; }
}
// jmarshall: AAS 1.08
else if (token == "generateTacticalFeatures") {
if (!ParseBool(src, generateTacticalFeatures)) { return false; }
else if ( token == "generateAllFaces" ) {
if ( !ParseBool( src, generateAllFaces ) ) { return false; }
}
else if ( token == "generateTacticalFeatures" ) {
if ( !ParseBool( src, generateTacticalFeatures ) ) { return false; }
}
// jmarshall end
else if ( token == "writeBrushMap" ) {
Expand Down Expand Up @@ -503,8 +507,8 @@ bool idAASSettings::WriteToFile( idFile *fp ) const {
fp->WriteFloatString( "\tallowSwimReachabilities = %d\n", allowSwimReachabilities );
fp->WriteFloatString( "\tallowFlyReachabilities = %d\n", allowFlyReachabilities );
// jmarshall - AAS 1.08
fp->WriteFloatString("\tgenerateAllFaces = 0\n");
fp->WriteFloatString("\tgenerateTacticalFeatures = 0\n");
fp->WriteFloatString( "\tgenerateAllFaces = %d\n", generateAllFaces );
fp->WriteFloatString( "\tgenerateTacticalFeatures = %d\n", generateTacticalFeatures );
// jmarshall end
fp->WriteFloatString( "\tfileExtension = \"%s\"\n", fileExtension.c_str() );
fp->WriteFloatString( "\tgravity = (%f %f %f)\n", gravity.x, gravity.y, gravity.z );
Expand Down
83 changes: 83 additions & 0 deletions tools/tests/aas_settings_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Guard the Quake 4 AAS 1.08 settings parser/writer round trip."""

from __future__ import annotations

from pathlib import Path


ROOT = Path(__file__).resolve().parents[2]
TEST_PATH = "tools/tests/aas_settings_contract.py"


def read(relative_path: str) -> str:
return (ROOT / relative_path).read_bytes().decode("windows-1252")


def function_body(source: str, signature: str, context: str) -> str:
start = source.find(signature)
if start < 0:
raise AssertionError(f"Missing {signature!r} in {context}")
opening = source.find("{", start + len(signature))
if opening < 0:
raise AssertionError(f"Missing body for {signature!r} in {context}")

depth = 0
for index in range(opening, len(source)):
if source[index] == "{":
depth += 1
elif source[index] == "}":
depth -= 1
if depth == 0:
return source[opening + 1 : index]
raise AssertionError(f"Unterminated body for {signature!r} in {context}")


def require(source: str, token: str, context: str) -> None:
if token not in source:
raise AssertionError(f"Missing {token!r} in {context}")


def validate_aas_settings_round_trip() -> None:
source = read("src/aas/AASFile.cpp")

constructor = function_body(source, "idAASSettings::idAASSettings( void )", "AAS settings constructor")
require(constructor, "generateAllFaces = false;", "AAS settings constructor")
require(constructor, "generateTacticalFeatures = false;", "AAS settings constructor")

parser = function_body(source, "bool idAASSettings::FromParser( idLexer &src )", "AAS settings parser")
require(parser, 'token == "generateAllFaces"', "AAS 1.08 settings parser")
require(parser, "ParseBool( src, generateAllFaces )", "AAS 1.08 settings parser")
require(parser, 'token == "generateTacticalFeatures"', "AAS 1.08 settings parser")
require(parser, "ParseBool( src, generateTacticalFeatures )", "AAS 1.08 settings parser")

writer = function_body(source, "bool idAASSettings::WriteToFile( idFile *fp ) const", "AAS settings writer")
for setting in ("generateAllFaces", "generateTacticalFeatures"):
require(writer, f'"\\t{setting} = %d\\n", {setting}', "AAS 1.08 settings writer")
if f'"\\t{setting} = 0\\n"' in writer:
raise AssertionError(f"AAS settings writer still hardcodes {setting}")


def validate_ci_wiring() -> None:
validator = read("tools/validation/openq4_validate.py")
if validator.count("aas_settings_contract.py") != 1:
raise AssertionError("Local validation must register the AAS settings contract exactly once")

for workflow_path in (
".github/workflows/commit-validation.yml",
".github/workflows/push-verification.yml",
):
workflow = read(workflow_path)
if workflow.count(TEST_PATH) != 2:
raise AssertionError(f"{workflow_path} must compile and run {TEST_PATH}")
require(workflow, f"python {TEST_PATH}", workflow_path)


def main() -> None:
validate_aas_settings_round_trip()
validate_ci_wiring()
print("aas_settings_contract: ok")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions tools/validation/openq4_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ def validate_game_libs_repo_path(game_libs_repo: Path) -> Path:

def run_python_tests(args: argparse.Namespace, root: Path, env: dict[str, str]) -> None:
tests = [
root / "tools" / "tests" / "aas_settings_contract.py",
root / "tools" / "tests" / "arena_campaign.py",
root / "tools" / "tests" / "async_drop_client_contract.py",
root / "tools" / "tests" / "campaign_split_state_transition.py",
Expand Down
Loading