Combine Workshop addon and fix prison startup
Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
This commit is contained in:
+15
-20
@@ -1,4 +1,4 @@
|
||||
"""Explicit v3 DEVELOPMENT packed Chat consumption; v1/v2 remain unchanged.
|
||||
"""Explicit v4 DEVELOPMENT combined-mod consumption; v1/v2 remain unchanged.
|
||||
Only the audited installed external closure is linked, never copied or published.
|
||||
"""
|
||||
import hashlib
|
||||
@@ -11,13 +11,8 @@ import uuid
|
||||
import external_addons as external
|
||||
from project_schema import parse_project
|
||||
|
||||
PROJECTS = {
|
||||
'core': ('ERR_Core', '2528466DE220D91C', ()),
|
||||
'ai': ('ERR_Ai', '5192138CB9D5B911', ('core',)),
|
||||
'intel': ('ERR_Intel', 'D31039C7C53E348A', ('core',)),
|
||||
'map': ('ERR_Map', '9AAEE9041D79809E', ('core',)),
|
||||
'chat': ('ERR_Chat', 'FA804F411BDDF2C6', ('core','ai','intel','map')),
|
||||
}
|
||||
from source_bundle import PROJECTS
|
||||
|
||||
FILES = {'addon.gproj','data.pak','resourceDatabase.rdb'}
|
||||
MANIFEST = 'packed-input-provenance.json'
|
||||
|
||||
@@ -35,8 +30,8 @@ def digest(path):
|
||||
|
||||
def inventory(folder):
|
||||
folder=Path(folder)
|
||||
require(folder.is_dir() and not folder.is_symlink() and folder==folder.resolve(),'Packed v3 closure must be a canonical real directory')
|
||||
require({p.name for p in folder.iterdir()}==set(PROJECTS),'Packed v3 requires exactly five successor addon folders')
|
||||
require(folder.is_dir() and not folder.is_symlink() and folder==folder.resolve(),'Packed v4 closure must be a canonical real directory')
|
||||
require({p.name for p in folder.iterdir()}==set(PROJECTS),'Packed v4 requires exactly the combined core addon folder')
|
||||
records={}
|
||||
for key,(name,guid,deps) in PROJECTS.items():
|
||||
addon=folder/key
|
||||
@@ -51,16 +46,16 @@ def inventory(folder):
|
||||
parsed=parse_project(text,allow_core_menu=key=='core')
|
||||
actual=re.findall(r'"([A-F0-9]{16})"',parsed[4]) if parsed else []
|
||||
expected={'58D0FB3206B6F859',*(PROJECTS[d][1] for d in deps),*external.direct_for(key)}
|
||||
require(parsed and parsed[1]==name and parsed[2]==guid and set(actual)==expected and len(actual)==len(expected),'Packed v3 exact project identity/dependency mismatch: '+key)
|
||||
require(parsed and parsed[1]==name and parsed[2]==guid and set(actual)==expected and len(actual)==len(expected),'Packed v4 exact project identity/dependency mismatch: '+key)
|
||||
return records
|
||||
|
||||
def request(args,root):
|
||||
require(args.project=='chat' and getattr(args,'packed_addons',None) is not None and getattr(args,'packed_core',None) is None,'--external-addons requires --project chat --packed-addons (explicit packed v3)')
|
||||
require(args.profile==Path('/tmp/erri-game-profile') and args.settle==15,'Packed v3 owns a new private profile and requires 15-second settling')
|
||||
require(args.project=='chat' and getattr(args,'packed_addons',None) is not None and getattr(args,'packed_core',None) is None,'--external-addons requires --project chat --packed-addons (explicit packed v4)')
|
||||
require(args.profile==Path('/tmp/erri-game-profile') and args.settle==15,'Packed v4 owns a new private profile and requires 15-second settling')
|
||||
session=getattr(args,'packed_session',None)
|
||||
require(session is not None,'Packed v3 requires --packed-session')
|
||||
require(session is not None,'Packed v4 requires --packed-session')
|
||||
session=Path(session)
|
||||
require(session.is_absolute() and session.parent==Path('/tmp') and session==session.resolve() and re.fullmatch(r'erri-packed-chat-[A-Za-z0-9_-]+',session.name) and not session.exists() and not session.is_symlink(),'Packed v3 requires a new canonical /tmp/erri-packed-chat-<name> session')
|
||||
require(session.is_absolute() and session.parent==Path('/tmp') and session==session.resolve() and re.fullmatch(r'erri-packed-chat-[A-Za-z0-9_-]+',session.name) and not session.exists() and not session.is_symlink(),'Packed v4 requires a new canonical /tmp/erri-packed-chat-<name> session')
|
||||
candidate=Path(args.packed_addons);artifacts=inventory(candidate)
|
||||
policy_path=Path(root)/'docs/external-addons.json';policy_sha=digest(policy_path);rules=external.policy(root)
|
||||
require(digest(policy_path)==policy_sha,'External policy changed during parsing')
|
||||
@@ -72,7 +67,7 @@ def request(args,root):
|
||||
match=re.search(r'\bID\s+"([A-Za-z0-9_-]+)"',data.decode())
|
||||
require(match and match[1] not in {'core','ArmaReforger',*(p[0] for p in PROJECTS.values()),*names.values()},'Invalid/duplicate external project mount name')
|
||||
names[guid]=match[1]
|
||||
return {'schema_version':3,'project':'chat','candidate':candidate,'session':session,'input':session/'input/addons','manifest':artifacts,'external_source':source,'policy_path':policy_path,'policy_sha256':policy_sha,'policy':rules,'external_records':records,'external_names':names,'external_cache':cache}
|
||||
return {'schema_version':4,'project':'chat','candidate':candidate,'session':session,'input':session/'input/addons','manifest':artifacts,'external_source':source,'policy_path':policy_path,'policy_sha256':policy_sha,'policy':rules,'external_records':records,'external_names':names,'external_cache':cache}
|
||||
|
||||
def verify_external(packed,profile=None):
|
||||
require(digest(packed['policy_path'])==packed['policy_sha256'],'External policy changed')
|
||||
@@ -121,14 +116,14 @@ def prepare_profile(packed,steam,profile):
|
||||
def snapshot(packed,command,profile):
|
||||
verify_external(packed,profile)
|
||||
require(inventory(packed['input'])==packed['manifest'] and inventory(packed['candidate'])==packed['manifest'],'Packed inputs changed before launch')
|
||||
value={'schema_version':3,'kind':'packed-chat-external-runtime-input','project':'chat','addon_order':list(PROJECTS),'launch_id':str(uuid.uuid4()),'captured_unix':time.time(),'candidate_directory':str(packed['candidate']),'runtime_directory':str(packed['input']),'profile':str(profile),'command':list(command),'artifacts':packed['manifest'],'external_policy_sha256':packed['policy_sha256'],'external_policy':packed['policy'],'external_addons':packed['external_records'],'external_project_names':packed['external_names'],'build_qualification':'DEVELOPMENT unqualified candidate; original packaging verdict unchanged'}
|
||||
value={'schema_version':4,'kind':'packed-chat-external-runtime-input','project':'chat','addon_order':list(PROJECTS),'launch_id':str(uuid.uuid4()),'captured_unix':time.time(),'candidate_directory':str(packed['candidate']),'runtime_directory':str(packed['input']),'profile':str(profile),'command':list(command),'artifacts':packed['manifest'],'external_policy_sha256':packed['policy_sha256'],'external_policy':packed['policy'],'external_addons':packed['external_records'],'external_project_names':packed['external_names'],'build_qualification':'DEVELOPMENT unqualified candidate; original packaging verdict unchanged'}
|
||||
raw=(json.dumps(value,indent=2)+'\n').encode();(packed['session']/MANIFEST).write_bytes(raw)
|
||||
return raw
|
||||
|
||||
def seal(packed,raw,session):
|
||||
value=json.loads(raw);unchanged=False;error=None
|
||||
try:
|
||||
require(value['schema_version']==3 and value['kind']=='packed-chat-external-runtime-input','Wrong packed v3 provenance schema')
|
||||
require(value['schema_version']==4 and value['kind']=='packed-chat-external-runtime-input','Wrong packed v4 provenance schema')
|
||||
require(value['external_addons']==packed['external_records'] and value['external_policy_sha256']==packed['policy_sha256'] and value['external_policy']==packed['policy'] and value['external_project_names']==packed['external_names'],'External provenance mismatch')
|
||||
verify_external(packed,packed['profile'])
|
||||
require(inventory(packed['input'])==value['artifacts'] and inventory(packed['candidate'])==value['artifacts'],'Packed inputs changed after launch')
|
||||
@@ -137,7 +132,7 @@ def seal(packed,raw,session):
|
||||
unchanged=True
|
||||
except (OSError,ValueError,KeyError) as caught:error=str(caught)
|
||||
console=session/'console.log' if session else None
|
||||
return {'packed_only':True,'packed_schema_version':3,'packed_input_provenance':MANIFEST,'packed_input_provenance_sha256':hashlib.sha256(raw).hexdigest(),'packed_launch_id':value['launch_id'],'stopped_unix':time.time(),'packed_inputs_unchanged_after_run':unchanged,'packed_external_inputs_unchanged_after_run':unchanged,'packed_input_error':error,'external_addon_count':len(packed['external_records']),'console_sha256':digest(console) if console and console.is_file() else None,'build_qualified':False,'release_qualified':False}
|
||||
return {'packed_only':True,'packed_schema_version':4,'packed_input_provenance':MANIFEST,'packed_input_provenance_sha256':hashlib.sha256(raw).hexdigest(),'packed_launch_id':value['launch_id'],'stopped_unix':time.time(),'packed_inputs_unchanged_after_run':unchanged,'packed_external_inputs_unchanged_after_run':unchanged,'packed_input_error':error,'external_addon_count':len(packed['external_records']),'console_sha256':digest(console) if console and console.is_file() else None,'build_qualified':False,'release_qualified':False}
|
||||
|
||||
def evidence(content,packed,steam,profile,legacy):
|
||||
report=legacy.game_evidence(content,'chat');game=steam/'steamapps/common/Arma Reforger'
|
||||
@@ -188,4 +183,4 @@ def evidence(content,packed,steam,profile,legacy):
|
||||
if any({'WORKBENCH','ENF_WB'}.intersection(flags.split(',')) for _,flags in modules):violations.append('Workbench defines in packed ordinary runtime')
|
||||
path_proof=complete and mounted==set(mounts) and loaded_databases==set(databases) and bool(dirs)
|
||||
failed=report['failed'] or bool(violations)
|
||||
return {**report,'packed_only':True,'packed_schema_version':3,'external_addon_count':len(packed['external_records']),'packed_loaded_paths_verified':path_proof,'packed_input_violations':violations,'packed_normal_modules':normal,'failed':failed,'ready':report['ready'] and path_proof and normal and not failed,'build_qualified':False,'release_qualified':False,'scope':'DEVELOPMENT packed Chat plus exact audited external closure; ordinary local startup only, no clean-build, human escape, multiplayer, FPS or release qualification'}
|
||||
return {**report,'packed_only':True,'packed_schema_version':4,'external_addon_count':len(packed['external_records']),'packed_loaded_paths_verified':path_proof,'packed_input_violations':violations,'packed_normal_modules':normal,'failed':failed,'ready':report['ready'] and path_proof and normal and not failed,'build_qualified':False,'release_qualified':False,'scope':'DEVELOPMENT packed combined mod plus exact audited external closure; ordinary local startup only, no clean-build, human escape, multiplayer, FPS or release qualification'}
|
||||
|
||||
Reference in New Issue
Block a user