From 4680be3ba902d7d9f6a679e7f7a2eba055acc76b Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 7 Apr 2026 02:03:25 +0200 Subject: [PATCH] init: library wrapper done --- .gitignore | 69 +++++++ DiscordActivity.java | 107 +++++++++++ DiscordActivityBuilder.java | 265 +++++++++++++++++++++++++++ DiscordBridge.cpp | 347 ++++++++++++++++++++++++++++++++++++ DiscordBridge.java | 36 ++++ DiscordBridgeAdapter.java | 5 + README.md | 4 + export.sh | 20 +++ 8 files changed, 853 insertions(+) create mode 100644 .gitignore create mode 100644 DiscordActivity.java create mode 100644 DiscordActivityBuilder.java create mode 100644 DiscordBridge.cpp create mode 100644 DiscordBridge.java create mode 100644 DiscordBridgeAdapter.java create mode 100644 README.md create mode 100755 export.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a235e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,69 @@ +# Created by https://www.toptal.com/developers/gitignore/api/java,c++ +# Edit at https://www.toptal.com/developers/gitignore?templates=java,c++ + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +# End of https://www.toptal.com/developers/gitignore/api/java,c++ + + +discord.sh + +*.h \ No newline at end of file diff --git a/DiscordActivity.java b/DiscordActivity.java new file mode 100644 index 0000000..392ba07 --- /dev/null +++ b/DiscordActivity.java @@ -0,0 +1,107 @@ +package com.leohabrom.discord; + +import java.util.Objects; + +public class DiscordActivity { + // Basic Info + public String name = null; + public String state = null; + public String stateUrl = null; + public String details = null; + public String detailsUrl = null; + public int type = -1; + public int status = -1; + public int platform = -1; + + // Assets + public String largeImage = null; + public String largeText = null; + public String largeUrl = null; + public String smallImage = null; + public String smallText = null; + public String smallUrl = null; + public String inviteCoverImage = null; + + // Party + public String partyId = null; + public int partyCurrentSize = -1; + public int partyMaxSize = -1; + public int partyPrivacy = -1; + + // Timestamps (Unix milliseconds) + public long startTimestamp = -1; + public long endTimestamp = -1; + + //Buttons + public String firstButtonLabel = null; + public String firstButtonUrl = null; + public String secondButtonLabel = null; + public String secondButtonUrl = null; + + public String joinSecret = null; + + // Constants + public static final int TYPE_PLAYING = 0; + //public static final int TYPE_STREAMING = 1; + public static final int TYPE_LISTENING = 2; + public static final int TYPE_WATCHING = 3; + public static final int TYPE_COMPETING = 4; + //public static final int TYPE_CUSTOM = 5; + //public static final int TYPE_HANG = 6; + + public static final int STATUS_NAME = 0; + public static final int STATUS_STATE = 1; + public static final int STATUS_DETAIL = 2; + + public static final int PLATFORM_ANDROID = 0; + public static final int PLATFOTM_DESKTOP = 1; + public static final int PLATFORM_EMBEDDED = 2; + public static final int PLATFORM_IOS = 3; + public static final int PLATFORM_PS4 = 4; + public static final int PLATFORM_PS5 = 5; + public static final int PLATFORM_SAMSUNG = 6; + public static final int PLATFORM_XBOX = 7; + + public static final int PARTY_PRIVATE = 0; + public static final int PARTY_PUBLIC = 0; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscordActivity that = (DiscordActivity) o; + + return type == that.type && + status == that.status && + // small tolerance for timestamps to account for drift + Math.abs(startTimestamp - that.startTimestamp) < 500 && + Math.abs(endTimestamp - that.endTimestamp) < 500 && + Objects.equals(name, that.name) && + Objects.equals(state, that.state) && + Objects.equals(stateUrl, that.stateUrl) && + Objects.equals(details, that.details) && + Objects.equals(detailsUrl, that.detailsUrl) && + Objects.equals(largeImage, that.largeImage) && + Objects.equals(largeUrl, that.largeUrl) && + Objects.equals(largeText, that.largeText) && + Objects.equals(smallImage, that.smallImage) && + Objects.equals(smallUrl, that.smallUrl) && + Objects.equals(smallText, that.smallText) && + Objects.equals(inviteCoverImage, that.inviteCoverImage) && + Objects.equals(platform, that.platform) && + Objects.equals(partyId, that.partyId) && + Objects.equals(partyCurrentSize, that.partyCurrentSize) && + Objects.equals(partyMaxSize, that.partyMaxSize) && + Objects.equals(partyPrivacy, that.partyPrivacy) && + Objects.equals(firstButtonLabel, that.firstButtonLabel) && + Objects.equals(firstButtonUrl, that.firstButtonUrl) && + Objects.equals(secondButtonLabel, that.secondButtonLabel) && + Objects.equals(secondButtonUrl, that.secondButtonUrl) && + Objects.equals(joinSecret, that.joinSecret); + } + + @Override + public int hashCode() { + return Objects.hash(name, state, stateUrl, details, detailsUrl, type, status, platform, largeImage, largeText, largeUrl, smallImage, smallText, smallUrl, inviteCoverImage, partyId, partyCurrentSize, partyMaxSize, partyPrivacy, startTimestamp, endTimestamp, firstButtonLabel, firstButtonUrl, secondButtonLabel, secondButtonUrl, joinSecret); + } +} \ No newline at end of file diff --git a/DiscordActivityBuilder.java b/DiscordActivityBuilder.java new file mode 100644 index 0000000..b73f580 --- /dev/null +++ b/DiscordActivityBuilder.java @@ -0,0 +1,265 @@ +package com.leohabrom.discord; + +import com.leohabrom.discord.DiscordActivity; + +import java.net.URI; + +public class DiscordActivityBuilder { + public enum Type { + PLAYING(0), + WATCHING(3), + LISTENING(2), + COMPETING(4); + + private final int value; + + Type(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public enum Status { + NAME(0), + STATE(1), + DETAIL(2); + + private final int value; + + Status(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public enum Platform { + ANDROID(0), + DESKTOP(1), + EMBEDDED(2), + IOS(3), + PS4(4), + PS5(5), + SAMSUNG(6), + XBOX(7); + + private final int value; + + Platform(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public enum Privacy { + PRIVATE(0), + PUBLIC(1); + + private final int value; + + Privacy(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public enum Button { + FIRST(0), + SECOND(1); + + private final int value; + + Button(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + private final DiscordActivity activity; + + public DiscordActivityBuilder() { + activity = new DiscordActivity(); + } + + public DiscordActivityBuilder details(String details) { + activity.details = details; + return this; + } + + public DiscordActivityBuilder details(String details, URI url) { + activity.details = details; + activity.detailsUrl = url.toString(); + return this; + } + + public DiscordActivityBuilder name(String name) { + activity.name = name; + return this; + } + + public DiscordActivityBuilder state(String state) { + activity.state = state; + return this; + } + + public DiscordActivityBuilder state(String state, URI url) { + activity.state = state; + activity.stateUrl = url.toString(); + return this; + } + + public DiscordActivityBuilder type(Type type) { + activity.type = type.getValue(); + return this; + } + + public DiscordActivityBuilder statusDisplay(Status status) { + activity.status = status.getValue(); + return this; + } + + public DiscordActivityBuilder platform(Platform platform) { + activity.platform = platform.getValue(); + return this; + } + + public DiscordActivityBuilder largeImage(String idOrUrl) { + activity.largeImage = idOrUrl; + return this; + } + + public DiscordActivityBuilder largeImage(String idOrUrl, String text) { + activity.largeImage = idOrUrl; + activity.largeText = text; + return this; + } + + public DiscordActivityBuilder largeImage(String idOrUrl, URI url) { + activity.largeImage = idOrUrl; + activity.largeUrl = url.toString(); + return this; + } + + public DiscordActivityBuilder largeImage(String idOrUrl, String text, URI url) { + activity.largeImage = idOrUrl; + activity.largeText = text; + activity.largeUrl = url.toString(); + return this; + } + + public DiscordActivityBuilder smallImage(String idOrUrl) { + activity.smallImage = idOrUrl; + return this; + } + + public DiscordActivityBuilder smallImage(String idOrUrl, String text) { + activity.smallImage = idOrUrl; + activity.smallText = text; + return this; + } + + public DiscordActivityBuilder smallImage(String idOrUrl, URI url) { + activity.smallImage = idOrUrl; + activity.smallUrl = url.toString(); + return this; + } + + public DiscordActivityBuilder smallImage(String idOrUrl, String text, URI url) { + activity.smallImage = idOrUrl; + activity.smallText = text; + activity.smallUrl = url.toString(); + return this; + } + + public DiscordActivityBuilder inviteCoverImage(String idOrUrl) { + activity.inviteCoverImage = idOrUrl; + return this; + } + + public DiscordActivityBuilder party(String id) { + activity.partyId = id; + return this; + } + + + public DiscordActivityBuilder party(String id, int currentSize) { + activity.partyId = id; + activity.partyCurrentSize = currentSize; + return this; + } + + + public DiscordActivityBuilder party(String id, int currentSize, Privacy privacy) { + activity.partyId = id; + activity.partyCurrentSize = currentSize; + activity.partyPrivacy = privacy.getValue(); + return this; + } + + public DiscordActivityBuilder party(String id, Privacy privacy) { + activity.partyId = id; + activity.partyPrivacy = privacy.getValue(); + return this; + } + + public DiscordActivityBuilder party(String id, int currentSize, int maxSize, Privacy privacy) { + activity.partyId = id; + activity.partyCurrentSize = currentSize; + activity.partyMaxSize = maxSize; + activity.partyPrivacy = privacy.getValue(); + return this; + } + + public DiscordActivityBuilder time(long startTime) { + activity.startTimestamp = startTime; + return this; + } + + public DiscordActivityBuilder endTime(long endTime) { + activity.startTimestamp = endTime; + return this; + } + + public DiscordActivityBuilder time(long startTime, long endTime) { + activity.startTimestamp = startTime; + activity.endTimestamp = endTime; + return this; + } + + public DiscordActivityBuilder button(Button id, String label, URI url) { + switch (id) { + case FIRST -> { + activity.firstButtonLabel = label; + activity.firstButtonUrl = url.toString(); + } + case SECOND -> { + activity.secondButtonLabel = label; + activity.secondButtonUrl = url.toString(); + } + } + return this; + } + + public DiscordActivityBuilder joinSecret(String secret) { + activity.joinSecret = secret; + return this; + } + + public DiscordActivity build() { + return activity; + } +} diff --git a/DiscordBridge.cpp b/DiscordBridge.cpp new file mode 100644 index 0000000..f966fa0 --- /dev/null +++ b/DiscordBridge.cpp @@ -0,0 +1,347 @@ +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include "DiscordBridge.h" +#include +#include +#include + +std::shared_ptr g_client; +JavaVM *g_jvm = nullptr; +// Helper to check if a Java string field is set and return it +std::string getJString(JNIEnv* env, jobject obj, const char* fieldName) { + jclass objClass = env->GetObjectClass(obj); + jfieldID fid = env->GetFieldID(objClass, fieldName, "Ljava/lang/String;"); + jstring jstr = (jstring)env->GetObjectField(obj, fid); + + if (!jstr) return ""; + + // 1. Call String.getBytes("UTF-8") + jclass stringClass = env->FindClass("java/lang/String"); + jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); + jstring utf8 = env->NewStringUTF("UTF-8"); + jbyteArray array = (jbyteArray)env->CallObjectMethod(jstr, getBytes, utf8); + + // 2. Convert jbyteArray to std::string + jsize len = env->GetArrayLength(array); + jbyte* bytes = env->GetByteArrayElements(array, nullptr); + std::string result((char*)bytes, len); + + // 3. Cleanup + env->ReleaseByteArrayElements(array, bytes, JNI_ABORT); + env->DeleteLocalRef(utf8); + env->DeleteLocalRef(array); + + return result; +} + +void sendToJavaThreadSafe(jobject globalObj, const std::string &message) +{ + if (g_jvm == nullptr) + return; + + JNIEnv *env; + bool attached = false; + jint res = g_jvm->GetEnv((void **)&env, JNI_VERSION_1_6); + + if (res == JNI_EDETACHED) + { + if (g_jvm->AttachCurrentThread((void **)&env, nullptr) != 0) + return; + attached = true; + } + + jclass cls = env->GetObjectClass(globalObj); + jmethodID mid = env->GetMethodID(cls, "onMessage", "(Ljava/lang/String;)V"); + + if (mid != nullptr) + { + jstring jmsg = env->NewStringUTF(message.c_str()); + env->CallVoidMethod(globalObj, mid, jmsg); + env->DeleteLocalRef(jmsg); + } + + if (attached) + g_jvm->DetachCurrentThread(); +} +extern "C" +{ + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) + { + g_jvm = vm; + return JNI_VERSION_1_6; + } + + JNIEXPORT void JNICALL Java_com_leohabrom_discord_DiscordBridge_init(JNIEnv *env, jobject obj, jlong appId) + { + g_client = std::make_shared(); + g_client->SetApplicationId(static_cast(appId)); + } + + JNIEXPORT void JNICALL Java_com_leohabrom_discord_DiscordBridge_update(JNIEnv *env, jobject obj, jobject jAct) + { + if (!g_client) + { + return; + } + jobject globalObj = env->NewGlobalRef(obj); + + discordpp::Activity activity; + jclass cls = env->GetObjectClass(jAct); + + // 1. Basic Info & URLs + std::string name = getJString(env, jAct, "name"); + if (!name.empty()) + activity.SetName(name); + + std::string state = getJString(env, jAct, "state"); + if (!state.empty()) + { + activity.SetState(state); + std::string stateUrl = getJString(env, jAct, "stateUrl"); + if (!stateUrl.empty()) + activity.SetStateUrl(stateUrl); + } + + std::string details = getJString(env, jAct, "details"); + if (!details.empty()) + { + activity.SetDetails(details); + std::string detailsUrl = getJString(env, jAct, "detailsUrl"); + if (!detailsUrl.empty()) + activity.SetDetailsUrl(detailsUrl); + } + + // 2. Enums (Type, Status Display, Platform) + jint jType = env->GetIntField(jAct, env->GetFieldID(cls, "type", "I")); + switch (jType) + { + case 0: + activity.SetType(discordpp::ActivityTypes::Playing); + break; + // case 1: activity.SetType(discordpp::ActivityTypes::Streaming); break; + case 2: + activity.SetType(discordpp::ActivityTypes::Listening); + break; + case 3: + activity.SetType(discordpp::ActivityTypes::Watching); + break; + case 4: + activity.SetType(discordpp::ActivityTypes::Competing); + break; + // case 5: activity.SetType(discordpp::ActivityTypes::CustomStatus); break; + // case 6: activity.SetType(discordpp::ActivityTypes::HangStatus); break; + // If type is -1 or any other value, default to Playing + default: + break; + } + + jint jStatus = env->GetIntField(jAct, env->GetFieldID(cls, "status", "I")); + switch (jStatus) + { + case 0: + activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::Name); + break; + case 1: + activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::State); + break; + case 2: + activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::Details); + break; + default: + break; // Don't set if -1 + } + + jint jPlatform = env->GetIntField(jAct, env->GetFieldID(cls, "platform", "I")); + switch (jPlatform) + { + case 0: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Android); + break; + case 1: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop); + break; + case 2: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Embedded); + break; + case 3: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::IOS); + break; + case 4: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::PS4); + break; + case 5: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::PS5); + break; + case 6: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Samsung); + break; + case 7: + activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Xbox); + break; + default: + break; + } + + // 3. Assets + discordpp::ActivityAssets assets; + bool hasAnyAsset = false; + + // Handle Large Image Group + std::string li = getJString(env, jAct, "largeImage"); + if (!li.empty()) + { + assets.SetLargeImage(li); + hasAnyAsset = true; + + // Only set metadata if the image itself exists + std::string lt = getJString(env, jAct, "largeText"); + if (!lt.empty()) + assets.SetLargeText(lt); + + std::string lu = getJString(env, jAct, "largeUrl"); + if (!lu.empty()) + assets.SetLargeUrl(lu); + } + + // Handle Small Image Group (Independent of Large Image) + std::string si = getJString(env, jAct, "smallImage"); + if (!si.empty()) + { + assets.SetSmallImage(si); + hasAnyAsset = true; + + // Only set metadata if the image itself exists + std::string st = getJString(env, jAct, "smallText"); + if (!st.empty()) + assets.SetSmallText(st); + + std::string su = getJString(env, jAct, "smallUrl"); + if (!su.empty()) + assets.SetSmallUrl(su); + } + + std::string ci = getJString(env, jAct, "inviteCoverImage"); + if (!ci.empty()) + { + assets.SetInviteCoverImage(ci); + hasAnyAsset = true; + } + + // Only apply assets to the activity if at least one image was set + if (hasAnyAsset) + { + activity.SetAssets(assets); + } + + // 4. Party & Secrets Logic (Independent & Optional) + discordpp::ActivityParty party; + + // Party ID + std::string pId = getJString(env, jAct, "partyId"); + if (!pId.empty()) + { + party.SetId(pId); + + // Current Size + jint pCur = env->GetIntField(jAct, env->GetFieldID(cls, "partyCurrentSize", "I")); + if (pCur != -1) + { + party.SetCurrentSize(pCur); + } + + // Max Size + jint pMax = env->GetIntField(jAct, env->GetFieldID(cls, "partyMaxSize", "I")); + if (pMax != -1) + { + party.SetMaxSize(pMax); + } + + // Privacy + jint pPriv = env->GetIntField(jAct, env->GetFieldID(cls, "partyPrivacy", "I")); + if (pPriv != -1) + { + // 0 = Private, 1 = Public (based on your Java constants) + if (pPriv == 0) + party.SetPrivacy(discordpp::ActivityPartyPrivacy::Private); + else if (pPriv == 1) + party.SetPrivacy(discordpp::ActivityPartyPrivacy::Public); + } + + activity.SetParty(party); + } + + // 5. Timestamps Logic (Independent & Optional) + discordpp::ActivityTimestamps ts; + bool hasTimestamps = false; + + jlong start = env->GetLongField(jAct, env->GetFieldID(cls, "startTimestamp", "J")); + if (start != -1) + { + ts.SetStart(static_cast(start)); + hasTimestamps = true; + } + + jlong end = env->GetLongField(jAct, env->GetFieldID(cls, "endTimestamp", "J")); + if (end != -1) + { + ts.SetEnd(static_cast(end)); + hasTimestamps = true; + } + + if (hasTimestamps) + { + activity.SetTimestamps(ts); + } + + // 6. Buttons + bool hasButton = false; + + std::string b1Label = getJString(env, jAct, "firstButtonLabel"); + std::string b1Url = getJString(env, jAct, "firstButtonUrl"); + + if (!b1Label.empty() && !b1Url.empty()) + { + discordpp::ActivityButton b1; + b1.SetLabel(b1Label); + b1.SetUrl(b1Url); + hasButton = true; + activity.AddButton(b1); + } + + std::string b2Label = getJString(env, jAct, "secondButtonLabel"); + std::string b2Url = getJString(env, jAct, "secondButtonUrl"); + + if (!b2Label.empty() && !b2Url.empty()) + { + discordpp::ActivityButton b2; + b2.SetLabel(b2Label); + b2.SetUrl(b2Url); + hasButton = true; + activity.AddButton(b2); + } + + // Join Secret (Independent) + std::string joinSec = getJString(env, jAct, "joinSecret"); + if (!joinSec.empty() && !hasButton) + { + discordpp::ActivitySecrets secrets; + secrets.SetJoin(joinSec); + activity.SetSecrets(secrets); + } + + g_client->UpdateRichPresence(activity, [globalObj](const discordpp::ClientResult &r) + { + std::cout << r.ToString() << std::endl; + if (r.Successful()) sendToJavaThreadSafe(globalObj, "Update Sent"); + else sendToJavaThreadSafe(globalObj, "Update Failed"); + JNIEnv* t_env; + if (g_jvm->GetEnv((void**)&t_env, JNI_VERSION_1_6) == JNI_OK) { + t_env->DeleteGlobalRef(globalObj); + } }); + } + + JNIEXPORT void JNICALL Java_com_leohabrom_discord_DiscordBridge_runCallbacks(JNIEnv *env, jobject obj) + { + discordpp::RunCallbacks(); + } +} \ No newline at end of file diff --git a/DiscordBridge.java b/DiscordBridge.java new file mode 100644 index 0000000..fa4903a --- /dev/null +++ b/DiscordBridge.java @@ -0,0 +1,36 @@ +package com.leohabrom.discord; +import com.leohabrom.discord.DiscordBridgeAdapter; + +public class DiscordBridge { + static { + System.loadLibrary("discord_bridge"); + } + + public native void init(long appId); + public native void update(DiscordActivity activity); + public native void runCallbacks(); + private DiscordBridgeAdapter adapter = null; + + public void start() { + Thread heartbeat = new Thread(() -> { + try { + while (!Thread.currentThread().isInterrupted()) { + runCallbacks(); + Thread.sleep(16); + } + } catch (InterruptedException e) {} + }); + heartbeat.setDaemon(true); + heartbeat.start(); + } + + + + public void setAdapter(DiscordBridgeAdapter adapter) { + this.adapter = adapter; + } + + public void onMessage(String string) { + if (adapter!=null) adapter.onMessage(string); + } +} \ No newline at end of file diff --git a/DiscordBridgeAdapter.java b/DiscordBridgeAdapter.java new file mode 100644 index 0000000..315ef30 --- /dev/null +++ b/DiscordBridgeAdapter.java @@ -0,0 +1,5 @@ +package com.leohabrom.discord; + +public interface DiscordBridgeAdapter { + void onMessage(String message); +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..002a7f3 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +``` +export DISCORD_SDK= +./export.sh +``` \ No newline at end of file diff --git a/export.sh b/export.sh new file mode 100755 index 0000000..5227566 --- /dev/null +++ b/export.sh @@ -0,0 +1,20 @@ +rm *.class + +javac -g -d . DiscordActivity.java DiscordActivityBuilder.java DiscordBridgeAdapter.java DiscordBridge.java + +jar cvf out/lib/DiscordLib.jar com/ DiscordActivity.java DiscordActivityBuilder.java DiscordBridgeAdapter.java DiscordBridge.java +javac -h . DiscordActivity.java DiscordActivityBuilder.java DiscordBridgeAdapter.java DiscordBridge.java + + +g++ -shared -fPIC \ +-I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \ +-I"$DISCORD_SDK/include" \ +DiscordBridge.cpp \ +-L"$DISCORD_SDK/lib/release" \ +-ldiscord_partner_sdk \ +-Wl,-rpath,'$ORIGIN' \ +-o out/lib/libdiscord_bridge.so + +cp "$DISCORD_SDK/lib/release/libdiscord_partner_sdk.so" out/lib/ + +zip out/libraries.zip out/lib/DiscordLib.jar out/lib/libdiscord_bridge.so out/lib/libdiscord_partner_sdk.so \ No newline at end of file