Skip to content

Commit

Permalink
Merge pull request #3300 from Ghabry/fixes2
Browse files Browse the repository at this point in the history
Add callback API, update PR labeler
  • Loading branch information
fdelapena authored Nov 29, 2024
2 parents d9dd9be + 29b92af commit 972ef65
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 2 deletions.
33 changes: 32 additions & 1 deletion .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Building:
- .github/**
- CMakeLists.txt
- builds/**
- '!builds/android/app/**'
- Makefile.am
- configure.ac

Expand Down Expand Up @@ -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* ]
Expand All @@ -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:
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
157 changes: 157 additions & 0 deletions src/callback.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#ifndef EP_CALLBACK_H
#define EP_CALLBACK_H

#include <lcf/scope_guard.h>
#include <vector>
#include <functional>
#include <utility>
#include <algorithm>

#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<int /* variable_id */, int /* value */> 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<std::function<void()>>;

template</*typename Ret, */typename ...Args>
class Callback {
public:
using Ret = void;
using Fn = std::function<Ret(Args...)>;

template<typename T>
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<typename T>
BindingScopeGuard Bind(Ret (T::*func)(Args...), T* that, Args... args) {
int id = BindUnmanaged(func, that, args...);

return lcf::ScopeGuard<std::function<void()>>([this, id=id]() {
Unbind(id);
});
}

BindingScopeGuard Bind(Fn func) {
int id = BindUnmanaged(func);

return lcf::ScopeGuard<std::function<void()>>([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>(args)...);
}
}

void Clear() {
listeners.clear();
}

private:
std::vector<std::pair<int, Fn>> listeners;

int next_id = 1;
};

#endif
6 changes: 5 additions & 1 deletion src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/

// Headers

#include <algorithm>
#include <cstring>
#include <cstdlib>
Expand Down Expand Up @@ -81,6 +80,7 @@
#include "instrumentation.h"
#include "transition.h"
#include <lcf/scope_guard.h>
#include <lcf/log_handler.h>
#include "baseui.h"
#include "game_clock.h"
#include "message_overlay.h"
Expand Down Expand Up @@ -152,6 +152,10 @@ namespace {
}

void Player::Init(std::vector<std::string> 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
Expand Down

0 comments on commit 972ef65

Please sign in to comment.