From baaf79b125c8331e6637b5dde70e0eb658279a32 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 22 Nov 2024 15:43:16 +0100 Subject: [PATCH 1/3] Add callback API and documentation Currently unused but I added a long documentation to explain how to use it. Fix #3224 --- CMakeLists.txt | 1 + Makefile.am | 1 + src/callback.h | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 src/callback.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 48b917b7a4..f63d4ffa55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ add_library(${PROJECT_NAME} OBJECT src/bitmap_hslrgb.h src/cache.cpp src/cache.h + src/callback.h src/cmdline_parser.cpp src/cmdline_parser.h src/color.h diff --git a/Makefile.am b/Makefile.am index 7ee65f6792..b61c8d6fb8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ libeasyrpg_player_a_SOURCES = \ src/bitmap_hslrgb.h \ src/cache.cpp \ src/cache.h \ + src/callback.h \ src/cmdline_parser.cpp \ src/cmdline_parser.h \ src/color.h \ diff --git a/src/callback.h b/src/callback.h new file mode 100644 index 0000000000..1840fd38d1 --- /dev/null +++ b/src/callback.h @@ -0,0 +1,157 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + +#ifndef EP_CALLBACK_H +#define EP_CALLBACK_H + +#include +#include +#include +#include +#include + +#if 0 +Where to place a callback: +When it is related to something global put the callback where it is suitable. +When it is related to an instance of a class/struct add it as a public variable. + +Adding a new callback: + +Assuming you want to add a notification when a variable is changed. + +In the header of Game_Variables (game_variables.h) add a public field + +```cpp +Callback OnVariableChanged; +``` + +The /* names */ are for increated readability. +Functions that take two integer arguments can be bound to this callback. + +Signal the callback at a suitable location. +Here this would be in SetOp in game_variables.cpp: + +```cpp +OnVariableChanged.Call(variable_id, v); +``` + +This invocation will trigger all functions attached with Bind. + +Attaching to the callback: + +```cpp +// Attaching with a scope guard (the callback will automatically Unbind when the +// guard is not in scope anymore). +// This prevents stale handlers that crash. +auto guard = Main_Data::game_variables->OnVariableChanged.Bind([](int var_id, int val) { + Output::Warning("{} = {}", var_id, val); +}); + +// In case of a class the scope guard should be a field. +// It is automatically unbound when the instance is destroyed. +class MyClass { +public: + MyClass() { + guard = Main_Data::game_variables->OnVariableChanged.Bind([](int var_id, int val) { + Output::Warning("{} = {}", var_id, val); + }); + + // Alternative: Binding a method instead of a lambda + guard = Main_Data::game_variables->OnVariableChanged.Bind(&MyClass::VariableChanged, this); + } + + void VariableChanged(int var_id, int val) { + Output::Warning("{} = {}", var_id, val); + } + +private: + BindingScopeGuard guard; +} +``` + +Not recommended: + +If you want to (for whatever reason) manually manage the lifetime of the binding +use BindUnmanaged. + +This works the same as described above but the function returns an integer. + +And you must that integer to Unbind(id) to remove the handler. +#endif + +using BindingScopeGuard = lcf::ScopeGuard>; + +template +class Callback { +public: + using Ret = void; + using Fn = std::function; + + template + int BindUnmanaged(Ret (T::*func)(Args...), T* that, Args... args) { + Fn f = std::bind(std::mem_fn(func), that, std::placeholders::_1, args...); + return BindUnmanaged(f); + } + + int BindUnmanaged(Fn func) { + listeners.push_back({next_id, func}); + return next_id++; + } + + template + BindingScopeGuard Bind(Ret (T::*func)(Args...), T* that, Args... args) { + int id = BindUnmanaged(func, that, args...); + + return lcf::ScopeGuard>([this, id=id]() { + Unbind(id); + }); + } + + BindingScopeGuard Bind(Fn func) { + int id = BindUnmanaged(func); + + return lcf::ScopeGuard>([this, id]() { + Unbind(id); + }); + } + + void Unbind(int id) { + auto it = std::find_if(listeners.begin(), listeners.end(), [id](auto& listener){ + return listener.first == id; + }); + assert(it != listeners.end()); + + listeners.erase(it); + } + + void Call(Args... args) { + for (auto& listener: listeners) { + listener.second(std::forward(args)...); + } + } + + void Clear() { + listeners.clear(); + } + +private: + std::vector> listeners; + + int next_id = 1; +}; + +#endif From ff90208e543e08cfd46780f15fffa28563324b95 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 22 Nov 2024 15:55:14 +0100 Subject: [PATCH 2/3] PR Labeler: Add more labels, remove the broken negative glob The negative glob added "building" to every PR --- .github/labeler.yml | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 972b28d350..2e50dbdd35 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,7 +9,6 @@ Building: - .github/** - CMakeLists.txt - builds/** - - '!builds/android/app/**' - Makefile.am - configure.ac @@ -37,6 +36,22 @@ Audio: - changed-files: - any-glob-to-any-file: [ src/**/*audio* ] +Battle: +- changed-files: + - any-glob-to-any-file: + - src/**/scene_battle* + - src/**/window_battle* + - src/**/game_battle.* + - src/**/game_battlealgorithm.* + +Bitmaps: +- changed-files: + - any-glob-to-any-file: + - src/**/bitmap.* + - src/**/bitmap_* + - src/**/sprite.* + - src/**/sprite_* + FileFinder: - changed-files: - any-glob-to-any-file: [ src/**/filefinder*, src/**/filesystem* ] @@ -51,10 +66,26 @@ Fonts: - src/**/*font* - src/generated/bitmapfont_* +Input: +- changed-files: + - any-glob-to-any-file: [ src/**/*input* ] + +Messages: +- changed-files: + - any-glob-to-any-file: [ src/**/*message* ] + MIDI: - changed-files: - any-glob-to-any-file: [ src/**/*midi* ] +Settings: +- changed-files: + - any-glob-to-any-file: [ src/**/*config* ] + +Translation: +- changed-files: + - any-glob-to-any-file: [ src/**/translation* ] + # platforms 3DS: From 29b92af51f93d2a5259b0fcefa23cbe475f30657 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 22 Nov 2024 16:06:41 +0100 Subject: [PATCH 3/3] Attach lcf log handler Messages from liblcf are now saved in our logfile --- src/player.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/player.cpp b/src/player.cpp index 494f09a052..eedb575968 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -16,7 +16,6 @@ */ // Headers - #include #include #include @@ -81,6 +80,7 @@ #include "instrumentation.h" #include "transition.h" #include +#include #include "baseui.h" #include "game_clock.h" #include "message_overlay.h" @@ -152,6 +152,10 @@ namespace { } void Player::Init(std::vector args) { + lcf::LogHandler::SetHandler([](lcf::LogHandler::Level level, StringView message, lcf::LogHandler::UserData) { + Output::Debug("lcf ({}): {}", lcf::LogHandler::kLevelTags.tag(level), message); + }); + frames = 0; // Must be called before the first call to Output