31 Commits

Author SHA1 Message Date
a4fb6c19fd crash fix for missing party invite resources 2025-03-28 00:48:21 +01:00
6edd50d9c2 improved auto_token_manage, added invites and some more stuff 2025-03-28 00:36:01 +01:00
98a4b4783c implemented rich presences and better naming 2025-03-27 15:22:43 +01:00
bd7a102d3d kind of fixed the lobby 2025-03-26 22:33:57 +01:00
8b37af48b1 Merge branch 'discord-social-sdk' of https://github.com/vaporvee/discord-rpc-godot into discord-social-sdk 2025-03-26 22:07:09 +01:00
ce0aef8dc0 activity resource and fixed connection ready signal 2025-03-26 22:06:11 +01:00
Duskitten
9ec8019915 Added Lobby + Linked Channel Nodes 2025-03-26 16:44:32 -04:00
b363a11b3a a lot of changes 2025-03-26 19:48:49 +01:00
295601c7f6 fixed reparenting error 2025-03-26 02:32:59 +01:00
9ce43ef5ce better classes 2025-03-26 00:29:17 +01:00
d78f2ca856 got root connector node saved as variable 2025-03-26 00:11:28 +01:00
d07acbb149 fixed token loading crash 2025-03-25 22:58:22 +01:00
44685b12e8 more doc generation stuff 2025-03-25 22:08:45 +01:00
ff62edb36b continued working on discord connector 2025-03-25 14:07:51 +01:00
3f6e695c77 finished discord connector node 2025-03-25 11:43:16 +01:00
41b62a248c more signals 2025-03-25 10:56:56 +01:00
3c1d5c9e8f fixed fileaccess error 2025-03-25 10:20:08 +01:00
91d592c484 added option to manage token automatically 2025-03-24 17:45:00 +01:00
8e029178cd added signal and cleaned up old code 2025-03-24 14:42:20 +01:00
70969b5a19 fixed app id type and DiscordConnector node 2025-03-23 23:33:38 +01:00
e9a48bba97 a lot of social sdk progress 2025-03-23 22:36:54 +01:00
80bcfbd98d added TODO 2025-03-22 03:11:23 +01:00
27eafb1511 simple richpresence 2025-03-22 02:48:49 +01:00
7cd67693cc FINALLY fixed the build error 2025-03-22 01:33:28 +01:00
7621e73174 yeah no it was never fixed 2025-03-22 00:42:07 +01:00
115cf4c8c0 fixed import errors 2025-03-22 00:13:17 +01:00
bcb3cb95a0 more towards building 2025-03-19 16:53:37 +01:00
185f295ffb implemented scons for social sdk 2025-03-19 13:38:33 +01:00
8edabeddf8 new setup script 2025-03-19 02:37:08 +01:00
ac8c67b387 removed old sdk code 2025-03-19 02:18:04 +01:00
302c039171 began porting discord social sdk (insanely broken) 2025-03-18 18:06:09 +01:00
78 changed files with 2292 additions and 968 deletions

View File

@@ -21,7 +21,7 @@ body:
id: version id: version
attributes: attributes:
label: Version label: Version
description: What version of the Discord RPC Godot plugin are you running? description: What version of the Discord Social SDK Godot plugin are you running?
options: options:
- 1.3.1 - 1.3.1
- 1.3.0 - 1.3.0

11
.gitignore vendored
View File

