"""Actual authored-stock capture/access and exact post-delivery stock admission.""" import os,re from pathlib import Path from policy_harness import ROOT,body,run from test_finite_magazine_reservation import translated_reservation B=Path(os.environ.get('ERR_GROUND_DEPOT_ROOT',ROOT)) S=Path(os.environ.get('ERR_SITE_SERVICE_SOURCE',B/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c')) I=Path(os.environ.get('ERR_INFANTRY_RESUPPLY_SOURCE',B/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c')) L=Path(os.environ.get('ERR_GROUND_DEPOT_LOADING_SOURCE',B/'addons/core/Scripts/Game/ERR/10_AI/ERR_GroundDepotLoading.c')) assert 'int DepotItemCount(' in S.read_text() and 'bool AdmitDepotMine(' in I.read_text(), 'finite authored camp mine provenance/import API is absent' # Read the actual authored configuration, not a duplicated stock fixture. conf=(B/'addons/core/Configs/ERR/EveronSites.conf').read_text() pmn='{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et' camps=[] for match in re.finditer(r' ERR_SiteDefinition \{',conf): end=match.end();depth=1 while depth: if conf[end]=='{':depth+=1 elif conf[end]=='}':depth-=1 end+=1 block=conf[match.start():end] eligible=bool(re.search(r'^ Kind 3$',block,re.M)) and not re.search(r'^ RevealOnEscape 1$',block,re.M) stocks=re.findall(r'ERR_SiteStock \{ Prefab "([^"\n]+)" Count (\d+)([^}]*?) \}',block) mines=[row for row in stocks if row[0]==pmn] if eligible: assert len(mines)==1 and mines[0][1]=='2' and 'TargetId' not in mines[0][2] camps.append(re.search(r'Id "([^"]+)"',block).group(1)) else:assert not mines assert len(camps)==5 assert 'ERR_InfantryMineTransfer' not in S.read_text(), 'Core must not depend on the AI mine transfer layer' capture=body(S, 'if (spec.Kind == ERR_LocationKind.OUTPOST && !spec.RevealOnEscape && stock.TargetId.IsEmpty()') access='\n'.join(body(S,k) for k in ('int DepotMagazineCount(', 'int DepotMineCount(', 'int DepotItemCount(', 'IEntity DepotItem(', 'void DepotStock(', 'IEntity DepotMagazine(', 'bool DepotOriginal(')) admit='\n'.join(body(I,k) for k in ('bool DepotCurrent(', 'bool DepotAvailable(', 'bool DepotNeeded(', 'bool AdmitDepot(', 'bool DepotMineAvailable(', 'bool DepotMineNeeded(', 'bool AdmitDepotMine(', 'protected bool CaptureMines(')) delivered=body(L,'bool DeliveredTo(') def adapt(s): s=s.replace('array','Array').replace('out Array items','Array& items').replace('ERR_SiteRuntime site','Site* site').replace('ERR_GroundDepotLoading job','Loading* job').replace('ERR_InfantryResupply service','Service* service') s=s.replace('protected ', '').replace('ERR_SupplyConvoyTask supply = ERR_SupplyConvoyTask.Cast(m_Convoy);','Supply* supply = m_Convoy;') s=s.replace('InventoryItemComponent carried = InventoryItemComponent.Cast(item.FindComponent(InventoryItemComponent));','InventoryItem* carried = item->inventoryItem;') s=s.replace('foreach (IEntity item : m_MineStock)', 'for (IEntity item : m_MineStock)') s=s.replace('ERR_InfantryMineTransfer.', 'ERR_InfantryMineTransfer::').replace('ERR_InfantryAmmoTransfer.', 'ERR_InfantryAmmoTransfer::') for name in ('supply','m_MineBatch','carried'):s=s.replace(name+'.',name+'->') s=s.replace('GetParentSlot().','GetParentSlot()->') for name in ('site','spec','stock','item','m_Stock','m_Spent','m_Mounted','m_Truck','job','m_Move','m_Reservation'): if name not in ('m_Stock','m_Spent'): s=s.replace(name+'.',name+'->') s=s.replace('site->Definition.', 'site->Definition->').replace('site->Responders.', 'site->Responders->') s=s.replace('stock->TargetId.IsEmpty()','stock->TargetId.empty()').replace('item->FindComponent(BaseMagazineComponent)','item->magazine') s=s.replace('foreach (IEntity item : m_Spent)', 'for (IEntity item : m_Spent)').replace('int committed;', 'int committed=0;') s=s.replace('Replication.IsServer()','server').replace('ERR_PrepareResult.READY','1').replace('ERR_LocationKind.OUTPOST','3').replace('null','nullptr') return s run(r''' #include #include #include #include using string=std::string;bool server=true; templatestruct Array:std::vector{using std::vector::vector;int Count(){return this->size();}bool IsEmpty(){return this->empty();}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}void Insert(T x){this->push_back(x);}void RemoveItem(T x){this->erase(std::remove(this->begin(),this->end(),x),this->end());}}; struct Entity;using IEntity=Entity*; struct Slot{IEntity attached=nullptr;IEntity GetAttachedEntity(){return attached;}}; struct InventoryItem{Slot* slot=nullptr;Slot* GetParentSlot(){return slot;}}; struct Entity{bool magazine=true,ready=false,stored=true;Slot slot;InventoryItem inventory;InventoryItem*inventoryItem=&inventory;Entity(){slot.attached=this;inventory.slot=&slot;}}; // Native ready predicate and storage graph are doubled; actual readiness is separately tested by mine-transfer suites. struct ERR_InfantryMineTransfer{static bool ReadyMine(IEntity item){return item&&item->ready;}}; struct ERR_InfantryAmmoTransfer{static bool StoredUnder(IEntity item,IEntity){return item&&item->stored;}}; struct Def{int Kind=3;bool RevealOnEscape=false;};struct Stock{string TargetId,Prefab;}; struct Responders{bool Group=true;};struct Site{Def* Definition=nullptr;IEntity Container=nullptr;struct Responders* Responders=nullptr;ArrayDepotMagazines,DepotMines;}; struct Sites{bool m_Stopped=false;int m_State=0;Arraym_Sites;Arraym_Entities;bool Owns(IEntity x){return m_Entities.Contains(x);} ''' + adapt(access)+ '\nvoid Capture(Def* spec,Stock* stock,Site* site,IEntity item){'+adapt(capture)+r'''} }; ''' + translated_reservation()+r''' struct Service; struct Proof{bool done=true,delivery=true,mine=false;bool MineCargo(){return mine;}bool Confirmed(){return done;}bool Delivery(){return delivery;}}; struct Loading{bool m_Imported=false,valid=true,m_MineCargo=false;int m_State=5;Service* m_Service=nullptr;IEntity m_Item=nullptr;Proof* m_Move=nullptr;ERR_FiniteMagazineReservation* m_Reservation=nullptr;bool Valid(){return valid;}bool DeliveredTo(Service*,IEntity,bool=false);}; struct Mounted{bool busy=false;bool Busy(){return busy;}}; struct Truck{IEntity Entity=nullptr;}; struct Batch{bool complete=true;Arrayitems;bool Complete(){return complete;}int Count(){return items.Count();}IEntity At(int index){return items[index];}}; struct Supply{Batch*MineStock=nullptr;}; struct Service{Supply*m_Convoy=nullptr;Batch*m_MineBatch=nullptr;bool m_MineStockCaptured=false;Arraym_MineStock;int m_DepotMineImports=0;bool current=true,truckValid=true,unresolved=false,m_Move=false,m_MineMove=false;int m_State=1,audits=0,m_DepotImports=0;Arraym_Stock,m_Spent,cargo;Mounted* m_Mounted=nullptr;Truck* m_Truck=nullptr;bool Current(){return current;}bool Unresolved(){return unresolved;}bool TruckValid(bool endpoint){assert(endpoint);return truckValid;}bool Contains(IEntity,IEntity item){return cargo.Contains(item);}void Audit(string){audits++;} ''' + adapt(admit)+r'''}; ''' + adapt(delivered).replace('bool DeliveredTo(', 'bool Loading::DeliveredTo(').replace('bool mineCargo = false','bool mineCargo')+r''' struct F{Entity cache,item,foreign,truck;Def def;Stock stock;Responders responders;Site site;Sites sites;Arrayoriginal,spent;ERR_FiniteMagazineReservation reservation;Service service;Loading job;Proof proof;Truck vehicle;Mounted mounted; F():reservation(&original,&spent,&item){site.Definition=&def;site.Container=&cache;site.Responders=&responders;sites.m_Sites={&site};sites.m_Entities={&cache};sites.Capture(&def,&stock,&site,&item);original={&item};assert(reservation.Reserve()&&reservation.Commit());job.m_Reservation=&reservation;job.m_Move=&proof;job.m_Item=&item;job.m_Service=&service;vehicle.Entity=&truck;service.m_Truck=&vehicle;service.m_Mounted=&mounted;service.cargo={&item};} }; struct M:F{Entity initial;Batch batch;Supply supply; M(){item.magazine=false;item.ready=true;initial.magazine=false;initial.ready=true;batch.items={&initial};supply.MineStock=&batch;service.m_Convoy=&supply;service.m_MineBatch=&batch;service.m_Spent={&initial};job.m_MineCargo=true;proof.mine=true;} }; int main(){ // Authored inert mines join detached startup provenance, without replacing magazine APIs. {F f;f.sites.m_State=1;f.stock.Prefab="{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et";Entity mine;mine.magazine=false;mine.ready=true;f.sites.Capture(&f.def,&f.stock,&f.site,&mine);assert(f.sites.DepotMagazineCount(&f.site)==1&&f.sites.DepotMineCount(&f.site)==1&&f.sites.DepotItemCount(&f.site)==2);assert(f.sites.DepotItem(&f.site,1)==&mine&&f.sites.DepotOriginal(&f.site,&mine));Arrayout;f.sites.DepotStock(&f.site,out);out.clear();assert(f.sites.DepotItemCount(&f.site)==2);Entity other;other.magazine=false;other.ready=true;assert(!f.sites.DepotOriginal(&f.site,&other));f.sites.Capture(&f.def,&f.stock,&f.site,&other);Entity third;third.magazine=false;third.ready=true;f.sites.Capture(&f.def,&f.stock,&f.site,&third);assert(f.sites.DepotMineCount(&f.site)==2&&!f.sites.DepotOriginal(&f.site,&third));} for(int bad=0;bad<4;bad++){F f;f.item.magazine=false;f.item.ready=true;f.stock.Prefab="{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et";f.site.DepotMagazines.clear();if(bad==0)f.def.RevealOnEscape=true;if(bad==1)f.def.Kind=4;if(bad==2)f.stock.TargetId="radio";if(bad==3)f.stock.Prefab="foreign";f.sites.Capture(&f.def,&f.stock,&f.site,&f.item);assert(f.site.DepotMines.empty());} {M f;assert(f.service.DepotMineNeeded()&&!f.service.DepotNeeded());assert(f.service.AdmitDepotMine(&f.job,&f.item));assert(f.service.m_MineStock.Count()==2&&f.service.m_DepotMineImports==1&&f.service.m_Stock.empty());assert(!f.service.DepotMineNeeded()&&!f.service.AdmitDepotMine(&f.job,&f.item));assert(f.spent.Count()==1&&f.service.m_Spent.Count()==1);ERR_FiniteMagazineReservation next(&f.service.m_MineStock,&f.service.m_Spent,&f.item);assert(next.Reserve()&&next.Commit());assert(f.service.DepotMineNeeded());} // Known mine expenditure only; missing/deleted/magazine identities never become mine demand. {M f;f.service.m_Spent.clear();assert(!f.service.DepotMineNeeded());f.service.m_Spent={nullptr,&f.foreign};f.service.m_Stock={&f.foreign};assert(f.service.DepotNeeded()&&!f.service.DepotMineNeeded());f.service.m_MineStock={nullptr};assert(!f.service.DepotMineNeeded());} for(int bad=0;bad<21;bad++){M f;assert(f.service.DepotMineNeeded());if(bad==0)f.service.current=false;if(bad==1)f.service.m_State=7;if(bad==2)f.batch.complete=false;if(bad==3)f.supply.MineStock=nullptr;if(bad==4)f.service.m_MineBatch=nullptr;if(bad==5)f.service.m_MineStock.resize(96,&f.initial);if(bad==6)f.service.m_Spent.resize(96,&f.initial);if(bad==7)f.item.ready=false;if(bad==8)f.service.m_Stock.Insert(&f.item);if(bad==9)f.service.m_Spent.Insert(&f.item);if(bad==10)f.service.cargo.clear();if(bad==11)f.item.stored=false;if(bad==12)f.item.inventoryItem=nullptr;if(bad==13)f.item.inventory.slot=nullptr;if(bad==14)f.item.slot.attached=&f.foreign;if(bad==15)f.job.m_MineCargo=false;if(bad==16)f.proof.mine=false;if(bad==17)f.job.m_State=7;if(bad==18)f.job.valid=false;if(bad==19)f.proof.done=false;if(bad==20)f.service.m_Spent.clear();int before=f.service.m_MineStock.Count();assert(!f.service.AdmitDepotMine(&f.job,&f.item));assert(f.service.m_MineStock.Count()==before&&f.service.m_DepotMineImports==0);} {M f;f.batch.items.clear();f.service.m_Spent.clear();assert(!f.service.DepotMineNeeded()&&!f.service.AdmitDepotMine(&f.job,&f.item));} {M f;assert(f.service.DepotMineNeeded());f.service.m_MineStock.resize(95,&f.initial);assert(f.service.AdmitDepotMine(&f.job,&f.item));assert(f.service.m_MineStock.Count()==96&&f.service.DepotCurrent()&&!f.service.DepotMineAvailable());} {F f;Arrayout;f.sites.DepotStock(&f.site,out);assert(out.empty());f.sites.m_State=1;f.sites.DepotStock(&f.site,out);assert(out.Count()==1&&out[0]==&f.item);out[0]=&f.foreign;out.clear();assert(f.sites.DepotOriginal(&f.site,&f.item)&&!f.sites.DepotOriginal(&f.site,&f.foreign));assert(f.sites.DepotMagazine(&f.site,-1)==nullptr&&f.sites.DepotMagazine(&f.site,1)==nullptr);f.sites.m_Stopped=true;assert(!f.sites.DepotOriginal(&f.site,&f.item));} for(int kind=0;kind<6;kind++){F f;f.site.DepotMagazines.clear();if(kind==0)f.def.RevealOnEscape=true;if(kind==1)f.def.Kind=4;if(kind==2)f.stock.TargetId="radio";if(kind==3)f.item.magazine=false;if(kind==4)f.site.DepotMagazines.resize(96,&f.foreign);f.sites.Capture(&f.def,&f.stock,&f.site,&f.item);assert(f.site.DepotMagazines.Contains(&f.item)==(kind==5));} for(int bad=0;bad<7;bad++){F f;f.sites.m_State=1;if(bad==0)server=false;if(bad==1)f.sites.m_Sites.clear();if(bad==2)f.site.Container=nullptr;if(bad==3)f.sites.m_Entities.clear();if(bad==4)f.site.Responders=nullptr;if(bad==5)f.responders.Group=false;if(bad==6)f.site.DepotMagazines.resize(97,&f.item);assert(f.sites.DepotMagazineCount(&f.site)==0);server=true;} {F f;assert(f.service.AdmitDepot(&f.job,&f.item));assert(f.service.m_Stock.Count()==1&&f.service.m_Stock[0]==&f.item&&f.service.audits==1);assert(!f.service.AdmitDepot(&f.job,&f.item));ERR_FiniteMagazineReservation outgoing(&f.service.m_Stock,&f.service.m_Spent,&f.item);assert(outgoing.Reserve()&&outgoing.Commit());assert(f.spent.Count()==1&&f.service.m_Spent.Count()==1);assert(!f.service.AdmitDepot(&f.job,&f.item));} // Deleted identities and mine-only spent entries cannot create magazine demand. {F f;f.service.m_Stock={nullptr};f.service.m_Spent={nullptr,&f.foreign};assert(!f.service.DepotNeeded());f.service.m_Stock.Insert(&f.foreign);assert(f.service.DepotNeeded());f.service.m_DepotImports=1;assert(!f.service.DepotNeeded());Entity another;f.service.m_Stock.Insert(&another);f.service.m_Spent.Insert(&another);assert(f.service.DepotNeeded());} // Final lifetime slot can complete delivery and departure; admission then closes. {F f;f.service.m_Stock.resize(95,&f.foreign);f.service.m_Spent={&f.foreign};assert(f.service.DepotNeeded());assert(f.service.AdmitDepot(&f.job,&f.item));assert(f.service.m_Stock.Count()==96&&f.service.m_DepotImports==1);assert(f.service.DepotCurrent()&&!f.service.DepotAvailable()&&!f.service.DepotNeeded());} for(int bad=0;bad<21;bad++){F f; if(bad==0)f.service.current=false; if(bad==1)f.service.m_State=7; if(bad==2)f.service.m_Move=true; if(bad==3)f.service.m_MineMove=true; if(bad==4)f.service.unresolved=true; if(bad==5)f.mounted.busy=true; if(bad==6)f.service.truckValid=false; if(bad==7)f.service.m_Stock.resize(96,&f.foreign); if(bad==8)f.service.m_Spent.resize(96,&f.foreign); if(bad==9)f.service.m_Spent={&f.item}; if(bad==10)f.service.cargo.clear(); if(bad==11)f.job.m_Imported=true; if(bad==12)f.job.m_State=7; if(bad==13)f.job.m_Service=nullptr; if(bad==14)f.job.m_Item=&f.foreign; if(bad==15)f.proof.done=false; if(bad==16)f.proof.delivery=false; if(bad==17)f.job.m_Move=nullptr; if(bad==18)f.job.m_Reservation=nullptr; if(bad==19)f.job.valid=false; if(bad==20)f.spent.clear(); int before=f.service.m_Stock.Count();assert(!f.service.AdmitDepot(&f.job,&f.item));assert(f.service.m_Stock.Count()==before&&f.service.audits==0);} {F f;f.job.m_State=7;f.proof.done=true;assert(!f.service.AdmitDepot(&f.job,&f.item));f.job.m_State=5;f.job.valid=false;assert(!f.service.AdmitDepot(&f.job,&f.item));} } ''') print('PASS: actual mine/magazine provenance and separated finite mine imports; actual authored identity capture, READY-only detached provenance, same shared stock admission, completed exact two-leg proof, lifetime caps and Stop no late adoption')