Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
816dd664c2 |
33
meson.build
33
meson.build
@@ -1,7 +1,7 @@
|
||||
project('cell', ['c', 'cpp'],
|
||||
version: '0.9.3',
|
||||
meson_version: '>=1.4',
|
||||
default_options : [ 'cpp_std=c++11'])
|
||||
default_options : [ 'cpp_std=c++17'])
|
||||
|
||||
libtype = get_option('default_library')
|
||||
|
||||
@@ -302,6 +302,37 @@ else
|
||||
message('Storefront: ' + storefront)
|
||||
endif
|
||||
|
||||
# Discord SDK integration
|
||||
if host_machine.system() != 'emscripten'
|
||||
discord_sdk_path = meson.current_source_dir() / 'discord_social_sdk'
|
||||
|
||||
if host_machine.system() == 'darwin'
|
||||
discord_lib_path = discord_sdk_path / 'lib' / 'release' / 'libdiscord_partner_sdk.dylib'
|
||||
elif host_machine.system() == 'linux'
|
||||
discord_lib_path = discord_sdk_path / 'lib' / 'release' / 'libdiscord_partner_sdk.so'
|
||||
elif host_machine.system() == 'windows'
|
||||
discord_lib_path = discord_sdk_path / 'lib' / 'release' / 'discord_partner_sdk.lib'
|
||||
else
|
||||
discord_lib_path = ''
|
||||
endif
|
||||
|
||||
if fs.exists(discord_lib_path)
|
||||
discord_dep = declare_dependency(
|
||||
include_directories: include_directories('discord_social_sdk/include'),
|
||||
link_args: [discord_lib_path]
|
||||
)
|
||||
deps += discord_dep
|
||||
src += 'qjs_discord.cpp'
|
||||
message('Discord SDK enabled')
|
||||
else
|
||||
add_project_arguments('-NDISCORD', language: ['c', 'cpp'])
|
||||
message('Discord SDK not found at: ' + discord_lib_path)
|
||||
endif
|
||||
else
|
||||
add_project_arguments('-NDISCORD', language: ['c', 'cpp'])
|
||||
message('Discord SDK disabled for Emscripten')
|
||||
endif
|
||||
|
||||
link_args = link
|
||||
sources = []
|
||||
src += [
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
#ifndef NSTEAM
|
||||
#include "qjs_steam.h"
|
||||
#endif
|
||||
#ifndef NDISCORD
|
||||
#include "qjs_discord.h"
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
@@ -1682,6 +1685,10 @@ void ffi_load(JSContext *js)
|
||||
arrput(rt->module_registry, MISTLINE(steam));
|
||||
#endif
|
||||
|
||||
#ifndef NDISCORD
|
||||
arrput(rt->module_registry, MISTLINE(discord));
|
||||
#endif
|
||||
|
||||
JSValue globalThis = JS_GetGlobalObject(js);
|
||||
|
||||
JSValue prosp = JS_NewObject(js);
|
||||
|
||||
344
source/qjs_discord.cpp
Normal file
344
source/qjs_discord.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
#ifndef NDISCORD
|
||||
|
||||
// Include C headers first to get types
|
||||
#include "qjs_discord.h"
|
||||
#include "jsffi.h"
|
||||
#include "qjs_blob.h"
|
||||
|
||||
// C++ headers
|
||||
#define DISCORDPP_IMPLEMENTATION
|
||||
#include <optional>
|
||||
#include <discordpp.h>
|
||||
#include <string.h>
|
||||
#include <memory>
|
||||
|
||||
// Discord client wrapper
|
||||
static std::shared_ptr<discordpp::Client> discord_client = nullptr;
|
||||
static bool discord_initialized = false;
|
||||
static bool discord_ready = false;
|
||||
|
||||
extern "C" {
|
||||
|
||||
// DISCORD INITIALIZATION
|
||||
JSC_CCALL(discord_init,
|
||||
if (discord_initialized) {
|
||||
return JS_NewBool(js, true);
|
||||
}
|
||||
|
||||
try {
|
||||
discord_client = std::make_shared<discordpp::Client>();
|
||||
discord_initialized = true;
|
||||
return JS_NewBool(js, true);
|
||||
} catch (const std::exception& e) {
|
||||
return JS_ThrowInternalError(js, "Failed to initialize Discord client: %s", e.what());
|
||||
}
|
||||
)
|
||||
|
||||
JSC_CCALL(discord_shutdown,
|
||||
if (discord_initialized && discord_client) {
|
||||
discord_client.reset();
|
||||
discord_initialized = false;
|
||||
discord_ready = false;
|
||||
}
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(discord_run_callbacks,
|
||||
if (discord_initialized && discord_client) {
|
||||
discordpp::RunCallbacks();
|
||||
}
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
// LOGGING CALLBACKS
|
||||
static JSValue js_log_callback = JS_NULL;
|
||||
static JSContext *js_log_context = nullptr;
|
||||
|
||||
static void discord_log_callback(std::string message, discordpp::LoggingSeverity severity) {
|
||||
if (!JS_IsNull(js_log_callback) && js_log_context) {
|
||||
const char* severity_str;
|
||||
switch (severity) {
|
||||
case discordpp::LoggingSeverity::Verbose: severity_str = "verbose"; break;
|
||||
case discordpp::LoggingSeverity::Info: severity_str = "info"; break;
|
||||
case discordpp::LoggingSeverity::Warning: severity_str = "warning"; break;
|
||||
case discordpp::LoggingSeverity::Error: severity_str = "error"; break;
|
||||
default: severity_str = "unknown"; break;
|
||||
}
|
||||
|
||||
JSValue args[2];
|
||||
args[0] = JS_NewString(js_log_context, message.c_str());
|
||||
args[1] = JS_NewString(js_log_context, severity_str);
|
||||
|
||||
JSValue ret = JS_Call(js_log_context, js_log_callback, JS_NULL, 2, args);
|
||||
JS_FreeValue(js_log_context, args[0]);
|
||||
JS_FreeValue(js_log_context, args[1]);
|
||||
if (JS_IsException(ret)) {
|
||||
JS_GetException(js_log_context);
|
||||
}
|
||||
JS_FreeValue(js_log_context, ret);
|
||||
}
|
||||
}
|
||||
|
||||
JSC_CCALL(discord_add_log_callback,
|
||||
if (!discord_client) return JS_ThrowInternalError(js, "Discord not initialized");
|
||||
|
||||
if (!JS_IsNull(js_log_callback)) {
|
||||
JS_FreeValue(js, js_log_callback);
|
||||
}
|
||||
|
||||
js_log_callback = JS_DupValue(js, argv[0]);
|
||||
js_log_context = js;
|
||||
|
||||
discordpp::LoggingSeverity severity_level = discordpp::LoggingSeverity::Info;
|
||||
if (argc > 1) {
|
||||
int32_t severity_int;
|
||||
JS_ToInt32(js, &severity_int, argv[1]);
|
||||
severity_level = (discordpp::LoggingSeverity)severity_int;
|
||||
}
|
||||
|
||||
discord_client->AddLogCallback(discord_log_callback, severity_level);
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
// STATUS CALLBACKS
|
||||
static JSValue js_status_callback = JS_NULL;
|
||||
static JSContext *js_status_context = nullptr;
|
||||
|
||||
static void discord_status_callback(discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) {
|
||||
if (!JS_IsNull(js_status_callback) && js_status_context) {
|
||||
std::string status_str_cpp = discordpp::Client::StatusToString(status);
|
||||
std::string error_str_cpp = discordpp::Client::ErrorToString(error);
|
||||
const char* status_str = status_str_cpp.c_str();
|
||||
const char* error_str = error_str_cpp.c_str();
|
||||
|
||||
JSValue args[3];
|
||||
args[0] = JS_NewString(js_status_context, status_str);
|
||||
args[1] = JS_NewString(js_status_context, error_str);
|
||||
args[2] = JS_NewInt32(js_status_context, errorDetail);
|
||||
|
||||
JSValue ret = JS_Call(js_status_context, js_status_callback, JS_NULL, 3, args);
|
||||
JS_FreeValue(js_status_context, args[0]);
|
||||
JS_FreeValue(js_status_context, args[1]);
|
||||
JS_FreeValue(js_status_context, args[2]);
|
||||
if (JS_IsException(ret)) {
|
||||
JS_GetException(js_status_context);
|
||||
}
|
||||
JS_FreeValue(js_status_context, ret);
|
||||
|
||||
discord_ready = (status == discordpp::Client::Status::Ready);
|
||||
}
|
||||
}
|
||||
|
||||
JSC_CCALL(discord_set_status_callback,
|
||||
if (!discord_client) return JS_ThrowInternalError(js, "Discord not initialized");
|
||||
|
||||
if (!JS_IsNull(js_status_callback)) {
|
||||
JS_FreeValue(js, js_status_callback);
|
||||
}
|
||||
|
||||
js_status_callback = JS_DupValue(js, argv[0]);
|
||||
js_status_context = js;
|
||||
|
||||
discord_client->SetStatusChangedCallback(discord_status_callback);
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
// CONNECTIONS (simplified)
|
||||
JSC_CCALL(discord_connect,
|
||||
if (!discord_client) return JS_ThrowInternalError(js, "Discord not initialized");
|
||||
|
||||
try {
|
||||
discord_client->Connect();
|
||||
return JS_NULL;
|
||||
} catch (const std::exception& e) {
|
||||
return JS_ThrowInternalError(js, "Connect failed: %s", e.what());
|
||||
}
|
||||
)
|
||||
|
||||
// RELATIONSHIPS (Friends)
|
||||
JSC_CCALL(discord_get_relationships,
|
||||
if (!discord_client) return JS_ThrowInternalError(js, "Discord not initialized");
|
||||
if (!discord_ready) return JS_ThrowInternalError(js, "Discord not ready");
|
||||
|
||||
try {
|
||||
auto relationships = discord_client->GetRelationships();
|
||||
|
||||
JSValue arr = JS_NewArray(js);
|
||||
int index = 0;
|
||||
for (const auto& rel : relationships) {
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "id", JS_NewString(js, std::to_string(rel.Id()).c_str()));
|
||||
|
||||
auto user_opt = rel.User();
|
||||
if (user_opt.has_value()) {
|
||||
auto user = user_opt.value();
|
||||
JS_SetPropertyStr(js, obj, "username", JS_NewString(js, user.Username().c_str()));
|
||||
JS_SetPropertyStr(js, obj, "display_name", JS_NewString(js, user.DisplayName().c_str()));
|
||||
|
||||
auto avatar_opt = user.Avatar();
|
||||
if (avatar_opt.has_value()) {
|
||||
JS_SetPropertyStr(js, obj, "avatar", JS_NewString(js, avatar_opt.value().c_str()));
|
||||
} else {
|
||||
JS_SetPropertyStr(js, obj, "avatar", JS_NewString(js, ""));
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(js, obj, "status", JS_NewInt32(js, (int)user.Status()));
|
||||
}
|
||||
|
||||
JS_SetPropertyUint32(js, arr, index++, obj);
|
||||
}
|
||||
|
||||
return arr;
|
||||
} catch (const std::exception& e) {
|
||||
return JS_ThrowInternalError(js, "Get relationships failed: %s", e.what());
|
||||
}
|
||||
)
|
||||
|
||||
// RICH PRESENCE (simplified)
|
||||
JSC_CCALL(discord_update_rich_presence,
|
||||
if (!discord_client) return JS_ThrowInternalError(js, "Discord not initialized");
|
||||
if (!discord_ready) return JS_ThrowInternalError(js, "Discord not ready");
|
||||
|
||||
JSValue activity_obj = argv[0];
|
||||
JSValue callback = argv[1];
|
||||
|
||||
try {
|
||||
discordpp::Activity activity;
|
||||
|
||||
// Get activity type
|
||||
JSValue type_val = JS_GetPropertyStr(js, activity_obj, "type");
|
||||
if (!JS_IsNull(type_val)) {
|
||||
int32_t type;
|
||||
JS_ToInt32(js, &type, type_val);
|
||||
activity.SetType((discordpp::ActivityTypes)type);
|
||||
}
|
||||
JS_FreeValue(js, type_val);
|
||||
|
||||
// Get state
|
||||
JSValue state_val = JS_GetPropertyStr(js, activity_obj, "state");
|
||||
if (!JS_IsNull(state_val)) {
|
||||
const char *state = JS_ToCString(js, state_val);
|
||||
if (state) {
|
||||
activity.SetState(state);
|
||||
JS_FreeCString(js, state);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(js, state_val);
|
||||
|
||||
// Get details
|
||||
JSValue details_val = JS_GetPropertyStr(js, activity_obj, "details");
|
||||
if (!JS_IsNull(details_val)) {
|
||||
const char *details = JS_ToCString(js, details_val);
|
||||
if (details) {
|
||||
activity.SetDetails(details);
|
||||
JS_FreeCString(js, details);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(js, details_val);
|
||||
|
||||
// Store callback for later use
|
||||
static JSValue presence_callback = JS_NULL;
|
||||
static JSContext *presence_context = nullptr;
|
||||
|
||||
if (!JS_IsNull(presence_callback)) {
|
||||
JS_FreeValue(presence_context, presence_callback);
|
||||
}
|
||||
presence_callback = JS_DupValue(js, callback);
|
||||
presence_context = js;
|
||||
|
||||
discord_client->UpdateRichPresence(activity, [](discordpp::ClientResult result) {
|
||||
if (presence_context && !JS_IsNull(presence_callback)) {
|
||||
JSValue args[1];
|
||||
args[0] = JS_NewBool(presence_context, result.Successful());
|
||||
|
||||
JSValue ret = JS_Call(presence_context, presence_callback, JS_NULL, 1, args);
|
||||
JS_FreeValue(presence_context, args[0]);
|
||||
if (JS_IsException(ret)) {
|
||||
JS_GetException(presence_context);
|
||||
}
|
||||
JS_FreeValue(presence_context, ret);
|
||||
}
|
||||
});
|
||||
|
||||
return JS_NULL;
|
||||
} catch (const std::exception& e) {
|
||||
return JS_ThrowInternalError(js, "Update rich presence failed: %s", e.what());
|
||||
}
|
||||
)
|
||||
|
||||
JSC_CCALL(discord_clear_rich_presence,
|
||||
if (!discord_client) return JS_ThrowInternalError(js, "Discord not initialized");
|
||||
if (!discord_ready) return JS_ThrowInternalError(js, "Discord not ready");
|
||||
|
||||
try {
|
||||
discord_client->ClearRichPresence();
|
||||
return JS_NULL;
|
||||
} catch (const std::exception& e) {
|
||||
return JS_ThrowInternalError(js, "Clear rich presence failed: %s", e.what());
|
||||
}
|
||||
)
|
||||
|
||||
// UTILITIES
|
||||
JSC_CCALL(discord_get_default_presence_scopes,
|
||||
std::string scopes = discordpp::Client::GetDefaultPresenceScopes();
|
||||
return JS_NewString(js, scopes.c_str());
|
||||
)
|
||||
|
||||
// Get SDK version
|
||||
JSC_CCALL(discord_get_version,
|
||||
return JS_NewString(js, "Discord Social SDK C++ v1.0");
|
||||
)
|
||||
|
||||
// Check if ready
|
||||
JSC_CCALL(discord_is_ready,
|
||||
return JS_NewBool(js, discord_ready);
|
||||
)
|
||||
|
||||
// Activity Types Constants
|
||||
JSC_CCALL(discord_activity_types,
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "Playing", JS_NewInt32(js, (int)discordpp::ActivityTypes::Playing));
|
||||
JS_SetPropertyStr(js, obj, "Streaming", JS_NewInt32(js, (int)discordpp::ActivityTypes::Streaming));
|
||||
JS_SetPropertyStr(js, obj, "Listening", JS_NewInt32(js, (int)discordpp::ActivityTypes::Listening));
|
||||
JS_SetPropertyStr(js, obj, "Watching", JS_NewInt32(js, (int)discordpp::ActivityTypes::Watching));
|
||||
JS_SetPropertyStr(js, obj, "Competing", JS_NewInt32(js, (int)discordpp::ActivityTypes::Competing));
|
||||
return obj;
|
||||
)
|
||||
|
||||
// Function list for Discord module - simplified set
|
||||
static const JSCFunctionListEntry js_discord_funcs[] = {
|
||||
// Core functions
|
||||
JS_CFUNC_DEF("init", 0, js_discord_init),
|
||||
JS_CFUNC_DEF("shutdown", 0, js_discord_shutdown),
|
||||
JS_CFUNC_DEF("run_callbacks", 0, js_discord_run_callbacks),
|
||||
|
||||
// Callbacks
|
||||
JS_CFUNC_DEF("add_log_callback", 2, js_discord_add_log_callback),
|
||||
JS_CFUNC_DEF("set_status_callback", 1, js_discord_set_status_callback),
|
||||
|
||||
// Connection
|
||||
JS_CFUNC_DEF("connect", 0, js_discord_connect),
|
||||
|
||||
// Relationships
|
||||
JS_CFUNC_DEF("get_relationships", 0, js_discord_get_relationships),
|
||||
|
||||
// Rich Presence
|
||||
JS_CFUNC_DEF("update_rich_presence", 2, js_discord_update_rich_presence),
|
||||
JS_CFUNC_DEF("clear_rich_presence", 0, js_discord_clear_rich_presence),
|
||||
|
||||
// Utilities
|
||||
JS_CFUNC_DEF("get_default_presence_scopes", 0, js_discord_get_default_presence_scopes),
|
||||
JS_CFUNC_DEF("get_version", 0, js_discord_get_version),
|
||||
JS_CFUNC_DEF("is_ready", 0, js_discord_is_ready),
|
||||
JS_CFUNC_DEF("activity_types", 0, js_discord_activity_types),
|
||||
};
|
||||
|
||||
JSValue js_discord_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_discord_funcs, sizeof(js_discord_funcs) / sizeof(js_discord_funcs[0]));
|
||||
return mod;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // !NDISCORD
|
||||
16
source/qjs_discord.h
Normal file
16
source/qjs_discord.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef QJS_DISCORD_H
|
||||
#define QJS_DISCORD_H
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JSValue js_discord_use(JSContext *js);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // QJS_DISCORD_H
|
||||
71
tests/discord.ce
Normal file
71
tests/discord.ce
Normal file
@@ -0,0 +1,71 @@
|
||||
// Test Discord integration
|
||||
var discord = use('discord');
|
||||
var time = use('time');
|
||||
|
||||
log.console("Testing Discord integration...");
|
||||
|
||||
// Test basic initialization
|
||||
if (discord.init()) {
|
||||
log.console("✓ Discord initialized successfully");
|
||||
|
||||
// Test version info
|
||||
log.console("Discord SDK version: " + discord.get_version());
|
||||
|
||||
// Test activity types
|
||||
var types = discord.activity_types();
|
||||
log.console("Activity types available:", types);
|
||||
|
||||
// Test status callback
|
||||
discord.set_status_callback((status, error, detail) => {
|
||||
log.console("Discord status changed:", status, error, detail);
|
||||
|
||||
if (status === "Ready") {
|
||||
log.console("✓ Discord is ready! Setting rich presence...");
|
||||
|
||||
// Set rich presence
|
||||
var activity = {
|
||||
type: types.Playing,
|
||||
state: "Testing Prosperon Engine",
|
||||
details: "Running Discord Integration Test"
|
||||
};
|
||||
|
||||
discord.update_rich_presence(activity, (success) => {
|
||||
if (success) {
|
||||
log.console("✓ Rich presence updated successfully!");
|
||||
log.console("Check your Discord profile - you should see the activity");
|
||||
log.console("Waiting 10 seconds so you can see the rich presence...");
|
||||
|
||||
// Wait 10 seconds so user can see the rich presence
|
||||
time.delay(() => {
|
||||
log.console("Clearing rich presence and shutting down...");
|
||||
discord.clear_rich_presence();
|
||||
discord.shutdown();
|
||||
log.console("✓ Discord test completed successfully");
|
||||
}, 10);
|
||||
} else {
|
||||
log.console("✗ Failed to set rich presence");
|
||||
discord.shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Test log callback
|
||||
discord.add_log_callback((message, severity) => {
|
||||
log.console("[Discord " + severity + "] " + message);
|
||||
}, 2); // Info level
|
||||
|
||||
// Test default scopes
|
||||
log.console("Default presence scopes: " + discord.get_default_presence_scopes());
|
||||
|
||||
log.console("✓ Discord module functions loaded successfully");
|
||||
log.console("Attempting to connect to Discord...");
|
||||
|
||||
// Connect to Discord
|
||||
discord.connect();
|
||||
|
||||
} else {
|
||||
log.console("✗ Failed to initialize Discord");
|
||||
}
|
||||
|
||||
log.console("Discord integration test started - waiting for connection...");
|
||||
Reference in New Issue
Block a user