@@ -22,7 +22,14 @@ build/
*.a *.a
#Clean files from setup.py #Clean files from setup.py
src/lib/discord_game_sdk/ src/lib/discord_social_sdk/*
src/lib/godot-cpp/*
discordpp.h
cdiscord.h
*.zip
*.so
*.dll
*.dylib
# release # release
release/ release/
@@ -31,3 +38,5 @@ project/export/
# venv # venv
venv/ venv/
# docs
src/gen/

2
.gitmodules vendored
View File

@@ -1,4 +1,4 @@
[submodule "src/lib/godot-cpp"] [submodule "src/lib/godot-cpp"]
path = src/lib/godot-cpp path = src/lib/godot-cpp
url = https://github.com/godotengine/godot-cpp url = https://github.com/godotengine/godot-cpp
branch = 4.1 branch = 4.4

View File

@@ -7,7 +7,8 @@
"${workspaceFolder}/src/lib/godot-cpp/gen/include", "${workspaceFolder}/src/lib/godot-cpp/gen/include",
"${workspaceFolder}/src/lib/godot-cpp/include", "${workspaceFolder}/src/lib/godot-cpp/include",
"${workspaceFolder}/src/lib/godot-cpp/gdextension", "${workspaceFolder}/src/lib/godot-cpp/gdextension",
"${workspaceFolder}/src/lib/godot-cpp/gen/include" "${workspaceFolder}/src/lib/godot-cpp/gen/include",
"${workspaceFolder}/src/lib/discord_social_sdk/include"
], ],
"defines": [ "defines": [
"_DEBUG", "_DEBUG",

73
.vscode/settings.json vendored
View File

@@ -4,10 +4,79 @@
"${workspaceFolder}/src/lib/godot-cpp/include" "${workspaceFolder}/src/lib/godot-cpp/include"
], ],
"files.associations": { "files.associations": {
"*.gdextension": "ini" "*.gdextension": "ini",
"charconv": "cpp",
"chrono": "cpp",
"optional": "cpp",
"format": "cpp",
"ratio": "cpp",
"system_error": "cpp",
"array": "cpp",
"functional": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"any": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"string_view": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"ranges": "cpp",
"semaphore": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"text_encoding": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"variant": "cpp",
"condition_variable": "cpp",
"mutex": "cpp"
}, },
"[python]": { "[python]": {
"editor.defaultFormatter": "ms-python.black-formatter" "editor.defaultFormatter": "ms-python.black-formatter"
}, },
"python.formatting.provider": "none" "python.formatting.provider": "none",
"cmake.ignoreCMakeListsMissing": true
} }

View File

@@ -1,4 +1,4 @@
<img src="/project/assets/discordgodot_banner.png" alt="Project Banner"> <img src="/project/assets/discordgodot_banner.png" alt="Project Banner" />
Discord recently released its new SDK. This plugin will get a huge overhaul and new docs as soon as possible. Discord recently released its new SDK. This plugin will get a huge overhaul and new docs as soon as possible.
<a href="https://discord.com/blog/announcing-discord-social-sdk-to-power-game-comms" target="_blank">Discord's anouncement</a> <a href="https://discord.com/blog/announcing-discord-social-sdk-to-power-game-comms" target="_blank">Discord's anouncement</a>
@@ -6,11 +6,11 @@ Discord recently released its new SDK. This plugin will get a huge overhaul and
--- ---
### This is for the Discord Game SDK NOT the Embedded App SDK ### This is for the Discord Game SDK NOT the Embedded App SDK
**Discord RPC Plugin for GDScript with an easy-to-use code pattern in Godot Engine 4.1+, with optional Editor Rich Presence! (Compatible with Linux, Windows, & MacOS)**<br><br> **Discord Social SDK Plugin for GDScript with an easy-to-use code pattern in Godot Engine 4.1+, with optional Editor Rich Presence! (Compatible with Linux, Windows, & MacOS)**<br><br>
<br> <br />
### [My Discord Server](https://discord.gg/3gqUrtbaur) ### [My Discord Server](https://discord.gg/3gqUrtbaur)
# [Quick start :rocket: (click here)](https://docs.vaporvee.com/discord-rpc-godot#quick-start) # [Quick start :rocket: (click here)](https://docs.vaporvee.com/discord-rpc-godot#quick-start)
<br> <br />
**A small donation with the sponsor button would be nice if you sell your project with this addon but is of course not mandatory!** **A small donation with the sponsor button would be nice if you sell your project with this addon but is of course not mandatory!**
@@ -21,15 +21,14 @@ Discord recently released its new SDK. This plugin will get a huge overhaul and
- User information - User information
- Relationship Manager (Get friendlist and its updates) - Relationship Manager (Get friendlist and its updates)
- Overlay management - Overlay management
- Editor Presence (optional) <br> - Editor Presence (optional)
<br>
<img width="600px" src="https://raw.githubusercontent.com/vaporvee/discord-sdk-godot/main/project/assets/GodotEditorPresenceBanner.png"> <img width="600px" src="https://raw.githubusercontent.com/vaporvee/discord-sdk-godot/main/project/assets/GodotEditorPresenceBanner.png">
<br> <br />
<br> <br />
### Credit ### Credit
[@Pukimaa](https://github.com/pukimaa) - Designer<br> [@Pukimaa](https://github.com/pukimaa) - Designer<br>
<br> <br />
*This project is not endorsed or affiliated with Discord Inc. or the Godot Foundation.* *This project is not endorsed or affiliated with Discord Inc. or the Godot Foundation.*

View File

@@ -3,19 +3,26 @@ import os
env = SConscript("src/lib/godot-cpp/SConstruct") env = SConscript("src/lib/godot-cpp/SConstruct")
env.Append(CPPPATH=["src/","src/nodes/", "src/resources/", "src/lib/discord_social_sdk/include/"])
sources = [Glob("src/*.cpp"), Glob("src/nodes/*.cpp"), Glob("src/resources/*.cpp")]
if env["target"] in ["editor", "template_debug"]:
try:
doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
sources.append(doc_data)
except AttributeError:
print("Not including class reference as we're targeting a pre-4.3 baseline.")
if env["platform"] == "macos": if env["platform"] == "macos":
discord_library = "libdiscord_game_sdk.dylib" discord_library = "libdiscord_partner_sdk.dylib"
discord_library_second = ""
libexportfolder = "/macos/" libexportfolder = "/macos/"
elif env["platform"] in ("linuxbsd", "linux"): elif env["platform"] in ("linuxbsd", "linux"):
discord_library = "libdiscord_game_sdk.so" discord_library = "libdiscord_partner_sdk.so"
discord_library_second = ""
libexportfolder = "/linux/" libexportfolder = "/linux/"
elif env["platform"] == "windows": elif env["platform"] == "windows":
discord_library = "discord_game_sdk.dll" discord_library = "discord_partner_sdk.dll"
discord_library_second = "discord_game_sdk_x86.dll"
libexportfolder = "/windows/" libexportfolder = "/windows/"
if env["target"] == "template_debug": if env["target"] == "template_debug":
@@ -23,26 +30,15 @@ if env["target"] == "template_debug":
else: else:
debugsuffix = "" debugsuffix = ""
if env.get("arch") == "arm64": env.Append(LIBPATH=["src/lib/discord_social_sdk/lib/release/"])
armsuffix = "_arm64" env.Append(LIBS=["discord_partner_sdk"])
else:
armsuffix = ""
env.Append(LIBPATH=["src/lib/discord_game_sdk/bin/"])
sources = Glob("src/lib/discord_game_sdk/cpp/*.cpp")
env.Append(CPPPATH=["src/lib/discord_game_sdk/cpp/"])
env.Append(LIBS=["discord_game_sdk"])
env.Append(CPPPATH=["src/"])
sources += Glob("src/*.cpp")
env.Append(CPPDEFINES=["HOT_RELOAD_ENABLED"]) env.Append(CPPDEFINES=["HOT_RELOAD_ENABLED"])
library = env.SharedLibrary( library = env.SharedLibrary(
target="project/addons/discord-rpc-gd/bin/" target="project/addons/discord-rpc-gd/bin/"
+ libexportfolder + libexportfolder
+ "discord_game_sdk_binding" + "discord_partner_sdk_binding"
+ armsuffix
+ debugsuffix, + debugsuffix,
source=sources, source=sources,
) )
@@ -50,20 +46,9 @@ env.Depends(
library, library,
Command( Command(
"project/addons/discord-rpc-gd/bin/" + libexportfolder + discord_library, "project/addons/discord-rpc-gd/bin/" + libexportfolder + discord_library,
"src/lib/discord_game_sdk/bin/" + discord_library, "src/lib/discord_social_sdk/lib/release/" + discord_library,
Copy("$TARGET", "$SOURCE"), Copy("$TARGET", "$SOURCE"),
), ),
) )
if discord_library_second != "":
env.Depends(
library,
Command(
"project/addons/discord-rpc-gd/bin/"
+ libexportfolder
+ discord_library_second,
"src/lib/discord_game_sdk/bin/" + discord_library_second,
Copy("$TARGET", "$SOURCE"),
),
)
Default(library) Default(library)

View File

@@ -31,10 +31,10 @@ elif choice in no:
cur_working_dir = os.path.dirname(os.path.realpath(__file__)) + '/project/addons/discord-rpc-gd/bin/macos/' cur_working_dir = os.path.dirname(os.path.realpath(__file__)) + '/project/addons/discord-rpc-gd/bin/macos/'
os.chdir(cur_working_dir) os.chdir(cur_working_dir)
os.system( os.system(
"lipo -create libdiscord_game_sdk_binding_arm64_debug.dylib libdiscord_game_sdk_binding_debug.dylib -output libdiscord_game_sdk_binding_debug.dylib" "lipo -create libdiscord_partner_sdk_binding_arm64_debug.dylib libdiscord_partner_sdk_binding_debug.dylib -output libdiscord_partner_sdk_binding_debug.dylib"
) )
os.system( os.system(
"lipo -create libdiscord_game_sdk_binding_arm64.dylib libdiscord_game_sdk_binding.dylib -output libdiscord_game_sdk_binding.dylib" "lipo -create libdiscord_partner_sdk_binding_arm64.dylib libdiscord_partner_sdk_binding.dylib -output libdiscord_partner_sdk_binding.dylib"
) )
else: # Linux else: # Linux
os.system( os.system(

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="DiscordConnector" inherits="DiscordSocialSDK" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="connect_user">
<return type="void" />
<description>
</description>
</method>
<method name="refresh_user_token">
<return type="void" />
<param index="0" name="current_refresh_token" type="String" />
<description>
</description>
</method>
<method name="update_user_token">
<return type="void" />
<param index="0" name="access_token" type="String" />
<description>
</description>
</method>
</methods>
<members>
<member name="app_id" type="int" setter="set_app_id" getter="get_app_id" default="0">
</member>
<member name="auto_connect" type="bool" setter="set_auto_connect" getter="get_auto_connect" default="false">
</member>
<member name="auto_encryption_key" type="String" setter="set_auto_encryption_key" getter="get_auto_encryption_key" default="&quot;&quot;">
</member>
<member name="auto_token_manage" type="bool" setter="set_auto_token_manage" getter="get_auto_token_manage" default="false">
</member>
</members>
<signals>
<signal name="user_connected">
<param index="0" name="access_token" type="String" />
<param index="1" name="refresh_token" type="String" />
<param index="2" name="expires_in" type="int" />
<description>
</description>
</signal>
<signal name="user_connection_failed">
<param index="0" name="error" type="String" />
<description>
</description>
</signal>
<signal name="user_token_refresh_failed">
<param index="0" name="error" type="String" />
<description>
</description>
</signal>
<signal name="user_token_refreshed">
<param index="0" name="access_token" type="String" />
<param index="1" name="refresh_token" type="String" />
<param index="2" name="expires_in" type="int" />
<description>
</description>
</signal>
<signal name="user_update_failed">
<param index="0" name="error" type="String" />
<description>
</description>
</signal>
<signal name="user_updated">
<description>
</description>
</signal>
</signals>
</class>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="DiscordSocialSDK" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="DiscordUtil" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="debug">
<return type="void" />
<description>
</description>
</method>
<method name="generate_auto_encryption_key">
<return type="String" />
<description>
</description>
</method>
<method name="run_callbacks">
<return type="void" />
<description>
</description>
</method>
<method name="save_tokens">
<return type="void" />
<param index="0" name="access_token" type="String" />
<param index="1" name="refresh_token" type="String" />
<param index="2" name="expires_in" type="int" />
<param index="3" name="auto_encryption_key" type="String" />
<description>
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorPresence" inherits="DiscordSocialSDK" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
</class>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://csl0e2px0cwc1"
path="res://.godot/imported/Logo_V2_No_Bg.png-ed667fb599fe1e17ebcfc361ff7c9c93.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/discord-rpc-gd/Logo_V2_No_Bg.png"
dest_files=["res://.godot/imported/Logo_V2_No_Bg.png-ed667fb599fe1e17ebcfc361ff7c9c93.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -1,6 +1,6 @@
MINIMUM GODOT VERSION: 4.2 MINIMUM GODOT VERSION: 4.2
PLEASE ACTIVATE THE PLUGIN UNDER Project -> Project Settings... -> Plugins -> DiscordRPC -> Status PLEASE ACTIVATE THE PLUGIN UNDER Project -> Project Settings... -> Plugins -> DiscordUtil -> Status
IGNORE THE RED ERRORS ON THE FIRST 2 RESTARTS IGNORE THE RED ERRORS ON THE FIRST 2 RESTARTS
READ THE TUTORIAL LINKED IN THE WINDOW THAT WILL OPEN ON PLUGIN ENABLE READ THE TUTORIAL LINKED IN THE WINDOW THAT WILL OPEN ON PLUGIN ENABLE

View File

@@ -1,29 +1,23 @@
[configuration] [configuration]
entry_symbol = "discordrpcgd_library_init" entry_symbol = "DiscordUtilgd_library_init"
compatibility_minimum = 4.1 compatibility_minimum = 4.1
[libraries] [libraries]
macos.debug = "macos/libdiscord_game_sdk_binding_debug.dylib" macos.debug = "macos/libdiscord_partner_sdk_binding_debug.dylib"
macos.release = "macos/libdiscord_game_sdk_binding.dylib" macos.release = "macos/libdiscord_partner_sdk_binding.dylib"
windows.debug.x86_64 = "windows/discord_game_sdk_binding_debug.dll" windows.debug.x86_64 = "windows/discord_partner_sdk_binding_debug.dll"
windows.release.x86_64 = "windows/discord_game_sdk_binding.dll" windows.release.x86_64 = "windows/discord_partner_sdk_binding.dll"
linux.debug.x86_64 = "linux/libdiscord_game_sdk_binding_debug.so" linux.debug.x86_64 = "linux/libdiscord_partner_sdk_binding_debug.so"
linux.release.x86_64 = "linux/libdiscord_game_sdk_binding.so" linux.release.x86_64 = "linux/libdiscord_partner_sdk_binding.so"
linux.debug.arm64 = "linux/libdiscord_game_sdk_binding_debug.so" linux.debug.arm64 = "linux/libdiscord_partner_sdk_binding_debug.so"
linux.release.arm64 = "linux/libdiscord_game_sdk_binding.so" linux.release.arm64 = "linux/libdiscord_partner_sdk_binding.so"
linux.debug.rv64 = "linux/libdiscord_game_sdk_binding_debug.so" linux.debug.rv64 = "linux/libdiscord_partner_sdk_binding_debug.so"
linux.release.rv64 = "linux/libdiscord_game_sdk_binding.so" linux.release.rv64 = "linux/libdiscord_partner_sdk_binding.so"
[dependencies] [dependencies]
macos = { "macos/libdiscord_game_sdk.dylib": "" } macos = { "macos/libdiscord_partner_sdk.dylib": "" }
windows.debug.x86_64 = { "windows/discord_game_sdk.dll": "" } windows.x86_64 = { "windows/discord_partner_sdk.dll": "" }
windows.release.x86_64 = { "windows/discord_game_sdk.dll": "" } linux = { "linux/libdiscord_partner_sdk.so": "" }
linux.debug.x86_64 = { "linux/libdiscord_game_sdk.so": "" }
linux.release.x86_64 = { "linux/libdiscord_game_sdk.so": "" }
linux.debug.arm64 = { "linux/libdiscord_game_sdk.so": "" }
linux.release.arm64 = { "linux/libdiscord_game_sdk.so": "" }
linux.debug.rv64 = { "linux/libdiscord_game_sdk.so": "" }
linux.release.rv64 = { "linux/libdiscord_game_sdk.so": "" }

View File

@@ -0,0 +1 @@
uid://jcdwy5o1utpj

View File

@@ -1,8 +1,8 @@
class_name DiscordRPCTutorial class_name DiscordUtilTutorial
extends Node extends Node
## 1. Put the addons/ folder in your Godot project[br] ## 1. Put the addons/ folder in your Godot project[br]
## 2. Enable the addon in your Project Settings under "Plugins" and "DiscordRPC". [br](if it doesn't show up restart your project and try again)[br] ## 2. Enable the addon in your Project Settings under "Plugins" and "DiscordUtil". [br](if it doesn't show up restart your project and try again)[br]
## 3. Restart your project[br] ## 3. Restart your project[br]
## 4. Create an Application under https://discord.com/developers/applications and get the Application ID br] ## 4. Create an Application under https://discord.com/developers/applications and get the Application ID br]
## 5. (optional) Set images under "Rich Presence" and "Art Assets" and remember the keys[br] ## 5. (optional) Set images under "Rich Presence" and "Art Assets" and remember the keys[br]
@@ -11,27 +11,27 @@ extends Node
## [codeblock] ## [codeblock]
## func _ready(): ## func _ready():
## # Application ID ## # Application ID
## DiscordRPC.app_id = 1099618430065324082 ## DiscordUtil.app_id = 1099618430065324082
## # this is boolean if everything worked ## # this is boolean if everything worked
## print("Discord working: " + str(DiscordRPC.get_is_discord_working())) ## print("Discord working: " + str(DiscordUtil.get_is_discord_working()))
## # Set the first custom text row of the activity here ## # Set the first custom text row of the activity here
## DiscordRPC.details = "A demo activity by vaporvee#1231" ## DiscordUtil.details = "A demo activity by vaporvee#1231"
## # Set the second custom text row of the activity here ## # Set the second custom text row of the activity here
## DiscordRPC.state = "Checkpoint 23/23" ## DiscordUtil.state = "Checkpoint 23/23"
## # Image key for small image from "Art Assets" from the Discord Developer website ## # Image key for small image from "Art Assets" from the Discord Developer website
## DiscordRPC.large_image = "game" ## DiscordUtil.large_image = "game"
## # Tooltip text for the large image ## # Tooltip text for the large image
## DiscordRPC.large_image_text = "Try it now!" ## DiscordUtil.large_image_text = "Try it now!"
## # Image key for large image from "Art Assets" from the Discord Developer website ## # Image key for large image from "Art Assets" from the Discord Developer website
## DiscordRPC.small_image = "boss" ## DiscordUtil.small_image = "boss"
## # Tooltip text for the small image ## # Tooltip text for the small image
## DiscordRPC.small_image_text = "Fighting the end boss! D:" ## DiscordUtil.small_image_text = "Fighting the end boss! D:"
## # "02:41 elapsed" timestamp for the activity ## # "02:41 elapsed" timestamp for the activity
## DiscordRPC.start_timestamp = int(Time.get_unix_time_from_system()) ## DiscordUtil.start_timestamp = int(Time.get_unix_time_from_system())
## # "59:59 remaining" timestamp for the activity ## # "59:59 remaining" timestamp for the activity
## DiscordRPC.end_timestamp = int(Time.get_unix_time_from_system()) + 3600 ## DiscordUtil.end_timestamp = int(Time.get_unix_time_from_system()) + 3600
## # Always refresh after changing the values! ## # Always refresh after changing the values!
## DiscordRPC.refresh() ## DiscordUtil.refresh()
## [/codeblock] ## [/codeblock]
## ##
## @tutorial(More information here): https://github.com/vaporvee/discord-rpc-godot/wiki/Quick-start ## @tutorial(More information here): https://github.com/vaporvee/discord-rpc-godot/wiki/Quick-start

View File

@@ -0,0 +1 @@
uid://dkv4pruvla8bn

View File

@@ -10,11 +10,8 @@ script/source = "extends Node
@onready var animation_player: AnimationPlayer = $Panel/TextureRect/AnimationPlayer @onready var animation_player: AnimationPlayer = $Panel/TextureRect/AnimationPlayer
func _ready():
DiscordRPC.connect(\"activity_join_request\",_on_activity_join_request)
func _process(_delta) -> void: func _process(_delta) -> void:
if(DiscordRPC.get_is_discord_working()): if(false):
$Panel/TextureRect.self_modulate = Color(\"#3eff8d\") $Panel/TextureRect.self_modulate = Color(\"#3eff8d\")
if !animation_player.is_playing(): if !animation_player.is_playing():
animation_player.play(\"pulsate\") animation_player.play(\"pulsate\")
@@ -48,35 +45,13 @@ Is party public: {ppublic} (needs to be activated in Discord client settings)
Is instanced: {instanced} Is instanced: {instanced}
\" \"
$Panel/Info.text = $Panel/Info.text.replace(\"{ppublic}\",str(DiscordRPC.is_public_party)).replace(\"{instanced}\",str(DiscordRPC.instanced)).replace(\"{ssecret}\",DiscordRPC.spectate_secret).replace(\"{jsecret}\",DiscordRPC.join_secret).replace(\"{msecret}\",DiscordRPC.match_secret).replace(\"{mpartysize}\",str(DiscordRPC.max_party_size)).replace(\"{cpartysize}\",str(DiscordRPC.current_party_size)).replace(\"{partyid}\",DiscordRPC.party_id).replace(\"{id}\",str(DiscordRPC.app_id)).replace(\"{details}\",DiscordRPC.details).replace(\"{state}\",DiscordRPC.state).replace(\"{lkey}\",DiscordRPC.large_image).replace(\"{ltext}\",DiscordRPC.large_image_text).replace(\"{skey}\",DiscordRPC.small_image).replace(\"{stext}\",DiscordRPC.small_image_text).replace(\"{stimestamp}\",str(DiscordRPC.start_timestamp)).replace(\"{etimestamp}\",str(DiscordRPC.end_timestamp)) #$Panel/Info.text = $Panel/Info.text.replace(\"{ppublic}\",str(DiscordUtil.is_public_party)).replace(\"{instanced}\",str(DiscordUtil.instanced)).replace(\"{ssecret}\",DiscordUtil.spectate_secret).replace(\"{jsecret}\",DiscordUtil.join_secret).replace(\"{msecret}\",DiscordUtil.match_secret).replace(\"{mpartysize}\",str(DiscordUtil.max_party_size)).replace(\"{cpartysize}\",str(DiscordUtil.current_party_size)).replace(\"{partyid}\",DiscordUtil.party_id).replace(\"{id}\",str(DiscordUtil.app_id)).replace(\"{details}\",DiscordUtil.details).replace(\"{state}\",DiscordUtil.state).replace(\"{lkey}\",DiscordUtil.large_image).replace(\"{ltext}\",DiscordUtil.large_image_text).replace(\"{skey}\",DiscordUtil.small_image).replace(\"{stext}\",DiscordUtil.small_image_text).replace(\"{stimestamp}\",str(DiscordUtil.start_timestamp)).replace(\"{etimestamp}\",str(DiscordUtil.end_timestamp))
var user_request: Dictionary = {}; var user_request: Dictionary = {};
func _on_activity_join_request(user_requesting: Dictionary) -> void: func _on_activity_join_request(user_requesting: Dictionary) -> void:
print(user_requesting) print(user_requesting)
user_request = user_requesting user_request = user_requesting
func _on_accept_join_request_pressed() -> void:
if(!user_request.is_empty()):
DiscordRPC.accept_join_request(user_request.id)
func _on_invite_with_user_id_text_submitted(new_text: String) -> void:
DiscordRPC.send_invite(int(new_text),true,\"this is a test invite sent from godot\")
func _on_accept_with_user_id_text_submitted(new_text: String) -> void:
DiscordRPC.accept_invite(int(new_text))
func _on_print_current_user_on_console_pressed() -> void:
print(DiscordRPC.get_current_user())
func _on_toggle_sdk_toggled(button_pressed: bool) -> void:
if(button_pressed):
DiscordRPC.unclear()
else:
DiscordRPC.clear(false)
func _on_print_friends_pressed() -> void:
print(DiscordRPC.get_all_relationships())
" "
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8abo6"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8abo6"]
@@ -115,8 +90,8 @@ tracks/0/keys = {
[sub_resource type="AnimationLibrary" id="AnimationLibrary_a7ofc"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_a7ofc"]
_data = { _data = {
"RESET": SubResource("Animation_mmtmn"), &"RESET": SubResource("Animation_mmtmn"),
"pulsate": SubResource("Animation_5u02v") &"pulsate": SubResource("Animation_5u02v")
} }
[node name="DebugNodeGroup" type="Node"] [node name="DebugNodeGroup" type="Node"]
@@ -260,7 +235,7 @@ texture = ExtResource("3_goflf")
[node name="AnimationPlayer" type="AnimationPlayer" parent="Panel/TextureRect"] [node name="AnimationPlayer" type="AnimationPlayer" parent="Panel/TextureRect"]
libraries = { libraries = {
"": SubResource("AnimationLibrary_a7ofc") &"": SubResource("AnimationLibrary_a7ofc")
} }
[connection signal="pressed" from="Panel/PrintCurrentUserOnConsole" to="." method="_on_print_current_user_on_console_pressed"] [connection signal="pressed" from="Panel/PrintCurrentUserOnConsole" to="." method="_on_print_current_user_on_console_pressed"]

View File

@@ -1,6 +1,6 @@
## This is a Debug Node wich will show some usefull info and buttons/input ## This is a Debug Node wich will show some usefull info and buttons/input
## ##
## The DiscordRPC Debug Node will show info about the current values of its variables and some buttons to change them. ## The DiscordUtil Debug Node will show info about the current values of its variables and some buttons to change them.
## ##
## @tutorial: https://github.com/vaporvee/discord-rpc-godot/wiki ## @tutorial: https://github.com/vaporvee/discord-rpc-godot/wiki
@tool @tool

View File

@@ -0,0 +1 @@
uid://46tue7u6crd6

View File

@@ -1,13 +0,0 @@
## This is a GDscript Node wich gets automatically added as Autoload while installing the addon.
##
## It can run in the background to comunicate with Discord.
## You don't need to use it. If you remove it make sure to run [code]DiscordRPC.run_callbacks()[/code] in a [code]_process[/code] function.
##
## @tutorial: https://github.com/vaporvee/discord-rpc-godot/wiki
extends Node
func _ready() -> void:
pass
func _process(_delta) -> void:
DiscordRPC.run_callbacks()

View File

@@ -1,7 +1,7 @@
[plugin] [plugin]
name="DiscordRPC" name="Discord Social SDK"
description="Discord RPC Plugin for GDScript in Godot" description="Discord Social SDK Plugin for GDScript in Godot"
author="vaporvee" author="vaporvee"
version="1.3.2" version="1.3.2"
script="plugin.gd" script="plugin.gd"

View File

@@ -1,42 +1,42 @@
@tool @tool
extends EditorPlugin extends EditorPlugin
const DiscordRPCDebug: GDScript = preload("res://addons/discord-rpc-gd/nodes/debug.gd") const DiscordSocialSDKDebug: GDScript = preload("res://addons/discord-rpc-gd/nodes/debug.gd")
const DiscordRPCDebug_icon: Texture2D = preload("res://addons/discord-rpc-gd/Debug.svg") const DiscordSocialSDKDebug_icon: Texture2D = preload("res://addons/discord-rpc-gd/Debug.svg")
var loaded_DiscordRPCDebug: DiscordRPCDebug = DiscordRPCDebug.new() var loaded_DiscordSocialSDKDebug: DiscordSocialSDKDebug = DiscordSocialSDKDebug.new()
var restart_window: ConfirmationDialog = preload("res://addons/discord-rpc-gd/restart_window.tscn").instantiate() var restart_window: ConfirmationDialog = preload("res://addons/discord-rpc-gd/restart_window.tscn").instantiate()
var plugin_cfg: ConfigFile = ConfigFile.new() var plugin_cfg: ConfigFile = ConfigFile.new()
const plugin_data_filename: String = "/plugin_data.cfg" const plugin_data_filename: String = "/plugin_data.cfg"
func _enter_tree() -> void:
add_custom_type("DiscordRPCDebug","Node",DiscordRPCDebug,DiscordRPCDebug_icon)
get_editor_interface().get_editor_settings().settings_changed.connect(_on_editor_settings_changed)
func _ready() -> void: func _ready() -> void:
await get_tree().create_timer(0.5).timeout await get_tree().create_timer(0.5).timeout
plugin_cfg.load(get_editor_interface().get_editor_paths().get_data_dir() + plugin_data_filename) plugin_cfg.load(get_editor_interface().get_editor_paths().get_data_dir() + plugin_data_filename)
if !get_editor_interface().get_editor_settings().has_setting("DiscordRPC/EditorPresence/enabled"): if !get_editor_interface().get_editor_settings().has_setting("DiscordSocialSDK/EditorPresence/enabled"):
get_editor_interface().get_editor_settings().set_setting("DiscordRPC/EditorPresence/enabled",plugin_cfg.get_value("Discord","editor_presence",false)) get_editor_interface().get_editor_settings().set_setting("DiscordSocialSDK/EditorPresence/enabled",plugin_cfg.get_value("Discord","editor_presence",false))
func _enter_tree() -> void:
add_custom_type("DiscordSocialSDKDebug","Node",DiscordSocialSDKDebug,DiscordSocialSDKDebug_icon)
get_editor_interface().get_editor_settings().settings_changed.connect(_on_editor_settings_changed)
func _exit_tree() -> void: func _exit_tree() -> void:
if get_editor_interface().get_editor_settings().has_setting("DiscordRPC/EditorPresence/enabled"): if get_editor_interface().get_editor_settings().has_setting("DiscordSocialSDK/EditorPresence/enabled"):
get_editor_interface().get_editor_settings().erase("DiscordRPC/EditorPresence/enabled") get_editor_interface().get_editor_settings().erase("DiscordSocialSDK/EditorPresence/enabled")
func _enable_plugin() -> void: func _enable_plugin() -> void:
add_custom_type("DiscordSocialSDKDebug","Node",DiscordSocialSDKDebug,DiscordSocialSDKDebug_icon)
get_editor_interface().get_editor_settings().settings_changed.connect(_on_editor_settings_changed)
if FileAccess.file_exists(ProjectSettings.globalize_path("res://") + "addons/discord-rpc-gd/bin/.gdignore"): if FileAccess.file_exists(ProjectSettings.globalize_path("res://") + "addons/discord-rpc-gd/bin/.gdignore"):
DirAccess.remove_absolute(ProjectSettings.globalize_path("res://") + "addons/discord-rpc-gd/bin/.gdignore") DirAccess.remove_absolute(ProjectSettings.globalize_path("res://") + "addons/discord-rpc-gd/bin/.gdignore")
add_autoload_singleton("DiscordRPCLoader","res://addons/discord-rpc-gd/nodes/discord_autoload.gd")
restart_window.connect("confirmed", save_no_restart) restart_window.connect("confirmed", save_no_restart)
restart_window.connect("canceled", save_and_restart) restart_window.connect("canceled", save_and_restart)
get_editor_interface().popup_dialog_centered(restart_window) get_editor_interface().popup_dialog_centered(restart_window)
print("IGNORE RED ERROR MESSAGES BEFORE THE SECOND RESTART!") print("IGNORE RED ERROR MESSAGES BEFORE THE SECOND RESTART!")
func _disable_plugin() -> void: func _disable_plugin() -> void:
remove_autoload_singleton("DiscordRPCLoader")
FileAccess.open("res://addons/discord-rpc-gd/bin/.gdignore",FileAccess.WRITE) FileAccess.open("res://addons/discord-rpc-gd/bin/.gdignore",FileAccess.WRITE)
remove_custom_type("DiscordRPCDebug") remove_custom_type("DiscordSocialSDKDebug")
get_editor_interface().get_editor_settings().erase("DiscordRPC/EditorPresence/enabled") get_editor_interface().get_editor_settings().erase("DiscordSocialSDK/EditorPresence/enabled")
push_warning("Please restart the editor to fully disable the DiscordRPC plugin") push_warning("Please restart the editor to fully disable the Discord Social SDK plugin")
func save_and_restart() -> void: func save_and_restart() -> void:
get_editor_interface().restart_editor(true) get_editor_interface().restart_editor(true)
@@ -46,11 +46,11 @@ func save_no_restart() -> void:
var editor_presence: Node var editor_presence: Node
func _on_editor_settings_changed() -> void: func _on_editor_settings_changed() -> void:
plugin_cfg.set_value("Discord","editor_presence",get_editor_interface().get_editor_settings().get_setting("DiscordRPC/EditorPresence/enabled")) plugin_cfg.set_value("Discord","editor_presence",get_editor_interface().get_editor_settings().get_setting("DiscordSocialSDK/EditorPresence/enabled"))
plugin_cfg.save(get_editor_interface().get_editor_paths().get_data_dir() + plugin_data_filename) plugin_cfg.save(get_editor_interface().get_editor_paths().get_data_dir() + plugin_data_filename)
if ClassDB.class_exists("EditorPresence") && editor_presence == null: if ClassDB.class_exists("EditorPresence") && editor_presence == null:
editor_presence = ClassDB.instantiate("EditorPresence") editor_presence = ClassDB.instantiate("EditorPresence")
if get_editor_interface().get_editor_settings().has_setting("DiscordRPC/EditorPresence/enabled") && get_editor_interface().get_editor_settings().get_setting("DiscordRPC/EditorPresence/enabled"): if get_editor_interface().get_editor_settings().has_setting("DiscordSocialSDK/EditorPresence/enabled") && get_editor_interface().get_editor_settings().get_setting("DiscordSocialSDK/EditorPresence/enabled"):
add_child(editor_presence) add_child(editor_presence)
else: else:
editor_presence.queue_free() editor_presence.queue_free()

View File

@@ -0,0 +1 @@
uid://citpv7uf0k1ku

View File

@@ -9,7 +9,7 @@ Button/styles/focus = SubResource("StyleBoxEmpty_1t7mm")
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5vqdt"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5vqdt"]
[sub_resource type="Image" id="Image_f4u3i"] [sub_resource type="Image" id="Image_4rf8i"]
data = { data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 168, 224, 224, 224, 233, 224, 224, 224, 236, 224, 224, 224, 170, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 234, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 168, 224, 224, 224, 255, 224, 224, 224, 186, 224, 224, 224, 32, 224, 224, 224, 33, 224, 224, 224, 187, 224, 224, 224, 255, 225, 225, 225, 167, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 255, 224, 224, 224, 234, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 38, 224, 224, 224, 255, 224, 224, 224, 229, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 164, 224, 224, 224, 255, 224, 224, 224, 187, 225, 225, 225, 34, 227, 227, 227, 36, 224, 224, 224, 192, 224, 224, 224, 255, 224, 224, 224, 162, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 229, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 224, 224, 224, 216, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 161, 224, 224, 224, 232, 224, 224, 224, 231, 225, 225, 225, 159, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 107, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 105, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 224, 224, 224, 130, 255, 255, 255, 1, 255, 255, 255, 1, 225, 225, 225, 134, 224, 224, 224, 224, 225, 225, 225, 223, 224, 224, 224, 132, 255, 255, 255, 1, 255, 255, 255, 6, 224, 224, 224, 137, 224, 224, 224, 231, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 130, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 129, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 220, 225, 225, 225, 223, 224, 224, 224, 255, 226, 226, 226, 61, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 222, 224, 224, 224, 231, 224, 224, 224, 255, 227, 227, 227, 62, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 222, 224, 224, 224, 255, 227, 227, 227, 63, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 230, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 127, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 126, 225, 225, 225, 135, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 1, 224, 224, 224, 128, 224, 224, 224, 220, 224, 224, 224, 219, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 134, 224, 224, 224, 229, 224, 224, 224, 255, 255, 255, 255, 0), "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 168, 224, 224, 224, 233, 224, 224, 224, 236, 224, 224, 224, 170, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 234, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 168, 224, 224, 224, 255, 224, 224, 224, 186, 224, 224, 224, 32, 224, 224, 224, 33, 224, 224, 224, 187, 224, 224, 224, 255, 225, 225, 225, 167, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 255, 224, 224, 224, 234, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 38, 224, 224, 224, 255, 224, 224, 224, 229, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 164, 224, 224, 224, 255, 224, 224, 224, 187, 225, 225, 225, 34, 227, 227, 227, 36, 224, 224, 224, 192, 224, 224, 224, 255, 224, 224, 224, 162, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 229, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 224, 224, 224, 216, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 161, 224, 224, 224, 232, 224, 224, 224, 231, 225, 225, 225, 159, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 107, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 105, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 224, 224, 224, 130, 255, 255, 255, 1, 255, 255, 255, 1, 225, 225, 225, 134, 224, 224, 224, 224, 225, 225, 225, 223, 224, 224, 224, 132, 255, 255, 255, 1, 255, 255, 255, 6, 224, 224, 224, 137, 224, 224, 224, 231, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 130, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 129, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 220, 225, 225, 225, 223, 224, 224, 224, 255, 226, 226, 226, 61, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 222, 224, 224, 224, 231, 224, 224, 224, 255, 227, 227, 227, 62, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 222, 224, 224, 224, 255, 227, 227, 227, 63, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 230, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 127, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 126, 225, 225, 225, 135, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 1, 224, 224, 224, 128, 224, 224, 224, 220, 224, 224, 224, 219, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 134, 224, 224, 224, 229, 224, 224, 224, 255, 255, 255, 255, 0),
"format": "RGBA8", "format": "RGBA8",
@@ -19,7 +19,7 @@ data = {
} }
[sub_resource type="ImageTexture" id="ImageTexture_gdtpn"] [sub_resource type="ImageTexture" id="ImageTexture_gdtpn"]
image = SubResource("Image_f4u3i") image = SubResource("Image_4rf8i")
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7v0rg"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7v0rg"]

View File

@@ -0,0 +1,16 @@
extends DiscordConnector
@onready var discord_activity: DiscordActivity = $DiscordActivity
func _on_authenticated(access_token: String, refresh_token: String, expires_in: int) -> void:
print_debug("Access token: %s \nRefresh Token: %s \nExpires in: %s" % [access_token, refresh_token, expires_in])
func _on_authentication_failed(error: String) -> void:
push_error("Auth failed! Error: " + error)
func _on_connection_error(error: String) -> void:
push_error(error)
func _on_connection_ready() -> void:
print_debug("CONNECTION READY")
discord_activity.update()

View File

@@ -0,0 +1 @@
uid://kmubk5a6i385

View File

@@ -40,7 +40,7 @@ application/file_version=""
application/product_version="" application/product_version=""
application/company_name="" application/company_name=""
application/product_name="" application/product_name=""
application/file_description="Discord RPC Godot Test" application/file_description="Discord Social SDK Godot Test"
application/copyright="" application/copyright=""
application/trademarks="" application/trademarks=""
application/export_angle=0 application/export_angle=0

View File

@@ -1,63 +0,0 @@
extends Node
func _ready() -> void:
if GDExtensionManager.is_extension_loaded("res://addons/discord-rpc-gd/bin/discord-rpc-gd.gdextension"):
set_activity()
DiscordRPC.connect("activity_join_request",_on_activity_join_request)
DiscordRPC.connect("activity_join",_on_activity_join)
DiscordRPC.connect("activity_spectate",_on_activity_spectate)
DiscordRPC.connect("relationships_init",_on_relationship_init)
DiscordRPC.connect("updated_relationship", _on_updated_relationship)
func set_activity() -> void:
DiscordRPC.clear(false)
DiscordRPC.app_id = 1099618430065324082
DiscordRPC.details = "A demo activity by vaporvee#1231"
DiscordRPC.state = "Checkpoint 23/23"
DiscordRPC.large_image = "example_game"
DiscordRPC.large_image_text = "Try it now!"
DiscordRPC.small_image = "boss"
DiscordRPC.small_image_text = "Fighting the end boss! D:"
DiscordRPC.end_timestamp = int(Time.get_unix_time_from_system()) + 3600 # +1 hour in unix time
# It is NOT recommended to manage secrets locally! It's meant to be a payload wich the server understands and
# returns the other variables like current_party_size, party_id etc. Most of the values must differ from the others.
var my_secret: String = str(randi_range(0,999999))
DiscordRPC.party_id = "mylobbycanbeeverything_" + my_secret
DiscordRPC.current_party_size = 1
DiscordRPC.max_party_size = 4
DiscordRPC.match_secret = "m_" + my_secret #better use seeds with 1 to 1 range instead of just chars
DiscordRPC.join_secret = "j_" + my_secret
DiscordRPC.spectate_secret = "s_" + my_secret
DiscordRPC.is_public_party = true
DiscordRPC.instanced = true #required for spectate
#DiscordRPC.start_timestamp = int(Time.get_unix_time_from_system())
DiscordRPC.register_command("C:\\Users\\yanni\\Desktop\\demo\\DiscordRPC.exe")
#DiscordRPC.register_steam(1389990)
DiscordRPC.refresh()
var user_request: Dictionary;
func _on_activity_join_request(user_requesting: Dictionary) -> void:
print(user_requesting)
user_request = user_requesting
func _on_activity_join(secret: String) -> void:
if(DiscordRPC.join_secret != secret):
DiscordRPC.current_party_size = clamp(int(secret) + 1, 0, DiscordRPC.max_party_size)
DiscordRPC.party_id = secret.replace("j_","mylobbycanbeeverything_")
DiscordRPC.match_secret = secret.replace("j_","m_")
DiscordRPC.join_secret = secret
DiscordRPC.spectate_secret = secret.replace("j_","s_")
DiscordRPC.refresh()
func _on_activity_spectate(secret: String) -> void:
print(secret)
func _on_relationship_init() -> void:
print("initialized")
func _on_updated_relationship(relationship: Dictionary) -> void:
print(relationship)

View File

@@ -1,11 +1,18 @@
[gd_scene load_steps=4 format=3 uid="uid://dyc3kseph4el7"] [gd_scene load_steps=5 format=3 uid="uid://dyc3kseph4el7"]
[ext_resource type="Script" path="res://main.gd" id="1_kl8ri"]
[ext_resource type="Texture2D" uid="uid://b3qm246m7pnsx" path="res://assets/Logo_V2.png" id="2_gd222"] [ext_resource type="Texture2D" uid="uid://b3qm246m7pnsx" path="res://assets/Logo_V2.png" id="2_gd222"]
[ext_resource type="Script" path="res://addons/discord-rpc-gd/nodes/debug.gd" id="6_ujijw"] [ext_resource type="Script" uid="uid://kmubk5a6i385" path="res://discord_connector.gd" id="3_h2yge"]
[ext_resource type="Script" uid="uid://46tue7u6crd6" path="res://addons/discord-rpc-gd/nodes/debug.gd" id="6_ujijw"]
[sub_resource type="RichPresence" id="RichPresence_h2yge"]
details = "Godot -> Discord Social SDK"
state = "Inside a Node"
large_image = "example_game"
large_text = "Example"
small_image = "boss"
small_text = "Fighting the boss D:"
[node name="Node" type="Node"] [node name="Node" type="Node"]
script = ExtResource("1_kl8ri")
[node name="ColorRect" type="ColorRect" parent="."] [node name="ColorRect" type="ColorRect" parent="."]
anchors_preset = 15 anchors_preset = 15
@@ -23,7 +30,7 @@ position = Vector2(789, 330.5)
scale = Vector2(0.408203, 0.408203) scale = Vector2(0.408203, 0.408203)
texture = ExtResource("2_gd222") texture = ExtResource("2_gd222")
[node name="DiscordRPC Test" type="RichTextLabel" parent="."] [node name="DiscordUtil Test" type="RichTextLabel" parent="."]
anchors_preset = -1 anchors_preset = -1
anchor_left = 0.293 anchor_left = 0.293
anchor_top = 0.59 anchor_top = 0.59
@@ -38,7 +45,28 @@ grow_vertical = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
size_flags_vertical = 4 size_flags_vertical = 4
bbcode_enabled = true bbcode_enabled = true
text = "[center][font s=60]DiscordRPC Test" text = "[center][font s=60]DiscordUtil Test"
[node name="DiscordRPCDebug" type="Node" parent="."] [node name="DiscordSocialSDKDebug" type="Node" parent="."]
script = ExtResource("6_ujijw") script = ExtResource("6_ujijw")
[node name="DiscordConnector" type="DiscordConnector" parent="."]
app_id = 1099618430065324082
auto_connect = true
script = ExtResource("3_h2yge")
[node name="DiscordActivity" type="DiscordActivity" parent="DiscordConnector"]
rich_presence = SubResource("RichPresence_h2yge")
root_connector = NodePath("..")
[node name="DiscordLobby" type="DiscordLobby" parent="DiscordConnector"]
root_connector = NodePath("..")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
position = Vector2(789, 330.5)
scale = Vector2(0.408203, 0.408203)
[connection signal="authenticated" from="DiscordConnector" to="DiscordConnector" method="_on_authenticated"]
[connection signal="authentication_failed" from="DiscordConnector" to="DiscordConnector" method="_on_authentication_failed"]
[connection signal="connection_error" from="DiscordConnector" to="DiscordConnector" method="_on_connection_error"]
[connection signal="connection_ready" from="DiscordConnector" to="DiscordConnector" method="_on_connection_ready"]

View File

@@ -0,0 +1,3 @@
[gd_resource type="ActivityResource" format=3 uid="uid://coryo3jlwiadb"]
[resource]

View File

@@ -0,0 +1,4 @@
[gd_resource type="Compositor" format=3 uid="uid://cbg8e12ms0vvc"]
[resource]
compositor_effects = Array[CompositorEffect]([null])

View File

@@ -1 +0,0 @@
extends Node

View File

@@ -10,19 +10,15 @@ config_version=5
[application] [application]
config/name="GDExtension DiscordRPC Test Project" config/name="GDExtension DiscordUtil Test Project"
config/tags=PackedStringArray("vaporvee") config/tags=PackedStringArray("vaporvee")
run/main_scene="res://main.tscn" run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.3") config/features=PackedStringArray("4.4")
boot_splash/bg_color=Color(0.25098, 0.305882, 0.929412, 1) boot_splash/bg_color=Color(0.25098, 0.305882, 0.929412, 1)
boot_splash/image="res://assets/discordgodot_cover.png"
boot_splash/fullsize=false boot_splash/fullsize=false
boot_splash/image="res://assets/discordgodot_cover.png"
config/icon="res://assets/Logo_V2.png" config/icon="res://assets/Logo_V2.png"
[autoload]
PluginManager="*res://plugin_manager.gd"
[debug] [debug]
gdscript/warnings/untyped_declaration=1 gdscript/warnings/untyped_declaration=1
@@ -35,11 +31,16 @@ gdscript/warnings/unsafe_call_argument=1
[display] [display]
window/size/resizable=false window/size/resizable=false
display_server/driver.linuxbsd="wayland"
[dotnet] [dotnet]
project/assembly_name="GDExtension Test Project" project/assembly_name="GDExtension Test Project"
[editor_plugins]
enabled=PackedStringArray("res://addons/discord-rpc-gd/plugin.cfg")
[filesystem] [filesystem]
import/blender/enabled=false import/blender/enabled=false

View File

@@ -1,61 +1,44 @@
import zipfile import zipfile
from distutils.dir_util import copy_tree
import shutil
import os import os
import re
import shutil
import sys import sys
import glob
with zipfile.ZipFile("src/lib/discord_game_sdk.zip", "r") as zip_ref: EXPECTED_VERSION = "1.0.7445"
zip_ref.extractall("src/lib/discord_game_sdk/")
# Patch the SDK to actually build, since it's missing an include zip_files = glob.glob("src/lib/DiscordSocialSdk-*.zip")
with open("src/lib/discord_game_sdk/cpp/types.h", "r+") as f: if not zip_files:
s = f.read() print("Error: No zip file matching 'DiscordSocialSdk-*.zip' found.")
f.seek(0) sys.exit(1)
f.write("#include <cstdint>\n" + s)
copy_tree("src/lib/discord_game_sdk/lib/", "src/lib/discord_game_sdk/bin/") zip_path = zip_files[0] # Use the first matching file
os.rename( match = re.search(r"DiscordSocialSdk-(\d+\.\d+\.\d+)\.zip", zip_path)
"src/lib/discord_game_sdk/bin/aarch64/discord_game_sdk.dylib", if match:
"src/lib/discord_game_sdk/bin/aarch64/discord_game_sdk_aarch64.dylib", extracted_version = match.group(1)
) if extracted_version != EXPECTED_VERSION:
os.rename( print(f"Warning: Last tested version {EXPECTED_VERSION}, but found {extracted_version} in the zip file.")
"src/lib/discord_game_sdk/bin/x86_64/discord_game_sdk.so", print(f"Extracting {zip_path}...")
"src/lib/discord_game_sdk/bin/x86_64/libdiscord_game_sdk.so",
)
os.rename(
"src/lib/discord_game_sdk/bin/x86/discord_game_sdk.dll",
"src/lib/discord_game_sdk/bin/x86/discord_game_sdk_x86.dll",
)
os.rename( # Extract the zip file ignoring the version in the filename
"src/lib/discord_game_sdk/bin/x86_64/discord_game_sdk.dll.lib", with zipfile.ZipFile(zip_path, "r") as zip_ref:
"src/lib/discord_game_sdk/bin/x86_64/discord_game_sdk.lib", zip_ref.extractall("src/lib/")
)
copy_tree("src/lib/discord_game_sdk/bin/aarch64/", "src/lib/discord_game_sdk/bin/")
copy_tree("src/lib/discord_game_sdk/bin/x86/", "src/lib/discord_game_sdk/bin/")
copy_tree("src/lib/discord_game_sdk/bin/x86_64/", "src/lib/discord_game_sdk/bin/")
shutil.rmtree("src/lib/discord_game_sdk/c/", ignore_errors=True) release_dll_src = "src/lib/discord_social_sdk/bin/release/discord_partner_sdk.dll"
shutil.rmtree("src/lib/discord_game_sdk/csharp/", ignore_errors=True) release_dll_dest = "src/lib/discord_social_sdk/lib/release/"
shutil.rmtree("src/lib/discord_game_sdk/examples/", ignore_errors=True) os.makedirs(release_dll_dest, exist_ok=True)
shutil.rmtree("src/lib/discord_game_sdk/lib/", ignore_errors=True) shutil.copy(release_dll_src, release_dll_dest)
shutil.rmtree("src/lib/discord_game_sdk/bin/aarch64/", ignore_errors=True)
shutil.rmtree("src/lib/discord_game_sdk/bin/x86/", ignore_errors=True)
shutil.rmtree("src/lib/discord_game_sdk/bin/x86_64/", ignore_errors=True)
os.remove("src/lib/discord_game_sdk/README.md")
if sys.platform == "darwin": debug_dll_src = "src/lib/discord_social_sdk/bin/debug/discord_partner_sdk.dll"
# Combine the two libraries into one debug_dll_dest = "src/lib/discord_social_sdk/lib/debug/"
os.system( os.makedirs(debug_dll_dest, exist_ok=True)
"lipo src/lib/discord_game_sdk/bin/{discord_game_sdk.dylib,discord_game_sdk_aarch64.dylib} -output src/lib/discord_game_sdk/bin/libdiscord_game_sdk.dylib -create" shutil.copy(debug_dll_src, debug_dll_dest)
)
# Change the install name to (library's location)/(its new name)
os.system(
"install_name_tool -id '@loader_path/libdiscord_game_sdk.dylib'\
src/lib/discord_game_sdk/bin/libdiscord_game_sdk.dylib"
)
# Remove the ones it's made of
os.remove("src/lib/discord_game_sdk/bin/discord_game_sdk.dylib")
os.remove("src/lib/discord_game_sdk/bin/discord_game_sdk_aarch64.dylib")
bin_folder = "src/lib/discord_social_sdk/bin"
shutil.rmtree(bin_folder, ignore_errors=True)
print(f"Extracted and copied Discord Social SDK version: {extracted_version}")
print("Getting submodules...")
os.system("git submodule update --init --remote") os.system("git submodule update --init --remote")
print("Finished setup.")

18
src/definitions.h Normal file
View File

@@ -0,0 +1,18 @@
#define BIND_METHOD(class_name, method, ...) godot::ClassDB::bind_method(D_METHOD(#method, ##__VA_ARGS__), &class_name::method)
#define BIND_SET_GET(class_name, property_name, variant_type, ...) \
godot::ClassDB::bind_method(D_METHOD("get_" #property_name), &class_name::get_##property_name); \
godot::ClassDB::bind_method(D_METHOD("set_" #property_name, #variant_type), &class_name::set_##property_name); \
godot::ClassDB::add_property(class_name::get_class_static(), PropertyInfo(variant_type, #property_name, ##__VA_ARGS__), "set_" #property_name, "get_" #property_name)
#define BIND_SIGNAL(signal_name, ...) ADD_SIGNAL(MethodInfo(#signal_name, ##__VA_ARGS__))
// getter isn't mandatory for this project
#define SET_GET(class_name, variable, setter, ...) decltype(class_name::variable) class_name::get_##variable() { return variable; } void class_name::set_##variable(decltype(class_name::variable) value) { variable = value; setter; }
#define RESOLVE_TYPE(default_value) \
typename std::conditional<std::is_same<decltype(default_value), const char (&)[1]>::value, godot::String, \
typename std::conditional<std::is_same<decltype(default_value), bool>::value, bool, \
typename std::conditional<std::is_integral<decltype(default_value)>::value, int64_t, \
decltype(default_value)>::type>::type>::type
#define H_SET_GET(property_name, default_value) private: RESOLVE_TYPE(default_value) property_name = default_value; public: RESOLVE_TYPE(default_value) get_##property_name(); void set_##property_name(RESOLVE_TYPE(default_value) value);
#define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)

View File

@@ -1,430 +0,0 @@
#include "discordgodot.h"
#include "lib/discord_game_sdk/cpp/discord.h"
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#define BIND_METHOD(method, ...) godot::ClassDB::bind_method(D_METHOD(#method, ##__VA_ARGS__), &DiscordRPC::method)
#define BIND_SET_GET(property_name, variant_type) \
godot::ClassDB::bind_method(D_METHOD("get_" #property_name), &DiscordRPC::get_##property_name); \
godot::ClassDB::bind_method(D_METHOD("set_" #property_name, #variant_type), &DiscordRPC::set_##property_name); \
godot::ClassDB::add_property(get_class_static(), PropertyInfo(variant_type, #property_name), "set_" #property_name, "get_" #property_name)
#define BIND_SIGNAL(signal_name, ...) godot::ClassDB::add_signal(get_class_static(), MethodInfo(#signal_name, ##__VA_ARGS__))
#define SET_GET(variable, setter, ...) /*getter isn't mandatory for this project*/ \
decltype(DiscordRPC::variable) DiscordRPC::get_##variable() { return variable; } \
void DiscordRPC::set_##variable(decltype(DiscordRPC::variable) value) \
{ \
variable = value; \
setter; \
}
DiscordRPC *DiscordRPC::singleton = nullptr;
discord::Core *core{};
discord::Result result;
discord::Activity activity{};
discord::User user{};
void DiscordRPC::_bind_methods()
{
BIND_SET_GET(app_id, Variant::INT);
BIND_SET_GET(state, Variant::STRING);
BIND_SET_GET(details, Variant::STRING);
BIND_SET_GET(large_image, Variant::STRING);
BIND_SET_GET(large_image_text, Variant::STRING);
BIND_SET_GET(small_image, Variant::STRING);
BIND_SET_GET(small_image_text, Variant::STRING);
BIND_SET_GET(start_timestamp, Variant::INT);
BIND_SET_GET(end_timestamp, Variant::INT);
BIND_SET_GET(party_id, Variant::STRING);
BIND_SET_GET(current_party_size, Variant::INT);
BIND_SET_GET(max_party_size, Variant::INT);
BIND_SET_GET(match_secret, Variant::STRING);
BIND_SET_GET(join_secret, Variant::STRING);
BIND_SET_GET(spectate_secret, Variant::STRING);
BIND_SET_GET(instanced, Variant::BOOL);
BIND_SET_GET(is_public_party, Variant::BOOL);
BIND_SIGNAL(activity_join, PropertyInfo(Variant::STRING, "join_secret"));
BIND_SIGNAL(activity_spectate, PropertyInfo(Variant::STRING, "spectate_secret"));
BIND_SIGNAL(activity_join_request, PropertyInfo(Variant::DICTIONARY, "user_requesting"));
BIND_SIGNAL(updated_relationship, PropertyInfo(Variant::DICTIONARY, "relationship"));
BIND_SIGNAL(overlay_toggle, PropertyInfo(Variant::BOOL, "is_locked"));
BIND_SIGNAL(relationships_init);
BIND_METHOD(debug);
BIND_METHOD(run_callbacks);
BIND_METHOD(refresh);
ClassDB::bind_method(D_METHOD("clear", "reset_values"), &DiscordRPC::clear, DEFVAL(false));
BIND_METHOD(unclear);
BIND_METHOD(register_command, "command");
BIND_METHOD(register_steam, "steam_id");
BIND_METHOD(accept_join_request, "user_id");
BIND_METHOD(send_invite, "user_id", "is_spectate", "message_content");
BIND_METHOD(accept_invite, "user_id");
BIND_METHOD(get_current_user);
BIND_METHOD(get_all_relationships);
BIND_METHOD(get_is_overlay_enabled);
BIND_METHOD(get_is_overlay_locked);
BIND_METHOD(open_invite_overlay, "is_spectate");
BIND_METHOD(open_server_invite_overlay, "invite_code");
BIND_METHOD(open_voice_settings);
BIND_METHOD(get_is_discord_working);
BIND_METHOD(get_result_int);
}
SET_GET(state, activity.SetState(value.utf8().get_data()))
SET_GET(details, activity.SetDetails(value.utf8().get_data()))
SET_GET(large_image, activity.GetAssets().SetLargeImage(value.utf8().get_data()))
SET_GET(large_image_text, activity.GetAssets().SetLargeText(value.utf8().get_data()))
SET_GET(small_image, activity.GetAssets().SetSmallImage(value.utf8().get_data()))
SET_GET(small_image_text, activity.GetAssets().SetSmallText(value.utf8().get_data()))
SET_GET(start_timestamp, activity.GetTimestamps().SetStart(value))
SET_GET(end_timestamp, activity.GetTimestamps().SetEnd(value))
SET_GET(party_id, activity.GetParty().SetId(value.utf8().get_data()))
SET_GET(current_party_size, activity.GetParty().GetSize().SetCurrentSize(value))
SET_GET(max_party_size, activity.GetParty().GetSize().SetMaxSize(value))
SET_GET(match_secret, activity.GetSecrets().SetMatch(value.utf8().get_data()))
SET_GET(join_secret, activity.GetSecrets().SetJoin(value.utf8().get_data()))
SET_GET(spectate_secret, activity.GetSecrets().SetSpectate(value.utf8().get_data()))
SET_GET(instanced, activity.SetInstance(value))
SET_GET(is_public_party, activity.GetParty().SetPrivacy(static_cast<discord::ActivityPartyPrivacy>(value)))
DiscordRPC::DiscordRPC()
{
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
}
DiscordRPC::~DiscordRPC()
{
app_id = 0;
delete core; // couldn't use destructor because it would not compile on linux
core = nullptr;
ERR_FAIL_COND(singleton != this);
singleton = nullptr;
}
DiscordRPC *DiscordRPC::get_singleton()
{
return singleton;
}
void DiscordRPC::run_callbacks()
{
if (result == discord::Result::Ok && app_id > 0)
::core->RunCallbacks();
}
void DiscordRPC::debug()
{
result = discord::Core::Create(1080224638845591692, DiscordCreateFlags_NoRequireDiscord, &core);
activity.SetState("Test from Godot!");
activity.SetDetails("I worked months on this");
activity.GetAssets().SetLargeImage("test1");
activity.GetAssets().SetLargeText("wow test text for large image");
activity.GetAssets().SetSmallImage("godot");
activity.GetAssets().SetSmallText("wow test text for small image");
activity.GetTimestamps().SetStart(1682242800);
if (result == discord::Result::Ok)
{
core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
}
else
UtilityFunctions::push_warning("Discord Activity couldn't be updated. It could be that Discord isn't running!");
}
void DiscordRPC::set_app_id(int64_t value)
{
app_id = value;
if (app_id > 0)
{
result = discord::Core::Create(value, DiscordCreateFlags_NoRequireDiscord, &core); // after setting app_ID it initializes everything
if (result == discord::Result::Ok)
{
// initialize currentuser
core->UserManager().OnCurrentUserUpdate.Connect([]()
{discord::User user{};
core->UserManager().GetCurrentUser(&user); });
// signals
core->ActivityManager().OnActivityJoin.Connect([](const char *secret)
{ DiscordRPC::get_singleton()
->emit_signal("activity_join", secret); });
core->ActivityManager().OnActivitySpectate.Connect([](const char *secret)
{ DiscordRPC::get_singleton()
->emit_signal("activity_spectate", secret); });
core->ActivityManager().OnActivityJoinRequest.Connect([this](discord::User const &user)
{ DiscordRPC::get_singleton()
->emit_signal("activity_join_request", user2dict(user)); });
core->OverlayManager().OnToggle.Connect([](bool is_locked)
{ DiscordRPC::get_singleton()
->emit_signal("overlay_toggle", is_locked); });
core->RelationshipManager().OnRefresh.Connect([&]()
{ DiscordRPC::get_singleton()
->emit_signal("relationships_init"); });
core->RelationshipManager().OnRelationshipUpdate.Connect([&](discord::Relationship const &relationship)
{ DiscordRPC::get_singleton()
->emit_signal("updated_relationship", relationship2dict(relationship)); });
}
}
}
int64_t DiscordRPC::get_app_id()
{
if (app_id != 0)
return app_id;
return old_app_id;
}
void DiscordRPC::refresh()
{
if (get_is_discord_working())
{
activity.GetParty().SetPrivacy(discord::ActivityPartyPrivacy::Public);
activity.SetType(discord::ActivityType::Playing);
core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
}
else
UtilityFunctions::push_warning("Discord Activity couldn't be updated. It could be that Discord isn't running!");
}
void DiscordRPC::clear(bool reset_values = false)
{
if (get_is_discord_working())
{
if (reset_values)
{
old_app_id = 0;
set_state("");
set_details("");
set_large_image("");
set_large_image_text("");
set_small_image("");
set_small_image_text("");
set_start_timestamp(0);
set_end_timestamp(0);
set_party_id("");
set_current_party_size(0);
set_max_party_size(0);
set_match_secret("");
set_join_secret("");
set_spectate_secret("");
set_instanced(false);
set_is_public_party(false);
set_is_overlay_locked(false);
core->ActivityManager().ClearActivity([](discord::Result result) {});
}
else
old_app_id = app_id;
app_id = 0;
delete core;
core = nullptr;
}
}
void DiscordRPC::unclear()
{
if (old_app_id > 0)
{
set_app_id(old_app_id);
refresh();
old_app_id = 0;
}
else
UtilityFunctions::push_warning("Discord Activity couldn't be uncleared. Maybe it didn't get cleared before?");
}
bool DiscordRPC::get_is_overlay_enabled()
{
bool ie;
if (get_is_discord_working())
core->OverlayManager().IsEnabled(&ie);
return ie;
}
bool DiscordRPC::get_is_overlay_locked()
{
bool il;
if (get_is_discord_working())
core->OverlayManager().IsLocked(&il);
return il;
}
void DiscordRPC::set_is_overlay_locked(bool value)
{
is_overlay_locked = value;
if (get_is_discord_working())
core->OverlayManager().SetLocked(value, {});
}
void DiscordRPC::open_invite_overlay(bool is_spectate)
{
if (get_is_discord_working())
core->OverlayManager().OpenActivityInvite(static_cast<discord::ActivityActionType>(is_spectate + 1), {});
}
void DiscordRPC::open_server_invite_overlay(String invite_code)
{
if (get_is_discord_working())
core->OverlayManager().OpenGuildInvite(invite_code.utf8().get_data(), {});
}
void DiscordRPC::open_voice_settings()
{
if (get_is_discord_working())
core->OverlayManager().OpenVoiceSettings({});
}
void DiscordRPC::accept_join_request(int64_t user_id)
{
if (get_is_discord_working())
core->ActivityManager().SendRequestReply(user_id, static_cast<discord::ActivityJoinRequestReply>(1), {});
}
void DiscordRPC::send_invite(int64_t user_id, bool is_spectate = false, String message_content = "")
{
if (get_is_discord_working())
core->ActivityManager().SendInvite(user_id, static_cast<discord::ActivityActionType>(is_spectate + 1), message_content.utf8().get_data(), {});
}
void DiscordRPC::accept_invite(int64_t user_id)
{
if (get_is_discord_working())
core->ActivityManager().AcceptInvite(user_id, {});
}
void DiscordRPC::register_command(String value)
{
if (get_is_discord_working())
core->ActivityManager().RegisterCommand(value.utf8().get_data());
}
void DiscordRPC::register_steam(int32_t value)
{
if (get_is_discord_working())
core->ActivityManager().RegisterSteam(value);
}
Dictionary DiscordRPC::get_current_user()
{
Dictionary userdict;
if (get_is_discord_working())
{
discord::User user{};
core->UserManager().GetCurrentUser(&user);
return user2dict(user);
}
return userdict;
}
Dictionary DiscordRPC::get_relationship(int64_t user_id)
{
if (get_is_discord_working())
{
discord::Relationship relationship{};
core->RelationshipManager().Get(user_id, &relationship);
return relationship2dict(relationship);
}
Dictionary dict;
return dict;
}
Array DiscordRPC::get_all_relationships()
{
Array all_relationships;
core->RelationshipManager().Filter(
[](discord::Relationship const &relationship) -> bool
{ return true; });
int32_t friendcount{0};
core->RelationshipManager().Count(&friendcount);
for (int i = 0; i < friendcount; i++)
{
discord::Relationship relationship{};
core->RelationshipManager().GetAt(i, &relationship);
all_relationships.append(relationship2dict(relationship));
}
return all_relationships;
}
int DiscordRPC::get_result_int()
{
return static_cast<int>(result);
}
Dictionary DiscordRPC::user2dict(discord::User user)
{
Dictionary userdict;
userdict["avatar"] = user.GetAvatar(); // can be empty when user has no avatar
userdict["is_bot"] = user.GetBot();
userdict["discriminator"] = user.GetDiscriminator();
userdict["id"] = user.GetId();
userdict["username"] = user.GetUsername();
if (String(userdict["avatar"]).is_empty())
userdict["avatar_url"] = String(std::string("https://cdn.discordapp.com/embed/avatars/" + std::to_string((userdict["discriminator"].INT % 5) - 1) + ".png").c_str());
else
userdict["avatar_url"] = String(std::string("https://cdn.discordapp.com/avatars/" + std::to_string(user.GetId()) + "/" + user.GetAvatar() + ".png").c_str());
userdict.make_read_only();
return userdict;
}
Dictionary DiscordRPC::relationship2dict(discord::Relationship relationship)
{
Dictionary dict_relationship;
Dictionary presence;
Dictionary presence_activity;
switch (static_cast<int>(relationship.GetPresence().GetStatus()))
{
case 0:
presence["status"] = "Offline";
break;
case 1:
presence["status"] = "Online";
break;
case 2:
presence["status"] = "Idle";
break;
case 3:
presence["status"] = "DoNotDisturb";
break;
default:
presence["status"] = "NotAvailable";
break;
}
presence_activity["application_id"] = relationship.GetPresence().GetActivity().GetApplicationId();
presence_activity["name"] = relationship.GetPresence().GetActivity().GetName();
presence_activity["state"] = relationship.GetPresence().GetActivity().GetState();
presence_activity["details"] = relationship.GetPresence().GetActivity().GetDetails();
presence_activity["large_image"] = relationship.GetPresence().GetActivity().GetAssets().GetLargeImage();
presence_activity["large_text"] = relationship.GetPresence().GetActivity().GetAssets().GetLargeText();
presence_activity["small_image"] = relationship.GetPresence().GetActivity().GetAssets().GetSmallImage();
presence_activity["small_text"] = relationship.GetPresence().GetActivity().GetAssets().GetSmallText();
presence_activity["timestamps_start"] = relationship.GetPresence().GetActivity().GetTimestamps().GetStart();
presence_activity["timestamps_end"] = relationship.GetPresence().GetActivity().GetTimestamps().GetEnd();
presence_activity["instance"] = relationship.GetPresence().GetActivity().GetInstance();
presence_activity["party_id"] = relationship.GetPresence().GetActivity().GetParty().GetId();
presence_activity["current_party_size"] = relationship.GetPresence().GetActivity().GetParty().GetSize().GetCurrentSize();
presence_activity["max_party_size"] = relationship.GetPresence().GetActivity().GetParty().GetSize().GetMaxSize();
presence_activity["join_secret"] = relationship.GetPresence().GetActivity().GetSecrets().GetJoin();
presence_activity["spectate_secret"] = relationship.GetPresence().GetActivity().GetSecrets().GetSpectate();
presence_activity["match_secret"] = relationship.GetPresence().GetActivity().GetSecrets().GetMatch();
presence["activity"] = presence_activity;
presence.make_read_only();
switch (relationship.GetType())
{
case discord::RelationshipType::None:
dict_relationship["type"] = "None";
break;
case discord::RelationshipType::Friend:
dict_relationship["type"] = "Friend";
break;
case discord::RelationshipType::Blocked:
dict_relationship["type"] = "Blocked";
break;
case discord::RelationshipType::PendingIncoming:
dict_relationship["type"] = "PendingIncoming";
break;
case discord::RelationshipType::PendingOutgoing:
dict_relationship["type"] = "PendingOutgoing";
break;
case discord::RelationshipType::Implicit:
dict_relationship["type"] = "Implicit";
break;
default:
dict_relationship["type"] = "NotAvailable";
break;
}
dict_relationship["user"] = user2dict(relationship.GetUser());
dict_relationship["presence"] = presence;
dict_relationship.make_read_only();
return dict_relationship;
}
bool DiscordRPC::get_is_discord_working()
{
return result == discord::Result::Ok && app_id > 0;
}

