Another rework for scripthooks, for better performance.
continuous-integration/drone/push Build is passing Details

This new version caches pointers to the pointers to scripts, so that we can build the data once and then simply iterate over it whenever we want to run a hook.
This commit is contained in:
Deukhoofd 2019-11-10 17:08:42 +01:00
parent e1a8d80863
commit d8332f9e40
19 changed files with 118 additions and 72 deletions

View File

@ -7,12 +7,12 @@
namespace CreatureLib::Battling{
class ChoiceQueue {
std::vector<const BaseTurnChoice*> _queue;
std::vector<BaseTurnChoice*> _queue;
size_t _current = 0;
public:
bool HasCompletedQueue = false;
explicit ChoiceQueue(std::vector<const BaseTurnChoice*> queue)
explicit ChoiceQueue(std::vector<BaseTurnChoice*> queue)
:_queue(std::move(queue)){}
const BaseTurnChoice* Dequeue(){
@ -25,7 +25,7 @@ namespace CreatureLib::Battling{
return _current < _queue.size();
}
std::vector<const BaseTurnChoice*>& GetInnerQueue(){
std::vector<BaseTurnChoice*>& GetInnerQueue(){
return _queue;
}
};

View File

@ -1,8 +1,6 @@
#include "TurnOrdering.hpp"
#include "../TurnChoices/AttackTurnChoice.hpp"
#include "../Models/Creature.hpp"
#include "../Models/Battle.hpp"
#include "../../Core/Exceptions/NotImplementedException.hpp"
#include <algorithm>
@ -15,8 +13,8 @@ bool ___ChoiceOrderFunc(const BaseTurnChoice* a, const BaseTurnChoice* b, Core::
if (aKind != bKind)
return aKind > bKind;
if (aKind == TurnChoiceKind::Attack){
auto aPriority = static_cast<const AttackTurnChoice*>(a)->GetPriority();
auto bPriority = static_cast<const AttackTurnChoice*>(b)->GetPriority();
auto aPriority = dynamic_cast<const AttackTurnChoice*>(a)->GetPriority();
auto bPriority = dynamic_cast<const AttackTurnChoice*>(b)->GetPriority();
if (aPriority != bPriority)
return aPriority > bPriority;
}
@ -29,7 +27,7 @@ bool ___ChoiceOrderFunc(const BaseTurnChoice* a, const BaseTurnChoice* b, Core::
return randomValue == 0;
}
void TurnOrdering::OrderChoices(std::vector<const BaseTurnChoice *> &vec, Core::Random& rand) {
void TurnOrdering::OrderChoices(std::vector<BaseTurnChoice *> &vec, Core::Random& rand) {
auto comp = [&](const BaseTurnChoice * a,const BaseTurnChoice * b)-> bool {
return ___ChoiceOrderFunc(a,b,rand);
};

View File

@ -8,7 +8,7 @@
namespace CreatureLib::Battling {
class TurnOrdering {
public:
static void OrderChoices(std::vector<const BaseTurnChoice*>& vec, CreatureLib::Core::Random& rand);
static void OrderChoices(std::vector<BaseTurnChoice*>& vec, CreatureLib::Core::Random& rand);
};
}

View File

@ -33,10 +33,10 @@ void Battle::CheckChoicesSetAndRun() {
return;
}
}
auto choices = std::vector<const BaseTurnChoice*>(_numberOfSides * _creaturesPerSide);
auto choices = std::vector<BaseTurnChoice*>(_numberOfSides * _creaturesPerSide);
auto i = 0;
for (auto side: _sides){
for (const BaseTurnChoice* choice: side->GetChoices()){
for (BaseTurnChoice* choice: side->GetChoices()){
if (choice->GetKind() == TurnChoiceKind::Attack){
auto attack = dynamic_cast<const AttackTurnChoice*>(choice)->GetAttack();
uint8_t uses = 1;
@ -93,6 +93,6 @@ void Battle::FillRecall(uint8_t side, uint8_t, Creature *c) {
}
}
void Battle::GetActiveScripts(ScriptAggregator &aggr) const {
aggr.Add(&_volatile);
void Battle::GetActiveScripts(std::vector<ScriptWrapper> &scripts) {
scripts.emplace_back(&_volatile);
}

View File

@ -42,7 +42,7 @@ namespace CreatureLib::Battling {
void FillRecall(uint8_t side, uint8_t, Creature* c);
void GetActiveScripts(ScriptAggregator &aggr) const override;
void GetActiveScripts(std::vector<ScriptWrapper> &scripts) override;
};
}

View File

@ -16,11 +16,11 @@ void BattleSide::ResetChoices() {
}
}
const std::vector<const BaseTurnChoice *>& BattleSide::GetChoices() const{
const std::vector<BaseTurnChoice *>& BattleSide::GetChoices() const{
return _choices;
}
void BattleSide::SetChoice(const BaseTurnChoice *choice) {
void BattleSide::SetChoice(BaseTurnChoice *choice) {
auto find = std::find(_creatures.begin(), _creatures.end(), choice->GetUser());
if (find ==_creatures.end())
throw CreatureException("User not found");
@ -41,7 +41,7 @@ Creature *BattleSide::GetCreature(uint8_t index) const {
return _creatures[index];
}
void BattleSide::GetActiveScripts(ScriptAggregator &aggr) const {
aggr.Add(&_volatile);
_battle->GetActiveScripts(aggr);
void BattleSide::GetActiveScripts(std::vector<ScriptWrapper> &scripts) {
scripts.emplace_back(&_volatile);
_battle->GetActiveScripts(scripts);
}

View File

@ -9,7 +9,7 @@ namespace CreatureLib::Battling{
class BattleSide : public ScriptSource{
uint8_t _creaturesPerSide;
std::vector<Creature*> _creatures;
std::vector<const BaseTurnChoice*> _choices;
std::vector<BaseTurnChoice*> _choices;
uint8_t _choicesSet = 0;
ScriptSet _volatile;
Battle* _battle;
@ -21,9 +21,9 @@ namespace CreatureLib::Battling{
}
[[nodiscard]] bool AllChoicesSet() const;
[[nodiscard]] const std::vector<const BaseTurnChoice*>& GetChoices() const;
[[nodiscard]] const std::vector<BaseTurnChoice*>& GetChoices() const;
void SetChoice(const BaseTurnChoice* choice);
void SetChoice(BaseTurnChoice* choice);
void ResetChoices();
void SetCreature(Creature* creature, uint8_t index);
@ -31,7 +31,7 @@ namespace CreatureLib::Battling{
Creature* GetCreature(uint8_t index) const;
bool CreatureOnSide(const Creature* creature) const;
void GetActiveScripts(ScriptAggregator &aggr) const override;
void GetActiveScripts(std::vector<ScriptWrapper> &scripts) override;
};
}

View File

@ -1,4 +1,5 @@
#include <algorithm>
#include <utility>
#include "Creature.hpp"
#include "../Models/Battle.hpp"
@ -20,9 +21,9 @@ Battling::Creature::Creature(const Library::CreatureSpecies* species, const Libr
__Gender(gender),
__Coloring(coloring),
__HeldItem(heldItem),
_nickname(nickname),
_nickname(std::move(nickname)),
_talentIndex(talent),
_attacks(attacks)
_attacks(std::move(attacks))
{}
@ -140,8 +141,8 @@ bool Battling::Creature::HasType(uint8_t type) const {
return std::find(t.begin(), t.end(), type) != t.end();
}
void Battling::Creature::GetActiveScripts(Battling::ScriptAggregator &aggr) const {
aggr.Add(_status);
aggr.Add(&_volatile);
_side->GetActiveScripts(aggr);
void Battling::Creature::GetActiveScripts(std::vector<ScriptWrapper> &scripts) {
scripts.emplace_back(&_status);
scripts.emplace_back(&_volatile);
_side->GetActiveScripts(scripts);
}

View File

@ -18,7 +18,8 @@ namespace CreatureLib::Battling{
class Creature : public ScriptSource{
GetProperty(const Library::CreatureSpecies*, Species);
GetProperty(const Library::SpeciesVariant*, Variant);
GetProperty(const Library::SpeciesVariant*, Variant)
GetProperty(uint8_t, Level);
GetProperty(uint32_t, Experience);
GetProperty(Core::StatisticSet<uint8_t >, StatExperience);
@ -67,7 +68,7 @@ namespace CreatureLib::Battling{
[[nodiscard]] const std::vector<uint8_t>& GetTypes() const;
[[nodiscard]] bool HasType(uint8_t type) const;
void GetActiveScripts(ScriptAggregator& aggr) const override;
void GetActiveScripts(std::vector<ScriptWrapper> &scripts) override;
//region Stat APIs
@ -85,6 +86,7 @@ namespace CreatureLib::Battling{
void RecalculateBoostedStats();
void RecalculateFlatStat(Core::Statistic);
void RecalculateBoostedStat(Core::Statistic);
//endregion

View File

@ -89,8 +89,10 @@ namespace CreatureLib::Battling {
return _attack;
}
void GetActiveScripts(ScriptAggregator &aggr) const override {
aggr.Add(_script);
protected:
void GetActiveScripts(std::vector<ScriptWrapper> &scripts) override {
scripts.emplace_back(&_script);
GetUser()->GetActiveScripts(scripts);
}
};
}

View File

@ -6,26 +6,23 @@
#include "Script.hpp"
#include "ScriptSet.hpp"
#include "../../Core/Exceptions/NotReachableException.hpp"
#include "ScriptWrapper.hpp"
namespace CreatureLib::Battling{
class ScriptAggregator{
std::queue<std::any> _queue;
__gnu_cxx::__normal_iterator<ScriptWrapper *, std::vector<ScriptWrapper>> _selfIterator;
__gnu_cxx::__normal_iterator<ScriptWrapper *, std::vector<ScriptWrapper>> _selfEnd;
bool _isSetSet = false;
std::__detail::_Node_const_iterator<std::pair<const std::string, Script *>, false, true> _setIterator;
std::__detail::_Node_const_iterator<std::pair<const std::string, Script *>, false, true> _setEnd;
public:
ScriptAggregator() = default;
void Add(Script* script){
_queue.push(script);
}
void Add(const ScriptSet* scriptSet){
_queue.push(scriptSet);
}
ScriptAggregator(std::vector<ScriptWrapper> scripts){
_selfIterator = scripts.begin();
_selfEnd = scripts.end();
};
bool HasNext(){
return !_queue.empty() || _isSetSet;
return _selfIterator != _selfEnd || _isSetSet;
}
Script* GetNext(){
@ -42,15 +39,17 @@ namespace CreatureLib::Battling{
}
return s;
}
if (_queue.empty())
if (_selfIterator == _selfEnd)
return nullptr;
auto next = _queue.front();
_queue.pop();
if (next.type() == typeid(Script*)){
return std::any_cast<Script*>(next);
auto next = *_selfIterator;
if (!next.IsSet()){
auto scriptPtr = next.GetScript();
if (scriptPtr == nullptr)
return GetNext();
return *scriptPtr;
}
else{
auto set = std::any_cast<const ScriptSet*>(next);
auto set = next.GetScriptSet();
if (set->Count() == 0)
return GetNext();
auto it = set->GetIterator();

View File

@ -1,7 +1,6 @@
#define HOOK(hookName, source, ... ) \
{ \
auto aggregator = CreatureLib::Battling::ScriptAggregator(); \
source -> GetActiveScripts(aggregator); \
auto aggregator = source -> GetScriptIterator(); \
while (aggregator.HasNext()){ \
auto next = aggregator.GetNext(); \
if (next == nullptr) continue; \

View File

@ -7,8 +7,18 @@
namespace CreatureLib::Battling{
class ScriptSource {
bool _areScriptsInitialized = false;
std::vector<ScriptWrapper> _scripts;
protected:
virtual void GetActiveScripts(std::vector<ScriptWrapper>& scripts) = 0;
public:
virtual void GetActiveScripts(ScriptAggregator& aggr) const = 0;
ScriptAggregator GetScriptIterator(){
if (!_areScriptsInitialized){
GetActiveScripts(_scripts);
_areScriptsInitialized = true;
}
return ScriptAggregator(_scripts);
}
};
}

View File

@ -0,0 +1,3 @@
#include "ScriptWrapper.hpp"

View File

@ -0,0 +1,33 @@
#ifndef CREATURELIB_SCRIPTWRAPPER_HPP
#define CREATURELIB_SCRIPTWRAPPER_HPP
#include <variant>
#include "Script.hpp"
#include "ScriptSet.hpp"
namespace CreatureLib::Battling{
class ScriptWrapper {
std::variant<Script**, ScriptSet*> _value;
bool _isSet;
public:
ScriptWrapper(Script** s) : _value(s), _isSet(false){}
ScriptWrapper(ScriptSet* s) : _value(s), _isSet(true){}
bool IsSet() const{
return _isSet;
}
Script** GetScript() const{
return std::get<Script**>(_value);
}
ScriptSet* GetScriptSet() const{
return std::get<ScriptSet*>(_value);
}
};
}
#endif //CREATURELIB_SCRIPTWRAPPER_HPP

View File

@ -31,10 +31,12 @@ namespace CreatureLib::Battling{
return _target;
}
void GetActiveScripts(ScriptAggregator &aggr) const override {
aggr.Add(_attackScript);
GetUser()->GetActiveScripts(aggr);
protected:
void GetActiveScripts(std::vector<ScriptWrapper> &scripts) override {
scripts.emplace_back(&_attackScript);
GetUser()->GetActiveScripts(scripts);
}
};
}

View File

@ -17,10 +17,6 @@ namespace CreatureLib::Battling{
[[nodiscard]] inline Creature* GetUser() const{
return _user;
}
void GetActiveScripts(ScriptAggregator &aggr) const override {
}
};
}

View File

@ -12,8 +12,9 @@ namespace CreatureLib::Battling {
return TurnChoiceKind ::Pass;
}
void GetActiveScripts(ScriptAggregator &aggr) const override {
GetUser()->GetActiveScripts(aggr);
protected:
void GetActiveScripts(std::vector<ScriptWrapper> &scripts) override {
GetUser()->GetActiveScripts(scripts);
}
};
}

View File

@ -11,12 +11,12 @@ using namespace CreatureLib::Battling;
TEST_CASE( "Turn ordering: Attack before pass", "[Battling]" ) {
auto choice1 = new PassTurnChoice(nullptr);
auto choice2 = new AttackTurnChoice(nullptr, nullptr, Target(0,0));
auto vec = std::vector<const BaseTurnChoice*>{choice1, choice2};
auto vec = std::vector<BaseTurnChoice*>{choice1, choice2};
auto rand = Core::Random();
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
vec = std::vector<const BaseTurnChoice*>{choice2, choice1};
vec = std::vector<BaseTurnChoice*>{choice2, choice1};
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
@ -31,12 +31,12 @@ TEST_CASE( "Turn ordering: High priority goes before no priority", "[Battling]"
auto a2 = new LearnedAttack(l->GetAttack("highPriority"), AttackLearnMethod::Unknown);
auto choice1 = new AttackTurnChoice(nullptr, a1, Target(0,0));
auto choice2 = new AttackTurnChoice(nullptr, a2, Target(0,0));
auto vec = std::vector<const BaseTurnChoice*>{choice1, choice2};
auto vec = std::vector<BaseTurnChoice*>{choice1, choice2};
auto rand = Core::Random();
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
vec = std::vector<const BaseTurnChoice*>{choice2, choice1};
vec = std::vector<BaseTurnChoice*>{choice2, choice1};
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
@ -53,12 +53,12 @@ TEST_CASE( "Turn ordering: Higher priority goes before high priority", "[Battlin
auto a2 = new LearnedAttack(l->GetAttack("higherPriority"), AttackLearnMethod::Unknown);
auto choice1 = new AttackTurnChoice(nullptr, a1, Target(0,0));
auto choice2 = new AttackTurnChoice(nullptr, a2, Target(0,0));
auto vec = std::vector<const BaseTurnChoice*>{choice1, choice2};
auto vec = std::vector<BaseTurnChoice*>{choice1, choice2};
auto rand = Core::Random();
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
vec = std::vector<const BaseTurnChoice*>{choice2, choice1};
vec = std::vector<BaseTurnChoice*>{choice2, choice1};
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
@ -75,12 +75,12 @@ TEST_CASE( "Turn ordering: High priority goes before low priority", "[Battling]"
auto a2 = new LearnedAttack(l->GetAttack("higherPriority"), AttackLearnMethod::Unknown);
auto choice1 = new AttackTurnChoice(nullptr, a1, Target(0,0));
auto choice2 = new AttackTurnChoice(nullptr, a2, Target(0,0));
auto vec = std::vector<const BaseTurnChoice*>{choice1, choice2};
auto vec = std::vector<BaseTurnChoice*>{choice1, choice2};
auto rand = Core::Random();
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
vec = std::vector<const BaseTurnChoice*>{choice2, choice1};
vec = std::vector<BaseTurnChoice*>{choice2, choice1};
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
@ -97,12 +97,12 @@ TEST_CASE( "Turn ordering: No priority goes before low priority", "[Battling]" )
auto a2 = new LearnedAttack(l->GetAttack("standard"), AttackLearnMethod::Unknown);
auto choice1 = new AttackTurnChoice(nullptr, a1, Target(0,0));
auto choice2 = new AttackTurnChoice(nullptr, a2, Target(0,0));
auto vec = std::vector<const BaseTurnChoice*>{choice1, choice2};
auto vec = std::vector<BaseTurnChoice*>{choice1, choice2};
auto rand = Core::Random();
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);
vec = std::vector<const BaseTurnChoice*>{choice2, choice1};
vec = std::vector<BaseTurnChoice*>{choice2, choice1};
TurnOrdering::OrderChoices(vec,rand);
CHECK(vec[0] == choice2);
CHECK(vec[1] == choice1);