Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
103 lines
6.5 KiB
Python
103 lines
6.5 KiB
Python
"""Execute production view-distance policy, local ownership and command bodies.
|
|
Native rendering and replication require separate engine validation.
|
|
"""
|
|
import re
|
|
from policy_harness import ROOT, body, run
|
|
core = ROOT/'addons/core/Scripts/Game/ERR/00_Core'
|
|
service = core/'ERR_ViewDistance.c'
|
|
admin = core/'ERR_AdminService.c'
|
|
settings = core/'ERR_Settings.c'
|
|
def cpp(s):
|
|
s = s.replace('protected ', '').replace('out int ', 'int& ')
|
|
for name in ('Math', 'System', 'ERR_Settings', 'ERR_CommandArguments', 'ERR_TimeService', 'ERR_EnvironmentService', 'ERR_AdminMenuFields'):
|
|
s = s.replace(name + '.', name + '::')
|
|
s = s.replace('GetGame().', 'GetGame()->').replace('mode.Settings().', 'mode.Settings()->')
|
|
s = s.replace('GetGame()->GetViewDistanceServerLimit().ToString()', 'std::to_string(GetGame()->GetViewDistanceServerLimit())')
|
|
s = s.replace('mode.Settings()->Get("viewDistanceMetres").ToString()', 'std::to_string(mode.Settings()->Get("viewDistanceMetres"))')
|
|
s = s.replace('value.ToString()', 'std::to_string(value)')
|
|
s = s.replace('"0123456789".IndexOf(', 'string("0123456789").IndexOf(')
|
|
return s.replace('array<string>', 'Strings').replace('ERR_GameMode mode', 'ERR_GameMode& mode').replace('Strings replies', 'Strings& replies')
|
|
methods = '\n'.join(cpp(body(service, sig)) for sig in ('static int ServerDistance(', 'static float LocalDistance(', 'void Apply(', 'void Restore('))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
struct string : std::string {
|
|
using std::string::string; string(std::string s):std::string(s){}
|
|
bool IsEmpty(){return empty();} int Length(){return size();}
|
|
string Substring(int a,int b){return substr(a,b);} int ToInt(){return std::stoi(*this);}
|
|
int IndexOf(string s){auto n=find(s);return n==npos?-1:int(n);}
|
|
};
|
|
struct Strings:std::vector<string> {using vector::vector;int Count(){return size();}void Insert(string s){push_back(s);}};
|
|
namespace Math {float Min(float a,float b){return std::min(a,b);}float Max(float a,float b){return std::max(a,b);}float AbsFloat(float f){return std::abs(f);}}
|
|
namespace System {bool dedicated=false;bool IsConsoleApp(){return dedicated;}}
|
|
struct Game {float current=1600,minimum=500,maximum=10000,limit=5000;int writes=0;
|
|
float GetViewDistance(){return current;}float GetMinimumViewDistance(){return minimum;}
|
|
float GetMaximumViewDistance(){return maximum;}float GetViewDistanceServerLimit(){return limit;}
|
|
void SetViewDistance(float v){current=v;++writes;}} game;
|
|
Game* live=&game;Game* GetGame(){return live;}
|
|
struct Distance {float m_Original=-1,m_Applied=-1;
|
|
''' + methods + r'''
|
|
};
|
|
struct ERR_Settings {
|
|
''' + cpp(body(settings,'static bool Valid(')) + cpp(body(settings,'static int AddedDefault(')) + cpp(body(settings,'static bool Randomizable(')) + r'''
|
|
int saved=0;bool fail=false;int Get(string key){return saved;}bool Set(string key,int value){if(fail||!Valid(key,value))return false;saved=value;return true;}
|
|
};
|
|
struct ERR_TimeService {static bool ParseClock(string,int&){return false;}};
|
|
struct ERR_EnvironmentService {static bool ParseDate(string,int&){return false;}static bool ParseProfile(string,int&){return false;}};
|
|
struct ERR_CommandArguments {
|
|
''' + cpp(body(admin,'static bool Integer(')) + cpp(body(admin,'static bool Selection(')) + r'''
|
|
};
|
|
struct ERR_AdminMenuFields {static string Display(string,string value){return value;}};
|
|
struct ERR_GameMode {ERR_Settings storage;int published=0;ERR_Settings* Settings(){return &storage;}void PublishViewDistance(){++published;}};
|
|
struct Admin {
|
|
''' + cpp(body(admin,'protected bool ViewDistanceCommand(')) + r'''
|
|
};
|
|
int main(){
|
|
assert(ERR_Settings::AddedDefault("viewDistanceMetres")==0);
|
|
assert(!ERR_Settings::Randomizable("viewDistanceMetres"));
|
|
for(int v:{-2,-1,1,499,10001})assert(!ERR_Settings::Valid("viewDistanceMetres",v));
|
|
for(int v:{0,500,2500,10000})assert(ERR_Settings::Valid("viewDistanceMetres",v));
|
|
assert(Distance::ServerDistance(0,1600)==0);
|
|
assert(Distance::ServerDistance(5000,1600)==1600);
|
|
assert(Distance::ServerDistance(3000,0)==3000);
|
|
assert(Distance::LocalDistance(3000,500,2500,5000)==2500);
|
|
assert(Distance::LocalDistance(3000,500,10000,1600)==1600);
|
|
assert(Distance::LocalDistance(500,1000,10000,800)==800);
|
|
Distance local;local.Apply(0);assert(game.writes==0);
|
|
local.Apply(3000);assert(game.current==3000&&game.writes==1);
|
|
local.Apply(3000);assert(game.writes==1);
|
|
local.Apply(6000);assert(game.current==5000);
|
|
local.Apply(0);assert(game.current==1600);
|
|
local.Apply(2500);game.current=900;local.Restore();assert(game.current==900); // another camera owner wins
|
|
local.Apply(3500);local.Restore();assert(game.current==900); // new override captures fresh baseline
|
|
System::dedicated=true;local.Apply(3000);assert(game.current==900);System::dedicated=false;
|
|
live=nullptr;local.Apply(3000);local.Restore();live=&game;
|
|
Admin admin;ERR_GameMode mode;Strings replies;
|
|
assert(!admin.ViewDistanceCommand(mode,{"mission","weather","show"},replies));
|
|
assert(admin.ViewDistanceCommand(mode,{"mission","viewdistance","set","3000"},replies));
|
|
assert(mode.storage.saved==3000&&mode.published==1);
|
|
mode.storage.fail=true;admin.ViewDistanceCommand(mode,{"mission","viewdistance","set","4000"},replies);
|
|
assert(mode.storage.saved==3000&&mode.published==1);mode.storage.fail=false;
|
|
for(string v:{"-1","random","499","10001","1.5","2500junk","9999999"}){
|
|
admin.ViewDistanceCommand(mode,{"mission","viewdistance","set",v},replies);
|
|
assert(mode.storage.saved==3000&&mode.published==1);
|
|
}
|
|
admin.ViewDistanceCommand(mode,{"mission","viewdistance","show"},replies);assert(mode.published==1);
|
|
admin.ViewDistanceCommand(mode,{"mission","viewdistance","set","default"},replies);
|
|
assert(mode.storage.saved==0&&mode.published==2);
|
|
}
|
|
''')
|
|
# Lifecycle wiring: callback + periodic application cover JIP and native menu resets.
|
|
mode=(core/'ERR_GameMode.c').read_text()
|
|
assert '[RplProp(onRplName: "ApplyLocalViewDistance")]' in mode
|
|
assert 'CallLater(ApplyLocalViewDistance, 1000, true)' in body(core/'ERR_GameMode.c','protected override void EOnInit(')
|
|
assert 'GetTopMenu()) return;' in body(core/'ERR_GameMode.c','protected void ApplyLocalViewDistance(')
|
|
for sig in ('protected override void OnGameModeEnd(', 'protected void StopServices('):
|
|
assert 'StopLocalViewDistance();' in body(core/'ERR_GameMode.c',sig)
|
|
execute=body(admin,'void Execute(')
|
|
assert execute.index('if (!authorized)') < execute.index('ViewDistanceCommand(')
|
|
print('PASS: view-distance bounds, default, save failures, command parsing, temporary ownership, server/platform limits and lifecycle wiring')
|