View File

@@ -1,79 +0,0 @@
#ifndef DISCORDGODOT_H
#define DISCORDGODOT_H
#include <stdio.h>
#include "lib/discord_game_sdk/cpp/discord.h"
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/core/class_db.hpp>
#define H_SET_GET(variable_type, property_name) \
variable_type property_name; \
variable_type get_##property_name(); \
void set_##property_name(variable_type value);
using namespace godot;
class DiscordRPC : public Object
{
GDCLASS(DiscordRPC, Object);
static DiscordRPC *singleton;
protected:
static void _bind_methods();
public:
static DiscordRPC *
get_singleton();
DiscordRPC();
~DiscordRPC();
// INTERBNAL
int64_t old_app_id;
Dictionary relationship2dict(discord::Relationship relationship);
Dictionary user2dict(discord::User user);
///
void set_app_id(int64_t value);
int64_t get_app_id();
int64_t app_id = 0; // needs to be directly setted to 0 or it will crash randomly
H_SET_GET(String, state)
H_SET_GET(String, details)
H_SET_GET(String, large_image)
H_SET_GET(String, large_image_text)
H_SET_GET(String, small_image)
H_SET_GET(String, small_image_text)
H_SET_GET(int64_t, start_timestamp)
H_SET_GET(int64_t, end_timestamp)
H_SET_GET(String, party_id)
H_SET_GET(int32_t, current_party_size)
H_SET_GET(int32_t, max_party_size)
H_SET_GET(String, match_secret)
H_SET_GET(String, join_secret)
H_SET_GET(String, spectate_secret)
H_SET_GET(bool, is_public_party)
H_SET_GET(bool, instanced)
H_SET_GET(bool, is_overlay_locked)
void debug();
void run_callbacks();
void refresh();
void clear(bool reset_values);
void unclear();
bool get_is_overlay_enabled();
void open_invite_overlay(bool is_spectate);
void open_server_invite_overlay(String invite_code);
void open_voice_settings();
void accept_join_request(int64_t user_id);
void send_invite(int64_t user_id, bool is_spectate, String message_content);
void accept_invite(int64_t user_id);
void register_command(String value);
void register_steam(int32_t value);
Dictionary get_current_user();
Dictionary get_relationship(int64_t user_id);
Array get_all_relationships();
bool get_is_discord_working();
int get_result_int();
};
#endif

