Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
72 lines
4.4 KiB
Diff
72 lines
4.4 KiB
Diff
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_MortarSupplyTransfer.c
|
|
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_MortarSupplyTransfer.c
|
|
@@ -81,6 +81,13 @@
|
|
ERR_MortarSupplyState Poll(float now)
|
|
{
|
|
if (!Replication.IsServer()) return ERR_MortarSupplyState.FAILED;
|
|
+ // The exact deleted shell cannot complete later. Match courier settlement:
|
|
+ // release only uncertainty, never authorize a shot or retry this lost item.
|
|
+ if (!m_Shell)
|
|
+ {
|
|
+ m_Resolved = true; m_Invalidated = true; m_Ready = false;
|
|
+ return ERR_MortarSupplyState.FAILED;
|
|
+ }
|
|
bool valid = Valid(now);
|
|
bool sourceContains = Contains(m_Source);
|
|
bool destinationContains = Contains(m_Destination);
|
|
--- a/tests/test_mortar_transactions.py
|
|
+++ b/tests/test_mortar_transactions.py
|
|
@@ -1,7 +1,9 @@
|
|
"""Actual Enforce policies and transaction observation body under narrow API fakes."""
|
|
from policy_harness import ROOT, body, run
|
|
+from pathlib import Path
|
|
+import os
|
|
r=ROOT/'addons/core/Scripts/Game/ERR/10_AI'
|
|
-readiness=r/'ERR_MortarReadiness.c'; transfer=r/'ERR_MortarSupplyTransfer.c'; battery=r/'ERR_MortarBattery.c'
|
|
+readiness=r/'ERR_MortarReadiness.c'; transfer=Path(os.environ.get('ERR_MORTAR_SUPPLY_TRANSFER_SOURCE',r/'ERR_MortarSupplyTransfer.c')); battery=r/'ERR_MortarBattery.c'
|
|
policy='\n'.join([body(readiness,'static ERR_MortarSupplyState Population('),body(readiness,'static bool AllocationValid('),body(readiness,'static int CycleGate('),body(transfer,'static bool Authority('),body(transfer,'static bool Proven(')])
|
|
policy=policy.replace('ERR_MortarSupplyState.','ERR_MortarSupplyState::')
|
|
run('''#include <cassert>
|
|
@@ -44,6 +46,7 @@
|
|
poll=poll.replace('ERR_MortarSupplyState.','ERR_MortarSupplyState::').replace('Replication.IsServer()','Replication::IsServer()').replace('InventoryItemComponent item;','InventoryItemComponent* item=nullptr;').replace('if (m_Shell) item = InventoryItemComponent.Cast(m_Shell.FindComponent(InventoryItemComponent));','if (m_Shell) item = m_Shell;').replace('item.GetParentSlot().GetStorage()','item->GetParentSlot()->GetStorage()').replace('item.GetParentSlot()','item->GetParentSlot()')
|
|
ack=body(transfer,'void Acknowledge(')
|
|
run('''#include <cassert>
|
|
+#include <initializer_list>
|
|
enum class ERR_MortarSupplyState {FAILED=-1,PENDING,READY};
|
|
struct Replication { static bool IsServer(){return true;} };
|
|
struct Slot { void* storage; void* GetStorage(){return storage;} };
|
|
@@ -57,7 +60,10 @@
|
|
Slot slot{this}; InventoryItemComponent item{&slot}; InventoryItemComponent* m_Shell=&item;
|
|
bool Valid(float){return valid&&!m_Invalidated;}
|
|
bool Contains(int which){return m_Shell && (which==1 ? source : destination);}
|
|
-'''+ack+poll+'''
|
|
+'''+ack+body(transfer,'bool Unresolved(')+poll+'''
|
|
+};
|
|
+struct Battery {Transfer* m_Transfer=nullptr;
|
|
+'''+body(battery,'bool SupplyPending(').replace('m_Transfer.Unresolved()', 'm_Transfer->Unresolved()')+'''
|
|
};
|
|
int main(){
|
|
using S=ERR_MortarSupplyState;
|
|
@@ -76,8 +82,19 @@
|
|
assert(late.Poll(8000)==S::FAILED); // no deadline-boundary success bypass
|
|
Transfer failed;failed.Acknowledge(false);failed.Acknowledge(true);
|
|
assert(failed.Poll(1)==S::FAILED && failed.m_Acknowledgement==-1 && failed.m_Resolved);
|
|
- Transfer gone;gone.m_Shell=nullptr;gone.Acknowledge(true);
|
|
- assert(gone.Poll(1)==S::PENDING && !gone.m_Resolved); // ambiguous until deadline, never another move
|
|
+ // Exact deletion is permanent loss, not an operation that can still arrive.
|
|
+ for(int ack=0;ack<3;ack++)for(bool invalidated:{false,true}){
|
|
+ Transfer gone;Battery battery;battery.m_Transfer=&gone;gone.m_Invalidated=invalidated;assert(gone.Unresolved()&&battery.SupplyPending());gone.m_Shell=nullptr;
|
|
+ if(ack==1)gone.Acknowledge(true);
|
|
+ if(ack==2)gone.Acknowledge(false);
|
|
+ assert(gone.Poll(1)==S::FAILED&&gone.m_Resolved&&gone.m_Invalidated&&!gone.m_Ready&&!gone.Unresolved());
|
|
+ assert(!battery.SupplyPending());
|
|
+ gone.Acknowledge(true);assert(gone.Poll(90000)==S::FAILED&&!gone.Unresolved()&&!gone.m_Ready);
|
|
+ }
|
|
+ // A surviving detached shell, missing inventory or lost authority remains
|
|
+ // uncertain; only exact identity deletion permits unconditional settlement.
|
|
+ Transfer detached;detached.source=false;detached.destination=false;detached.Acknowledge(true);
|
|
+ assert(detached.Poll(1)==S::PENDING&&detached.Unresolved());assert(detached.Poll(8000)==S::FAILED&&detached.Unresolved());
|
|
Transfer unauthorized;unauthorized.valid=false;
|
|
assert(unauthorized.Poll(0)==S::FAILED && !unauthorized.m_Resolved);
|
|
}
|