Skip to content

thiagola92/discord-social-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

296 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Discord Social SDK

Wrapper around Discord Social SDK.

Logo

What is Discord Social SDK? You can read about it from Discord it self:

But if you want me to be brief... It's basically a way to use Discord infrastructure in your game. For example, instead of developing a text/voice chat for your game, you could just request Discord to create one for you.

What is this GDExtension? It's a wrapper around the SDK, so you can interact with the SDK through GDScript (instead of C++).

What can you do with the SDK?

  • Send/Accept friend request.
  • Block/Unblock users.
  • Send/Read direct messages.
  • Interact with Rich Presence.
    • Setup your Activity.
    • Invite to game.
  • Create lobbies.
    • Use lobby text chat.
    • Use lobby voice chat.
  • I don't know all functionalities but is suppose to do everything that the SDK can do...

Platforms

These are the currently supported platforms:

  • Linux (x86_64)
  • Windows

If you want to request support for other build, make an issue.
If you already knows how to make other build, feel free to make a pull request.

Installation

Important

Before starting, you need to follow 3 steps from Getting Started with C++:

  • Step 1: Create a Discord Developer Team.
  • Step 2: Create a Discord Application.
  • Step 3: Enable Discord Social SDK for Your App.

Recommended

It's available in Godot Asset Library, so you can search and install through Godot.

Not recommended (manual installation)

  • Go to releases from Github.
  • Download latest release ZIP.
  • Extract addons directory from ZIP.
    • It will be inside a demo directory.
  • Move addons directory to your project directory.
    • If your project already have an addons directory, copy addons/discord_social_sdk to your project addons.

Usage

This GDExtension is a wrapper around the C++ SDK, which means that each GDScript method it's just calling the C++ counterpart.

If you understand C++, you could easily read a C++ code and convert it to GDScript. For example, I was able to convert their conclusion code from Getting Started with C++ to:

extends Control

# Replace with your Discord Application ID
var APPLICATION_ID: int = 1349146942634065960

var client := DiscordClient.new()
var args := DiscordAuthorizationArgs.new()
var code_verifier: DiscordAuthorizationCodeVerifier = null


func _ready() -> void:
	print("๐Ÿš€ Initializing Discord SDK...")
	
	client.add_log_callback(_on_log_message, DiscordLoggingSeverity.INFO)
	client.set_status_changed_callback(_on_status_changed)
	
	code_verifier = client.create_authorization_code_verifier()
	
	args.set_client_id(APPLICATION_ID)
	args.set_scopes(DiscordClient.get_default_presence_scopes())
	args.set_code_challenge(code_verifier.challenge())
	client.authorize(args, _on_authorized)


func _process(_delta: float) -> void:
	Discord.run_callbacks()


func _on_log_message(message: String, severity: DiscordLoggingSeverity.Enum) -> void:
	print("[%s] %s" % [Discord.enum_to_string(severity, DiscordLoggingSeverity.id), message])


func _on_status_changed(status: DiscordClientStatus.Enum, error: DiscordClientError.Enum, error_detail: int) -> void:
	print("๐Ÿ”„ Status changed: %s" % status)
	
	if status == DiscordClientStatus.READY:
		print("โœ… Client is ready! You can now call SDK functions.")
		print("๐Ÿ‘ฅ Friends Count: %s" % client.get_relationships().size())
		
		var activity := DiscordActivity.new()
		activity.set_type(DiscordActivityTypes.PLAYING)
		activity.set_state("In Competitive Match")
		activity.set_details("Rank: Diamond II")
		
		client.update_rich_presence(activity, _on_rich_presence_updated)
	elif error != DiscordClientError.NONE:
		print("โŒ Connection Error: %s - Details: %s" % [error, error_detail])


func _on_rich_presence_updated(result: DiscordClientResult) -> void:
	if result.successful():
		print("๐ŸŽฎ Rich Presence updated successfully!")
	else:
		print("โŒ Rich Presence update failed")


func _on_authorized(result: DiscordClientResult, code: String, redirect_uri: String) -> void:
	if not result.successful():
		print("โŒ Authentication Error: %s" % result.error())
	else:
		print("โœ… Authorization successful! Getting access token...")
		
		client.get_token(APPLICATION_ID, code, code_verifier.verifier(), redirect_uri, _on_token_received)