View File

@@ -1,81 +0,0 @@
#include "editor_presence.h"
#include "lib/discord_game_sdk/cpp/discord.h"
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
EditorPresence *EditorPresence::singleton = nullptr;
void EditorPresence::_bind_methods()
{
}
EditorPresence::EditorPresence()
{
singleton = this;
}
EditorPresence::~EditorPresence()
{
singleton = nullptr;
delete core; // couldn't use destructor because it would not compile on linux
core = nullptr;
}
EditorPresence *EditorPresence::get_singleton()
{
return singleton;
}
void EditorPresence::_ready()
{
result = discord::Core::Create(1108142249990176808, DiscordCreateFlags_NoRequireDiscord, &core);
activity.SetState("Editing a project...");
activity.SetDetails(String(project_settings->get_setting("application/config/name")).utf8());
if (project_settings->has_setting("application/config/name"))
{
activity.GetAssets().SetLargeImage("godot");
}
activity.GetAssets().SetLargeText(String(engine->get_version_info()["string"]).utf8());
activity.GetTimestamps().SetStart(time->get_unix_time_from_system());
if (result == discord::Result::Ok)
core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
else
UtilityFunctions::push_warning("EditorPresence couldn't be loaded! Maybe your Discord isn't running?");
}
void EditorPresence::_process(double delta)
{
godot::Node *edited_scene_root = get_tree()->get_edited_scene_root();
if (edited_scene_root != nullptr)
{
godot::String scene_path = edited_scene_root->get_scene_file_path();
if (scene_path.is_empty())
{
state_string = "Editing: (not saved scene)";
}
else
{
state_string = "Editing: \"" + scene_path.replace("res://", "") + "\"";
}
if (state_string.utf8() != activity.GetState())
{
activity.SetState(state_string.utf8());
if (result == discord::Result::Ok)
core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
}
}
else
{
godot::String default_state = "No Scene Loaded";
if (default_state.utf8() != activity.GetState())
{
activity.SetState(default_state.utf8());
if (result == discord::Result::Ok)
core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
}
}
if (result == discord::Result::Ok)
core->RunCallbacks();
}

