From d8e577a57c3dac541c6ee88215ec0f5ab24bac20 Mon Sep 17 00:00:00 2001 From: Nathaniel Tseng Date: Wed, 24 Jul 2024 14:05:53 -0700 Subject: [PATCH] remove discord integration --- pom.xml | 8 +- .../chronoMods/network/NetworkHelper.java | 106 +++--- .../network/discord/DiscordIntegration.java | 357 ------------------ .../network/discord/DiscordLobby.java | 288 -------------- .../network/discord/DiscordPlayer.java | 287 -------------- .../chronoMods/ui/lobby/MainLobbyScreen.java | 26 +- .../chronoMods/ui/lobby/NewGameScreen.java | 42 +-- .../chrono/images/Discord-Logo-Color-92px.png | Bin 3107 -> 0 bytes 8 files changed, 81 insertions(+), 1033 deletions(-) delete mode 100644 src/main/java/chronoMods/network/discord/DiscordIntegration.java delete mode 100644 src/main/java/chronoMods/network/discord/DiscordLobby.java delete mode 100644 src/main/java/chronoMods/network/discord/DiscordPlayer.java delete mode 100644 src/main/resources/chrono/images/Discord-Logo-Color-92px.png diff --git a/pom.xml b/pom.xml index 66ea104..799a776 100755 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,6 @@ 4.0.0 3.1.11 1.2 - v0.5.5 @@ -60,11 +59,6 @@ system ${ascension-plus-jar-path} - - com.github.JnCrMx - discord-game-sdk4j - ${discord-game-sdk4j.version} - @@ -108,4 +102,4 @@ - \ No newline at end of file + diff --git a/src/main/java/chronoMods/network/NetworkHelper.java b/src/main/java/chronoMods/network/NetworkHelper.java index 9bf4614..e1963b9 100644 --- a/src/main/java/chronoMods/network/NetworkHelper.java +++ b/src/main/java/chronoMods/network/NetworkHelper.java @@ -14,7 +14,6 @@ import chronoMods.coop.infusions.InfusionReward; import chronoMods.coop.infusions.InfusionSet; import chronoMods.coop.relics.*; -import chronoMods.network.discord.DiscordIntegration; import chronoMods.network.steam.SteamIntegration; import chronoMods.ui.deathScreen.EndScreenBingoVictory; import chronoMods.ui.deathScreen.EndScreenCoopLoss; @@ -70,9 +69,7 @@ import java.util.Map; public class NetworkHelper { - public static chronoMods.network.steam.SteamIntegration steam; - //public static DiscordIntegration discord; public static ArrayList networks = new ArrayList(); public static ArrayList lobbies = new ArrayList(); @@ -93,16 +90,6 @@ public static void initialize() { } else { TogetherManager.log("Steam Integration not found."); } - // If Discord available, add DiscordIntegration - DiscordIntegration discord = new DiscordIntegration(); - discord.initialize(); - if (discord.isInitialized()) { - TogetherManager.log("Discord Started."); - networks.add(discord); - } - else { - TogetherManager.log("Discord Integration not found."); - } } @SpirePatch(clz=CardCrawlGame.class, method="update") @@ -123,8 +110,7 @@ public static void update() { service().getPacket(packet); while (packet.hasPacket()) { - - if (TogetherManager.currentLobby != null) + if (TogetherManager.currentLobby != null) parseData(packet.data(), packet.player()); else return; @@ -133,7 +119,7 @@ public static void update() { service().getPacket(packet); else return; - } + } } public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { @@ -370,7 +356,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { case SendCard: // Unused // TogetherManager.log("Send card direct: " + stringOuts); // AbstractDungeon.player.masterDeck.addToTop(CardLibrary.getCopy(stringOuts, upgrades, miscs)); - break; + break; case SendCardGhost: if (playerInfo.isUser(TogetherManager.currentUser)) { return; } @@ -402,7 +388,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { } GhostWriter.rareCards.removeCard(removeMe); } else { - GhostWriter.rareCards.addToBottom(ghostOutCard); + GhostWriter.rareCards.addToBottom(ghostOutCard); } break; @@ -502,7 +488,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { // Obtain the potion if (AbstractDungeon.player.potions.size() > potslotb) - if (AbstractDungeon.player.potions.get(potslotb) instanceof PotionSlot) + if (AbstractDungeon.player.potions.get(potslotb) instanceof PotionSlot) AbstractDungeon.player.obtainPotion(potslotb, PotionHelper.getPotion(stringOutb)); break; @@ -546,12 +532,12 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { // Unlocks a room we are leaving CoopEmptyRoom.LockedRoomField.locked.set(currentNodec.getRoom(), false); - + // Sets the next room of a multi-room AbstractRoom secondRoom = CoopMultiRoom.secondRoomField.secondRoom.get(currentNodec); AbstractRoom thirdRoom = CoopMultiRoom.thirdRoomField.thirdRoom.get(currentNodec); - // Resolve the multinodes by advancing the 'queue' + // Resolve the multinodes by advancing the 'queue' currentNodec.room = secondRoom; CoopMultiRoom.secondRoomField.secondRoom.set(currentNodec, thirdRoom); CoopMultiRoom.thirdRoomField.thirdRoom.set(currentNodec, null); @@ -648,13 +634,13 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { AbstractDungeon.topLevelEffects.add(new ShowCardAndObtainEffect( new Tombstone(playerInfo.userName, "", playerInfo.getPortrait()), Settings.WIDTH / 2.0F, Settings.HEIGHT / 2.0F)); if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) { - AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, "", playerInfo.getPortrait()), 1)); + AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, "", playerInfo.getPortrait()), 1)); } } else { AbstractDungeon.topLevelEffects.add(new ShowCardAndObtainEffect( new Tombstone(playerInfo.userName, MonsterHelper.getEncounterName(killedBy), playerInfo.getPortrait()), Settings.WIDTH / 2.0F, Settings.HEIGHT / 2.0F)); if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) { - AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, MonsterHelper.getEncounterName(killedBy), playerInfo.getPortrait()), 1)); + AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, MonsterHelper.getEncounterName(killedBy), playerInfo.getPortrait()), 1)); } } } else if (AbstractDungeon.player.hasBlight("ChainsOfFate")) { @@ -685,13 +671,13 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { AbstractDungeon.topLevelEffects.add(new ShowCardAndObtainEffect( new Tombstone(playerInfo.userName, "", playerInfo.getPortrait()), Settings.WIDTH / 2.0F, Settings.HEIGHT / 2.0F)); if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) { - AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, "", playerInfo.getPortrait()), 1)); + AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, "", playerInfo.getPortrait()), 1)); } } else { AbstractDungeon.topLevelEffects.add(new ShowCardAndObtainEffect( new Tombstone(playerInfo.userName, MonsterHelper.getEncounterName(killedBy), playerInfo.getPortrait()), Settings.WIDTH / 2.0F, Settings.HEIGHT / 2.0F)); if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) { - AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, MonsterHelper.getEncounterName(killedBy), playerInfo.getPortrait()), 1)); + AbstractDungeon.actionManager.addToBottom((AbstractGameAction)new MakeTempCardInHandAction(new Tombstone(playerInfo.userName, MonsterHelper.getEncounterName(killedBy), playerInfo.getPortrait()), 1)); } } } @@ -719,7 +705,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { kickID = playerkick; } if (kickID != null) - removePlayer(kickID); + removePlayer(kickID); } break; @@ -732,7 +718,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { } if (TogetherManager.currentUser.isUser(steamIDrk) && !Settings.hasRubyKey) { - AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.RED)); + AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.RED)); AbstractDungeon.topLevelEffects.add(new SpeechTextEffect(Settings.WIDTH/2.0f, Settings.HEIGHT/2.0f, 5f, "#r" + playerInfo.userName + CardCrawlGame.languagePack.getUIString("Keys").TEXT[0], DialogWord.AppearEffect.FADE_IN)); } @@ -746,7 +732,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { } if (TogetherManager.currentUser.isUser(steamIDbk) && !Settings.hasSapphireKey) { - AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.BLUE)); + AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.BLUE)); AbstractDungeon.topLevelEffects.add(new SpeechTextEffect(Settings.WIDTH/2.0f, Settings.HEIGHT/2.0f, 5f, "#b" + playerInfo.userName + CardCrawlGame.languagePack.getUIString("Keys").TEXT[1], DialogWord.AppearEffect.FADE_IN)); } @@ -760,7 +746,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { } if (TogetherManager.currentUser.isUser(steamIDgk) && !Settings.hasEmeraldKey) { - AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.GREEN)); + AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.GREEN)); AbstractDungeon.topLevelEffects.add(new SpeechTextEffect(Settings.WIDTH/2.0f, Settings.HEIGHT/2.0f, 5f, "#g" + playerInfo.userName + CardCrawlGame.languagePack.getUIString("Keys").TEXT[2], DialogWord.AppearEffect.FADE_IN)); } @@ -847,7 +833,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { removeMeFromDeck = c; } playerInfo.deck.removeCard(removeMeFromDeck); - } else { + } else { playerInfo.deck.addToBottom(deckInfoOutCard); } @@ -886,11 +872,11 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { // Roll for cards CardGroup anyCard = new CardGroup(CardGroup.CardGroupType.UNSPECIFIED); - + for (Map.Entry c : CardLibrary.cards.entrySet()) { if (((AbstractCard)c.getValue()).color == playerInfo.character.getCardColor() && ((AbstractCard)c.getValue()).rarity == rare) - anyCard.addToBottom(((AbstractCard)c.getValue()).makeCopy()); - } + anyCard.addToBottom(((AbstractCard)c.getValue()).makeCopy()); + } anyCard.shuffle(AbstractDungeon.cardRng); // Create RewardItem and make sure there's no dupes @@ -899,9 +885,9 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { int numCards = 3; for (AbstractRelic r : AbstractDungeon.player.relics) - numCards = r.changeNumberOfCardsInReward(numCards); + numCards = r.changeNumberOfCardsInReward(numCards); if (ModHelper.isModEnabled("Binary")) - numCards--; + numCards--; for (int i = 0; i < numCards; i++) { boolean containsDupe = true; AbstractCard card = null; @@ -911,11 +897,11 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { for (AbstractCard c : transferItemBooster.cards) { if (c.cardID.equals(card.cardID)) containsDupe = true; - } + } } - if (card != null) + if (card != null) transferItemBooster.cards.add(card); - } + } // Hardcoded relic shit because that's how we roll now if (AbstractDungeon.player.hasBlight("PneumaticPost")) @@ -1091,7 +1077,7 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { // Get the set and add 3 packages InfusionSet infSet = InfusionHelper.getSetByID(stringOutInfuse); - + for (int i = 0; i < 3; i++) TogetherManager.getCurrentUser().packages.add(new InfusionReward(infSet.getRandomInfusion())); @@ -1111,12 +1097,12 @@ public static void parseData(ByteBuffer data, RemotePlayer playerInfo) { public static enum dataType { Rules, Start, Ready, Version, Floor, Act, Hp, Money, BossRelic, Finish, SendCard, SendCardGhost, TransferCard, TransferRelic, TransferPotion, UsePotion, SendPotion, EmptyRoom, BossChosen, Splits, SetDisplayRelics, ClearRoom, LockRoom, ChooseNeow, ChooseTeamRelic, LoseLife, Kick, GetRedKey, GetBlueKey, GetGreenKey, Character, GetPotion, AddPotionSlot, SendRelic, ModifyBrainFreeze, DrawMap, ClearMap, DeckInfo, RelicInfo, RequestVersion, SendCardMessageBottle, AtDoor, Victory, TransferBooster, Bingo, BingoRules, TeamChange, BingoCard, TeamName, CustomMark, LastBoss, SendMessage, BluntScissorCard, MergeUncommon, Infusion, HeartChoice; - + private dataType() {} } public static void sendData(NetworkHelper.dataType type) { - ByteBuffer data = NetworkHelper.generateData(type); + ByteBuffer data = NetworkHelper.generateData(type); if (data == null) { return; } service().sendPacket(data); @@ -1281,7 +1267,7 @@ private static ByteBuffer generateData(NetworkHelper.dataType type) { data.put(rewardghost.getBytes()); ((Buffer)data).rewind(); - GhostWriter.sendCard = null; + GhostWriter.sendCard = null; break; case SendCardMessageBottle: CardDataBuffer messageCard = new CardDataBuffer(MessageInABottle.sendCard); @@ -1292,8 +1278,8 @@ private static ByteBuffer generateData(NetworkHelper.dataType type) { data.put(messageCard.getBytes()); ((Buffer)data).rewind(); - MessageInABottle.sendCard = null; - break; + MessageInABottle.sendCard = null; + break; case TransferCard: CardDataBuffer rewardc = new CardDataBuffer(TogetherManager.courierScreen.transferCard); @@ -1305,7 +1291,7 @@ private static ByteBuffer generateData(NetworkHelper.dataType type) { data.put(rewardc.getBytes()); ((Buffer)data).rewind(); - TogetherManager.courierScreen.transferCard = null; + TogetherManager.courierScreen.transferCard = null; break; case TransferRelic: String rewardr = TogetherManager.courierScreen.transferRelic.relicId; @@ -1318,7 +1304,7 @@ private static ByteBuffer generateData(NetworkHelper.dataType type) { data.put(rewardr.getBytes()); ((Buffer)data).rewind(); - TogetherManager.courierScreen.transferRelic = null; + TogetherManager.courierScreen.transferRelic = null; break; case TransferPotion: String rewardp = TogetherManager.courierScreen.transferPotion.ID; @@ -1331,7 +1317,7 @@ private static ByteBuffer generateData(NetworkHelper.dataType type) { data.put(rewardp.getBytes()); ((Buffer)data).rewind(); - TogetherManager.courierScreen.transferPotion = null; + TogetherManager.courierScreen.transferPotion = null; break; case UsePotion: data = ByteBuffer.allocateDirect(8); @@ -1468,8 +1454,8 @@ else if (AbstractDungeon.player.hasBlight("ChainsOfFate")) { int upgraded = 0; for (AbstractCard cup : AbstractDungeon.player.masterDeck.group) { - upgraded += cup.timesUpgraded; - } + upgraded += cup.timesUpgraded; + } data.putInt(8, upgraded); ((Buffer)data).position(12); @@ -1544,7 +1530,7 @@ else if (AbstractDungeon.player.hasBlight("ChainsOfFate")) { } } break; - case CustomMark: + case CustomMark: byte[] bytes = new FileHandle(TogetherManager.config.getString("mark")).readBytes(); data = ByteBuffer.allocateDirect(4 + bytes.length); @@ -1587,7 +1573,7 @@ else if (AbstractDungeon.player.hasBlight("ChainsOfFate")) { data.put(mergeCard.getBytes()); ((Buffer)data).rewind(); - BluntScissors.cardSent = null; + BluntScissors.cardSent = null; break; case MergeUncommon: AbstractCard cu = AbstractDungeon.player.masterDeck.group.get(AbstractDungeon.player.masterDeck.group.size()-1); @@ -1603,7 +1589,7 @@ else if (AbstractDungeon.player.hasBlight("ChainsOfFate")) { case Infusion: data = ByteBuffer.allocateDirect(12 + TransfusionBag.set.setID.getBytes().length); data.putLong(4, TogetherManager.courierScreen.getRecipient().getAccountID()); // Selected recipient - + ((Buffer)data).position(12); data.put(TransfusionBag.set.setID.getBytes()); ((Buffer)data).rewind(); @@ -1626,12 +1612,12 @@ else if (AbstractDungeon.player.hasBlight("ChainsOfFate")) { } public static Integration service() { - if (TogetherManager.currentLobby == null) { - return null; + if (TogetherManager.currentLobby == null) { + return null; } if (TogetherManager.currentLobby.service == null) { - return null; + return null; } return TogetherManager.currentLobby.service; @@ -1689,7 +1675,7 @@ public static void getLobbies() { service.getLobbies(); } - public static void addPlayer(RemotePlayer player) { + public static void addPlayer(RemotePlayer player) { // Make sure we're not adding a dupe for (RemotePlayer oldplayer : TogetherManager.players) if (oldplayer.isUser(player)) @@ -1700,7 +1686,7 @@ public static void addPlayer(RemotePlayer player) { // TopPanelPlayerPanels.playerWidgets.add(new BingoPlayerWidget(player)); // else // TopPanelPlayerPanels.playerWidgets.add(new RemotePlayerWidget(player)); - + TogetherManager.log("Member joined: " + player.userName); } @@ -1726,12 +1712,12 @@ public static void removePlayer(RemotePlayer player) { // Unlocks a room we are leaving CoopEmptyRoom.LockedRoomField.locked.set(currentNodec.getRoom(), false); - + // Sets the next room of a multi-room AbstractRoom secondRoom = CoopMultiRoom.secondRoomField.secondRoom.get(currentNodec); AbstractRoom thirdRoom = CoopMultiRoom.thirdRoomField.thirdRoom.get(currentNodec); - // Resolve the multinodes by advancing the 'queue' + // Resolve the multinodes by advancing the 'queue' currentNodec.room = secondRoom; CoopMultiRoom.secondRoomField.secondRoom.set(currentNodec, thirdRoom); CoopMultiRoom.thirdRoomField.thirdRoom.set(currentNodec, null); @@ -1752,7 +1738,7 @@ public static void removePlayer(RemotePlayer player) { // Advance pas tthe heart boolean passHeart = true; - for (RemotePlayer r: TogetherManager.players) + for (RemotePlayer r: TogetherManager.players) if (!r.victory) passHeart = false; diff --git a/src/main/java/chronoMods/network/discord/DiscordIntegration.java b/src/main/java/chronoMods/network/discord/DiscordIntegration.java deleted file mode 100644 index fbad734..0000000 --- a/src/main/java/chronoMods/network/discord/DiscordIntegration.java +++ /dev/null @@ -1,357 +0,0 @@ -package chronoMods.network.discord; - -import chronoMods.TogetherManager; -import chronoMods.network.Integration; -import chronoMods.network.NetworkHelper; -import chronoMods.network.Packet; -import chronoMods.network.RemotePlayer; -import chronoMods.ui.lobby.NewScreenUpdateRender; -import chronoMods.ui.mainMenu.NewMenuButtons; -import com.badlogic.gdx.graphics.Texture; -import com.evacipated.cardcrawl.modthespire.lib.SpirePatch; -import com.megacrit.cardcrawl.core.CardCrawlGame; -import com.megacrit.cardcrawl.helpers.ImageMaster; -import de.jcm.discordgamesdk.*; -import de.jcm.discordgamesdk.lobby.LobbySearchQuery; -import de.jcm.discordgamesdk.lobby.LobbyTransaction; -import de.jcm.discordgamesdk.lobby.LobbyType; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.ConcurrentLinkedQueue; - -public class DiscordIntegration implements Integration { - public boolean initialized = false; - public Texture logo; - public CreateParams createParams; - public Core core; - public String ourRoute; - public static List instances = new ArrayList<>(); - //public final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - //public ScheduledFuture callbacksExecutor; - public DiscordEventHandler eventHandler = new DiscordEventHandler(); - public ConcurrentLinkedQueue incomingMessages = new ConcurrentLinkedQueue<>(); - boolean needsFlush = false; - - public static ConcurrentLinkedQueue postedRunnables = new ConcurrentLinkedQueue<>(); - - @SpirePatch(clz= CardCrawlGame.class, method="update") - public static class DiscordUpdate - { - public static void Prefix(CardCrawlGame __instance) { - DiscordIntegration.runCallbacks(); - } - public static void Postfix(CardCrawlGame __instance) - { - DiscordIntegration.flushNetwork(); - } - } - - /// Posted runnables will run once on the main thread, after runCallbacks() but before flushNetwork() - public static void postRunnable(Runnable r) { - postedRunnables.add(r); - } - - public static void runCallbacks() { - if (instances.size() > 1) { - TogetherManager.log("Multiple instances of DiscordIntegration!"); - } - instances.forEach(i -> i.core.runCallbacks()); - while (!postedRunnables.isEmpty()) { - postedRunnables.poll().run(); - } - } - - public static void flushNetwork() { - instances.forEach(i -> i.core.networkManager().flush()); - } - - public File extractDiscordNative() throws IOException, UnsupportedOperationException, RuntimeException { - // Code modified from https://github.com/JnCrMx/discord-game-sdk4j/blob/d9f40b3e3f2772e1daa680a631f5b2f3ee6186ae/src/main/java/de/jcm/discordgamesdk/Core.java#L46 - // which is licensed under the MIT license: - /* - MIT License - - Copyright (c) 2020 JCM - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ - - Path tempDir = Files.createTempDirectory("StSTogetherDiscordNative"); - tempDir.toFile().deleteOnExit(); - String name = "discord_game_sdk"; - String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); - String arch = System.getProperty("os.arch").toLowerCase(Locale.ROOT); - String objectName; - - if(osName.contains("windows")) - { - osName = "windows"; - objectName = name + ".dll"; - } - else if(osName.contains("linux")) - { - osName = "linux"; - objectName = name + ".so"; - } - else - { - throw new UnsupportedOperationException("OS not supported by Discord library"); - } - String path = "/discordNative/"+arch+"/"+objectName; - InputStream in = DiscordIntegration.class.getResourceAsStream(path); - if(in == null) - throw new RuntimeException(new FileNotFoundException("cannot find native library at "+path)); - File discordNativeFile = tempDir - .resolve(objectName) - .toFile(); - discordNativeFile.deleteOnExit(); - Files.copy(in, discordNativeFile.toPath()); - return discordNativeFile; - } - @Override - public void initialize() { - //TogetherManager.log("DiscordIntegration.initialize()"); - logo = ImageMaster.loadImage("chrono/images/Discord-Logo-Color-92px.png"); - //if (callbacksExecutor != null && !callbacksExecutor.isCancelled()) callbacksExecutor.cancel(false); - try { - Core.init(extractDiscordNative()); - if (createParams != null) { - createParams.close(); - } - createParams = new CreateParams(); - createParams.setClientID(850108081882136606L); // App ID for Slay the Spire - createParams.setFlags(1L); // NoRequireDiscord - createParams.registerEventHandler(eventHandler); - eventHandler.removeAllListeners(); - eventHandler.addListener(new DiscordEventAdapter() { - @Override - public void onRouteUpdate(String routeData) { - ourRoute = routeData; - } - - @Override - public void onActivityJoin(String secret) { - //TogetherManager.log("onActivityJoin"); - TogetherManager.clearMultiplayerData(); - core.lobbyManager().connectLobbyWithActivitySecret(secret, (result, lobby) -> { - if (result != Result.OK) { - TogetherManager.log(result.name() + " when joining via invite"); - return; - } - DiscordLobby createdLobby = new DiscordLobby(lobby, DiscordIntegration.this); - TogetherManager.currentLobby = createdLobby; - if (TogetherManager.currentLobby.mode.equals("Versus")) - TogetherManager.gameMode = TogetherManager.mode.Versus; - else if (TogetherManager.currentLobby.mode.equals("Bingo")) - TogetherManager.gameMode = TogetherManager.mode.Bingo; - else - TogetherManager.gameMode = TogetherManager.mode.Coop; - - TogetherManager.players = TogetherManager.currentLobby.getLobbyMembers(); - TogetherManager.currentUser = makeCurrentUser(); - createdLobby.setUpNetworking(); - createdLobby.startActivity(); - - NewScreenUpdateRender.joinFlag = true; - NetworkHelper.sendData(NetworkHelper.dataType.Version); - }); - } - }); - this.core = new Core(createParams); - this.core.setLogHook(LogLevel.DEBUG, ((logLevel, s) -> TogetherManager.log(logLevel + ":" + s))); - /* - callbacksExecutor = scheduler.scheduleAtFixedRate( - () -> { - try { - core.runCallbacks(); - - if (needsFlush) { - TogetherManager.log("Flushing network"); - core.networkManager().flush(); - needsFlush = false; - } - } - catch (Exception e) { - TogetherManager.log(e.toString()); - e.printStackTrace(); - } - }, - 0, - 1000 / 15, - TimeUnit.MILLISECONDS - ); - */ - instances.add(this); - //TODO make sure all modules are actually initialized - initialized = true; - } - catch (IOException | UnsatisfiedLinkError e) { - TogetherManager.log(e.toString()); - e.printStackTrace(); - //TODO - } - catch (UnsupportedOperationException e) { - // OS not supported (yet) - TogetherManager.log(e.getMessage()); - } - catch (GameSDKException e) { - TogetherManager.log("Error initializing Discord: " + e); - } - } - - @Override - public boolean isInitialized() { - return initialized; - } - - @Override - public RemotePlayer makeCurrentUser() { - // Discord users currently only exist in the context of a lobby - // And all things that set currentLobby should also add the local player to that lobby - if (TogetherManager.currentLobby instanceof DiscordLobby) { - RemotePlayer player = TogetherManager.players.stream() - .filter(p -> p.isUser(core.userManager().getCurrentUser().getUserId())) - .findAny() - .orElseGet(() -> { - DiscordPlayer p = new DiscordPlayer( - core.userManager().getCurrentUser(), - this, - (DiscordLobby) TogetherManager.currentLobby - ); - if (TogetherManager.config.has("mark")) - p.bingoMark = TogetherManager.customMark; - - NetworkHelper.addPlayer(p); - return p; - }); - - if (TogetherManager.config.has("mark")) - player.bingoMark = TogetherManager.customMark; - - return player; - } - return null; - } - - @Override - public void updateLobbyData() { - // This will probably be handled by NetworkHelper or something interacting directly with a DiscordLobby - } - - @Override - public void createLobby(TogetherManager.mode gameMode) { - LobbyTransaction txn = core.lobbyManager().getLobbyCreateTransaction(); - txn.setType(lobbyPrivate ? LobbyType.PRIVATE : LobbyType.PUBLIC); - txn.setCapacity(gameMode == TogetherManager.mode.Coop ? 6 : 200); - core.lobbyManager().createLobby(txn, ((result, lobby) -> { - if (result != Result.OK) { - //TODO report error somehow - return; - } - DiscordLobby createdLobby = new DiscordLobby(lobby, this); - - TogetherManager.currentLobby = createdLobby; - TogetherManager.currentUser = makeCurrentUser(); - NetworkHelper.updateLobbyData(); - NetworkHelper.addPlayer(TogetherManager.currentUser); - NetworkHelper.sendData(NetworkHelper.dataType.Version); - createdLobby.setUpNetworking(); - createdLobby.startActivity(); - })); - } - public boolean lobbyPrivate = false; - @Override - public void setLobbyPrivate(boolean priv) { - lobbyPrivate = priv; - if (TogetherManager.currentLobby instanceof DiscordLobby) { - TogetherManager.currentLobby.setPrivate(priv); - } - } - - @Override - public void getLobbies() { - //core.runCallbacks(); - LobbySearchQuery query = core.lobbyManager().getSearchQuery(); - query.filter("metadata.mode", LobbySearchQuery.Comparison.EQUAL, LobbySearchQuery.Cast.STRING, TogetherManager.gameMode.toString()); - query.distance(LobbySearchQuery.Distance.GLOBAL); - core.lobbyManager().search(query, result -> { - if (result != Result.OK) return; //TODO error handling - for (de.jcm.discordgamesdk.lobby.Lobby l : core.lobbyManager().getLobbies()) { - NetworkHelper.lobbies.add(new DiscordLobby(l, this)); - } - // the Steam version does this inside the loop, but I don't see why - NewMenuButtons.lobbyScreen.createFreshGameList(); - }); - } - - @Override - public void getPacket(Packet packet) { - try { - //core.runCallbacks(); - } - catch (Exception e) { - TogetherManager.log(e.toString()); - e.printStackTrace(); - } - Packet p = incomingMessages.poll(); - if (p == null) packet.clear(); - else packet.set(p.player(), p.data()); - return; - } - - @Override - public void sendPacket(ByteBuffer data) { - //TogetherManager.log("sendPacket input is " + data.order()); - for (RemotePlayer p : TogetherManager.players) { - if (p instanceof DiscordPlayer) { - ((DiscordPlayer) p).sendMessage(data); - } - } - //core.networkManager().flush(); - needsFlush = true; - } - - @Override - public void messageUser(RemotePlayer player) { - // Discord does not provide this functionality - } - - public Texture getLogo() { return logo; } - - @Override - public void dispose() { - TogetherManager.log("Discord integration shutting down"); - //callbacksExecutor.cancel(false); - instances.remove(this); - core.close(); - createParams.close(); - createParams = null; - TogetherManager.log("Discord integration shut down successfully"); - initialized = false; - } -} diff --git a/src/main/java/chronoMods/network/discord/DiscordLobby.java b/src/main/java/chronoMods/network/discord/DiscordLobby.java deleted file mode 100644 index 9ee7521..0000000 --- a/src/main/java/chronoMods/network/discord/DiscordLobby.java +++ /dev/null @@ -1,288 +0,0 @@ -package chronoMods.network.discord; - -import chronoMods.TogetherManager; -import chronoMods.network.NetworkHelper; -import chronoMods.network.RemotePlayer; -import chronoMods.ui.hud.RemotePlayerWidget; -import chronoMods.ui.hud.TopPanelPlayerPanels; -import chronoMods.ui.lobby.NewScreenUpdateRender; -import chronoMods.ui.mainMenu.NewMenuButtons; -import com.megacrit.cardcrawl.core.CardCrawlGame; -import de.jcm.discordgamesdk.DiscordEventAdapter; -import de.jcm.discordgamesdk.DiscordEventHandler; -import de.jcm.discordgamesdk.Result; -import de.jcm.discordgamesdk.activity.Activity; -import de.jcm.discordgamesdk.lobby.Lobby; -import de.jcm.discordgamesdk.lobby.LobbyMemberTransaction; -import de.jcm.discordgamesdk.lobby.LobbyTransaction; -import de.jcm.discordgamesdk.lobby.LobbyType; -import de.jcm.discordgamesdk.user.DiscordUser; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Consumer; -import java.util.stream.Collectors; - - -public class DiscordLobby extends chronoMods.network.Lobby { - public DiscordIntegration integration; - public Lobby lobby; - public Map metadata; - public DiscordLobby(Lobby lobby, DiscordIntegration integration) { - super(integration); - this.integration = integration; - this.lobby = lobby; - metadata = integration.core.lobbyManager().getLobbyMetadata(lobby); - fetchAllMetadata(); - } - @Override - public String getOwnerName() { - try { - return integration.core.lobbyManager().getMemberUser(lobby, lobby.getOwnerId()).getUsername(); - } catch (Exception e) { - return "Unknown Discord Player"; - } - } - - @Override - public long getOwner() { - long id = lobby.getOwnerId(); - return id; - } - - @Override - public boolean isOwner() { - boolean isOwner = lobby.getOwnerId() == integration.core.userManager().getCurrentUser().getUserId(); - return isOwner; - } - - @Override - public void newOwner() { - for (RemotePlayer player : TogetherManager.players) { - if (!TogetherManager.currentUser.isUser(player) && player instanceof DiscordPlayer) { - update(txn -> { - txn.setOwner(((DiscordPlayer)player).user.getUserId()); - txn.setMetadata("owner", player.userName); - }); - return; - } - } - } - - /* - @Override - public void updateOwner() { - LobbyTransaction txn = integration.core.lobbyManager().getLobbyUpdateTransaction(lobby); - txn.setOwner(integration.core.userManager().getCurrentUser().getUserId()); - integration.core.lobbyManager().updateLobby(lobby, txn); - } - */ - - @Override - public int getMemberCount() { - int count = integration.core.lobbyManager().memberCount(lobby); - return count; - } - - @Override - public CopyOnWriteArrayList getLobbyMembers() { - CopyOnWriteArrayList players = integration.core.lobbyManager().getMemberUsers(lobby).stream() - .map(u -> new DiscordPlayer(u, integration, DiscordLobby.this)) - .peek(p -> TopPanelPlayerPanels.playerWidgets.add(new RemotePlayerWidget(p))) - .collect(Collectors.toCollection(CopyOnWriteArrayList::new)); - return players; - } - - @Override - public String getMemberNameList() { - String names = integration.core.lobbyManager().getMemberUsers(lobby) - .stream() - .map(DiscordUser::getUsername) - .collect(Collectors.joining("\t")); - return names; - } - - @Override - public Object getID() { - return lobby; - } - - public boolean lobbyLeft = false; - @Override - public void leaveLobby() { - stopActivity(); - integration.eventHandler.removeListener(callbacks); - lobbyLeft = true; - TogetherManager.players - .forEach(p -> callbacks.onMemberDisconnect(lobby.getId(), p.getAccountID())); - integration.core.lobbyManager().disconnectLobby(lobby); - } - - @Override - public void setJoinable(boolean toggle) { - TogetherManager.log("Discord lobby setJoinable: " + toggle); - update(txn -> txn.setLocked(!toggle), r -> updateActivity()); - } - - @Override - public void setPrivate(boolean toggle) { - update(txn -> txn.setType(toggle ? LobbyType.PRIVATE : LobbyType.PUBLIC), r -> updateActivity()); - } - - @Override - public void join() { - integration.core.lobbyManager().connectLobby(lobby, (r, l) -> { - if (r == Result.OK) { - lobbyLeft = false; - metadata = integration.core.lobbyManager().getLobbyMetadata(lobby); - fetchAllMetadata(); - setUpNetworking(); - startActivity(); - TogetherManager.currentLobby = DiscordLobby.this; - TogetherManager.players = TogetherManager.currentLobby.getLobbyMembers(); - TogetherManager.currentUser = integration.makeCurrentUser(); - - NewScreenUpdateRender.joinFlag = true; - NetworkHelper.sendData(NetworkHelper.dataType.Version); - } - else { - TogetherManager.infoPopup.show(CardCrawlGame.languagePack.getUIString("Network").TEXT[5], CardCrawlGame.languagePack.getUIString("Network").TEXT[6]); - } - }); - } - - public static Map map(String... data) { - if (data.length % 2 != 0) { - throw new IllegalArgumentException("Must have an even number of arguments"); - } - Map result = new HashMap<>(); - for (int i = 0; i < data.length; i += 2) { - result.put(data[i], data[i+1]); - } - return result; - } - public DiscordEventHandler callbacks = new DiscordEventHandler(); - { - callbacks.addListener(new DiscordEventAdapter() { - @Override - public void onRouteUpdate(String routeData) { - //TogetherManager.log("onRouteUpdate"); - setOurMetadata(map("route", routeData)); - } - - @Override - public void onMemberConnect(long lobbyId, long userId) { - //TogetherManager.log("onMemberConnect"); - if (lobbyId != lobby.getId()) return; - if (NetworkHelper.embarked) { - // Player reconnecting. Adding them to the game will break things, - // and there's no way to kick them from the lobby, so just ignore them. - return; - } - NetworkHelper.addPlayer(new DiscordPlayer( - integration.core.lobbyManager().getMemberUser(lobby, userId), - integration, - DiscordLobby.this - )); - - NetworkHelper.sendData(NetworkHelper.dataType.Version); - NetworkHelper.sendData(NetworkHelper.dataType.Ready); - if (TogetherManager.gameMode == TogetherManager.mode.Coop) - NetworkHelper.sendData(NetworkHelper.dataType.Character); - - NewMenuButtons.newGameScreen.playerList.setPlayers(TogetherManager.players); - - if (TogetherManager.currentLobby.isOwner()) { - setMetadata(map("members", getMemberNameList())); - } - if (TogetherManager.gameMode == TogetherManager.mode.Bingo) - NetworkHelper.sendData(NetworkHelper.dataType.BingoRules); - NetworkHelper.sendData(NetworkHelper.dataType.Rules); - updateActivity(); - } - }); - }; - - public void setOurMetadata(Map pairs) { - //TogetherManager.log("setOurMetadata"); - LobbyMemberTransaction txn = integration.core.lobbyManager().getMemberUpdateTransaction( - lobby, - integration.core.userManager().getCurrentUser().getUserId() - ); - for (Map.Entry pair : pairs.entrySet()) { - txn.setMetadata(pair.getKey(), pair.getValue()); - } - integration.core.lobbyManager().updateMember( - lobby, - integration.core.userManager().getCurrentUser().getUserId(), - txn - ); - } - public void setUpNetworking() { - integration.eventHandler.addListener(callbacks); - setOurMetadata(map( - "peerID", String.valueOf(integration.core.networkManager().getPeerId()), - "route", integration.ourRoute - )); - } - - public Activity activity; - - public void startActivity() { - activity = new Activity(); - activity.setInstance(true); - activity.setState(String.format("Spire with Friends: %s", mode)); //TODO localize - activity.timestamps().setStart(Instant.now()); - activity.party().setID(Long.toString(lobby.getId())); - updateActivity(); - } - public void updateActivity() { - if (activity == null) return; - activity.party().size().setCurrentSize(getMemberCount()); - activity.party().size().setMaxSize(getCapacity()); - activity.secrets().setJoinSecret(integration.core.lobbyManager().getLobbyActivitySecret(lobby)); - { - boolean joinable = !lobby.isLocked() && getMemberCount() < getCapacity(); - activity.setDetails(joinable ? "Waiting for players" : "Locked"); //TODO localize - } - integration.core.activityManager().updateActivity(activity); - } - - public void stopActivity() { - integration.core.activityManager().clearActivity(); - activity.close(); - activity = null; - } - - @Override - public int getCapacity() { - return lobby.getCapacity(); - } - - @Override - public String getMetadata(String key) { - return metadata.get(key); - } - - @Override - public void setMetadata(Map pairs) { - //TogetherManager.log("setMetadata"); - update(txn -> { - for (Map.Entry pair : pairs.entrySet()) { - txn.setMetadata(pair.getKey(), pair.getValue()); - } - }); - metadata = pairs; - fetchAllMetadata(); - } - - public void update(Consumer body) { update(body, r -> {}); } - - public void update(Consumer body, Consumer callback) { - LobbyTransaction txn = integration.core.lobbyManager().getLobbyUpdateTransaction(lobby); - body.accept(txn); - integration.core.lobbyManager().updateLobby(lobby, txn, callback); - } -} diff --git a/src/main/java/chronoMods/network/discord/DiscordPlayer.java b/src/main/java/chronoMods/network/discord/DiscordPlayer.java deleted file mode 100644 index 81cbb86..0000000 --- a/src/main/java/chronoMods/network/discord/DiscordPlayer.java +++ /dev/null @@ -1,287 +0,0 @@ -package chronoMods.network.discord; - -import chronoMods.TogetherManager; -import chronoMods.network.NetworkHelper; -import chronoMods.network.Packet; -import chronoMods.network.RemotePlayer; -import chronoMods.ui.mainMenu.NewMenuButtons; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import de.jcm.discordgamesdk.DiscordEventAdapter; -import de.jcm.discordgamesdk.GameSDKException; -import de.jcm.discordgamesdk.Result; -import de.jcm.discordgamesdk.image.ImageDimensions; -import de.jcm.discordgamesdk.image.ImageHandle; -import de.jcm.discordgamesdk.image.ImageType; -import de.jcm.discordgamesdk.user.DiscordUser; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentLinkedQueue; - -public class DiscordPlayer extends RemotePlayer - implements AutoCloseable { - public DiscordUser user; - public DiscordIntegration integration; - public DiscordLobby lobby; - public long peerID; - public Pixmap pixmap; - - // Opening a connection takes time, but we might be given messages to send before then. - // Queue them up, then send them once a connection is established. - public ConcurrentLinkedQueue packetsToSend = new ConcurrentLinkedQueue<>(); - public boolean isConnected = false; - public Timer reconnectTimer; - public boolean timedOut = false; - public boolean peerOpened = false; - public DiscordEventAdapter callbacks = new DiscordEventAdapter() { - @Override - public void onMemberUpdate(long lobbyId, long userId) { - if (userId == integration.core.userManager().getCurrentUser().getUserId()) { - return; - } - if (lobbyId != lobby.lobby.getId()) return; - if (userId != user.getUserId()) return; - //TogetherManager.log("OnMemberUpdate for DiscordPlayer " + user.getUsername()); - if (isConnected || peerOpened) { - //TogetherManager.log("Updating peer"); - integration.core.networkManager().updatePeer( - peerID, - integration.core.lobbyManager().getMemberMetadataValue(lobbyId, userId, "route") - ); - integration.needsFlush = true; - } - else { - //TogetherManager.log("Trying to connect"); - Map metadata = integration.core.lobbyManager().getMemberMetadata(lobbyId, userId); - if (metadata.containsKey("peerID") && metadata.containsKey("route")) { - TogetherManager.log("Keys found"); - peerID = Long.parseLong(metadata.get("peerID")); - integration.core.networkManager().openPeer(peerID, metadata.get("route")); - - // main channel - integration.core.networkManager().openChannel(peerID, (byte)0, true); - - // meta channel, used to talk about whether the main channel is open on both ends - integration.core.networkManager().openChannel(peerID, (byte)1, true); - integration.core.networkManager().sendMessage(peerID, (byte)1, new byte[1]); - peerOpened = true; - - //integration.core.networkManager().flush(); - integration.needsFlush = true; - } - } - } - - @Override - public void onMessage(long peerId, byte channelId, byte[] data) { - if (peerId != peerID) return; - if (channelId == 1) { - //TogetherManager.log("Got meta channel message"); - // meta channel - // this exists because messages will only be received if the channel is open on both ends - // when we open the channels, we also send a message on channel 1 - // if you receive a message on channel 1, you know the channels must be open on both ends, - // and it's safe to start sending real messages - if (isConnected) return; - isConnected = true; - - // since we were the first to open the channels, the other party never got our channel 1 message - // send them another to let them know it's open - //TogetherManager.log("Sending one back"); - integration.core.networkManager().sendMessage(peerID, (byte)1, new byte[0]); - - // since we know the connection's open, send all queued messages on the main channel - for (ByteBuffer b = packetsToSend.poll(); b != null; b = packetsToSend.poll()) { - //TogetherManager.log("Sending a queued message"); - //TogetherManager.log("Queued message is: " + b.order()); - byte[] array = toBytes(b); - //TogetherManager.log(DatatypeConverter.printHexBinary(array)); - integration.core.networkManager().sendMessage(peerID, (byte)0, array); - } - //integration.core.networkManager().flush(); - integration.needsFlush = true; - } - else { - // real message - //TogetherManager.log("Got main channel message"); - //TogetherManager.log("Length: " + data.length); - //TogetherManager.log(DatatypeConverter.printHexBinary(data)); - ByteBuffer buf = ByteBuffer.allocate(data.length); - buf.put(data); - ((Buffer)buf).rewind(); - //TogetherManager.log("Incoming message is: " + buf.order()); - integration.incomingMessages.add(new Packet(DiscordPlayer.this, buf)); - } - } - - @Override - public void onMemberConnect(long lobbyId, long userId) { - if (lobbyId != lobby.lobby.getId()) return; - if (userId != user.getUserId()) return; - if (timedOut) return; - if (reconnectTimer != null) { - reconnectTimer.cancel(); - reconnectTimer = null; - // simulate a route update to reestablish connection - lobby.callbacks.onRouteUpdate(integration.ourRoute); - } - } - - @Override - public void onMemberDisconnect(long lobbyId, long userId) { - if (lobbyId != lobby.lobby.getId()) return; - if (userId != user.getUserId()) return; - if (lobby.lobbyLeft) { - TogetherManager.log("OnMemberDisconnect: lobbyLeft is true"); - } - else { - TogetherManager.log("OnMemberDisconnect: lobbyLeft is false"); - } - close(); - Runnable completeDisconnect = () -> { - TogetherManager.log("Completing disconnect"); - if (reconnectTimer != null) reconnectTimer.cancel(); - timedOut = true; - lobby.callbacks.removeListener(callbacks); - NetworkHelper.removePlayer(DiscordPlayer.this); - NewMenuButtons.newGameScreen.playerList.setPlayers(TogetherManager.players); - if (TogetherManager.currentLobby.isOwner()) { - lobby.setMetadata(DiscordLobby.map("members", lobby.getMemberNameList())); - } - }; - if (NetworkHelper.embarked && !lobby.lobbyLeft) { - TogetherManager.log("Starting timer..."); - reconnectTimer = new Timer(); - reconnectTimer.schedule( - new TimerTask() { - @Override - public void run() { - TogetherManager.log("Posting runnable..."); - DiscordIntegration.postRunnable(completeDisconnect); - } - }, - 1000L - ); - } - else { - completeDisconnect.run(); - } - } - }; - public DiscordPlayer(DiscordUser user, DiscordIntegration integration, DiscordLobby lobby) { - super(); - this.user = user; - this.lobby = lobby; - this.integration = integration; - - this.userName = user.getUsername(); - updateAvatar(); - - lobby.callbacks.addListener(callbacks); - // parse metadata's initial values - callbacks.onMemberUpdate(lobby.lobby.getId(), user.getUserId()); - } - - public void updateAvatar() { - integration.core.imageManager().fetch( - new ImageHandle(ImageType.USER, user.getUserId(), 128), - false, - (result, handle) -> { - if (result != Result.OK) { - TogetherManager.log("Got result " + result.name() + " trying to fetch avatar for user with id " + user.getUserId()); - return; - } - ImageDimensions dimensions = integration.core.imageManager().getDimensions(handle); - pixmap = new Pixmap(dimensions.getWidth(), dimensions.getHeight(), Pixmap.Format.RGBA8888); - // both Discord and our Pixmap use RGBA8888 - // unfortunately, BufferedImage.getRGB can only return ARGB8888, so we'll do this directly with bytes - // even though using an image class would be more elegant - // BufferedImage source = integration.core.imageManager().getAsBufferedImage(handle, dimensions); - ByteBuffer source = ByteBuffer.wrap(integration.core.imageManager().getData(handle, dimensions)); - for (int y = 0; y < dimensions.getHeight(); y++) { - for (int x = 0; x < dimensions.getWidth(); x++) { - pixmap.drawPixel(x, y, source.getInt()); - } - } - // // Runnable needed to establish GL Context - // Gdx.app.postRunnable(() -> { - // for (RemotePlayer player : TogetherManager.players) { - // if (player.isUser(this)) { - // player.portraitImg = new Texture(pixmap); - // player.getPortrait().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); - // } - // } - // }); - } - ); - } - - public Texture getPortrait() { - if (portraitImg == null) { - if (pixmap == null) - portraitImg = new Texture(new Pixmap(120, 120, Pixmap.Format.RGBA8888)); - else - portraitImg = new Texture(pixmap); - portraitImg.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); - } - - return portraitImg; - } - - public void sendMessage(ByteBuffer bytes) { - if (user.getUserId() == integration.core.userManager().getCurrentUser().getUserId()) { - ((Buffer)bytes).rewind(); - //TogetherManager.log("loopback message buffer is: " + bytes.order()); - //TogetherManager.log(DatatypeConverter.printHexBinary(toBytes(bytes))); - integration.incomingMessages.add(new Packet(this, bytes)); - return; - } - if (isConnected) { - //TogetherManager.log("sendMessage buffer is: " + bytes.order()); - byte[] array = toBytes(bytes); - //TogetherManager.log(DatatypeConverter.printHexBinary(array)); - integration.core.networkManager().sendMessage(peerID, (byte)0, array); - } - else { - packetsToSend.add(bytes); - } - } - - public static byte[] toBytes(ByteBuffer buf) { - byte[] array; - if (buf.hasArray()) array = buf.array(); - else { - ((Buffer)buf).rewind(); - array = new byte[buf.remaining()]; - buf.get(array, 0, buf.remaining()); - ((Buffer)buf).rewind(); - } - return array; - } - - public boolean isUser(Object player) { - if (player instanceof DiscordPlayer) - return ((DiscordPlayer) player).user.getUserId() == user.getUserId(); - return false; - } - - public long getAccountID() { return user.getUserId(); } - public void close() { - if (isConnected) { - isConnected = false; - try { - integration.core.networkManager().closeChannel(peerID, (byte) 0); - integration.core.networkManager().closeChannel(peerID, (byte) 1); - integration.core.networkManager().closePeer(peerID); - } - catch (GameSDKException e) { - // maybe this is fine? - } - peerOpened = false; - } - } -} diff --git a/src/main/java/chronoMods/ui/lobby/MainLobbyScreen.java b/src/main/java/chronoMods/ui/lobby/MainLobbyScreen.java index b6a89f3..ad357f7 100644 --- a/src/main/java/chronoMods/ui/lobby/MainLobbyScreen.java +++ b/src/main/java/chronoMods/ui/lobby/MainLobbyScreen.java @@ -1,7 +1,7 @@ package chronoMods.ui.lobby; import chronoMods.TogetherManager; -import chronoMods.coop.drawable.CycleButton; +// import chronoMods.coop.drawable.CycleButton; import chronoMods.network.Integration; import chronoMods.network.Lobby; import chronoMods.network.NetworkHelper; @@ -37,7 +37,7 @@ public static class Enum public int page = 0; // Buttons - public CycleButton serviceToggle = new CycleButton(Settings.WIDTH - 64f * Settings.xScale, 64f * Settings.xScale, ""); + // public CycleButton serviceToggle = new CycleButton(Settings.WIDTH - 64f * Settings.xScale, 64f * Settings.xScale, ""); public MenuCancelButton button = new MenuCancelButton(); public GridSelectConfirmButton confirmButton = new GridSelectConfirmButton(CardCrawlGame.languagePack.getUIString("Lobby").TEXT[13]); @@ -70,8 +70,8 @@ public void open() { this.mode = mode; // Service Toggle - for (Integration service : NetworkHelper.networks) - serviceToggle.addOption(service.getLogo()); + // for (Integration service : NetworkHelper.networks) + // serviceToggle.addOption(service.getLogo()); // Screen Swap CardCrawlGame.mainMenuScreen.darken(); @@ -124,7 +124,7 @@ public void update() { } // Service Toggle - serviceToggle.update(); + // serviceToggle.update(); // Lobby list for (MainLobbyInfo lobby : gameList) { @@ -171,7 +171,7 @@ public void update() { // Scrollbar boolean isDraggingScrollBar = this.scrollBar.update(); if (!isDraggingScrollBar) - updateScrolling(); + updateScrolling(); } public void deselect() { @@ -187,16 +187,16 @@ private void updateScrolling() { this.scrollTargetY += Settings.SCROLL_SPEED; } else if (InputHelper.scrolledUp) { this.scrollTargetY -= Settings.SCROLL_SPEED; - } + } if (InputHelper.justClickedLeft) { this.grabbedScreen = true; this.grabStartY = y - this.scrollTargetY; - } + } } else if (InputHelper.isMouseDown) { this.scrollTargetY = y - this.grabStartY; } else { this.grabbedScreen = false; - } + } this.scrollY = MathHelper.scrollSnapLerpSpeed(this.scrollY, this.scrollTargetY); resetScrolling(); updateBarPosition(); @@ -244,7 +244,7 @@ public void render(SpriteBatch sb) { this.button.render(sb); this.confirmButton.render(sb); - serviceToggle.render(sb); + // serviceToggle.render(sb); renderHeaders(sb); lobbyDetails.render(sb); @@ -277,7 +277,7 @@ public void renderHeaders(SpriteBatch sb) { float LINE_THICKNESS = 4.0F * Settings.scale; String[] msg = CardCrawlGame.languagePack.getUIString("Lobby").TEXT; - + FontHelper.renderFontLeftTopAligned(sb, FontHelper.smallDialogOptionFont, msg[14], RANK_X, 920.0F * Settings.yScale, creamColor); FontHelper.renderFontLeftTopAligned(sb, FontHelper.smallDialogOptionFont, msg[15], NAME_X, 920.0F * Settings.yScale, creamColor); @@ -294,7 +294,7 @@ public void renderHeaders(SpriteBatch sb) { sb.draw(ImageMaster.WHITE_SQUARE_IMG, 982.0F * Settings.scale, 814.0F * Settings.scale, 630.0F * Settings.scale, 16.0F * Settings.scale); sb.setColor(creamColor); - sb.draw(ImageMaster.WHITE_SQUARE_IMG, 982.0F * Settings.scale, 820.0F * Settings.scale, 630.0F * Settings.scale, LINE_THICKNESS);*/ + sb.draw(ImageMaster.WHITE_SQUARE_IMG, 982.0F * Settings.scale, 820.0F * Settings.scale, 630.0F * Settings.scale, LINE_THICKNESS);*/ } private void drawRect(SpriteBatch sb, float x, float y, float width, float height, float thickness) { @@ -303,4 +303,4 @@ private void drawRect(SpriteBatch sb, float x, float y, float width, float heigh sb.draw(ImageMaster.WHITE_SQUARE_IMG, x, y+height-thickness, width, thickness); sb.draw(ImageMaster.WHITE_SQUARE_IMG, x+width-thickness, y, thickness, height); } -} \ No newline at end of file +} diff --git a/src/main/java/chronoMods/ui/lobby/NewGameScreen.java b/src/main/java/chronoMods/ui/lobby/NewGameScreen.java index fcb6733..54787e4 100644 --- a/src/main/java/chronoMods/ui/lobby/NewGameScreen.java +++ b/src/main/java/chronoMods/ui/lobby/NewGameScreen.java @@ -79,7 +79,7 @@ public static class Enum private static final float TOGGLE_X_RIGHT = 1400f * Settings.xScale; private static final float TOGGLE_X_LEFT = 640.0F * Settings.xScale; - + private static final float TOOLTIP_X_OFFSET = 1.03f; private static final float TOOLTIP_Y_OFFSET = 50.0F * Settings.scale; @@ -128,7 +128,7 @@ public static class Enum public NewGameScreen() { - characterSelectWidget.move(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.65f); // 780y + characterSelectWidget.move(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.65f); // 780y ascensionSelectWidget.move(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.5625f); // 675y seedSelectWidget.move(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.458f); // 550y playerList.move(Settings.WIDTH / 2.0F, Settings.HEIGHT * 0.6875f); // -375y @@ -144,7 +144,7 @@ public NewGameScreen() { customModeButton = new Button(64.0f * Settings.xScale, Settings.HEIGHT * 0.65f, LOBBY[23], ImageMaster.END_TURN_BUTTON); - bingoDifficulty = new DropdownMenu(this, BINGO, FontHelper.tipBodyFont, Settings.CREAM_COLOR); + bingoDifficulty = new DropdownMenu(this, BINGO, FontHelper.tipBodyFont, Settings.CREAM_COLOR); teamsToggle = new ToggleWidget(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.458f, LOBBY[26], false); uniqueBoardToggle = new ToggleWidget(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.395f, LOBBY[28], false); blackoutToggle = new ToggleWidget(TOGGLE_X_RIGHT, Settings.HEIGHT * 0.332f, LOBBY[35], false); @@ -169,11 +169,11 @@ public void open() { long sourceTime = System.nanoTime(); Random rng = new Random(Long.valueOf(sourceTime)); Settings.seed = Long.valueOf(SeedHelper.generateUnoffensiveSeed(rng)); - + Settings.specialSeed = null; // Steam Stuff - NetworkHelper.createLobby(NetworkHelper.networks.get(NewMenuButtons.lobbyScreen.serviceToggle.index)); + NetworkHelper.createLobby(NetworkHelper.networks.get(0)); // Populate the player list for (RemotePlayer player : TogetherManager.players) { @@ -246,7 +246,7 @@ public void update() { if (bingoDifficulty.isOpen) { bingoDifficulty.update(); return; - } + } } // Typical Buttons @@ -311,9 +311,9 @@ public void update() { if (TogetherManager.gameMode == TogetherManager.mode.Bingo) { if (teamsToggle.hb.hovered) - TipHelper.renderGenericTip(this.teamsToggle.hb.cX * TOOLTIP_X_OFFSET, this.teamsToggle.hb.cY + TOOLTIP_Y_OFFSET, LOBBY[26], LOBBY[27]); + TipHelper.renderGenericTip(this.teamsToggle.hb.cX * TOOLTIP_X_OFFSET, this.teamsToggle.hb.cY + TOOLTIP_Y_OFFSET, LOBBY[26], LOBBY[27]); if (uniqueBoardToggle.hb.hovered) - TipHelper.renderGenericTip(this.uniqueBoardToggle.hb.cX * TOOLTIP_X_OFFSET, this.uniqueBoardToggle.hb.cY + TOOLTIP_Y_OFFSET, LOBBY[28], LOBBY[29]); + TipHelper.renderGenericTip(this.uniqueBoardToggle.hb.cX * TOOLTIP_X_OFFSET, this.uniqueBoardToggle.hb.cY + TOOLTIP_Y_OFFSET, LOBBY[28], LOBBY[29]); if (bingoDifficulty.getHitbox().hovered) TipHelper.renderGenericTip(this.bingoDifficulty.getHitbox().cX * TOOLTIP_X_OFFSET, this.bingoDifficulty.getHitbox().cY + TOOLTIP_Y_OFFSET, LOBBY[30], LOBBY[31]); if (blackoutToggle.hb.hovered) @@ -351,7 +351,7 @@ public void update() { teamName = "Team " + TogetherManager.getCurrentUser().team; this.renamePopup.open(teamName); - } + } this.renamePopup.update(); } @@ -417,7 +417,7 @@ public void embark() { // Recreate Watcher for Hard Mode CardCrawlGame.characterManager.recreateCharacter(AbstractPlayer.PlayerClass.WATCHER); - // Custom bingo images + // Custom bingo images if (TogetherManager.gameMode == TogetherManager.mode.Bingo && TogetherManager.customMark != null) NetworkHelper.sendData(NetworkHelper.dataType.CustomMark); @@ -509,7 +509,7 @@ public void embark() { TogetherManager.getCurrentUser().bingoCardIndices = Caller.makeBingoCard(1,3,1); break; } - + if (teamsToggle.isTicked()) { TogetherManager.getCurrentUser().setColour(RemotePlayer.colourChoices[TogetherManager.getCurrentUser().team%(RemotePlayer.colourChoices.length-1)]); } else { @@ -538,7 +538,7 @@ public void embark() { TogetherManager.log("Adding " + p.userName + " to team " + p.team); teamMap.putIfAbsent(p.team, new ArrayList()); teamMap.get(p.team).add(p); - } + } // Single player Bingo Widgets else { TopPanelPlayerPanels.playerWidgets.add(new BingoPlayerWidget(p)); @@ -554,7 +554,7 @@ public void embark() { teamMap.forEach((id, teamList) -> { TogetherManager.log("Team " + id + " size " + teamList.size()); TopPanelPlayerPanels.playerWidgets.add(new BingoPlayerWidget(teamList)); }); } - TopPanelPlayerPanels.SortWidgets(); + TopPanelPlayerPanels.SortWidgets(); } public void changedSelectionTo(DropdownMenu dropdownMenu, int index, String optionText) { @@ -562,7 +562,7 @@ public void changedSelectionTo(DropdownMenu dropdownMenu, int index, String opti if (TogetherManager.currentLobby.isOwner()) NetworkHelper.sendData(NetworkHelper.dataType.BingoRules); } - + public void render(SpriteBatch sb) { FontHelper.renderFontCentered(sb, FontHelper.SCP_cardTitleFont_small, "Lobby", Settings.WIDTH / 2.0f, @@ -584,14 +584,14 @@ public void render(SpriteBatch sb) { FontHelper.renderFontRightAligned(sb, FontHelper.panelEndTurnFont, teamName, this.renameHb.cX - 35.0F*Settings.scale, this.renameHb.cY, Settings.GREEN_TEXT_COLOR); } else { FontHelper.renderFontRightAligned(sb, FontHelper.panelEndTurnFont, teamName, this.renameHb.cX - 35.0F*Settings.scale, this.renameHb.cY, Settings.CREAM_COLOR); - } + } // Draw Team Members int tm = 2; // Offsetting for (RemotePlayer p : TogetherManager.players) { if (p.team == TogetherManager.getCurrentUser().team) { // (Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY) { - sb.draw(TogetherManager.teamTags, this.renameHb.cX - 160f, this.renameHb.cY-tm*70f*Settings.scale, + sb.draw(TogetherManager.teamTags, this.renameHb.cX - 160f, this.renameHb.cY-tm*70f*Settings.scale, 329f/2f, 52f/2f, 329f, 52f, Settings.scale, Settings.scale, 0f, 0,0,329,52, @@ -606,7 +606,7 @@ public void render(SpriteBatch sb) { float scale = Settings.scale; if (this.renameHb.hovered) - scale = Settings.scale * 1.04F; + scale = Settings.scale * 1.04F; sb.draw(ImageMaster.PROFILE_RENAME, this.renameHb.cX - 50.0F, this.renameHb.cY - 50.0F, 50.0F, 50.0F, 100.0F, 100.0F, scale/2f, scale/2f, 0.0F, 0, 0, 100, 100, false, false); if (this.renameHb.hovered) { sb.setColor(new Color(1.0F, 1.0F, 1.0F, 0.4F)); @@ -614,26 +614,26 @@ public void render(SpriteBatch sb) { sb.draw(ImageMaster.PROFILE_RENAME, this.renameHb.cX - 50.0F, this.renameHb.cY - 50.0F, 50.0F, 50.0F, 100.0F, 100.0F, scale/2f, scale/2f, 0.0F, 0, 0, 100, 100, false, false); sb.setBlendFunction(770, 771); sb.setColor(uiColor); - } + } } } playerList.render(sb); if (TogetherManager.currentLobby != null && TogetherManager.gameMode != TogetherManager.mode.Coop && !TogetherManager.currentLobby.isOwner()) - ShaderHelper.setShader(sb, ShaderHelper.Shader.GRAYSCALE); + ShaderHelper.setShader(sb, ShaderHelper.Shader.GRAYSCALE); characterSelectWidget.render(sb); if (TogetherManager.currentLobby != null && !TogetherManager.currentLobby.isOwner()) - ShaderHelper.setShader(sb, ShaderHelper.Shader.GRAYSCALE); + ShaderHelper.setShader(sb, ShaderHelper.Shader.GRAYSCALE); ascensionSelectWidget.render(sb); privateToggle.render(sb); if (Loader.isModLoaded("downfall")) downfallToggle.render(sb); - if (TogetherManager.gameMode == TogetherManager.mode.Coop) + if (TogetherManager.gameMode == TogetherManager.mode.Coop) hardToggle.render(sb); if (TogetherManager.gameMode != TogetherManager.mode.Bingo) { diff --git a/src/main/resources/chrono/images/Discord-Logo-Color-92px.png b/src/main/resources/chrono/images/Discord-Logo-Color-92px.png deleted file mode 100644 index 4b61c99579412c77f79f3a38796e9c1796018668..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3107 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