func _on_token_received(
		_result: DiscordClientResult,
		access_token: String,
		_refresh_token: String,
		_token_type: DiscordAuthorizationTokenType.Enum,
		_expires_in: int,
		_scopes: String
) -> void:
	print("๐Ÿ”“ Access token received! Establishing connection...")
	
	client.update_token(DiscordAuthorizationTokenType.BEARER, access_token, _on_token_updated)


func _on_token_updated(result: DiscordClientResult) -> void:
	if result.successful():
		print("๐Ÿ”‘ Token updated, connecting to Discord...")
		
		client.connect_discord()

Examples

Check these two directories:

You probably already noticed, but their SDK makes heavy use of callbacks and we just replicate their behaviour in this GDExtension.

Security

At first you may think that your Application ID is public information... But it's not!

Reading Discord Developer Terms of Service section 2.d, you will find:

You will use any developer credentials (such as your Application ID, passwords, keys, tokens, and client secrets) we assign to you solely with your Application and the applicable APIs (and will not permit or enable any other Application to use them) and will treat them as Discord confidential information...

This security is needed because Application ID is all that somebody needs to interact with the Discord Social SDK. In other words, knowing your Application ID is everything that a person needs to impersonate your application.

That's why Discord Social SDK should only be used on games that already have servers (unless someone prove me wrong).

If your game runs everything locally, that means that you need to put your Application ID in the binary and this means that someone can reverse engineer to get it (even if you compile with PCK encryption key).

The Good, The Bad, The Ugly

The Good

  1. As I said before, the GDExtension is just a wrapper around C++ SDK. In other words, you can probably make all the same things that the C++ SDK can do (I hope).
  2. I belive that they use Doxygen to generate their documentation, which I'm also using in this project. So we probably have the same level of documentation (it may need a little formatting, but we have it!).

The Bad

  1. As counterpart of C++ std::optional<T> type, I'm using Variant (which will hold null or an actual value).
    • This could change in the future if I decide to create my own class "Optional".
  2. Some functions were renamed because their name was already being used in Godot class. I just added a _discord to their name.
  3. There is no uint in GDScript, so you always receive an int from the GDExtension.
    • If you don't intend to change the data, everything will be fine because there is no data lost when converting between uint and int.
    • If you do intend to change the data, you should know which operations can corrupt your data.
    • Reference: godotengine/godot-proposals#9740 (comment)
  4. No signals usage.
    • I would love to transform some of these callbacks into signals (โ™ฅ๏ธ), but is not possible to identify when can it be done just by looking at functions signature. For example: void xxxxx(Callback cb);
      • If the function name is set_xxx_callback: You know that will call you function when "xxx" happens.
      • If the function name is do_xxx: You know that will do "xxx" and call you function when it's done.
      • The first case can be transformed to signal, while the second can't... Analysing the function name and deciding what to do is a bit too much for me.

The Ugly

  1. Each enum has it own class.
    • The enum discordpp::HttpStatusCode will be DiscordHttpStatusCode.
    • The enum discordpp::Client::Status will be DiscordClientStatus.
    • This happens because the Godot C++ doesn't let me use the same name in different enums.
      • Just to be clear, you can do it through GDScript but not through Godot C++.
      • Any enum created through Godot C++ will also be added as constant for the class.
        • Creating Discord.HttpStatusCode.NONE will also create Discord.NONE, which would cause conflict when creating Discord.ExternalIdentityProviderType.NONE because would attempt to create another Discord.NONE.
      • Reference: godotengine/godot-cpp#1910
  2. There is no function overloading in GDScript, so I had to change the function signature in any way possible to tell me which function to call.
    • These functions will have an extra performance cost.
      • In compiled languages, the identification of the right function can be done during compilation.
      • In interpreted languages that use Duck typing (like GDScript), this happens during execution.
    • For example:
      • discordpp::EnumToString() receives 1 argument which can be many enums:
        • discordpp::ActivityActionTypes value
        • discordpp::ActivityGamePlatforms value
        • discordpp::ActivityPartyPrivacy value
        • discordpp::ActivityTypes value
        • ...
      • Discord.enum_to_string() receives 2 arguements, where the second identify which enum:
        • value: int, enum_id: int
          • Where enum_id is a constant in the enum class:
            • DiscordActivityActionTypes.id
            • DiscordActivityGamePlatforms.id
            • DiscordActivityPartyPrivacy.id
            • DiscordActivityTypes.id

Donation

Development

For development details go to DEVELOPMENT.md.

About

GDExtension for Discord Social SDK

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors