diff options
author | syeyoung <cyong06@naver.com> | 2021-08-10 23:35:14 +0900 |
---|---|---|
committer | syeyoung <cyong06@naver.com> | 2021-08-10 23:35:14 +0900 |
commit | a48cb5c96e16fae589cb7d4f09146a17b3787924 (patch) | |
tree | f96af8b3c438357dad8ea6e0b223ce5351e82212 /src/main/java/kr/syeyoung/dungeonsguide/rpc | |
parent | 3f2a8ec601bdc43bbb2a48ef61e372e4c907ce8d (diff) | |
download | Skyblock-Dungeons-Guide-a48cb5c96e16fae589cb7d4f09146a17b3787924.tar.gz Skyblock-Dungeons-Guide-a48cb5c96e16fae589cb7d4f09146a17b3787924.tar.bz2 Skyblock-Dungeons-Guide-a48cb5c96e16fae589cb7d4f09146a17b3787924.zip |
Better Rich Presence
Diffstat (limited to 'src/main/java/kr/syeyoung/dungeonsguide/rpc')
4 files changed, 404 insertions, 0 deletions
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordActivity.java b/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordActivity.java new file mode 100644 index 00000000..f25022f2 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordActivity.java @@ -0,0 +1,72 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.rpc; + +import kr.syeyoung.dungeonsguide.gamesdk.GameSDK; +import kr.syeyoung.dungeonsguide.gamesdk.jna.datastruct.DiscordActivity; +import kr.syeyoung.dungeonsguide.gamesdk.jna.enumuration.EDiscordActivityType; +import lombok.Data; + +@Data +public class JDiscordActivity { + private EDiscordActivityType activityType; + private long applicationId; + private String name, state, details; + private long start, end; + private String largeImage, largeText, smallImage, smallText; + private String partyId; + private int partyCurr, partyMax; + private String matchSecret, joinSecret, spectateSecret; + private boolean instance; + + public static JDiscordActivity fromJNA(DiscordActivity discordActivity) { + if (discordActivity == null) return null; + JDiscordActivity jDiscordActivity = new JDiscordActivity(); + jDiscordActivity.activityType = discordActivity.activityType; + jDiscordActivity.applicationId = discordActivity.applicationId.longValue(); + jDiscordActivity.name = GameSDK.readString(discordActivity.name); + jDiscordActivity.state = GameSDK.readString(discordActivity.state); + jDiscordActivity.details = GameSDK.readString(discordActivity.details); + if (discordActivity.assets != null) { + jDiscordActivity.largeImage = GameSDK.readString(discordActivity.assets.large_image); + jDiscordActivity.largeText = GameSDK.readString(discordActivity.assets.large_text); + jDiscordActivity.smallImage = GameSDK.readString(discordActivity.assets.small_image); + jDiscordActivity.smallText = GameSDK.readString(discordActivity.assets.small_text); + } + if (discordActivity.timestamps != null) { + jDiscordActivity.start = discordActivity.timestamps.start.longValue(); + jDiscordActivity.end = discordActivity.timestamps.end.longValue(); + } + if (discordActivity.secrets != null) { + jDiscordActivity.matchSecret= GameSDK.readString(discordActivity.secrets.match); + jDiscordActivity.joinSecret = GameSDK.readString(discordActivity.secrets.join); + jDiscordActivity.spectateSecret = GameSDK.readString(discordActivity.secrets.spectate); + } + if (discordActivity.party != null) { + jDiscordActivity.partyId = GameSDK.readString(discordActivity.party.id); + if (discordActivity.party.discordActivityParty != null) { + jDiscordActivity.partyCurr = discordActivity.party.discordActivityParty.current_size.intValue(); + jDiscordActivity.partyMax = discordActivity.party.discordActivityParty.max_size.intValue(); + } + } + jDiscordActivity.instance = discordActivity.instance; + + return jDiscordActivity; + } +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordRelation.java b/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordRelation.java new file mode 100644 index 00000000..3086c936 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordRelation.java @@ -0,0 +1,42 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.rpc; + +import kr.syeyoung.dungeonsguide.gamesdk.jna.datastruct.DiscordRelationship; +import kr.syeyoung.dungeonsguide.gamesdk.jna.enumuration.EDiscordRelationshipType; +import kr.syeyoung.dungeonsguide.gamesdk.jna.enumuration.EDiscordStatus; +import lombok.Data; + +@Data +public class JDiscordRelation { + private EDiscordRelationshipType discordRelationshipType; + private EDiscordStatus status; + private JDiscordActivity discordActivity; + private JDiscordUser discordUser; + + public static JDiscordRelation fromJNA(DiscordRelationship relationship) { + JDiscordRelation jDiscordRelation = new JDiscordRelation(); + jDiscordRelation.discordUser = JDiscordUser.fromJNA(relationship.user); + jDiscordRelation.discordActivity = JDiscordActivity.fromJNA(relationship.presence.activity); + jDiscordRelation.status = relationship.presence.status; + jDiscordRelation.discordRelationshipType = relationship.type; + return jDiscordRelation; + } + +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordUser.java b/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordUser.java new file mode 100644 index 00000000..635ac405 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/rpc/JDiscordUser.java @@ -0,0 +1,40 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.rpc; + +import kr.syeyoung.dungeonsguide.gamesdk.GameSDK; +import kr.syeyoung.dungeonsguide.gamesdk.jna.datastruct.DiscordUser; +import lombok.Data; + +@Data +public class JDiscordUser { + private long id; + private String username, discriminator, avatar; + private boolean bot; + + public static JDiscordUser fromJNA(DiscordUser discordUser) { + JDiscordUser jDiscordUser = new JDiscordUser(); + jDiscordUser.id = discordUser.id.longValue(); + jDiscordUser.username = GameSDK.readString(discordUser.username); + jDiscordUser.discriminator = GameSDK.readString(discordUser.discriminator); + jDiscordUser.avatar = GameSDK.readString(discordUser.avatar); + jDiscordUser.bot = discordUser.bot; + return jDiscordUser; + } +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/rpc/RichPresenceManager.java b/src/main/java/kr/syeyoung/dungeonsguide/rpc/RichPresenceManager.java new file mode 100644 index 00000000..23f8433d --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/rpc/RichPresenceManager.java @@ -0,0 +1,250 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.rpc; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import kr.syeyoung.dungeonsguide.DungeonsGuide; +import kr.syeyoung.dungeonsguide.SkyblockStatus; +import kr.syeyoung.dungeonsguide.dungeon.DungeonContext; +import kr.syeyoung.dungeonsguide.events.DiscordUserJoinRequestEvent; +import kr.syeyoung.dungeonsguide.events.DiscordUserUpdateEvent; +import kr.syeyoung.dungeonsguide.features.FeatureRegistry; +import kr.syeyoung.dungeonsguide.gamesdk.GameSDK; +import kr.syeyoung.dungeonsguide.gamesdk.jna.NativeGameSDK; +import kr.syeyoung.dungeonsguide.gamesdk.jna.datastruct.*; +import kr.syeyoung.dungeonsguide.gamesdk.jna.enumuration.*; +import kr.syeyoung.dungeonsguide.gamesdk.jna.interfacestruct.*; +import kr.syeyoung.dungeonsguide.gamesdk.jna.typedef.*; +import kr.syeyoung.dungeonsguide.party.PartyManager; +import kr.syeyoung.dungeonsguide.stomp.StompHeader; +import kr.syeyoung.dungeonsguide.stomp.StompPayload; +import lombok.Getter; +import net.minecraftforge.common.MinecraftForge; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +public class RichPresenceManager implements Runnable { + public static RichPresenceManager INSTANCE = new RichPresenceManager(); + private Thread t = new Thread(this); + + public RichPresenceManager() { + t.start(); + } + @Getter + private NativeGameSDK nativeGameSDK; + @Getter + private IDiscordCore iDiscordCore; + private IDiscordActivityManager activityManager; + private IDiscordRelationshipManager relationshipManager; + private IDiscordActivityEvents.ByReference callbacks; + private IDiscordRelationshipEvents.ByReference relation_callbacks; + + @Getter + private Map<Long, JDiscordRelation> relationMap = new HashMap<>(); + + @Getter + private int lastSetupCode = -99999; + public int setup() { + System.out.println("SETUP!!! "+ iDiscordCore); + if (iDiscordCore != null) { + System.out.println("DETROYED"); + iDiscordCore.Destroy.destroy(iDiscordCore); + activityManager = null; callbacks = null; relation_callbacks = null; relationMap.clear(); + } + + nativeGameSDK = GameSDK.getNativeGameSDK(); + if (nativeGameSDK == null) return -9999; + DiscordCreateParams discordCreateParams = new DiscordCreateParams(); + discordCreateParams.flags = new UInt64(EDiscordCreateFlags.DiscordCreateFlags_Default.getValue()); + discordCreateParams.client_id = new DiscordClientID(816298079732498473L); + + callbacks = new IDiscordActivityEvents.ByReference(); + callbacks.OnActivityInvite = (eventData, type, user, activity) -> { + try { + MinecraftForge.EVENT_BUS.post(new DiscordUserJoinRequestEvent(user, true)); + } catch (Throwable t) { + t.printStackTrace(); + } + System.out.println("Received Join Request from "+user.id.longValue()+" ("+GameSDK.readString(user.username)+")"); + }; + callbacks.OnActivityJoin = (eventData, secret) -> { + DungeonsGuide.getDungeonsGuide().getStompConnection().send(new StompPayload().method(StompHeader.SEND) + .header("destination", "/app/party.askedtojoin") + .payload(new JSONObject().put("token", secret).toString())); + System.out.println("Trying to join with token "+secret); + }; + callbacks.OnActivityJoinRequest = (eventData, user) -> { + try { + MinecraftForge.EVENT_BUS.post(new DiscordUserJoinRequestEvent(user, false)); + } catch (Throwable t) { + t.printStackTrace(); + } + System.out.println("Received Join Request from "+user.id.longValue()+" - "+GameSDK.readString(user.username)); + }; + callbacks.OnActivitySpectate = (eventData, secret) -> { + }; + callbacks.write(); + + relation_callbacks = new IDiscordRelationshipEvents.ByReference(); + relation_callbacks.OnRefresh = (p) -> { + IDiscordRelationshipManager iDiscordRelationshipManager = iDiscordCore.GetRelationshipManager.getRelationshipManager(iDiscordCore); + iDiscordRelationshipManager.Filter.filter(iDiscordRelationshipManager, Pointer.NULL, (d, relation) -> true); + IntByReference intByReference = new IntByReference(); + iDiscordRelationshipManager.Count.count(iDiscordRelationshipManager, intByReference); + int count = intByReference.getValue(); + for (int i = 0; i < count; i++) { + DiscordRelationship discordRelationship = new DiscordRelationship(); + iDiscordRelationshipManager.GetAt.getAt(iDiscordRelationshipManager, new UInt32(i), discordRelationship); + + JDiscordRelation jDiscordRelation = JDiscordRelation.fromJNA(discordRelationship); + relationMap.put(jDiscordRelation.getDiscordUser().getId(), jDiscordRelation); + } + }; + relation_callbacks.OnRelationshipUpdate = (p, rel) -> { + JDiscordRelation jDiscordRelation = JDiscordRelation.fromJNA(rel); + JDiscordRelation prev = relationMap.put(jDiscordRelation.getDiscordUser().getId(), jDiscordRelation); + System.out.println("Discord relation update!! "+jDiscordRelation); + try { + MinecraftForge.EVENT_BUS.post(new DiscordUserUpdateEvent(prev, jDiscordRelation)); + } catch (Throwable t) { + t.printStackTrace(); + } + }; + relation_callbacks.write(); + + discordCreateParams.activity_events = callbacks; + discordCreateParams.relationship_events = relation_callbacks; // 96 + + PointerByReference pointerByReference = new PointerByReference(); + discordCreateParams.write(); + + EDiscordResult eDiscordResult = nativeGameSDK.DiscordCreate(new DiscordVersion(NativeGameSDK.DISCORD_VERSION), discordCreateParams, pointerByReference); + if (eDiscordResult != EDiscordResult.DiscordResult_Ok) return eDiscordResult.getValue(); + if (pointerByReference.getValue() == Pointer.NULL) return -9998; + iDiscordCore = new IDiscordCore(pointerByReference.getValue()); + + iDiscordCore.SetLogHook.setLogHook(iDiscordCore, EDiscordLogLevel.DiscordLogLevel_Debug, Pointer.NULL, new IDiscordCore.LogHook() { + @Override + public void hook(Pointer hookData, EDiscordLogLevel level, String message) { + System.out.println(message+" - "+level+" - "+hookData); + } + }); + + activityManager = iDiscordCore.GetActivityManager.getActivityManager(iDiscordCore); + relationshipManager = iDiscordCore.GetRelationshipManager.getRelationshipManager(iDiscordCore); + + return eDiscordResult.getValue(); + } + + private final SkyblockStatus skyblockStatus = DungeonsGuide.getDungeonsGuide().getSkyblockStatus(); + + public void respond(DiscordSnowflake userID, EDiscordActivityJoinRequestReply reply) { + activityManager.SendRequestReply.sendRequestReply(activityManager, userID, reply, Pointer.NULL, (callbackData, result) -> { + System.out.println("Discord Returned "+result+" For Replying "+reply+" To "+userID.longValue()+"L"); + }); + } + + public void accept(DiscordSnowflake userID) { + activityManager.AcceptInvite.acceptInvite(activityManager, userID, Pointer.NULL, (callbackData, result) -> { + System.out.println("Discord Returned "+result+" For Accepting invite from "+userID.longValue()+"L"); + }); + } + public void updatePresence() { + if (!skyblockStatus.isOnHypixel() || !FeatureRegistry.DISCORD_RICHPRESENCE.isEnabled() || (!skyblockStatus.isOnSkyblock() && FeatureRegistry.DISCORD_RICHPRESENCE.<Boolean>getParameter("disablenotskyblock").getValue())) { + activityManager.ClearActivity.clearActivity(activityManager, Pointer.NULL, new NativeGameSDK.DiscordCallback() { + @Override + public void callback(Pointer callbackData, EDiscordResult result) { + } + }); + } else { + String name = skyblockStatus.getDungeonName() == null ? "" : skyblockStatus.getDungeonName(); + if (!skyblockStatus.isOnSkyblock()) name ="Somewhere on Hypixel"; + if (name.trim().equals("Your Island")) name = "Private Island"; + + DiscordActivity latestDiscordActivity = new DiscordActivity(); + latestDiscordActivity.assets = new DiscordActivityAssets(); + latestDiscordActivity.secrets = new DiscordActivitySecrets(); + latestDiscordActivity.party = new DiscordActivityParty(); + latestDiscordActivity.party.discordActivityParty = new DiscordPartySize(); + latestDiscordActivity.timestamps = new DiscordActivityTimestamps(); + GameSDK.writeString(latestDiscordActivity.assets.large_image, "mort"); + GameSDK.writeString(latestDiscordActivity.assets.large_text, "mort"); + GameSDK.writeString(latestDiscordActivity.state, name); + + GameSDK.writeString(latestDiscordActivity.party.id, PartyManager.INSTANCE.getPartyID() == null ? "" : PartyManager.INSTANCE.getPartyID()); + latestDiscordActivity.party.discordActivityParty.current_size = new Int32(PartyManager.INSTANCE.getMemberCount()); + latestDiscordActivity.party.discordActivityParty.max_size = new Int32(PartyManager.INSTANCE.getMaxParty()); + + if (skyblockStatus.getContext() != null) { + DungeonContext dungeonContext = skyblockStatus.getContext(); + long init = dungeonContext.getInit(); + latestDiscordActivity.timestamps.start = new DiscordTimestamp(init); + + if (dungeonContext.getBossfightProcessor() != null) { + GameSDK.writeString(latestDiscordActivity.details, "Fighting "+dungeonContext.getBossfightProcessor().getBossName()+": "+dungeonContext.getBossfightProcessor().getCurrentPhase()); + } else { + GameSDK.writeString(latestDiscordActivity.details, "Clearing Rooms"); + } + } else { + latestDiscordActivity.timestamps.start = new DiscordTimestamp(0); + GameSDK.writeString(latestDiscordActivity.details, "Dungeons Guide"); + } + if (PartyManager.INSTANCE.isAllowAskToJoin()) { + GameSDK.writeString(latestDiscordActivity.secrets.join, PartyManager.INSTANCE.getAskToJoinSecret()); + } else { + GameSDK.writeString(latestDiscordActivity.secrets.join, ""); + } + activityManager.UpdateActivity.updateActivity(activityManager, latestDiscordActivity, Pointer.NULL, new NativeGameSDK.DiscordCallback() { + @Override + public void callback(Pointer callbackData, EDiscordResult result) { + } + }); + } + } + + @Override + public void run() { + boolean setup = true; + int counter = 0; + while(!Thread.interrupted()) { + try { + if (iDiscordCore == null || setup) { + lastSetupCode = setup(); + System.out.println("Discord returned "+lastSetupCode+" for setup "+EDiscordResult.fromValue(lastSetupCode)); + setup = lastSetupCode != EDiscordResult.DiscordResult_Ok.getValue(); + counter = 0; + } else { + EDiscordResult eDiscordResult = iDiscordCore.RunCallbacks.runCallbacks(iDiscordCore); + if (eDiscordResult == EDiscordResult.DiscordResult_NotRunning) { + setup = true; + } else { + if (counter == 0) + updatePresence(); + if (++counter == 15) counter = 0; + } + } + Thread.sleep(16L); + } catch (Exception e) {e.printStackTrace();} + } + } +} |