71 lines
4.7 KiB
Python
71 lines
4.7 KiB
Python
"""Execute the production loader selection, quotas, retry and rotation gates."""
|
|
from pathlib import Path
|
|
import subprocess,tempfile
|
|
root=Path(__file__).resolve().parents[2]
|
|
s=(root/'src/server/game/World/AdventurePopulationMgr.cpp').read_text()
|
|
def function(sig):
|
|
a=s.index(sig);b=s.index('{',a);e=b+1;depth=1
|
|
while depth:depth+=(s[e]=='{')-(s[e]=='}');e+=1
|
|
return s[a:e]
|
|
code=r'''
|
|
#include "AdventurePopulationPolicy.h"
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <string>
|
|
using uint32=unsigned;using uint64=unsigned long long;
|
|
using namespace AdventurePopulationPolicy;
|
|
namespace GameTime{time_t now=100000;time_t GetGameTime(){return now;}}
|
|
struct ObjectGuid{enum{Player};unsigned id;ObjectGuid(int,unsigned i):id(i){}};
|
|
namespace HighGuid{constexpr int Player=0;}
|
|
namespace ObjectAccessor{std::unordered_set<unsigned>connected;bool FindConnectedPlayer(ObjectGuid g){return connected.count(g.id);}}
|
|
enum class ClientlessPlayerState{Offline,Loading,Online,Removing};
|
|
struct World{std::unordered_map<unsigned,ClientlessPlayerState>states;auto GetClientlessPlayerState(unsigned id){return states[id];}}world;auto sWorld=&world;
|
|
struct Core{bool succeed=true;std::vector<unsigned>started;bool StartPlayer(unsigned,unsigned guid,int){started.push_back(guid);if(succeed)world.states[guid]=ClientlessPlayerState::Loading;return succeed;}}core;auto sClientlessPlayerMgr=&core;
|
|
int CreateClientlessAdventureLuaController(){return 0;}
|
|
float sample=.5;float frand(int,int){return sample;}
|
|
#define TC_LOG_INFO(...) ((void)0)
|
|
struct AdventurePopulationMgr{
|
|
struct Bot{uint32 Guid,Account,Pool;uint64 Created;uint32 Failures=0;time_t Retry=0;};
|
|
unsigned _cursor=0,_rotationAge=86400;double _rotationChance=.15;
|
|
std::array<unsigned,4>_targets{};std::vector<Bot>_bots;std::unordered_set<unsigned>_considered;
|
|
std::vector<std::pair<unsigned,unsigned>>generated;
|
|
void Step();void Generate(unsigned pool,unsigned replace=0){generated.push_back({pool,replace});}
|
|
};
|
|
'''+function('void AdventurePopulationMgr::Step()')+r'''
|
|
int main(){
|
|
assert(Pool(1,true)==0 && Pool(1,false)==1 && Pool(2,true)==2 && Pool(2,false)==3);
|
|
assert(!Replace(true,true,false,100,0,0,1));assert(!Replace(false,false,false,100,0,0,1));
|
|
assert(!Replace(false,true,true,100,0,0,1));assert(!Replace(false,true,false,99,100,0,1));
|
|
assert(Replace(false,true,false,100,100,.1,.2));assert(!Replace(false,true,false,100,0,.3,.2));
|
|
assert(!Deficit(5,3,2) && Deficit(5,2,1)==2 && !Deficit(0,2,0));
|
|
AdventurePopulationMgr mgr;
|
|
mgr.Step();assert(mgr.generated.empty() && core.started.empty());
|
|
mgr._targets={1,2,3,4};
|
|
mgr.Step();assert(mgr.generated.back().first==0);mgr.Step();assert(mgr.generated.back().first==1);
|
|
mgr.Step();assert(mgr.generated.back().first==2);mgr.Step();assert(mgr.generated.back().first==3);
|
|
mgr={};mgr._targets={1,0,0,0};mgr._bots={{10,6,0,0},{11,6,0,0},{12,6,2,0}};
|
|
world.states[10]=ClientlessPlayerState::Online;mgr.Step();assert(core.started.empty());
|
|
world.states[10]=ClientlessPlayerState::Loading;mgr.Step();assert(core.started.empty());
|
|
world.states[10]=ClientlessPlayerState::Offline;mgr.Step();assert(core.started.back()==10);assert(mgr.generated.empty());
|
|
world.states[10]=ClientlessPlayerState::Offline;core.succeed=false;core.started.clear();
|
|
mgr._bots[0].Retry=0;mgr.Step();assert(core.started.size()==1 && mgr._bots[0].Failures==1);
|
|
mgr.Step();assert(core.started.back()==11 && mgr.generated.empty()); // retry other saved candidate
|
|
mgr.Step();assert(core.started.size()==2 && mgr.generated.empty()); // no unbounded regeneration
|
|
mgr={};world.states.clear();core.started.clear();core.succeed=true;
|
|
mgr._targets={0,1,0,0};mgr._bots={{20,6,1,1}};sample=0;mgr.Step();
|
|
assert(mgr.generated.size()==1 && mgr.generated[0].second==20 && core.started.empty());
|
|
mgr.Step();assert(core.started.back()==20); // considered once per boot, no replacement loop
|
|
mgr={};core.started.clear();world.states.clear();mgr._targets={1,0,0,0};mgr._bots={{30,6,0,1}};
|
|
mgr._rotationChance=1;mgr.Step();assert(mgr.generated.empty() && core.started.back()==30); // persistence absolute
|
|
mgr={};core.started.clear();world.states.clear();mgr._targets={0,0,0,1};mgr._bots={{40,6,3,1}};
|
|
ObjectAccessor::connected.insert(40);mgr.Step();assert(core.started.empty() && mgr.generated.empty()); // real connected player protected
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p=Path(d);(p/'test.cpp').write_text(code)
|
|
subprocess.run(['g++','-std=c++17','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
|
subprocess.run([str(p/'test')],check=True)
|
|
print('Production population: four independent faction/persistence targets, fair selection, pending reservations, retries, existing inventory, once-per-boot rotation and connected/persistent protection passed')
|