View File

@@ -1,44 +0,0 @@
#ifndef EDITOR_PRESENCE_H
#define EDITOR_PRESENCE_H
#include <stdio.h>
#include "lib/discord_game_sdk/cpp/discord.h"
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/time.hpp>
#include <godot_cpp/classes/tree.hpp>
using namespace godot;
class EditorPresence : public Node
{
GDCLASS(EditorPresence, Node);
static EditorPresence *singleton;
protected:
static void _bind_methods();
public:
static EditorPresence *
get_singleton();
godot::Engine *engine = godot::Engine::get_singleton();
godot::ProjectSettings *project_settings = godot::ProjectSettings::get_singleton();
godot::Time *time = godot::Time::get_singleton();
discord::Core *core{};
discord::Result result;
discord::Activity activity{};
String state_string;
EditorPresence();
~EditorPresence();
void _ready() override;
void _process(double delta) override;
};
#endif

696
src/gen/doc_data.gen.cpp Normal file
View File

@@ -0,0 +1,696 @@
/* THIS FILE IS GENERATED DO NOT EDIT */
#include <godot_cpp/godot.hpp>
static const char *_doc_data_hash = "8781033133787346433";
static const int _doc_data_uncompressed_size = 4148;
static const int _doc_data_compressed_size = 684;
static const unsigned char _doc_data_compressed[] = {
120,
218,
237,
87,
193,
114,
218,
48,
16,
61,
39,
95,
225,
209,
161,
183,
96,
146,
92,
58,
20,
146,
67,
211,
94,
154,
166,
157,
161,
153,
233,
205,
35,
75,
139,
81,
99,
75,
174,
36,
7,
248,
251,
174,
44,
139,
98,
195,
0,
45,
208,
118,
166,
185,
192,
90,
171,
221,
247,
118,
181,
43,
175,
135,
183,
243,
34,
143,
158,
65,
27,
161,
228,
136,
92,
246,
250,
36,
2,
201,
20,
23,
50,
27,
145,
199,
47,
239,
47,
94,
147,
232,
246,
230,
124,
200,
114,
106,
76,
36,
105,
1,
35,
114,
39,
12,
83,
154,
191,
85,
82,
2,
179,
74,
147,
72,
200,
41,
104,
97,
205,
82,
55,
86,
76,
208,
124,
124,
247,
129,
68,
8,
32,
205,
96,
110,
196,
136,
76,
173,
45,
7,
113,
60,
155,
205,
122,
179,
235,
158,
210,
89,
124,
213,
239,
95,
198,
95,
63,
222,
143,
217,
20,
10,
122,
33,
164,
177,
84,
50,
64,
43,
35,
6,
82,
61,
32,
158,
41,
41,
3,
175,
191,
87,
140,
218,
154,
168,
243,
100,
208,
149,
166,
179,
94,
38,
236,
180,
74,
43,
3,
154,
41,
105,
65,
218,
30,
83,
69,
156,
41,
174,
240,
33,
19,
18,
188,
28,
23,
212,
88,
208,
49,
87,
44,
174,
163,
233,
205,
13,
39,
55,
231,
103,
195,
84,
11,
152,
36,
28,
12,
211,
162,
116,
254,
221,
98,
188,
113,
181,
187,
169,
243,
108,
43,
76,
7,
6,
110,
106,
101,
235,
169,
0,
59,
85,
220,
137,
65,
110,
178,
201,
124,
26,
19,
23,
128,
163,
131,
122,
13,
182,
210,
50,
178,
139,
18,
245,
207,
74,
112,
18,
197,
94,
211,
198,
59,
91,
99,
128,
11,
222,
249,
58,
142,
134,
137,
6,
51,
173,
113,
18,
171,
158,
64,
238,
68,
43,
169,
166,
5,
30,
46,
135,
249,
136,
96,
101,
52,
132,
43,
173,
49,
203,
73,
112,
232,
125,
53,
246,
99,
171,
177,
116,
142,
193,
183,
42,
57,
181,
112,
56,
93,
202,
24,
24,
115,
52,
150,
65,
108,
14,
181,
72,
177,
119,
26,
242,
78,
14,
160,
101,
153,
56,
102,
30,
78,
72,
75,
34,
3,
22,
203,
111,
68,
240,
63,
9,
234,
172,
89,
203,
86,
214,
56,
76,
104,
149,
91,
23,
65,
128,
118,
142,
55,
96,
96,
121,
37,
77,
245,
4,
164,
84,
169,
188,
3,
213,
218,
213,
2,
108,
105,
150,
176,
19,
44,
88,
216,
7,
26,
239,
9,
189,
168,
83,
148,
60,
193,
162,
155,
218,
53,
14,
221,
237,
107,
84,
186,
27,
150,
140,
94,
125,
175,
148,
125,
227,
127,
247,
33,
86,
31,
117,
82,
80,
73,
51,
216,
145,
152,
246,
214,
53,
74,
109,
245,
246,
20,
5,
177,
46,
12,
35,
50,
233,
27,
63,
200,
161,
170,
93,
57,
55,
73,
7,
78,
14,
44,
220,
150,
225,
37,
233,
116,
250,
222,
150,
87,
193,
18,
230,
165,
64,
211,
68,
200,
86,
233,
238,
223,
38,
62,
214,
237,
97,
187,
3,
158,
80,
145,
239,
8,
31,
180,
118,
175,
150,
223,
109,
216,
173,
76,
252,
185,
134,
60,
253,
75,
100,
254,
183,
154,
104,
110,
249,
191,
124,
4,
158,
69,
192,
255,
21,
111,
65,
196,
86,
31,
250,
177,
2,
133,
3,
134,
170,
149,
193,
233,
231,
80,
245,
160,
56,
188,
12,
82,
199,
75,
242,
163,
21,
249,
106,
126,
63,
165,
223,
234,
119,
224,
75,
134,
183,
143,
170,
28,
210,
42,
59,
229,
140,
154,
129,
4,
237,
238,
131,
77,
3,
193,
6,
220,
227,
77,
155,
186,
146,
9,
163,
121,
158,
82,
246,
100,
78,
25,
162,
161,
207,
224,
111,
97,
115,
186,
129,
246,
207,
190,
3,
90,
54,
215,
100,
255,
25,
241,
176,
241,
251,
176,
171,
224,
29,
23,
88,
242,
159,
49,
42,
168,
187,
248,
229,
19,
118,
223,
155,
247,
7,
100,
41,
93,
61,
};
static godot::internal::DocDataRegistration _doc_data_registration(_doc_data_hash, _doc_data_uncompressed_size, _doc_data_compressed_size, _doc_data_compressed);

