Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make logic resolving 4x faster and allow CI codes for stages #166

Merged
merged 6 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dist/
*~
.DS_Store
thumbs.db
gmon.out

# exclude unused and scratch pad files
.trash
Expand Down
6 changes: 6 additions & 0 deletions api/lua/definition/poptracker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ function Tracker:UiHint(name, value) end
---@type boolean
Tracker.BulkUpdate = false

---Allow to evaluate logic rules fewer times than items are updated.
---Only available in PopTracker, since 0.27.1.
---Use as: `if Tracker.AllowDeferredLogicUpdate == false then Tracker.AllowDeferredLogicUpdate = true end`
---@type boolean
Tracker.AllowDeferredLogicUpdate = false


---- ScriptHost ----

Expand Down
1 change: 1 addition & 0 deletions doc/PACKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ The following interfaces are provided:
* `mixed :FindObjectForCode(string)`: returns items for `code` or location section for `@location/section`
* `void :UiHint(name, value)`: sends a hint to the Ui, see [Ui Hints](#ui-hints). Only available in PopTracker, since 0.11.0
* `bool .BulkUpdate`: can be set to true from Lua to pause running logic rules.
* `bool .AllowDeferredLogicUpdate`: can be set to true from Lua to allow evaluating logic rules fewer times than items are updated.


### global ScriptHost
Expand Down
9 changes: 5 additions & 4 deletions src/core/baseitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class BaseItem { // TODO: move stuff over to JsonItem; TODO: make some stuff pur
std::string _id;
std::string _name;
Type _type = Type::NONE;
std::list<std::string> _codes;
std::vector<std::string> _codes;
bool _capturable = false;
bool _loop = false;
bool _allowDisabled = false;
Expand Down Expand Up @@ -122,9 +122,10 @@ class BaseItem { // TODO: move stuff over to JsonItem; TODO: make some stuff pur
}


virtual int providesCode(const std::string code) const { // FIXME: make this pure?
if (_count && canProvideCode(code)) return _count;
return (_stage1 && canProvideCode(code));
virtual int providesCode(const std::string& code) const { // FIXME: make this pure?
if (_count && canProvideCode(code))
return _count;
return _stage1 && canProvideCode(code);
}

virtual bool canProvideCode(const std::string& code) const { // FIXME: make this pure?
Expand Down
12 changes: 10 additions & 2 deletions src/core/jsonitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ std::string JsonItem::getCodesString() const {
return s;
}

const std::list<std::string>& JsonItem::getCodes(int stage) const {
const std::vector<std::string>& JsonItem::getCodes(int stage) const {
if (_type == Type::TOGGLE) return _codes;
if (stage>=0 && (size_t)stage<_stages.size()) return _stages[stage].getCodes();
return _codes;
Expand Down Expand Up @@ -117,7 +117,15 @@ JsonItem JsonItem::FromJSON(json& j)
item._stage2 = std::max(0,std::min(to_int(j["initial_stage_idx"],0), (int)item._stages.size()-1));
item._count = std::max(item._minCount, std::min(to_int(j["initial_quantity"],0), item._maxCount));
if (item._type == Type::CONSUMABLE && item._count > 0) item._stage1=1;


#ifdef JSONITEM_CI_QUIRK
for (const auto& code : item._codes)
item._ciAllCodes.emplace(toLower(code));
for (const auto& stage : item._stages)
for (const auto& code : stage.getCodes())
item._ciAllCodes.emplace(toLower(code));
#endif

return item;
}

Expand Down
96 changes: 79 additions & 17 deletions src/core/jsonitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <luaglue/luainterface.h>
#include <algorithm>
#include <nlohmann/json.hpp>
#include <algorithm>
#include <unordered_set>
#ifdef _MSC_VER
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
Expand All @@ -24,7 +26,7 @@ class JsonItem final : public LuaInterface<JsonItem>, public BaseItem {

class Stage final {
protected:
std::list<std::string> _codes;
std::vector<std::string> _codes;
std::list<std::string> _secondaryCodes;
std::string _img;
std::string _disabledImg;
Expand All @@ -42,19 +44,26 @@ class JsonItem final : public LuaInterface<JsonItem>, public BaseItem {
const std::string& getDisabledImage() const { return _disabledImg; }
const std::list<std::string>& getImageMods() const { return _imgMods; }
const std::list<std::string>& getDisabledImageMods() const { return _disabledImgMods; }
const std::list<std::string>& getCodes() const { return _codes; }
const std::vector<std::string>& getCodes() const { return _codes; }
const std::list<std::string>& getSecondaryCodes() const { return _secondaryCodes; }
std::string getCodesString() const;
bool hasCode(const std::string& code) const { // NOTE: this is called canProvideCode in lua
#ifdef JSONITEM_CI_QUIRK
const auto cmp = [&code](const std::string& s) {
return code.length() == s.length() && strcasecmp(code.c_str(), s.c_str()) == 0;
};
return std::find_if(_codes.begin(), _codes.end(), cmp) != _codes.end();
#else
return std::find(_codes.begin(), _codes.end(), code) != _codes.end();
#endif
}
bool hasSecondaryCode(const std::string& code) const {
return std::find(_secondaryCodes.begin(), _secondaryCodes.end(), code) != _secondaryCodes.end();
}
const bool getInheritCodes() const { return _inheritCodes; }
const std::string& getName() const { return _name; }
};

protected:
std::vector<Stage> _stages;
bool _imgOverridden = false;
Expand All @@ -69,7 +78,23 @@ class JsonItem final : public LuaInterface<JsonItem>, public BaseItem {
bool _imgChanged = false;
bool _ignoreUserInput = false;

public:
#ifdef JSONITEM_CI_QUIRK
// CI comparison in list is way too expensive, so we create a lowercase set instead
std::unordered_set<std::string> _ciAllCodes; // includes codes for stages
#endif

public:
static void toLowerInPlace(std::string& s)
{
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
}

static std::string toLower(std::string s)
{
toLowerInPlace(s);
return s;
}

virtual size_t getStageCount() const override { return _stages.size(); }

virtual const std::string& getImage(size_t stage) const override {
Expand Down Expand Up @@ -100,9 +125,16 @@ class JsonItem final : public LuaInterface<JsonItem>, public BaseItem {
return _name;
}

#ifdef JSONITEM_CI_QUIRK
bool canProvideCodeLower(const std::string& code) const
{
return _ciAllCodes.count(code);
}
#endif

virtual bool canProvideCode(const std::string& code) const override {
#ifdef JSONITEM_CI_QUIRK
auto cmp = [&code](const std::string& s) {
const auto cmp = [&code](const std::string& s) {
return code.length() == s.length() && strcasecmp(code.c_str(), s.c_str()) == 0;
};
if (std::find_if(_codes.begin(), _codes.end(), cmp) != _codes.end()) return true;
Expand All @@ -115,28 +147,58 @@ class JsonItem final : public LuaInterface<JsonItem>, public BaseItem {
return false;
}

virtual int providesCode(const std::string code) const override {
// TODO: split at ':' for consumables to be able to check for a specific amount?
private:
int providesCodeImpl(const std::string& code, bool assumeCanProvide) const
{
if (_type == Type::COMPOSITE_TOGGLE) {
// composites do not provide left/right codes since that would duplicate numbers
#ifdef JSONITEM_CI_QUIRK
const auto cmp = [&code](const std::string& s) {
return code.length() == s.length() && strcasecmp(code.c_str(), s.c_str()) == 0;
};
if (std::find_if(_codes.begin(), _codes.end(), cmp) != _codes.end())
#else
if (std::find(_codes.begin(), _codes.end(), code) != _codes.end())
#endif
return ((_stage2&1) ? 1 : 0) + ((_stage2&2) ? 1 : 0);
else
return 0;
return 0;
}
if ((int)_stages.size()>_stage2) {
if (_allowDisabled && !_stage1) return 0;

if ((int)_stages.size() > _stage2) {
if (_allowDisabled && !_stage1)
return 0;
for (int i=_stage2; i>=0; i--) {
if (_stages[i].hasCode(code)) return 1;
if (!_stages[i].getInheritCodes()) break;
if (_stages[i].hasCode(code))
return 1;
if (!_stages[i].getInheritCodes())
break;
}
return false;
}
if (_count && canProvideCode(code)) return _count;
return (_stage1 && canProvideCode(code));

if (_count && (assumeCanProvide || canProvideCode(code)))
return _count;
return _stage1 && (assumeCanProvide || canProvideCode(code));
}

public:
#ifdef JSONITEM_CI_QUIRK
int providesCodeLower(const std::string& code) const
{
if (!canProvideCodeLower(code))
return 0;
return providesCodeImpl(code, true);
}
#endif

int providesCode(const std::string& code) const override
{
// TODO: split at ':' for consumables to be able to check for a specific amount?
return providesCodeImpl(code, false);
}
virtual std::string getCodesString() const override;
virtual const std::list<std::string>& getCodes(int stage) const;

std::string getCodesString() const override;
const std::vector<std::string>& getCodes(int stage) const;

virtual bool changeState(BaseItem::Action action) override {
if (_ignoreUserInput)
Expand Down
15 changes: 9 additions & 6 deletions src/core/jsonutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ static nlohmann::json parse_jsonc(std::string& s)
return j;
}

static void commasplit(const std::string& s, std::list<std::string>& l)
template <template <typename...> class T>
void commasplit(const std::string& s, T<std::string>& l)
{
auto sta = s.find_first_not_of(' '); // ltrim
auto end = s.find(',');
Expand All @@ -55,17 +56,19 @@ static void commasplit(const std::string& s, std::list<std::string>& l)
l.push_back(s.substr(sta, end - sta));
}

static std::list<std::string> commasplit(const std::string& s)
template <template <typename...> class T>
static T<std::string> commasplit(const std::string& s)
{
std::list<std::string> lst;
commasplit(s, lst);
T<std::string> lst;
commasplit<T>(s, lst);
return lst;
}

static std::list<std::string> commasplit(std::string&& s)
template <template <typename...> class T>
static T<std::string> commasplit(std::string&& s)
{
std::string tmp = s;
return commasplit(tmp);
return commasplit<T>(tmp);
}


Expand Down
6 changes: 3 additions & 3 deletions src/core/luaitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ bool LuaItem::Lua_NewIndex(lua_State *L, const char *key) {
} else if (strcmp(key,"IconMods")==0) {
// NOTE: these are applied on top of .Icon ImageReference
std::string s = luaL_checkstring(L,-1);
auto mods = commasplit(s);
auto mods = commasplit<std::list>(s);
if (_extraImgMods != mods) {
_extraImgMods = mods;
parseFullImg();
Expand Down Expand Up @@ -188,7 +188,7 @@ bool LuaItem::canProvideCode(const std::string& code) const
return res;
}

int LuaItem::providesCode(const std::string code) const
int LuaItem::providesCode(const std::string& code) const
{
if (!_providesCodeFunc.valid()) return false;
lua_rawgeti(_L, LUA_REGISTRYINDEX, _providesCodeFunc.ref);
Expand Down Expand Up @@ -353,7 +353,7 @@ void LuaItem::parseFullImg()
_imgMods = _extraImgMods;
} else {
_img = _fullImg.substr(0, pos);
_imgMods = commasplit(_fullImg.substr(pos + 1));
_imgMods = commasplit<std::list>(_fullImg.substr(pos + 1));
_imgMods.insert(_imgMods.end(), _extraImgMods.begin(), _extraImgMods.end());
}
}
2 changes: 1 addition & 1 deletion src/core/luaitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class LuaItem final : public LuaInterface<LuaItem>, public BaseItem {
LuaVariant Get(const char* key);

virtual bool canProvideCode(const std::string& code) const override;
virtual int providesCode(const std::string code) const override;
int providesCode(const std::string& code) const override;
virtual bool changeState(Action action) override;

virtual void SetOverlay(const char* text) override {
Expand Down
Loading
Loading