diff --git a/CHANGELOG.md b/CHANGELOG.md index 058a7355..781d54a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # PopTracker Changelog +## next + +* Pack Features + * Add support for Archipelago data storage api + ## v0.21.0 * App Features diff --git a/Makefile b/Makefile index 088810b3..469b9641 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,6 @@ SRC = $(wildcard $(SRC_DIR)/*.cpp) \ $(wildcard $(SRC_DIR)/usb2snes/*.cpp) \ $(wildcard $(SRC_DIR)/uat/*.cpp) \ $(wildcard $(SRC_DIR)/ap/*.cpp) \ - $(wildcard $(LIB_DIR)/wswrap/src/*.cpp) \ $(wildcard $(SRC_DIR)/http/*.cpp) \ $(wildcard $(SRC_DIR)/packmanager/*.cpp) #lib/gifdec/gifdec.c diff --git a/doc/AUTOTRACKING.md b/doc/AUTOTRACKING.md index 596f1650..6d13eae1 100644 --- a/doc/AUTOTRACKING.md +++ b/doc/AUTOTRACKING.md @@ -134,6 +134,8 @@ manifest.json and clicking on "AP" in the menu when the pack is loaded. * when a location is checked, Location handlers are called * when a location is scouted, Scout handlers are called * when a bounce is received, Bounced handlers are called +* when a watched data storage value is changed, SetReply handlers are called +* when a data storage value is polled (Get), Retrieved handlers are called ### global Archipelago * `.PlayerNumber` returns the slot number of the connected player or -1 if not connected @@ -142,6 +144,10 @@ manifest.json and clicking on "AP" in the menu when the pack is loaded. * `:AddLocationHandler(name, callback)` called when a location was checked; args: location_id, location_name * `:AddScoutHandler(name, callback)` called when a location was scouted; args: location_id, location_name, item_id, item_name, item_player * `:AddBouncedHandler(name, callback)` called when the server sends a bounce; args: json bounce message as table +* `:AddRetrievedHandler(name, callback)` called when the server replies to Get; args: key, value +* `:AddSetReplyHandler(name, callback)` called when a watched data storage value is changed; args: key, value, old_value +* `:Get(keys)` ask the server for values from data storage, run this from a ClearHandler, keys is an array of strings +* `:SetNotify(keys)` ask the server to notify when a data storage value is changed, run this from a ClearHandler, keys is an array of strings ## Other Stuff diff --git a/lib/apclientpp b/lib/apclientpp index f34ac338..872c0092 160000 --- a/lib/apclientpp +++ b/lib/apclientpp @@ -1 +1 @@ -Subproject commit f34ac33826236731bb4ad8bb7fe80bbee8b4d0fc +Subproject commit 872c0092d707c5f06d46e37b478ebd946622e9bd diff --git a/lib/wswrap b/lib/wswrap index 076d9def..3881ef35 160000 --- a/lib/wswrap +++ b/lib/wswrap @@ -1 +1 @@ -Subproject commit 076d9defe92ae575175ad4d084d1d2d366f17af5 +Subproject commit 3881ef3584733b956dd5e55f62cb4827a4fcf585 diff --git a/src/ap/aptracker.h b/src/ap/aptracker.h index 4fc06cf5..b9e3239e 100644 --- a/src/ap/aptracker.h +++ b/src/ap/aptracker.h @@ -150,6 +150,16 @@ class APTracker final { scout.item, _ap->get_item_name(scout.item), scout.player); } }); + _ap->set_retrieved_handler([this](const std::map& keys) { + auto lock = EventLock(_event); + for (const auto& pair: keys) { + onRetrieved.emit(this, pair.first, pair.second); + } + }); + _ap->set_set_reply_handler([this](const std::string& key, const json& value, const json& old) { + auto lock = EventLock(_event); + onSetReply.emit(this, key, value, old); + }); return true; } @@ -190,6 +200,16 @@ class APTracker final { return _ap ? _ap->get_player_number() : -1; } + bool SetNotify(const std::list& keys) + { + return _ap ? _ap->SetNotify(keys) : false; + } + + bool Get(const std::list& keys) + { + return _ap ? _ap->Get(keys) : false; + } + Signal onError; Signal onStateChanged; Signal onClear; // called when state has to be cleared, gives new slot_data @@ -197,6 +217,8 @@ class APTracker final { Signal onScout; // location, location_name, item, item_name, target player Signal onLocationChecked; // location, location_name Signal onBounced; // packet + Signal onRetrieved; + Signal onSetReply; private: APClient* _ap = nullptr; diff --git a/src/ap/archipelago.cpp b/src/ap/archipelago.cpp index bfec0885..c707c802 100644 --- a/src/ap/archipelago.cpp +++ b/src/ap/archipelago.cpp @@ -10,6 +10,10 @@ const LuaInterface::MethodMap Archipelago::Lua_Methods = { LUA_METHOD(Archipelago, AddLocationHandler, const char*, LuaRef), LUA_METHOD(Archipelago, AddScoutHandler, const char*, LuaRef), LUA_METHOD(Archipelago, AddBouncedHandler, const char*, LuaRef), + LUA_METHOD(Archipelago, AddRetrievedHandler, const char*, LuaRef), + LUA_METHOD(Archipelago, AddSetReplyHandler, const char*, LuaRef), + LUA_METHOD(Archipelago, SetNotify, json), + LUA_METHOD(Archipelago, Get, json), }; Archipelago::Archipelago(lua_State *L, APTracker *ap) @@ -117,6 +121,80 @@ bool Archipelago::AddBouncedHandler(const std::string& name, LuaRef callback) return true; } + +bool Archipelago::AddRetrievedHandler(const std::string& name, LuaRef callback) +{ + if (!_ap || !callback.valid()) return false; + int ref = callback.ref; + _ap->onRetrieved += {this, [this, ref, name](void*, const std::string& key, const json& value) { + lua_rawgeti(_L, LUA_REGISTRYINDEX, ref); + Lua(_L).Push(key.c_str()); + json_to_lua(_L, value); + if (lua_pcall(_L, 2, 0, 0)) { + const char* err = lua_tostring(_L, -1); + printf("Error calling Archipelago RetrievedHandler for %s: %s\n", + name.c_str(), err ? err : "Unknown"); + lua_pop(_L, 1); + } + }}; + return true; +} + +bool Archipelago::AddSetReplyHandler(const std::string& name, LuaRef callback) +{ + if (!_ap || !callback.valid()) return false; + int ref = callback.ref; + _ap->onSetReply += {this, [this, ref, name](void*, const std::string& key, const json& value, const json& old) { + lua_rawgeti(_L, LUA_REGISTRYINDEX, ref); + Lua(_L).Push(key.c_str()); + json_to_lua(_L, value); + json_to_lua(_L, old); + if (lua_pcall(_L, 3, 0, 0)) { + const char* err = lua_tostring(_L, -1); + printf("Error calling Archipelago SetReplyHandler for %s: %s\n", + name.c_str(), err ? err : "Unknown"); + lua_pop(_L, 1); + } + }}; + return true; +} + +bool Archipelago::SetNotify(const json& jKeys) +{ + if (!_ap || (!jKeys.is_array() && !jKeys.is_object())) { + printf("Archipelago.SetNotify: keys must be array/table!\n"); + return false; + } + std::list keys; + for (auto& el: jKeys.items()) { + if (el.value().is_string()) { + keys.push_back(el.value()); + } else { + printf("Archipelago.SetNotify: keys must be array/table of string!\n"); + return false; + } + } + return _ap->SetNotify(keys); +} + +bool Archipelago::Get(const json& jKeys) +{ + if (!_ap || (!jKeys.is_array() && !jKeys.is_object())) { + printf("Archipelago.Get: keys must be array/table!\n"); + return false; + } + std::list keys; + for (auto& el: jKeys.items()) { + if (el.value().is_string()) { + keys.push_back(el.value()); + } else { + printf("Archipelago.Get: keys must be array/table of string!\n"); + return false; + } + } + return _ap->Get(keys); +} + int Archipelago::Lua_Index(lua_State *L, const char* key) { if (strcmp(key, "PlayerNumber")==0) { lua_pushinteger(L, _ap ? _ap->getPlayerNumber() : -1); diff --git a/src/ap/archipelago.h b/src/ap/archipelago.h index d955c255..0a1e4258 100644 --- a/src/ap/archipelago.h +++ b/src/ap/archipelago.h @@ -22,6 +22,10 @@ class Archipelago : public LuaInterface { bool AddLocationHandler(const std::string& name, LuaRef callback); bool AddScoutHandler(const std::string& name, LuaRef callback); bool AddBouncedHandler(const std::string& name, LuaRef callback); + bool AddRetrievedHandler(const std::string& name, LuaRef callback); + bool AddSetReplyHandler(const std::string& name, LuaRef callback); + bool SetNotify(const json& keys); + bool Get(const json& keys); protected: lua_State *_L;