Files
EscapistsReintroduced/tests/test_player_admission_policy.py
T

76 lines
4.1 KiB
Python

"""Execute source-local admission bodies and the reservation rejection predicate.
Only vector arithmetic and opaque UUID equality/nullness are substituted. This is
not a character collision, controller, callback, reconnect or native engine test.
"""
import re
from policy_harness import ROOT, body, run
base = ROOT / 'addons/core/Scripts/Game/ERR'
rules = body(base/'ERR_PlayerRoster.c', 'static bool CanEnter(')
rules += '\n' + body(base/'ERR_SpawnFinalization.c', 'static bool Allows(')
rules += '\n' + body(base/'ERR_SpawnFinalization.c', 'static bool PositionClose(')
rules = rules.replace('ERR_Phase.', 'ERR_Phase::').replace('ERR_PlayerLifeRules.', '')
rules = rules.replace('vector.', 'vector::').replace('Math.', 'Math::')
# Extract the actual condition, rather than maintaining a second implementation.
# Native geometry/terrain branches of Clear are deliberately not emulated.
clear = body(base/'ERR_SpawnPlacement.c', 'bool Clear(')
match = re.search(r'if \((id != ownReservation[^\n]+)\) return false;', clear)
assert match, 'Reservation policy moved: update the source-local extraction'
predicate = match[1].replace('vector.', 'vector::').replace('Math.', 'Math::')
rules += '\nstatic bool ReservationAllows(int id, int ownReservation, vector position, vector reserved) { return !(' + predicate + '); }\n'
run('''#include <cassert>
#include <cmath>
#include <initializer_list>
namespace ERR_Phase {enum {PREPARING,PRISON,RADIO_SEARCH,RADIO_DEFENCE,EXTRACTION,VICTORY,DEFEAT};}
struct UUID {
int identity;
bool IsNull() const { return identity==0; }
bool operator==(UUID other) const { return identity==other.identity; }
};
struct vector {
float x,y,z;
float operator[](int axis) const { return axis==0?x:(axis==1?y:z); }
static float DistanceXZ(vector a, vector b) {return std::hypot(a.x-b.x,a.z-b.z);}
};
namespace Math {static float AbsFloat(float value) {return std::abs(value);}}
''' + rules + '''
int main() {
// Complete independent authorization matrix, including invalid phases and null IDs.
for (int phase=-1;phase<=7;++phase)
for (int flags=0;flags<32;++flags)
for (int oldId=0;oldId<=2;++oldId)
for (int currentId=0;currentId<=2;++currentId) {
bool connected=flags&1,usedLife=flags&2,sameController=flags&4;
bool sameRequest=flags&8,noControlledEntity=flags&16;
bool expected=phase>=1&&phase<=3&&connected&&!usedLife&&oldId>0&&oldId==currentId&&sameController&&sameRequest&&noControlledEntity;
assert(Allows(phase,connected,usedLife,UUID{oldId},UUID{currentId},sameController,sameRequest,noControlledEntity)==expected);
}
const vector origin={0,0,0};
// Inclusive vertical tolerance and just-inside/outside horizontal boundary.
for (float y : {-1.001f,-1.f,-0.2f,0.f,0.2f,1.f,1.001f}) {
assert(PositionClose(vector{0,y,0},origin)==(std::abs(y)<=1));
assert(PositionClose(vector{.599f,y,0},origin)==(std::abs(y)<=1));
assert(!PositionClose(vector{.601f,y,0},origin));
}
assert(PositionClose(vector{.3f,.2f,.3f},origin));
assert(!PositionClose(vector{.5f,0,.5f},origin)); // radial, not independent axis allowance
assert(!PositionClose(vector{0,-20,0},origin)); // fallen below unchanged XZ
assert(!PositionClose(vector{0,20,0},origin)); // displaced above unchanged XZ
// Connection IDs1 and13 must conflict identically; only the caller's own slot is exempt.
for (int id : {1,13,99})
for (int owner : {-1,1,13,99})
for (float x : {-1.201f,-1.199f,0.f,1.199f,1.201f})
for (float y : {-2.001f,-2.f,-1.999f,0.f,1.999f,2.f,2.001f}) {
bool expected=id==owner || std::abs(x)>=1.2 || std::abs(y)>=2;
assert(ReservationAllows(id,owner,origin,vector{x,y,0})==expected);
}
assert(!ReservationAllows(1,13,origin,vector{.8f,0,.8f}));
assert(ReservationAllows(1,13,origin,vector{.9f,0,.9f}));
assert(ReservationAllows(1,13,origin,vector{1.5f,0,0})); // authored grid spacing
// Own reservation is excluded while finalization still checks native geometry separately.
assert(ReservationAllows(13,13,origin,origin));
assert(!ReservationAllows(13,1,origin,origin));
}
''')
print('PASS: player admission phase/life/identity/controller matrix, preload displacement limits, pending reservation overlap and owner exclusion')