setting change events + remove platform console input queue + simplify

platform console API + add show-platform-console event listener
This commit is contained in:
HJfod 2022-09-28 19:15:27 +03:00
parent 5af74e9ab7
commit 1b8289a420
12 changed files with 209 additions and 87 deletions

View file

@ -7,3 +7,4 @@
#include "loader/Loader.hpp"
#include "loader/Interface.hpp"
#include "loader/Setting.hpp"
#include "loader/SettingEvent.hpp"

View file

@ -4,33 +4,40 @@
#include <type_traits>
#include "Mod.hpp"
#include "Interface.hpp"
#include <unordered_set>
namespace geode {
class Mod;
class Event;
enum class PassThrough : bool {
Propagate,
Stop,
};
struct GEODE_DLL BasicEventHandler {
virtual bool onEvent(Event*) = 0;
virtual PassThrough passThrough(Event*) = 0;
void listen();
void unlisten();
};
class GEODE_DLL Event {
static std::vector<BasicEventHandler*> handlers;
static std::unordered_set<BasicEventHandler*> s_handlers;
friend BasicEventHandler;
Mod* m_sender;
public:
static std::vector<BasicEventHandler*> const& getHandlers();
static std::unordered_set<BasicEventHandler*> const& getHandlers();
void postFrom(Mod* sender);
inline void post() {
postFrom(Mod::get());
}
Mod* sender();
Mod* getSender();
virtual ~Event();
};
@ -38,12 +45,12 @@ namespace geode {
template <typename T>
class EventHandler : public BasicEventHandler {
public:
virtual bool handle(T*) = 0;
bool onEvent(Event* ev) override {
virtual PassThrough handle(T*) = 0;
PassThrough passThrough(Event* ev) override {
if (auto myev = dynamic_cast<T*>(ev)) {
return handle(myev);
}
return true;
return PassThrough::Propagate;
}
EventHandler() {

View file

@ -17,12 +17,15 @@
namespace geode {
using ModJson = nlohmann::ordered_json;
class Setting;
class SettingNode;
class BoolSetting;
class IntSetting;
class FloatSetting;
class StringSetting;
struct ModInfo;
enum class SettingType {
Bool,
Int,
@ -34,9 +37,21 @@ namespace geode {
User,
};
class GEODE_DLL Setting {
/**
* Base class for all settings in Geode mods. Note that for most purposes
* you should use the built-in setting types. If you need a custom setting
* type however, inherit from this class. Do note that you are responsible
* for things like storing the default value, broadcasting value change
* events, making the setting node etc.
*/
class GEODE_DLL Setting :
public std::enable_shared_from_this<Setting>
{
protected:
std::string m_key;
std::string m_modID;
friend struct ModInfo;
static Result<std::shared_ptr<Setting>> parse(
std::string const& type,
@ -59,6 +74,8 @@ namespace geode {
virtual SettingNode* createNode(float width) = 0;
void valueChanged();
std::string getKey() const;
virtual SettingType getType() const = 0;
};
@ -161,6 +178,7 @@ namespace geode {
if constexpr (std::is_base_of_v<IMatch<Class, ValueType>, Class>) {
static_cast<Class*>(this)->constrainMatch(m_value);
}
this->valueChanged();
}
Result<> isValidValue(ValueType value) {
@ -392,8 +410,7 @@ namespace geode {
}
class GEODE_DLL BoolSetting :
public GeodeSetting<BoolSetting, bool, SettingType::Bool>,
public std::enable_shared_from_this<BoolSetting>
public GeodeSetting<BoolSetting, bool, SettingType::Bool>
{
public:
SettingNode* createNode(float width) override;
@ -403,7 +420,6 @@ namespace geode {
public GeodeSetting<IntSetting, int64_t, SettingType::Int>,
public IOneOf<IntSetting, int64_t>,
public IMinMax<int64_t>,
public std::enable_shared_from_this<IntSetting>,
public ICArrows, public ICSlider<int64_t>, public ICInput
{
public:
@ -414,7 +430,6 @@ namespace geode {
public GeodeSetting<FloatSetting, double, SettingType::Float>,
public IOneOf<FloatSetting, double>,
public IMinMax<double>,
public std::enable_shared_from_this<FloatSetting>,
public ICArrows, public ICSlider<double>, public ICInput
{
public:
@ -424,8 +439,7 @@ namespace geode {
class GEODE_DLL StringSetting :
public GeodeSetting<StringSetting, std::string, SettingType::String>,
public IOneOf<StringSetting, std::string>,
public IMatch<StringSetting, std::string>,
public std::enable_shared_from_this<StringSetting>
public IMatch<StringSetting, std::string>
{
public:
SettingNode* createNode(float width) override;
@ -433,24 +447,21 @@ namespace geode {
class GEODE_DLL FileSetting :
public GeodeSetting<FileSetting, ghc::filesystem::path, SettingType::File>,
public ICFileFilters,
public std::enable_shared_from_this<FileSetting>
public ICFileFilters
{
public:
SettingNode* createNode(float width) override;
};
class GEODE_DLL ColorSetting :
public GeodeSetting<ColorSetting, cocos2d::ccColor3B, SettingType::Color>,
public std::enable_shared_from_this<ColorSetting>
public GeodeSetting<ColorSetting, cocos2d::ccColor3B, SettingType::Color>
{
public:
SettingNode* createNode(float width) override;
};
class GEODE_DLL ColorAlphaSetting :
public GeodeSetting<ColorAlphaSetting, cocos2d::ccColor4B, SettingType::ColorAlpha>,
public std::enable_shared_from_this<ColorAlphaSetting>
public GeodeSetting<ColorAlphaSetting, cocos2d::ccColor4B, SettingType::ColorAlpha>
{
public:
SettingNode* createNode(float width) override;

View file

@ -0,0 +1,71 @@
#pragma once
#include "Event.hpp"
#include <optional>
#include "Setting.hpp"
namespace geode {
class GEODE_DLL SettingChangedEvent : public Event {
protected:
std::string m_modID;
std::shared_ptr<Setting> m_setting;
public:
SettingChangedEvent(
std::string const& modID,
std::shared_ptr<Setting> setting
);
std::string getModID() const;
std::shared_ptr<Setting> getSetting() const;
};
template<class T>
class SettingChangedEventHandler : public EventHandler<SettingChangedEvent> {
public:
using Consumer = void(*)(std::shared_ptr<T>);
static_assert(
std::is_base_of_v<Setting, T>,
"Setting must inherit from the Setting class"
);
protected:
Consumer m_consumer;
std::string m_modID;
std::optional<std::string> m_targetKey;
public:
PassThrough handle(SettingChangedEvent* event) override {
if (
m_modID == event->getModID() && (
!m_targetKey ||
m_targetKey.value() == event->getSetting()->getKey()
)) {
m_consumer(std::static_pointer_cast<T>(event->getSetting()));
}
return PassThrough::Propagate;
}
/**
* Listen to changes on a specific setting
*/
SettingChangedEventHandler(
std::string const& modID,
std::string const& settingID,
Consumer handler
) : m_modID(modID),
m_targetKey(settingID),
m_consumer(handler) {}
/**
* Listen to changes on all of a mods' settings
*/
SettingChangedEventHandler(
std::string const& modID,
Consumer handler
) : m_modID(modID),
m_targetKey(std::nullopt),
m_consumer(handler) {}
};
}

View file

@ -56,12 +56,14 @@ void InternalLoader::executeGDThreadQueue() {
m_gdThreadMutex.unlock();
}
void InternalLoader::queueConsoleMessage(LogPtr* msg) {
this->m_logQueue.push_back(msg);
void InternalLoader::logConsoleMessage(LogPtr* msg) {
if (m_platformConsoleOpen) {
std::cout << msg->toString(true);
}
}
bool InternalLoader::platformConsoleReady() const {
return m_platformConsoleReady;
bool InternalLoader::platformConsoleOpen() const {
return m_platformConsoleOpen;
}
bool InternalLoader::shownInfoAlert(std::string const& key) {
@ -85,42 +87,28 @@ void InternalLoader::platformMessageBox(const char* title, std::string const& in
MessageBoxA(nullptr, info.c_str(), title, MB_ICONERROR);
}
void InternalLoader::setupPlatformConsole() {
if (m_platformConsoleReady) return;
void InternalLoader::openPlatformConsole() {
if (m_platformConsoleOpen) return;
if (AllocConsole() == 0) return;
// redirect console output
freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
freopen_s(reinterpret_cast<FILE**>(stdin), "CONIN$", "r", stdin);
m_platformConsoleReady = true;
}
m_platformConsoleOpen = true;
void InternalLoader::awaitPlatformConsole() {
if (!m_platformConsoleReady) return;
for (auto const& log : m_logQueue) {
for (auto const& log : Loader::get()->getLogs()) {
std::cout << log->toString(true) << "\n";
m_logQueue.clear();
}
std::string inp;
getline(std::cin, inp);
std::string inpa;
std::stringstream ss(inp);
std::vector<std::string> args;
while (ss >> inpa) args.push_back(inpa);
ss.clear();
this->awaitPlatformConsole();
}
void InternalLoader::closePlatformConsole() {
if (!m_platformConsoleReady) return;
if (!m_platformConsoleOpen) return;
fclose(stdin);
fclose(stdout);
FreeConsole();
m_platformConsoleOpen = false;
}
#elif defined(GEODE_IS_MACOS)
@ -130,8 +118,8 @@ void InternalLoader::platformMessageBox(const char* title, std::string const& in
std::cout << title << ": " << info << std::endl;
}
void InternalLoader::setupPlatformConsole() {
m_platformConsoleReady = true;
void InternalLoader::openPlatformConsole() {
m_platformConsoleOpen = true;
}
void InternalLoader::awaitPlatformConsole() {
@ -150,13 +138,13 @@ void InternalLoader::platformMessageBox(const char* title, std::string const& in
std::cout << title << ": " << info << std::endl;
}
void InternalLoader::setupPlatformConsole() {
void InternalLoader::openPlatformConsole() {
ghc::filesystem::path(getpwuid(getuid())->pw_dir);
freopen(ghc::filesystem::path(
utils::file::geodeRoot() / "geode_log.txt"
).string().c_str(),"w",stdout);
InternalLoader::
m_platformConsoleReady = true;
m_platformConsoleOpen = true;
}
void InternalLoader::awaitPlatformConsole() {

View file

@ -17,10 +17,9 @@ USE_GEODE_NAMESPACE();
*/
class InternalLoader : public Loader {
protected:
std::vector<LogPtr*> m_logQueue;
std::vector<std::function<void(void)>> m_gdThreadQueue;
mutable std::mutex m_gdThreadMutex;
bool m_platformConsoleReady = false;
bool m_platformConsoleOpen = false;
std::unordered_set<std::string> m_shownInfoAlerts;
void saveInfoAlerts(nlohmann::json& json);
@ -48,10 +47,9 @@ public:
void queueInGDThread(std::function<void GEODE_CALL(void)> func);
void executeGDThreadQueue();
void queueConsoleMessage(LogPtr*);
bool platformConsoleReady() const;
void setupPlatformConsole();
void awaitPlatformConsole();
void logConsoleMessage(LogPtr*);
bool platformConsoleOpen() const;
void openPlatformConsole();
void closePlatformConsole();
static void platformMessageBox(const char* title, std::string const& info);

View file

@ -3,15 +3,14 @@
USE_GEODE_NAMESPACE();
std::vector<BasicEventHandler*> Event::handlers = {};
std::unordered_set<BasicEventHandler*> Event::s_handlers = {};
void BasicEventHandler::listen() {
if (!utils::vector::contains(Event::handlers, this))
Event::handlers.push_back(this);
Event::s_handlers.insert(this);
}
void BasicEventHandler::unlisten() {
utils::vector::erase(Event::handlers, this);
Event::s_handlers.erase(this);
}
Event::~Event() {}
@ -20,12 +19,17 @@ void Event::postFrom(Mod* m) {
if (m)
m_sender = m;
for (auto h : Event::handlers) {
if (!h->onEvent(this))
for (auto h : Event::s_handlers) {
if (h->passThrough(this) == PassThrough::Stop) {
break;
}
}
}
std::vector<BasicEventHandler*> const& Event::getHandlers() {
return Event::handlers;
Mod* Event::getSender() {
return m_sender;
}
std::unordered_set<BasicEventHandler*> const& Event::getHandlers() {
return Event::s_handlers;
}

View file

@ -376,11 +376,7 @@ Loader::~Loader() {
void Loader::pushLog(LogPtr* logptr) {
m_logs.push_back(logptr);
if (InternalLoader::get()->platformConsoleReady()) {
std::cout << logptr->toString(true);
} else {
InternalLoader::get()->queueConsoleMessage(logptr);
}
InternalLoader::get()->logConsoleMessage(logptr);
m_logStream << logptr->toString(true) << std::endl;
}
@ -458,12 +454,7 @@ bool Loader::supportedModVersion(VersionInfo const& version) {
}
void Loader::openPlatformConsole() {
if (!InternalLoader::get()->platformConsoleReady()) {
InternalLoader::get()->setupPlatformConsole();
std::thread([]() {
InternalLoader::get()->awaitPlatformConsole();
}).detach();
}
InternalLoader::get()->openPlatformConsole();
}
void Loader::closePlatfromConsole() {

View file

@ -136,7 +136,7 @@ void Log::flush() {
Log::~Log() {
this->flush();
if (InternalLoader::get()->platformConsoleReady()) {
if (InternalLoader::get()->platformConsoleOpen()) {
std::cout << std::endl;
}
}

View file

@ -64,9 +64,11 @@ Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
}
for (auto& [key, value] : root.has("settings").items()) {
auto sett = Setting::parse(key, value.json());
PROPAGATE(sett);
info.m_settings.push_back({ key, sett.value() });
auto settRes = Setting::parse(key, value.json());
PROPAGATE(settRes);
auto sett = settRes.value();
sett->m_modID = info.m_id;
info.m_settings.push_back({ key, sett });
}
if (auto resources = root.has("resources").obj()) {

View file

@ -1,4 +1,5 @@
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/utils/general.hpp>
#include <Geode/loader/SettingNode.hpp>
#include "../ui/internal/settings/GeodeSettingNode.hpp"
@ -54,30 +55,68 @@ Result<std::shared_ptr<Setting>> Setting::parse(
return Err("Setting value is not an object");
}
void Setting::valueChanged() {
SettingChangedEvent(m_modID, shared_from_this()).post();
}
SettingNode* BoolSetting::createNode(float width) {
return BoolSettingNode::create(shared_from_this(), width);
return BoolSettingNode::create(
std::static_pointer_cast<BoolSetting>(shared_from_this()),
width
);
}
SettingNode* IntSetting::createNode(float width) {
return IntSettingNode::create(shared_from_this(), width);
return IntSettingNode::create(
std::static_pointer_cast<IntSetting>(shared_from_this()),
width
);
}
SettingNode* FloatSetting::createNode(float width) {
return FloatSettingNode::create(shared_from_this(), width);
return FloatSettingNode::create(
std::static_pointer_cast<FloatSetting>(shared_from_this()),
width
);
}
SettingNode* StringSetting::createNode(float width) {
return StringSettingNode::create(shared_from_this(), width);
return StringSettingNode::create(
std::static_pointer_cast<StringSetting>(shared_from_this()),
width
);
}
SettingNode* FileSetting::createNode(float width) {
return FileSettingNode::create(shared_from_this(), width);
return FileSettingNode::create(
std::static_pointer_cast<FileSetting>(shared_from_this()),
width
);
}
SettingNode* ColorSetting::createNode(float width) {
return ColorSettingNode::create(shared_from_this(), width);
return ColorSettingNode::create(
std::static_pointer_cast<ColorSetting>(shared_from_this()),
width
);
}
SettingNode* ColorAlphaSetting::createNode(float width) {
return ColorAlphaSettingNode::create(shared_from_this(), width);
return ColorAlphaSettingNode::create(
std::static_pointer_cast<ColorAlphaSetting>(shared_from_this()),
width
);
}
SettingChangedEvent::SettingChangedEvent(
std::string const& modID,
std::shared_ptr<Setting> setting
) : m_modID(modID), m_setting(setting) {}
std::string SettingChangedEvent::getModID() const {
return m_modID;
}
std::shared_ptr<Setting> SettingChangedEvent::getSetting() const {
return m_setting;
}

View file

@ -1,5 +1,6 @@
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <InternalLoader.hpp>
#include <InternalMod.hpp>
#include <Geode/loader/Log.hpp>
@ -50,6 +51,16 @@ BOOL WINAPI DllMain(HINSTANCE lib, DWORD reason, LPVOID) {
}
#endif
static SettingChangedEventHandler<BoolSetting> _(
"geode.loader", "show-platform-console",
[](std::shared_ptr<BoolSetting> setting) {
if (setting->getValue()) {
Loader::get()->openPlatformConsole();
} else {
Loader::get()->closePlatfromConsole();
}
}
);
int geodeEntry(void* platformData) {
// setup internals
@ -108,8 +119,7 @@ int geodeEntry(void* platformData) {
<< "Set up loader";
if (InternalMod::get()->getSettingValue<bool>("show-platform-console")) {
InternalLoader::get()->setupPlatformConsole();
InternalLoader::get()->awaitPlatformConsole();
Loader::get()->openPlatformConsole();
}
InternalMod::get()->log()