Binary file not shown.

View File

@@ -0,0 +1,70 @@
#include "discord_activity.h"
void DiscordActivity::_bind_methods()
{
BIND_SET_GET(DiscordActivity, rich_presence, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichPresence");
BIND_SET_GET(DiscordActivity, party_invite, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PartyInvite");
BIND_METHOD(DiscordActivity, update);
}
DiscordActivity::DiscordActivity()
{
}
DiscordActivity::~DiscordActivity()
{
}
Ref<RichPresence> DiscordActivity::get_rich_presence()
{
return rich_presence;
}
void DiscordActivity::set_rich_presence(Ref<RichPresence> value)
{
rich_presence = value;
}
Ref<PartyInvite> DiscordActivity::get_party_invite()
{
return party_invite;
}
void DiscordActivity::set_party_invite(Ref<PartyInvite> value)
{
party_invite = value;
}
void DiscordActivity::update()
{
discordpp::Activity activity;
if (rich_presence.is_valid())
{
activity.SetState(rich_presence->get_state().utf8().get_data());
activity.SetDetails(rich_presence->get_details().utf8().get_data());
discordpp::ActivityAssets assets;
assets.SetSmallImage(String(rich_presence->get_small_image()).utf8().get_data());
assets.SetSmallText(rich_presence->get_small_text().utf8().get_data());
assets.SetLargeImage(String(rich_presence->get_large_image()).utf8().get_data());
assets.SetLargeText(rich_presence->get_large_text().utf8().get_data());
activity.SetAssets(assets);
discordpp::ActivityTimestamps timestamps;
timestamps.SetStart(rich_presence->get_timestamps_start());
timestamps.SetEnd(rich_presence->get_timestamps_end());
activity.SetTimestamps(timestamps);
}
if (party_invite.is_valid())
{
discordpp::ActivityParty party;
party.SetCurrentSize(party_invite->get_current_size());
party.SetMaxSize(party_invite->get_max_size());
party.SetId(party_invite->get_id().utf8().get_data());
party.SetPrivacy(party_invite->get_is_public_party() ? discordpp::ActivityPartyPrivacy::Public : discordpp::ActivityPartyPrivacy::Private);
discordpp::ActivitySecrets secrets;
secrets.SetJoin(party_invite->get_join_secret().utf8().get_data());
activity.SetSecrets(secrets);
activity.SetParty(party);
// TODO: Supported platforms
// activity.SetSupportedPlatforms();
// TODO: Error on invalid values inside getters
}
connector->client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {});
}

View File

@@ -0,0 +1,39 @@
#ifndef DISCORD_ACTIVITY_H
#define DISCORD_ACTIVITY_H
#include "discord_connected.h"
#include "../resources/rich_presence.h"
#include "../resources/party_invite.h"
using namespace godot;
class DiscordActivity : public DiscordConnected
{
GDCLASS(DiscordActivity, DiscordConnected);
protected:
static void _bind_methods();
public:
Ref<RichPresence> rich_presence;
Ref<RichPresence> get_rich_presence();
void set_rich_presence(Ref<RichPresence> value);
Ref<PartyInvite> party_invite;
Ref<PartyInvite> get_party_invite();
void set_party_invite(Ref<PartyInvite> value);
void register_launch_command(String command);
void register_steam(int32_t steam_id);
void accept_join_request(int64_t user_id);
void send_invite(int64_t friend_user_id, bool is_spectate, String message_content);
void accept_invite(int64_t user_id);
void update();
DiscordActivity();
~DiscordActivity();
};
#endif

View File

@@ -0,0 +1,43 @@
#include "discord_connected.h"
void DiscordConnected::_bind_methods()
{
BIND_SET_GET(DiscordConnected, root_connector, Variant::NODE_PATH, godot::PROPERTY_HINT_NODE_PATH_VALID_TYPES, "DiscordConnector");
}
DiscordConnected::DiscordConnected()
{
}
DiscordConnected::~DiscordConnected()
{
}
NodePath DiscordConnected::get_root_connector()
{
return root_connector;
}
void DiscordConnected::set_root_connector(NodePath value)
{
root_connector = value;
}
void DiscordConnected::_enter_tree()
{
if (cast_to<DiscordConnector>(get_parent()))
{
if (root_connector.is_empty())
{
root_connector = cast_to<DiscordConnector>(get_parent())->get_path();
if (!root_connector.is_empty())
{
connector = get_node<DiscordConnector>(root_connector);
}
}
else
{
if (get_node_or_null(root_connector)){
connector = get_node<DiscordConnector>(root_connector);
}
}
}
}

View File

@@ -0,0 +1,31 @@
#ifndef DISCORD_CONNECTED_H
#define DISCORD_CONNECTED_H
#include "discord_social_sdk.h"
#include "discord_connector.h"
using namespace godot;
class DiscordConnected : public DiscordSocialSDK
{
GDCLASS(DiscordConnected, DiscordSocialSDK);
protected:
static void _bind_methods();
private:
NodePath root_connector; // Change to NodePath object
public:
DiscordConnector *connector;
NodePath get_root_connector(); // Adjust return type
void set_root_connector(NodePath value); // Adjust parameter type
DiscordConnected();
~DiscordConnected();
void _enter_tree() override;
};
#endif

View File

@@ -0,0 +1,190 @@
#include "discord_connector.h"
void DiscordConnector::_bind_methods()
{
BIND_SET_GET(DiscordConnector, app_id, Variant::STRING, godot::PROPERTY_HINT_RANGE, "-99999,99999,or_less,or_greater,hide_slider");
ADD_GROUP("Automatic", "auto_");
BIND_SET_GET(DiscordConnector, auto_connect, Variant::BOOL);
BIND_SET_GET(DiscordConnector, auto_token_manage, Variant::BOOL);
BIND_SET_GET(DiscordConnector, auto_encryption_key, Variant::STRING, godot::PROPERTY_HINT_NONE, "", godot::PROPERTY_USAGE_NO_EDITOR);
BIND_METHOD(DiscordConnector, auth);
BIND_METHOD(DiscordConnector, token_connect, "access_token");
BIND_METHOD(DiscordConnector, token_refresh, "current_refresh_token");
BIND_SIGNAL(authenticated, PropertyInfo(Variant::STRING, "access_token"), PropertyInfo(Variant::STRING, "refresh_token"), PropertyInfo(Variant::INT, "expires_in"));
BIND_SIGNAL(authentication_failed, PropertyInfo(Variant::STRING, "error"));
BIND_SIGNAL(connection_ready);
BIND_SIGNAL(connection_error, PropertyInfo(Variant::STRING, "error"));
BIND_SIGNAL(token_refreshed, PropertyInfo(Variant::STRING, "access_token"), PropertyInfo(Variant::STRING, "refresh_token"), PropertyInfo(Variant::INT, "expires_in"));
BIND_SIGNAL(token_refresh_failed, PropertyInfo(Variant::STRING, "error"));
}
DiscordConnector::DiscordConnector()
{
}
DiscordConnector::~DiscordConnector()
{
}
void DiscordConnector::_ready()
{
if (Engine::get_singleton()->is_editor_hint() || editor_process)
return;
client = std::make_shared<discordpp::Client>();
client->SetStatusChangedCallback([this](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail)
{
if (status == discordpp::Client::Status::Ready)
{
emit_signal("connection_ready");
}
if (error != discordpp::Client::Error::None)
{
emit_signal("connection_error", String(discordpp::Client::ErrorToString(error).c_str()));
} });
if (auto_connect)
{
auth();
}
}
void DiscordConnector::_process(double delta)
{
if (!Engine::get_singleton()->is_editor_hint() && !editor_process && client)
{
discordpp::RunCallbacks();
}
}
void DiscordConnector::set_auto_connect(bool value)
{
auto_connect = value;
}
bool DiscordConnector::get_auto_connect()
{
return auto_connect;
}
void DiscordConnector::set_app_id(int64_t value)
{
app_id = static_cast<int64_t>(value);
}
int64_t DiscordConnector::get_app_id()
{
return static_cast<int64_t>(app_id);
}
void DiscordConnector::set_auto_token_manage(bool value)
{
auto_token_manage = value;
}
bool DiscordConnector::get_auto_token_manage()
{
return auto_token_manage;
}
void DiscordConnector::set_auto_encryption_key(String value)
{
auto_encryption_key = value;
}
String DiscordConnector::get_auto_encryption_key()
{
return auto_encryption_key;
}
void DiscordConnector::auth()
{
if (auto_token_manage)
{
if (auto_encryption_key.is_empty())
{
auto_encryption_key = DiscordUtil::get_singleton()->generate_auto_encryption_key();
}
Ref<godot::ConfigFile> config = DiscordUtil::get_singleton()->get_tokens(auto_encryption_key);
if (config->has_section_key("tokens", "access_token") &&
config->has_section_key("tokens", "refresh_token") &&
config->has_section_key("tokens", "expires_in"))
{
access_token = config->get_value("tokens", "access_token");
refresh_token = config->get_value("tokens", "refresh_token");
expires_in = config->get_value("tokens", "expires_in");
token_connect(access_token);
return;
}
else
{
DiscordUtil::get_singleton()->delete_tokens();
}
}
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
discordpp::AuthorizationArgs args{};
args.SetClientId(app_id);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process // TODO: option to open browser
client->Authorize(args, [this, codeVerifier](auto result, auto code, auto redirectUri)
{
if (!result.Successful()) {
emit_signal("authentication_failed", result.Error().c_str());
return;
} else {
client->GetToken(app_id, code, codeVerifier.Verifier(), redirectUri,
[this](discordpp::ClientResult result,
std::string accessToken,
std::string refreshToken,
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
if (result.Successful()) {
if (auto_token_manage)
{
DiscordUtil::get_singleton()->save_tokens(accessToken.c_str(), refreshToken.c_str(), expiresIn, auto_encryption_key);
}
} else {
emit_signal("authenticated", result.Error().c_str());
return;
}
token_connect(accessToken.c_str());
});
} });
}
void DiscordConnector::token_connect(String access_token)
{
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, access_token.utf8().get_data(), [this](discordpp::ClientResult result)
{
if(result.Successful()) {
client->Connect();
} else {
emit_signal("connection_error", result.Error().c_str());
} });
}
void DiscordConnector::token_refresh(String current_refresh_token)
{
client->RefreshToken(std::stoull(refresh_token.utf8().get_data()), refresh_token.utf8().get_data(), [this, current_refresh_token](discordpp::ClientResult result, std::string accessToken, std::string refreshToken, discordpp::AuthorizationTokenType tokenType, int32_t expiresIn, std::string scopes)
{
if (result.Successful())
{
emit_signal("token_refreshed", accessToken.c_str(), refreshToken.c_str(), expiresIn);
if (auto_token_manage)
{
DiscordUtil::get_singleton()->save_tokens(accessToken.c_str(), refreshToken.c_str(), expiresIn, auto_encryption_key);
token_connect(accessToken.c_str());
}
}
else
{
emit_signal("token_refresh_failed", result.Error().c_str());
} });
}

