Simplify content controls and add vehicle roles; record Workshop 0.0.144
continuous-integration/drone/push Build is passing
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:
@@ -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.
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user