"""Exercise universal connector exports, resource safety and project isolation.""" import copy import json import sys import tempfile from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'tools')) import terrain_adapter as adapter nogova = json.loads((adapter.ROOT / 'terrain-profiles/nogova.json').read_text()) production = adapter.files(nogova) for directory, expected in [('terrain-integrations/nogova', production), ('terrain-adapters/nogova', adapter.files(nogova, standalone=True))]: for name, content in expected.items(): assert (adapter.ROOT / directory / name).read_text() == content, (directory, name) assert 'addon.gproj' not in production # Terrain authors publish in their own addon. production_report = json.loads(production['terrain-adapter.json']) assert production_report['mode'] == 'embedded' assert production_report['addon'] == nogova['addon'] assert production_report['required_dependencies'] == [adapter.BASE, adapter.CORE] assert nogova['addon'] not in production_report['required_dependencies'] # No self-cycle. connector = (adapter.ROOT / 'addons/core/Prefabs/ERR/TerrainConnector.et').read_text() assert 'm_TerrainIndependent 1' in connector assert all(name in connector for name in ['PerceptionManager', 'SCR_FactionManager', 'SCR_LoadoutManager', 'RadioManagerEntity', 'ScriptedChatEntity']) assert 'Nogova' not in connector and 'SCR_AIWorld' not in connector assert json.loads(production['terrain-adapter.json'])['navigation_configured'] assert 'SCR_AIWorld' not in production['Worlds/ERR_nogova_Layers/default.layer'] assert 'coords 1525 0 5075' in production['Worlds/ERR_nogova_Layers/default.layer'] # Missing-navigation fixture remains hidden and clearly unqualified. nogova.pop('inherit_ai_world',None) rendered = adapter.files(nogova) report = json.loads(rendered['terrain-adapter.json']) assert not report['navigation_configured'] and report['qualification'] == 'unverified' assert report['scenarioId'] == '{681921DC9921F915}Missions/ERR_nogova.conf' assert nogova['world'] in rendered['Worlds/ERR_nogova.ent'] layer = rendered['Worlds/ERR_nogova_Layers/default.layer'] assert adapter.CONNECTOR in layer and 'Eden' not in layer assert 'SCR_FactionManager' not in layer # No copied managers to drift between maps. assert 'm_bShowInScenarioMenu 0' in rendered['Missions/ERR_nogova.conf'] standalone = adapter.files(nogova, standalone=True) assert nogova['addon'] in standalone['addon.gproj'] and adapter.CORE in standalone['addon.gproj'] assert json.loads(standalone['terrain-adapter.json'])['addon'] == '26C8E5E5AC063B1A' assert standalone['Worlds/ERR_nogova_Layers/default.layer'] == layer assert standalone['Missions/ERR_nogova.conf'] == rendered['Missions/ERR_nogova.conf'] # A different terrain needs only its own resources/hints, never another connector. other = dict(nogova, id='other_map', title='Other Map', addon='AAAAAAAAAAAAAAAA', world='{BBBBBBBBBBBBBBBB}Worlds/Other.ent', inherit_ai_world=True, airfields=[], notes='') other_files = adapter.files(other) assert other_files['Worlds/ERR_other_map_Layers/default.layer'] == 'ERR_GameMode Reintroduced : "'+adapter.CONNECTOR+'" {}\n' assert other['world'] in other_files['Worlds/ERR_other_map.ent'] assert 'Nogova' not in ''.join(other_files.values()) assert other_files['Missions/ERR_other_map.conf.meta'] != production['Missions/ERR_nogova.conf.meta'] ready = copy.deepcopy(nogova) ready['navigation'] = { 'Soldiers': ['{1111111111111111}Configs/Navmesh/Soldiers.conf'], 'BTRlike': ['{2222222222222222}Navmeshes/Vehicles.nmn'], 'LowRes': ['{3333333333333333}Navmeshes/LowRes.nmn'], } built = adapter.files(ready) layer = built['Worlds/ERR_nogova_Layers/default.layer'] assert 'NavmeshFile "{2222222222222222}Navmeshes/Vehicles.nmn"' in layer assert ': "{1111111111111111}Configs/Navmesh/Soldiers.conf" {}' in layer assert 'NavmeshSettings NavmeshWorld "{5C8C9B750B60C6E2}"' in layer assert json.loads(built['terrain-adapter.json'])['navigation_configured'] assert 'm_bShowInScenarioMenu 1' in built['Missions/ERR_nogova.conf'] assert adapter.files(ready) == built # Stable scenario IDs and files on regeneration. markers = copy.deepcopy(nogova) markers['ai_world'] = '{4444444444444444}Prefabs/AI/TerrainAIWorld.et' markers['airfields'] = [[1234, 0, -200], [5000, 30, 6000]] marker_files = adapter.files(markers) marker_layer = marker_files['Worlds/ERR_nogova_Layers/default.layer'] assert 'GenericEntity ERR_Airfield_0 {\n coords 1234 0 -200\n}' in marker_layer assert 'GenericEntity ERR_Airfield_1' in marker_layer assert markers['ai_world'] in marker_layer and 'NavmeshFile' not in marker_layer assert json.loads(marker_files['terrain-adapter.json'])['navigation_configured'] inherited = copy.deepcopy(nogova) inherited['inherit_ai_world'] = True inherited_files = adapter.files(inherited) assert 'SCR_AIWorld' not in inherited_files['Worlds/ERR_nogova_Layers/default.layer'] assert json.loads(inherited_files['terrain-adapter.json'])['navigation_configured'] try: adapter.files(dict(ready, inherit_ai_world=True)) except ValueError: pass else: raise AssertionError('Duplicate AI worlds allowed') for key, value in [('airfields', [[float('nan'), 0, 0]]), ('airfields', [[True, 0, 0]]), ('airfields', [[1, 2]]), ('airfields', [[1, 2, 3]] * 17), ('ai_world', '{1111111111111111}bad.conf'), ('id', '../escape'), ('title', 'Bad" title'), ('addon', 'bad'), ('world', '{1111111111111111}/absolute.ent'), ('world', '{1111111111111111}Worlds/../escape.ent'), ('world', '{1111111111111111}Worlds/./escape.ent'), ('world', '{1111111111111111}Worlds//escape.ent'), ('world', '{1111111111111111}Worlds/escape.conf'), ('navigation', {'Unknown': ['{1111111111111111}a.nmn']}), ('navigation', {'Soldiers': []}), ('navigation', {'Soldiers': ['{1111111111111111}a.nmn'] * 2})]: invalid = copy.deepcopy(nogova); invalid[key] = value try: adapter.files(invalid) except ValueError: pass else: raise AssertionError((key, value)) with tempfile.TemporaryDirectory() as temp: dest = Path(temp) / 'adapter' adapter.generate(ready, dest) before = {str(p.relative_to(dest)): p.read_bytes() for p in dest.rglob('*') if p.is_file()} try: adapter.generate(nogova, dest) except ValueError: pass else: raise AssertionError('Existing adapter was overwritten') assert before == {str(p.relative_to(dest)): p.read_bytes() for p in dest.rglob('*') if p.is_file()} print('PASS: universal connector, embedded/standalone exports, multiple terrains, stable IDs, native nav configs, missing-nav visibility, path validation and overwrite protection')