View File

@@ -0,0 +1,42 @@
#ifndef DISCORD_CONNECTOR_H
#define DISCORD_CONNECTOR_H
#include "discord_social_sdk.h"
#include "../util.h"
using namespace godot;
class DiscordConnector : public DiscordSocialSDK
{
GDCLASS(DiscordConnector, DiscordSocialSDK);
protected:
static void _bind_methods();
public:
bool editor_process = false;
std::shared_ptr<discordpp::Client> client;
H_SET_GET(app_id, 0)
H_SET_GET(auto_encryption_key, "")
H_SET_GET(auto_token_manage, true)
H_SET_GET(auto_connect, false)
String access_token;
String refresh_token;
int64_t expires_in;
void auth();
void token_connect(String access_token);
void token_refresh(String refresh_token);
void refresh_auto_encryption_key();
DiscordConnector();
~DiscordConnector();
void _ready() override;
void _process(double delta) override;
};
#endif

View File

@@ -0,0 +1,16 @@
#include "discord_linked_channel.h"
void DiscordLinkedChannel ::_bind_methods()
{
}
DiscordLinkedChannel::DiscordLinkedChannel ()
{
}
DiscordLinkedChannel::~DiscordLinkedChannel()
{
}

View File

@@ -0,0 +1,20 @@
#ifndef DISCORD_LINKED_CHANNEL_H
#define DISCORD_LINKED_CHANNEL_H
#include "discord_connected.h"
using namespace godot;
class DiscordLinkedChannel : public DiscordConnected
{
GDCLASS(DiscordLinkedChannel, DiscordConnected);
protected:
static void _bind_methods();
public:
DiscordLinkedChannel();
~DiscordLinkedChannel();
};
#endif

View File

@@ -0,0 +1,25 @@
#include "discord_lobby.h"
void DiscordLobby ::_bind_methods()
{
BIND_METHOD(DiscordLobby, create_or_join_lobby, "secret");
}
void DiscordLobby::create_or_join_lobby(String secret)
{
DiscordLobby::connector->client->CreateOrJoinLobby(secret.utf8().get_data(), [this](discordpp::ClientResult result, uint64_t lobbyId)
{
if(result.Successful()) {
std::cout << "Lobby created or joined successfully! Lobby Id: " << lobbyId << std::endl;
} else {
UtilityFunctions::print("Failed to create or join lobby: " + String(result.Error().c_str()));
} });
}
DiscordLobby::DiscordLobby()
{
}
DiscordLobby::~DiscordLobby()
{
}

21
src/nodes/discord_lobby.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef DISCORD_LOBBY_H
#define DISCORD_LOBBY_H
#include "discord_connected.h"
using namespace godot;
class DiscordLobby : public DiscordConnected
{
GDCLASS(DiscordLobby, DiscordConnected);
protected:
static void _bind_methods();
public:
void create_or_join_lobby(String secret);
DiscordLobby();
~DiscordLobby();
};
#endif

View File

@@ -0,0 +1,12 @@
#include "discord_social_sdk.h"
void DiscordSocialSDK::_bind_methods()
{
}
DiscordSocialSDK::DiscordSocialSDK()
{
}
DiscordSocialSDK::~DiscordSocialSDK()
{
}

View File

@@ -0,0 +1,33 @@
#ifndef DISCORD_SOCIAL_SDK_H
#define DISCORD_SOCIAL_SDK_H
#include "discordpp.h"
#include "../definitions.h"
#include <stdio.h>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/time.hpp>
#include <godot_cpp/classes/tree.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
using namespace godot;
class DiscordSocialSDK : public Node
{
GDCLASS(DiscordSocialSDK, Node);
protected:
static void _bind_methods();
public:
DiscordSocialSDK();
~DiscordSocialSDK();
};
#endif

View File

@@ -0,0 +1,40 @@
#include "editor_presence.h"
void EditorPresence::_bind_methods()
{
}
EditorPresence::EditorPresence()
{
}
EditorPresence::~EditorPresence()
{
}
void EditorPresence::_ready()
{
if (project_settings->has_setting("application/config/name"))
{
//activity.GetAssets().SetLargeImage("godot");
return;
}
/*activity.GetAssets().SetLargeText(String(engine->get_version_info()["string"]).utf8());
activity.GetTimestamps().SetStart(time->get_unix_time_from_system());
if (result == discord::Result::Ok)
//core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
else
UtilityFunctions::push_warning("EditorPresence couldn't be loaded! Maybe your Discord isn't running?");*/
}
void EditorPresence::_process(double delta)
{
/*if (state_string.utf8() != activity.GetState())
{
godot::Node *edited_scene_root = get_tree()->get_edited_scene_root();
activity.SetState(String("Editing: \"" + edited_scene_root->get_scene_file_path() + "\"").replace("res://", "").utf8());
if (result == discord::Result::Ok)
core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {});
}
if (result == discord::Result::Ok)
core->RunCallbacks();*/
return;
}

View File

@@ -0,0 +1,28 @@
#ifndef EDITOR_PRESENCE_H
#define EDITOR_PRESENCE_H
#include "discord_social_sdk.h"
using namespace godot;
class EditorPresence : public DiscordSocialSDK
{
GDCLASS(EditorPresence, DiscordSocialSDK);
protected:
static void _bind_methods();
public:
godot::Engine *engine = godot::Engine::get_singleton();
godot::ProjectSettings *project_settings = godot::ProjectSettings::get_singleton();
godot::Time *time = godot::Time::get_singleton();
String state_string;
EditorPresence();
~EditorPresence();
void _ready() override;
void _process(double delta) override;
};
#endif

View File

