Godot port progress!
The Godot port has been going well!  Finding a good groove in establishing a binding layer between my poker engine that I created and used on the original Cocos2dx release and Godot with GDExtension to expose  these objects to the engine and to GDScript.  As part of this created an adapter to translate the eventing layer I have in the poker engine to Godot signals and aggregating things where it makes sense.  Have run into some fun webassembly only build issues and even ran into a fun ABI compatibility issue that only manifested in the web build.  Have learned a lot about build configurations by interloping CMake, Scons, and the Godot editor's own build configurations.
I'm a systems programmer at heart so naturally have started creating my own DSL on top of Godot's macros as I find them too boilerplate and want something more developer friendly like Unreal's macros.
As an example
// Macro: expands to a static_assert that the type is valid
#define STATIC_ASSERT_TYPE_TOKEN_EXISTS(T) \
    ([]() consteval -> const char* {                      \
        static_assert(sizeof(tp::T) >= 0, "Type " #T " must exist"); \
        return #T;                                        \
    }())
#define STATIC_ASSERT_TYPE_TOKEN_EXISTS_NS(NS, T) \
    ([]() consteval -> const char* {                      \
        static_assert(sizeof(NS::T) >= 0, "Type " #NS "::" #T " must exist"); \
        return #T;                                        \
    }())
// Used for godot::Object derived type parameters to appear typed in GDScript signals and methods (this includes both ref and node types)
#define SIGNAL_OBJECT_PARAM(parameter_name, clazz) PropertyInfo(Variant::OBJECT, parameter_name, PROPERTY_HINT_RESOURCE_TYPE, STATIC_ASSERT_TYPE_TOKEN_EXISTS(clazz))
#define SIGNAL_OBJECT_PARAM_NS(parameter_name, ns, clazz) PropertyInfo(Variant::OBJECT, parameter_name, PROPERTY_HINT_RESOURCE_TYPE, STATIC_ASSERT_TYPE_TOKEN_EXISTS_NS(ns,clazz))
#define SIGNAL_ENUM_PARAM(name, Owner, EnumName) PropertyInfo(Variant::INT, name, PROPERTY_HINT_ENUM, #Owner "." #EnumName)
#define ADD_SIGNAL_DECL(...) \
    ADD_SIGNAL(MethodInfo(__VA_ARGS__))
// usage
// ...
ADD_SIGNAL_DECL(PokerTableSignals::ON_PLAYER_ADDED, SIGNAL_OBJECT_PARAM("event", GDPlayer));
I then discovered that GDExtension doesn't have a safe publication function like "post_on_game_thread" where I can run any functor on the game thread. The standard way to do it in Godot is to use "call_deferred" but that requires a GDScript-bound function to execute, which is limiting if you want to run any invocable object on that thread. So I came up with a template function that uses perfect forwarding to abstract this and then an event dispatcher class that dispatches std::function objects on the main thread using a godot::Object derived class that has the required GDScript bound function that can be called deferred.
Since this is mainly used for dispatching signals safely, an additional function template was created for that. I also have found that not every abstraction available in the engine is available through GDExtension (godot-cpp). There is a main thread helper function in the engine but to determine this through GDExtension, you have to get the OS object and compare thread ids.
    bool is_in_main_thread()
    {
        auto os = godot::OS::get_singleton();
        if(!os)
        {
            UtilityFunctions::push_error("OS singleton is not available.");
            return false;
        }
        const auto main_thread_id = os->get_main_thread_id();
        const auto current_thread_id = os->get_thread_caller_id();
        // Check if current thread is equal to main thread
        return current_thread_id == main_thread_id;
    }
    template<std::invocable func="">
    void invoke_on_main(Func&& f)
    {
        using namespace godot;
        // If already on main thread, just run it
        if (is_in_main_thread())
        {
            std::forward<func>(f)();
            return;
        }
        MainThreadDispatcher::get_singleton()->dispatch(std::forward<func>(f));
    }
    template <std::derived_from<godot::object> Emitter, typename... ArgProducers>
    void dispatch_signal(Emitter* emitter, const char* signal_name, ArgProducers&&... producers)
    {
        // if already on main thread then early out with direct invocation
        if (is_in_main_thread())
        {
            emitter->emit_signal(
                signal_name,
                std::invoke(std::forward<argproducers>(producers))...);
            return;
        }
        // capture all arg producers and allow moving them
        auto packed = std::make_tuple(std::forward<argproducers>(producers)...);
        invoke_on_main([emitter, signal_name, packed = std::move(packed)]() mutable 
        {
            // unpacks the tuple inside the lambda
            std::apply([&](auto&&... unpacked) 
            {
                emitter->emit_signal(
                    signal_name,
                    // calls each producer to get the actual signal argumen
                    std::invoke(std::forward<decltype(unpacked)>(unpacked))...
                );
            }, std::move(packed));
        });
    }
</decltype(unpacked)></argproducers></argproducers></std::derived_from<godot::object></func></func></std::invocable>
The "Arg Producers" are just invocables so that I can lazy translate objects. What's nice is after building these abstractions, which were fun on their own, I was able to cleanly dispatch in my signal adapter and add a bit more checks on top:
inline void GDPokerTable::ListenerSignalAdapter::onPlayerAdded(const PokerTable& pokerTable,const std::shared_ptr& player)
{
  dispatch(pokerTable, PokerTableSignals::ON_PLAYER_ADDED,
           [parent = this->parent, player]() { return parent->get_player(player); }
  );
}
Get Tournament Poker
Tournament Poker
Poker sim experience for serious players against bots with different play styles.
| Status | In development | 
| Author | GameSalutes | 
| Genre | Card Game, Simulation | 
| Tags | 2D, holdem, poker, Singleplayer | 
More posts
- Godot port underway!29 days ago
 - v. 0.2.1 ReleasedJan 11, 2025
 - Tournament Poker alpha released!Jan 02, 2025
 

Leave a comment
Log in with itch.io to leave a comment.