samsface / godot-discord-game-sdk

43 stars 4 forks source link

Can only send strings, no support for sending raw bytes #4

Open TheSecondReal0 opened 2 years ago

TheSecondReal0 commented 2 years ago

You must hand the implementation a string instead of a byte buffer, even though the string is converted to a byte buffer immediately.

samsface commented 2 years ago

I can add a function to send a byte array, something like send_lobby_message_as_bytes but I think most people for lobbies would rather just send a string like it is. If you're planning on doing serious networking with Discord, I'd suggest you fork and try implement the NetworkManager: https://discord.com/developers/docs/game-sdk/networking

TheSecondReal0 commented 2 years ago

Yeah it's not something particularly need, but the option could be nice. I'm making a turn based game so just the lobby networking should be enough but if it isn't I'll be sure to fork.

samsface commented 2 years ago

Yeah it's not something particularly need, but the option could be nice. I'm making a turn based game so just the lobby networking should be enough but if it isn't I'll be sure to fork.

Nice! I only put this together for a godot addon game jam in a couple of days so honestly I'm not sure how solid Discord's service is. Would be cool to see your results.

TheSecondReal0 commented 2 years ago

It seems pretty good, I've been putting together an abstracted network layer to let me easily switch between Godot's built-in stuff and discord API (which would also let me add support for other APIs in the future, like steam or something).

Using the lobby networking is definitely not the fastest but seems pretty performant? I've only tested locally though, I'll post more info when I can test with some other people.

TheSecondReal0 commented 2 years ago

I'll put some info about the abstracted network layer here in case it might help anyone else. The below code should be in an autoload (probably whichever autoload handles networking). Instead of calling rpc("do_thing", input1, input2) you would call Autoload.custom_rpc("do_thing", self, [input1, input2]).

Important to note: this implementation has literally no network security. You could remotely call literally any function (even if you didn't put a keyword like puppet or remotesync before it). If you're going to use this code in an actual game you're gonna want to add some sort of security.

enum RPC_INPUTS {FUNC_NAME, NODE_PATH, INPUTS, TARGET, SENDER}
enum NET_PROTOCOLS {WEBSOCKET, UDP, DISCORD}
var net_protocol: int = NET_PROTOCOLS.UDP

func custom_rpc(func_name: String, node: Node, inputs: Array) -> void:
    var rpc_dict: Dictionary = create_rpc_dict(func_name, node, inputs, rpc_mode)
    send_rpc(rpc_dict)

func custom_rpc_id(target_id: int, func_name: String, node: Node, inputs: Array) -> void:
    var rpc_dict: Dictionary = create_rpc_dict(func_name, node, inputs, rpc_mode)
    rpc_dict[RPC_INPUTS.TARGET] = target_id
    send_rpc(rpc_dict)

func send_rpc(rpc_dict: Dictionary) -> void:
    var rpc_dict_string: String = var2str(rpc_dict)
    match net_protocol:
        net_protocols.WEBSOCKET:
            rpc("receive_custom_rpc", rpc_dict_string)
        net_protocols.UDP:
            rpc("receive_custom_rpc", rpc_dict_string)
        net_protocols.DISCORD:
            # this is where you would tell your discord API to send a message
            pass

func create_rpc_dict(func_name: String, node: Node, inputs: Array) -> Dictionary:
    var dict: Dictionary = {}
    dict[RPC_INPUTS.FUNC_NAME] = func_name
    dict[RPC_INPUTS.NODE_PATH] = node.get_path()
    dict[RPC_INPUTS.INPUTS] = inputs
    dict[RPC_INPUTS.SENDER] = get_my_id()
    return dict

remotesync func receive_custom_rpc(rpc_dict_string: String) -> void:
    var rpc_dict: Dictionary = str2var(rpc_dict_string)
    if RPC_INPUTS.TARGET in rpc_dict:
        if get_my_id() != rpc_dict[RPC_INPUTS.TARGET]:
            return
    rpc_sender_id = rpc_dict[RPC_INPUTS.SENDER]
    var node: Node = get_node(rpc_dict[RPC_INPUTS.NODE_PATH])
    var func_name: String = rpc_dict[RPC_INPUTS.FUNC_NAME]
    var inputs: Array = rpc_dict[RPC_INPUTS.INPUTS]
    node.callv(func_name, inputs)