@@ -1,45 +1,45 @@
#include "register_types.h" #include "register_types.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/godot.hpp>
#include "discordgodot.h"
#include "editor_presence.h"
using namespace godot; using namespace godot;
static DiscordRPC *discordrpc; static DiscordUtil *discordutil;
void initialize_discordrpc_module(ModuleInitializationLevel p_level) void initialize_DiscordUtil_module(ModuleInitializationLevel p_level)
{ {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE)
{ {
ClassDB::register_class<DiscordRPC>(); ClassDB::register_abstract_class<DiscordSocialSDK>();
discordrpc = memnew(DiscordRPC); ClassDB::register_class<DiscordUtil>();
Engine::get_singleton()->register_singleton("DiscordRPC", DiscordRPC::get_singleton()); discordutil = memnew(DiscordUtil);
Engine::get_singleton()->register_singleton("DiscordUtil", DiscordUtil::get_singleton());
ClassDB::register_class<EditorPresence>(); ClassDB::register_class<EditorPresence>();
ClassDB::register_class<DiscordConnector>();
ClassDB::register_abstract_class<DiscordConnected>();
ClassDB::register_class<DiscordLobby>();
ClassDB::register_class<DiscordLinkedChannel>();
ClassDB::register_class<DiscordActivity>();
ClassDB::register_class<RichPresence>();
ClassDB::register_class<PartyInvite>();
} }
} }
void uninitialize_discordrpc_module(ModuleInitializationLevel p_level) void uninitialize_DiscordUtil_module(ModuleInitializationLevel p_level)
{ {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE)
{ {
Engine::get_singleton()->unregister_singleton("DiscordRPC"); Engine::get_singleton()->unregister_singleton("DiscordUtil");
memdelete(discordrpc); memdelete(discordutil);
} }
} }
extern "C" extern "C"
{ {
GDExtensionBool GDE_EXPORT discordrpcgd_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) GDExtensionBool GDE_EXPORT DiscordUtilgd_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{ {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_discordrpc_module); init_obj.register_initializer(initialize_DiscordUtil_module);
init_obj.register_terminator(uninitialize_discordrpc_module); init_obj.register_terminator(uninitialize_DiscordUtil_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init(); return init_obj.init();

View File

@@ -1,7 +1,26 @@
#ifndef REGISTER_TYPES_H #ifndef REGISTER_TYPES_H
#define REGISTER_TYPES_H #define REGISTER_TYPES_H
void initialize_discordrpc_module(); #define DISCORDPP_IMPLEMENTATION // this is way too important to forget
void uninitialize_discordrpc_module();
void initialize_DiscordUtil_module();
void uninitialize_DiscordUtil_module();
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/godot.hpp>
#include "util.h"
#include "nodes/discord_social_sdk.h"
#include "nodes/editor_presence.h"
#include "nodes/discord_connector.h"
#include "nodes/discord_connected.h"
#include "nodes/discord_lobby.h"
#include "nodes/discord_linked_channel.h"
#include "nodes/discord_activity.h"
#include "resources/rich_presence.h"
#include "resources/party_invite.h"
#endif // REGISTER_TYPES_H #endif // REGISTER_TYPES_H

View File

@@ -0,0 +1,76 @@
#include "party_invite.h"
void PartyInvite::_bind_methods()
{
BIND_SET_GET(PartyInvite, id, Variant::STRING);
BIND_SET_GET(PartyInvite, current_size, Variant::INT);
BIND_SET_GET(PartyInvite, max_size, Variant::INT);
BIND_SET_GET(PartyInvite, join_secret, Variant::STRING);
BIND_SET_GET(PartyInvite, instanced, Variant::BOOL);
BIND_SET_GET(PartyInvite, is_public_party, Variant::BOOL);
}
PartyInvite::PartyInvite()
{
}
String PartyInvite::get_id()
{
return id;
}
void PartyInvite::set_id(String value)
{
id = value;
}
int64_t PartyInvite::get_current_size()
{
return current_size;
}
void PartyInvite::set_current_size(int64_t value)
{
current_size = value;
}
int64_t PartyInvite::get_max_size()
{
return max_size;
}
void PartyInvite::set_max_size(int64_t value)
{
max_size = value;
}
String PartyInvite::get_join_secret()
{
return join_secret;
}
void PartyInvite::set_join_secret(String value)
{
join_secret = value;
}
bool PartyInvite::get_instanced()
{
return instanced;
}
void PartyInvite::set_instanced(bool value)
{
instanced = value;
}
bool PartyInvite::get_is_public_party()
{
return is_public_party;
}
void PartyInvite::set_is_public_party(bool value)
{
is_public_party = value;
}

View File

@@ -0,0 +1,29 @@
#ifndef PARTY_INVITE_H
#define PARTY_INVITE_H
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/classes/resource.hpp>
#include "../util.h"
using namespace godot;
class PartyInvite : public Resource {
GDCLASS(PartyInvite, Resource);
protected:
static void _bind_methods();
public:
H_SET_GET(id, "")
H_SET_GET(current_size, 1)
H_SET_GET(max_size, 4)
H_SET_GET(join_secret, "")
H_SET_GET(instanced, false)
H_SET_GET(is_public_party, false)
PartyInvite();
};
#endif

View File

@@ -0,0 +1,95 @@
#include "rich_presence.h"
void RichPresence::_bind_methods()
{
BIND_SET_GET(RichPresence, details, Variant::STRING);
BIND_SET_GET(RichPresence, state, Variant::STRING);
ADD_GROUP("Large Image", "large_");
BIND_SET_GET(RichPresence, large_image, Variant::STRING);
BIND_SET_GET(RichPresence, large_text, Variant::STRING);
ADD_GROUP("Small Image", "small_");
BIND_SET_GET(RichPresence, small_image, Variant::STRING);
BIND_SET_GET(RichPresence, small_text, Variant::STRING);
}
RichPresence::RichPresence() {}
void RichPresence::set_details(String value)
{
details = value;
}
String RichPresence::get_details()
{
return details;
}
void RichPresence::set_state(String value)
{
state = value;
}
String RichPresence::get_state()
{
return state;
}
void RichPresence::set_large_image(String value)
{
large_image = value;
}
String RichPresence::get_large_image()
{
return large_image;
}
void RichPresence::set_large_text(String value)
{
large_text = value;
}
String RichPresence::get_large_text()
{
return large_text;
}
void RichPresence::set_small_image(String value)
{
small_image = value;
}
String RichPresence::get_small_image()
{
return small_image;
}
void RichPresence::set_small_text(String value)
{
small_text = value;
}
String RichPresence::get_small_text()
{
return small_text;
}
void RichPresence::set_timestamps_start(int64_t value)
{
timestamps_start = value;
}
int64_t RichPresence::get_timestamps_start()
{
return timestamps_start;
}
void RichPresence::set_timestamps_end(int64_t value)
{
timestamps_end = value;
}
int64_t RichPresence::get_timestamps_end()
{
return timestamps_end;
}

View File

@@ -0,0 +1,32 @@
#ifndef RICH_PRESENCE_H
#define RICH_PRESENCE_H
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/classes/resource.hpp>
#include "../util.h"
using namespace godot;
class RichPresence : public Resource
{
GDCLASS(RichPresence, Resource);
protected:
static void _bind_methods();
public:
H_SET_GET(details, "")
H_SET_GET(state, "")
H_SET_GET(large_image, "")
H_SET_GET(large_text, "")
H_SET_GET(small_image, "")
H_SET_GET(small_text, "")
H_SET_GET(timestamps_start, 0)
H_SET_GET(timestamps_end, 0)
RichPresence();
};
#endif

243
src/util.cpp Normal file
View File

@@ -0,0 +1,243 @@
#include "util.h"
DiscordUtil *DiscordUtil::singleton = nullptr;
void DiscordUtil::_bind_methods()
{
BIND_METHOD(DiscordUtil, debug);
BIND_METHOD(DiscordUtil, run_callbacks);
BIND_METHOD(DiscordUtil, save_tokens, "access_token", "refresh_token", "expires_in", "auto_encryption_key");
BIND_METHOD(DiscordUtil, generate_auto_encryption_key);
}
DiscordUtil::DiscordUtil()
{
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
}
DiscordUtil::~DiscordUtil()
{
ERR_FAIL_COND(singleton != this);
singleton = nullptr;
}
DiscordUtil *DiscordUtil::get_singleton()
{
return singleton;
}
void DiscordUtil::run_callbacks()
{
discordpp::RunCallbacks();
}
void DiscordUtil::debug()
{
auto client = std::make_shared<discordpp::Client>();
client->AddLogCallback([](auto message, auto severity)
{ UtilityFunctions::print("[" + String(EnumToString(severity)) + "] " + message.c_str()); }, discordpp::LoggingSeverity::Info);
client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail)
{
UtilityFunctions::print(String("Status changed: ") + discordpp::Client::StatusToString(status).c_str());
if (status == discordpp::Client::Status::Ready) {
UtilityFunctions::print("Client is ready!\n");
UtilityFunctions::print("Friends Count: " + String::num_int64(client->GetRelationships().size()));
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
activity.SetState("Debug mode");
activity.SetDetails("Godot -> Discord Social SDK");
discordpp::ActivityAssets assets;
assets.SetSmallImage("boss");
assets.SetSmallText("Fighting the boss D:");
assets.SetLargeImage("example_game");
assets.SetLargeText("Example");
activity.SetAssets(assets);
// Update rich presence
client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
if(result.Successful()) {
UtilityFunctions::print("Rich Presence updated successfully!");
} else {
UtilityFunctions::push_error("Rich Presence update failed");
}
});
} else if (error != discordpp::Client::Error::None) {
UtilityFunctions::push_error("Connection Error: " + String(discordpp::Client::ErrorToString(error).c_str()) + " - Details: " + String::num_int64(errorDetail));
} });
// Generate OAuth2 code verifier for authentication
UtilityFunctions::print("Trying to authenticate...");
UtilityFunctions::print("Generating code verifier...");
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
// Set up authentication arguments
discordpp::AuthorizationArgs args{};
args.SetClientId(1099618430065324082);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());
// Begin authentication process // TODO: option to open browser
client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri)
{
if (!result.Successful()) {
UtilityFunctions::push_error("Authentication Error: " + String(result.Error().c_str()));
return;
} else {
UtilityFunctions::print("Authorization successful! Getting access token...");
client->GetToken(1099618430065324082, code, codeVerifier.Verifier(), redirectUri,
[client](discordpp::ClientResult result,
std::string accessToken, // needs to be stored securely
std::string refreshToken, // needs to be stored securely
discordpp::AuthorizationTokenType tokenType,
int32_t expiresIn,
std::string scope) {
UtilityFunctions::print("Access token received! Establishing connection...");
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) {
if(result.Successful()) {
UtilityFunctions::print("Token updated, connecting to Discord...");
client->Connect();
} else {
UtilityFunctions::push_error("Token update error: " + String(result.Error().c_str()));
}
});
});
} });
}
Dictionary DiscordUtil::user2dict(discordpp::UserHandle user)
{
Dictionary userdict;
/*userdict["avatar"] = user.GetAvatar(); // can be empty when user has no avatar
userdict["is_bot"] = user.GetBot();
userdict["discriminator"] = user.GetDiscriminator();
userdict["id"] = user.GetId();
userdict["username"] = user.GetUsername();
if (String(userdict["avatar"]).is_empty())
userdict["avatar_url"] = String(std::string("https://cdn.discordapp.com/embed/avatars/" + std::to_string((userdict["discriminator"].INT % 5) - 1) + ".png").c_str());
else
userdict["avatar_url"] = String(std::string("https://cdn.discordapp.com/avatars/" + std::to_string(user.GetId()) + "/" + user.GetAvatar() + ".png").c_str());
userdict.make_read_only();*/
return userdict;
}
Dictionary DiscordUtil::relationship2dict(discordpp::RelationshipHandle relationship)
{
Dictionary dict_relationship;
Dictionary presence;
Dictionary presence_activity;
/*switch (static_cast<int>(relationship.GetPresence().GetStatus()))
{
case 0:
presence["status"] = "Offline";
break;
case 1:
presence["status"] = "Online";
break;
case 2:
presence["status"] = "Idle";
break;
case 3:
presence["status"] = "DoNotDisturb";
break;
default:
presence["status"] = "NotAvailable";
break;
}
presence_activity["application_id"] = relationship.GetPresence().GetActivity().GetApplicationId();
presence_activity["name"] = relationship.GetPresence().GetActivity().GetName();
presence_activity["state"] = relationship.GetPresence().GetActivity().GetState();
presence_activity["details"] = relationship.GetPresence().GetActivity().GetDetails();
presence_activity["large_image"] = relationship.GetPresence().GetActivity().GetAssets().GetLargeImage();
presence_activity["large_text"] = relationship.GetPresence().GetActivity().GetAssets().GetLargeText();
presence_activity["small_image"] = relationship.GetPresence().GetActivity().GetAssets().GetSmallImage();
presence_activity["small_text"] = relationship.GetPresence().GetActivity().GetAssets().GetSmallText();
presence_activity["timestamps_start"] = relationship.GetPresence().GetActivity().GetTimestamps().GetStart();
presence_activity["timestamps_end"] = relationship.GetPresence().GetActivity().GetTimestamps().GetEnd();
presence_activity["instance"] = relationship.GetPresence().GetActivity().GetInstance();
presence_activity["party_id"] = relationship.GetPresence().GetActivity().GetParty().GetId();
presence_activity["current_party_size"] = relationship.GetPresence().GetActivity().GetParty().GetSize().GetCurrentSize();
presence_activity["max_party_size"] = relationship.GetPresence().GetActivity().GetParty().GetSize().GetMaxSize();
presence_activity["join_secret"] = relationship.GetPresence().GetActivity().GetSecrets().GetJoin();
presence_activity["spectate_secret"] = relationship.GetPresence().GetActivity().GetSecrets().GetSpectate();
presence_activity["match_secret"] = relationship.GetPresence().GetActivity().GetSecrets().GetMatch();
presence["activity"] = presence_activity;
presence.make_read_only();
switch (relationship.GetType())
{
case discordpp::RelationshipType::None:
dict_relationship["type"] = "None";
break;
case discordpp::RelationshipType::Friend:
dict_relationship["type"] = "Friend";
break;
case discordpp::RelationshipType::Blocked:
dict_relationship["type"] = "Blocked";
break;
case discordpp::RelationshipType::PendingIncoming:
dict_relationship["type"] = "PendingIncoming";
break;
case discordpp::RelationshipType::PendingOutgoing:
dict_relationship["type"] = "PendingOutgoing";
break;
case discordpp::RelationshipType::Implicit:
dict_relationship["type"] = "Implicit";
break;
default:
dict_relationship["type"] = "NotAvailable";
break;
}
dict_relationship["user"] = user2dict(relationship.GetUser());
dict_relationship["presence"] = presence;
dict_relationship.make_read_only();*/
return dict_relationship;
}
String DiscordUtil::generate_auto_encryption_key()
{
const char *charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%&/()=?*+#-.,;:_'";
String key;
for (int i = 0; i < 50; i++)
{
key += charset[rand() % strlen(charset)];
}
return key;
}
void DiscordUtil::save_tokens(String access_token, String refresh_token, int64_t expires_in, String auto_encryption_key)
{
Ref<ConfigFile> config;
config.instantiate();
config->set_value("tokens", "access_token", access_token);
config->set_value("tokens", "refresh_token", refresh_token);
config->set_value("tokens", "expires_in", expires_in);
config->save_encrypted_pass("user://discord_data.binary", auto_encryption_key);
}
void DiscordUtil::delete_tokens()
{
Ref<ConfigFile> config;
config.instantiate();
config->save("user://discord_data.binary");
}
Ref<ConfigFile> DiscordUtil::get_tokens(String auto_encryption_key)
{
Ref<ConfigFile> config;
config.instantiate();
if (!FileAccess::file_exists("user://discord_data.binary"))
{
return config;
}
Error err = config->load_encrypted_pass("user://discord_data.binary", auto_encryption_key);
if (err != OK)
{
config->save("user://discord_data.binary");
return config;
}
return config;
}

44
src/util.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef DISCORDGODOT_H
#define DISCORDGODOT_H
#include "definitions.h"
#include "discordpp.h"
#include <stdio.h>
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/config_file.hpp>
#include <godot_cpp/classes/file_access.hpp>
using namespace godot;
class DiscordUtil : public Object
{
GDCLASS(DiscordUtil, Object);
static DiscordUtil *singleton;
protected:
static void _bind_methods();
public:
static DiscordUtil *
get_singleton();
DiscordUtil();
~DiscordUtil();
Dictionary relationship2dict(discordpp::RelationshipHandle relationship);
Dictionary user2dict(discordpp::UserHandle user);
void save_tokens(String access_token, String refresh_token, int64_t expires_in, String auto_encryption_key);
String generate_auto_encryption_key();
void delete_tokens();
Ref<ConfigFile> get_tokens(String auto_encryption_key);
void debug();
void run_callbacks();
};
#endif

9
version.py Normal file
View File

@@ -0,0 +1,9 @@
short_name = "discord-sdk-godot"
name = "Discord Social SDK for Godot"
major = 2
minor = 0
patch = 0
status = "dev"
module_config = ""
website = "https://docs.vaporvee.com/"
docs = "latest"