Files
Viano-Core/tests/clientless/test_clientless_social_notifications.py
T

57 lines
3.6 KiB
Python

"""Exercise production map-worker social/helper queues concurrently."""
from pathlib import Path
import subprocess
import tempfile
root=Path(__file__).resolve().parents[2]
source=(root/'src/server/game/World/ClientlessPlayerMgr.cpp').read_text()
def function(signature):
a=source.index(signature);b=source.index('{',a);e=b+1;depth=1
while depth:depth+=(source[e]=='{')-(source[e]=='}');e+=1
return source[a:e]
code=r'''
#include <cassert>
#include <deque>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
using uint32=unsigned;
struct ObjectGuid{unsigned id;unsigned GetCounter()const{return id;}bool operator==(ObjectGuid b)const{return id==b.id;}};
struct Player;
struct Unit {ObjectGuid guid;Player*owner=nullptr;std::vector<Unit*> attackers;ObjectGuid GetGUID(){return guid;}virtual Player*ToPlayer(){return nullptr;}Player*GetControllingPlayer(){return owner;}auto&getAttackers(){return attackers;}};
struct Session {bool clientless=true;bool IsClientless(){return clientless;}};
struct Player:Unit {Session session;Player(){owner=this;}Player*ToPlayer()override{return this;}Session*GetSession(){return &session;}};
struct ClientlessPlayerMgr {
struct EmoteNotice{ObjectGuid Source;uint32 Emote;};struct HelpNotice{ObjectGuid Helper,Target;};
std::mutex _damageMutex;std::unordered_map<uint32,std::deque<EmoteNotice>> _emoteNotices;std::unordered_map<uint32,std::deque<HelpNotice>> _helpNotices;
void TextEmote(Player*,Player*,uint32);void SharedTargetDamage(Unit*,Unit*,uint32);
};
'''+function('void ClientlessPlayerMgr::TextEmote(')+'\n'+function('void ClientlessPlayerMgr::SharedTargetDamage(')+r'''
int main(){
ClientlessPlayerMgr mgr;std::vector<Player> bots(16);std::vector<std::thread> threads;
Player helper;helper.guid={77};helper.session.clientless=false;
for(unsigned i=0;i<bots.size();++i){bots[i].guid={i+1};threads.emplace_back([&,i]{
Unit target;target.guid={900+i};target.attackers={&bots[i],&helper};
for(unsigned n=1;n<=1000;++n){mgr.TextEmote(&helper,&bots[i],n);mgr.SharedTargetDamage(&target,&helper,n);}
});}
for(auto&t:threads)t.join();
for(unsigned i=0;i<bots.size();++i){
auto const&emotes=mgr._emoteNotices[i+1];assert(emotes.size()==8);
auto const&helps=mgr._helpNotices[i+1];assert(helps.size()==8);
for(unsigned n=0;n<8;++n){assert(emotes[n].Source.id==77 && emotes[n].Emote==n+1);assert(helps[n].Helper.id==77 && helps[n].Target.id==900+i);}
}
mgr._emoteNotices.clear();mgr.TextEmote(nullptr,&bots[0],1);mgr.TextEmote(&helper,&helper,1);mgr.TextEmote(&bots[0],&helper,1);assert(mgr._emoteNotices.empty());
mgr._helpNotices.clear();Unit target;target.guid={1000};target.attackers={&bots[0]};
mgr.SharedTargetDamage(&target,&helper,0);mgr.SharedTargetDamage(&target,nullptr,10);assert(mgr._helpNotices.empty());
Unit pet;pet.guid={800};pet.owner=&helper;mgr.SharedTargetDamage(&target,&pet,10);assert(mgr._helpNotices[1].front().Helper.id==77);
}
'''
with tempfile.TemporaryDirectory() as d:
p=Path(d);(p/'test.cpp').write_text(code)
subprocess.run(['g++','-std=c++17','-pthread',str(p/'test.cpp'),'-o',str(p/'test')],check=True)
subprocess.run([str(p/'test')],check=True)
unit=(root/'src/server/game/Entities/Unit/Unit.cpp').read_text();start=unit.index('uint32 Unit::DealDamage(')
hook=unit.index('sClientlessPlayerMgr->SharedTargetDamage(',start)
assert unit.index('GetCommandStatus(CHEAT_GOD)',start)<hook<unit.index('Unit::Kill(attacker, victim, durabilityLoss);',hook)
print('Production social/helper queues: concurrent GUID-only delivery, bounded queues, human/self/zero-damage gates, pet attribution and lethal-cleanup ordering passed')