#How to store a callable from a class to call it later on an instance of the class ?

11 messages · Page 1 of 1 (latest)

primal bane
#

Hi,

I'm doing a modular system for my multiplayer game to run actions on the server. So far it's working fine, an action is identified by a code on an enum, and it can run a callable to check conditions and then an other callable to actually do the action.

I do this by declaring these 2 variables :

var check: Callable = func(_player: Player) -> bool: return true
var action: Callable = func(_player: Player, _data: Variant) -> void: pass

and I then do this in my run method :

func run(data: Variant) -> void:
    var player: Player = GameServer.get_current_player()
    if player == null or player.party == null:
        return

    if check.call(player):
        print("%s (%d) running action %s" % [player.label, player.id, Code.keys()[code]])
        action.call(player, data)
    else:
        push_warning("%s (%d) could not run action %s" % [player.label, player.id, Code.keys()[code]])

It's ok but it's a bit cumbersome to register a new action, as I have to use lambdas to register them like this :

    var validate_hand_action := Action.new()\
        .with_check(func(player: Player) -> bool:
            return player.state.current == StateManager.EState.RESHUFFLE)\
        .with_action(func(player: Player, _data: Variant) -> void:
            player.validate_hand())
#

So I was wondering if instead I can register the callables to point to methods of my Player class and then bind the player instance directly to make the call in the run method. I saw I can store the method name a StringName but I don't like too much the idea of magic strings to refer to methods, and I didn't find a way to do that in the documentation... Ideally I'd like something like that :

    var validate_hand_action := Action.new()\
        .with_check(Player.check_validate_hand)\
        .with_action(Player.validate_hand)

        ## and in the run method
    if player.check.call():
        player.action.call(data)

Is it possible to do something like that ?

tiny nest
#

You can create a Callable with Callable(object, method_name) as well
Your desire not to use magic strings is admirable, but GDScript is just a scripting language with serious limitations when it comes to its type system and other stuff. What you do might be possible if validate_hand and check_validate_hand is declared static, but I don't know, you should just test to see if it works

weak hamlet
#

Maybe a generator-style approach would work? Have .with_check() and .with_action() be generators that create bound callables.

primal bane
# tiny nest You can create a `Callable` with `Callable(object, method_name)` as well Your de...

Thanks for the answer. I can't define these methods as static cause they run on an object instance so they need access to the instance data.
I tried something storing a StringName with the method's name but then it's all type guessing and error-prone code, I don't want that. I guess it's one of the limitations of the scripting language ^^' I'm planning to migrate to C# but I want to finish my first project first with gdscript, I'm too far into it to migrate now

primal bane
tiny nest
#

type guessing is what you have whenever you use Callable, it's impossible to specify function signature in godot's type system. Just have to live with that.

primal bane
#

oh but you're right, I could use a different signature and it would just fail at runtime, so yeah, not perfect

tiny nest
#

Side thought, but you can transform any method into a static function, the only difference between them is the presence or absense of implicit self argument, but you can also specify it explicitly:

static func validate_hand(player: Player, ...):
  # access member variables through player
  player.instance_var += 1
primal bane
#

yeah that might do it, though for clarity I'll probably handle this in a separate class like PlayerActions or something like that with only static methods. I'm gonna try that 🙂
Thanks for the discussion !