Simplify content controls and add vehicle roles; record Workshop 0.0.144
continuous-integration/drone/push Build is passing

Discover installed mod vehicles and save independent garage, traffic, patrol and convoy assignments. Retire the superseded RHS 1.9 overrides. Include native menu, crew/driving and publication evidence.
This commit is contained in:
2026-11-06 22:06:38 +01:00
parent 518a1a5744
commit 460d9808e5
116 changed files with 14244 additions and 404 deletions
+25 -5
View File
@@ -1,6 +1,20 @@
# RHS 1.9 compatibility
# RHS 1.9 compatibility — retired
## Embedded in Escape
## Retired on 6 November 2026
The user confirmed the upstream fix; installed RHS Status Quo is now 0.17.5281.
The two embedded script replacements have been removed from Escape. The generator
now refuses to run, so it cannot silently restore old RHS scripts during a build.
Use updated RHS directly, without the standalone `E119F1C05A274B83` compatibility
addon. Escape still requires RHS itself. The independent Reaper/RHS character-slot
workaround is unchanged.
The published standalone listing has not been changed by this source edit.
The descriptions beside this file contain retirement wording for a future update.
The following sections preserve the old patch and publication history; their
preparation commands are retired and must not be used for current RHS.
## Historical: embedded in Escape
The same two replacements can live directly in Escape. Prepare them before
opening Workbench, testing, or packaging a fresh checkout:
@@ -29,7 +43,13 @@ the inputs inside Escape's PAK. See the [embedded test evidence](../../docs/evid
It was published inside Escape **0.0.141** after successful Workshop upload and
backend processing. Inherited dependency diagnostics remain.
## Standalone experiment
## Standalone addon
Published as [RHS 1.9 Compatibility](https://reforger.armaplatform.com/workshop/E119F1C05A274B83)
version **1.0.0**, public, Systems category, APL-ND, with the gear preview.
Upload and backend processing were confirmed; see the
[publication evidence](../../docs/evidence/rhs-standalone-published-2026-11-05/README.md).
Escape 0.0.141 already embeds these fixes and does not need this extra addon.
Separate addon for RHS Status Quo **0.16.5237** and Reforger **1.9.0.37**.
It uses same-path script replacement to fix declarations before compilation:
@@ -53,7 +73,7 @@ python3 compatibility/rhs-1.9/prepare.py \
For an isolated test, add dependency `E119F1C05A274B83` to the test copy of
Escape's `addon.gproj`, and expose the generated addon through its addon search
directory. Keep the original RHS dependency. The GUID is a local project identity,
not a published Workshop ID.
also the published Workshop ID since 5 November 2026.
Both changes pass native compilation of GameLib, Game and WorkbenchGame with
Escape's complete RHS/WCS/BMP/Parachute dependency graph on 1.9.0.37. The current
@@ -62,7 +82,7 @@ scenario prepares and spawns the player. The six-faction rescue fixture complete
crews and cargo capacity, two physical landings, wounded boarding, and loaded
airborne departure. This is local Workbench evidence, not multiplayer or general
vehicle-handling qualification. Both replaced scripts are byte-identical in the
native packed output. No Workshop publication has been performed.
native packed output. The standalone addon has now been published as 1.0.0.
The full logs also exposed a WCS BMP tire initialization failure. A separate
local WCS experiment is not included in the published mission. Other inherited
@@ -1,7 +1,2 @@
Includes an unofficial RHS Status Quo 0.16.5237 script compatibility patch for
Arma Reforger 1.9.0.37. The original RHS addon and content packs remain required;
no separate RHS compatibility addon is needed.
Original RHS scripts and content are by Red Hammer Studios. This mission and
its compatibility changes are not an official RHS release.
RHS terms: https://docs.rhsmods.org/rhs-status-quo-user-documentation/arma-reforger/rhs-status-quo/eula
Requires updated RHS Status Quo. The legacy RHS 1.9 script compatibility
overrides have been removed; no separate RHS compatibility addon is needed.
+5 -139
View File
@@ -1,146 +1,12 @@
#!/usr/bin/env python3
"""Generate a local RHS 0.16.5237 compatibility addon from installed scripts.
Only two pinned scripts are read; installed Workshop files are never modified.
The generated addon replaces scripts at their original paths before compilation.
"""
import argparse
import hashlib
import json
from pathlib import Path
import shutil
import struct
import sys
import zlib
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parents[1] / 'tools'))
from pak_inventory import Limits, parse_tree
CAS = 'Scripts/Game/Editor/Components/EditableEntity/RHS_CAS_EditableEntityComponent.c'
TIRE = 'Scripts/Game/Vehicle/RHS_DestructionTireComponent.c'
EXPECTED = {
CAS: '1b4c5c8d3078f60f617cab94457bd0d9c96a048ad05117353e67e22b9972af37',
TIRE: '9fc9fffa3e63a1115e174e51faa222504038e57790f95c57ef691da8745039c9',
}
def replace_once(text, old, new):
if text.count(old) != 1:
raise ValueError('Expected exactly one patch location: ' + old)
return text.replace(old, new)
def patched(path, raw):
if hashlib.sha256(raw).hexdigest() != EXPECTED[path]:
raise ValueError('RHS script changed; review compatibility before patching: ' + path)
text = raw.decode('utf-8').replace('\r\n', '\n')
if path == CAS:
text = replace_once(text, 'override void SetAuthor(int playerID)',
'override void SetAuthor(int playerID, bool isGM = false)')
text = replace_once(text, 'super.SetAuthor(playerID);\n\n\t\tRHS_CAS_',
'super.SetAuthor(playerID, isGM);\n\n\t\tRHS_CAS_')
elif path == TIRE:
text = replace_once(text, 'return m_iWheelIndex;', 'return GetWheelIndex();')
return text.encode('utf-8')
def read_scripts(folder):
project = folder / 'addon.gproj'
expected_project = '51fda0afde5ebcd4dc88c6c9451d960b67aceaee437f02f1cc80aef724e3779c'
if hashlib.sha256(project.read_bytes()).hexdigest() != expected_project:
raise ValueError('Expected the audited RHS Status Quo 0.16.5237 project')
found = {}
for pak in sorted(folder.glob('*.pak')):
with pak.open('rb') as stream:
header = stream.read(12)
if header[:4] != b'FORM' or header[8:] != b'PAC1':
raise ValueError('Unsupported archive: ' + str(pak))
offset, chunks = 12, {}
for expected in (b'HEAD', b'DATA', b'FILE'):
stream.seek(offset)
tag, size = struct.unpack('>4sI', stream.read(8))
if tag != expected or offset + 8 + size > pak.stat().st_size:
raise ValueError('Invalid archive chunks')
chunks[tag] = (offset + 8, size)
offset += 8 + size
start, size = chunks[b'FILE']
if size > 32 * 1024**2:
raise ValueError('Archive tree too large')
stream.seek(start)
data_start, data_size = chunks[b'DATA']
entries, _, _ = parse_tree(stream.read(size), data_start,
data_start + data_size, Limits(total_bytes=32 * 1024**3))
for entry in entries:
path = entry['path']
if path not in EXPECTED:
continue
if path in found or entry['content_bytes'] > 1024**2:
raise ValueError('Duplicate or oversized target script')
stream.seek(entry['offset'])
raw = stream.read(entry['stored_bytes'])
if entry['compression'] == 'zlib':
raw = zlib.decompress(raw)
found[path] = raw
if set(found) != set(EXPECTED):
raise ValueError('Missing expected RHS scripts')
return found
"""Retired entry point: updated RHS supplies the 1.9 fixes itself."""
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--rhs', type=Path, required=True, help='Installed RHS Status Quo directory')
destination = parser.add_mutually_exclusive_group(required=True)
destination.add_argument('--output', type=Path, help='New standalone addon directory')
destination.add_argument('--mission', type=Path, help='Existing Escape addon directory to embed the fixes in')
args = parser.parse_args()
if args.mission:
mission = args.mission.resolve()
if mission.is_relative_to(args.rhs.resolve()):
parser.error('Mission must be outside the installed RHS directory')
project = (mission / 'addon.gproj').read_text()
if 'GUID "2528466DE220D91C"' not in project or '"595F2BF2F44836FB"' not in project:
parser.error('Expected the Escape project with its original RHS dependency')
if '"E119F1C05A274B83"' in project:
parser.error('Remove the standalone RHS fix dependency before embedding it')
originals = read_scripts(args.rhs)
outputs = {path: patched(path, raw) for path, raw in originals.items()}
# Validate all destinations before writing; never overwrite a local edit
# or follow a link into another addon. Repeating the same patch is safe.
for path, raw in outputs.items():
target = mission / path
if any(parent.is_symlink() for parent in [target, *target.parents] if parent != mission.parent):
parser.error('Symlink in mission destination: ' + path)
if target.exists() and (not target.is_file() or target.read_bytes() != raw):
parser.error('Existing mission script differs; preserve and review it: ' + path)
for path, raw in outputs.items():
target = mission / path
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(raw)
print('Embedded both pinned RHS fixes in ' + str(mission))
return
if args.output.exists():
parser.error('Output already exists; choose a new directory to preserve prior work')
if args.output.resolve().is_relative_to(args.rhs.resolve()):
parser.error('Output must be outside the installed RHS directory')
report_path = args.output.with_name(args.output.name + '-patch-inputs.json')
if report_path.exists():
parser.error('Output provenance already exists; choose a new directory')
# Validate every input and patch before creating any output.
originals = read_scripts(args.rhs)
outputs = {path: patched(path, raw) for path, raw in originals.items()}
args.output.mkdir(parents=True)
shutil.copy2(HERE / 'addon.gproj', args.output / 'addon.gproj')
report = {'rhs_version': '0.16.5237', 'target_game_version': '1.9.0.37',
'native_validation': 'required', 'scripts': {}}
for path, raw in outputs.items():
target = args.output / path
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(raw)
report['scripts'][path] = {'original_sha256': EXPECTED[path],
'patched_sha256': hashlib.sha256(raw).hexdigest()}
report_path.write_text(json.dumps(report, indent=2) + '\n')
print(args.output.resolve())
raise SystemExit(
"Retired: use updated RHS Status Quo. Remove the old RHS 1.9 "
"compatibility addon/overrides; do not regenerate them."
)
if __name__ == '__main__':
+6 -14
View File
@@ -1,17 +1,9 @@
RHS 1.9 Script Compatibility
RHS 1.9 Script Compatibility — retired
Unofficial compatibility patch for RHS Status Quo 0.16.5237 on Arma Reforger 1.9.0.37.
Requires the original RHS Status Quo addon and its content packs.
Updates the CAS editor component's SetAuthor signature and the tire component's
wheel-index accessor so the installed RHS scripts compile on 1.9. It does not
retune vehicle handling or add support for every new 1.9 feature.
Original RHS scripts and content are by Red Hammer Studios. This is a community
compatibility addon and is not an official RHS release.
Use the updated original RHS Status Quo addon and remove this compatibility
addon from your mod list. Its old script replacements are no longer needed
and can mask newer RHS changes.
This was an unofficial community patch for RHS 0.16.5237 on Reforger 1.9.0.37.
Original RHS scripts and content are by Red Hammer Studios.
RHS terms: https://docs.rhsmods.org/rhs-status-quo-user-documentation/arma-reforger/rhs-status-quo/eula
Built specifically for the versions above. Reassess or remove this patch when
RHS publishes its own compatible update, because these replacements can otherwise
hide newer upstream script changes.