diff options
17 files changed, 858 insertions, 733 deletions
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CosmeticsManager.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CosmeticsManager.java index 6482e5d5..707a4a20 100644 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CosmeticsManager.java +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CosmeticsManager.java @@ -19,7 +19,9 @@ package kr.syeyoung.dungeonsguide.mod.cosmetics; -import kr.syeyoung.dungeonsguide.mod.cosmetics.chatreplacers.*; +import kr.syeyoung.dungeonsguide.mod.cosmetics.chatdetectors.*; +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.SurgicalReplacer; import kr.syeyoung.dungeonsguide.mod.events.impl.PlayerListItemPacketEvent; import kr.syeyoung.dungeonsguide.mod.events.impl.StompConnectedEvent; import kr.syeyoung.dungeonsguide.mod.stomp.StompHeader; @@ -32,6 +34,8 @@ import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.network.play.server.S38PacketPlayerListItem; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.fml.common.eventhandler.EventPriority; @@ -224,26 +228,134 @@ public class CosmeticsManager { requestPerms(); } @Getter @Setter - private static List<IChatReplacer> iChatReplacers = new ArrayList<>(); + private static List<IChatDetector> iChatDetectors = new ArrayList<>(); static { - iChatReplacers.add(new ChatReplacerViewProfile()); - iChatReplacers.add(new ChatReplacerPV()); - iChatReplacers.add(new ChatReplacerSocialOptions()); - iChatReplacers.add(new ChatReplacerCoop()); - iChatReplacers.add(new ChatReplacerMessage()); - iChatReplacers.add(new ChatReplacerChatByMe()); + iChatDetectors.add(new ChatDetectorProbablyUniversal()); + iChatDetectors.add(new ChatDetectorFriendList()); + iChatDetectors.add(new ChatDetectorGuildPartyList()); + iChatDetectors.add(new ChatDetectorPartyMessages()); + iChatDetectors.add(new ChatDetectorJoinLeave()); } - @SubscribeEvent(priority = EventPriority.LOWEST) + private final ThreadLocal<List<ReplacementContext>> contextThreadLocal = new ThreadLocal<>(); + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onChatDetect(ClientChatReceivedEvent clientChatReceivedEvent) { + try { + if (clientChatReceivedEvent.type == 2) return; + List<ReplacementContext> total = new ArrayList<>(); + for (IChatDetector iChatReplacer : iChatDetectors) { + List<ReplacementContext> replacementContext = iChatReplacer.getReplacementContext(clientChatReceivedEvent.message); + if (replacementContext != null) { + total.addAll(replacementContext); + } + } + contextThreadLocal.set(total); + } catch (Throwable t) { + System.out.println(clientChatReceivedEvent.message); + t.printStackTrace(); + } + } + + @SubscribeEvent(priority = EventPriority.LOWEST, receiveCanceled = true) public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) { try { if (clientChatReceivedEvent.type == 2) return; - for (IChatReplacer iChatReplacer : iChatReplacers) { - if (iChatReplacer.isAcceptable(clientChatReceivedEvent)) { - iChatReplacer.translate(clientChatReceivedEvent, this); + if (clientChatReceivedEvent.isCanceled()) { + contextThreadLocal.set(null); + return; + } + + List<ReplacementContext> replacementContexts = contextThreadLocal.get(); + contextThreadLocal.set(null); + + LinkedList<IChatComponent> chatComponents = SurgicalReplacer.linearifyMoveColorCharToStyle(clientChatReceivedEvent.message); + for (ReplacementContext replacementContext : replacementContexts) { + if (replacementContext.getUsername().isEmpty()) continue; + List<ActiveCosmetic> activeCosmetics = getActiveCosmeticByPlayerNameLowerCase() + .get(replacementContext.getUsername().toLowerCase()); + String color=null, prefix="[coolprefix]"; + if (activeCosmetics != null) { + for (ActiveCosmetic activeCosmetic : activeCosmetics) { + CosmeticData cosmeticData = getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); + if (cosmeticData != null && cosmeticData.getCosmeticType().equals("color")) { + color = cosmeticData.getData().replace("&", "§"); + } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { + prefix = cosmeticData.getData().replace("&", "§"); + } + } + } + + if (color == null && prefix == null) continue; + + + StringBuilder sb = new StringBuilder(); + for (IChatComponent chatComponent : chatComponents) { + String str = chatComponent.getUnformattedText(); + sb.append(str); + } + + List<Integer> allIdxes = new ArrayList<>(); + int lastIdx = -1; + String theThing = sb.toString(); + do { + lastIdx = theThing.indexOf(replacementContext.getUsername(), lastIdx+1); + if (lastIdx != -1) + allIdxes.add(lastIdx); + } while (lastIdx != -1); + + int idx = allIdxes.stream() + .min(Comparator.comparingInt(a -> Math.abs(a - replacementContext.getNearIdx()))).orElse(-1); + + System.out.println("Was expecting to find " +replacementContext.getUsername()); + if (idx == -1) { + System.out.println("WTF?"); + + List<IChatComponent> components = SurgicalReplacer.inject(chatComponents, + SurgicalReplacer.linearifyMoveColorCharToStyle( + new ChatComponentText(prefix+" ") + .setChatStyle(SurgicalReplacer.getChatStyleAt(chatComponents, 0)) + ), + 0, 0); + clientChatReceivedEvent.message = SurgicalReplacer.combine(components); + // since we couldn't find username anywhere, just do this. return; } + + int stIdx = theThing.lastIndexOf('\n', idx) + 1; + String beforeUsername = theThing.substring(theThing.lastIndexOf('\n', idx) + 1, idx); + int startingSearch = beforeUsername.length(); + while (true) { + startingSearch = beforeUsername.lastIndexOf(' ', startingSearch-1); + if (startingSearch == -1) break; + if (startingSearch-1 >= 0) { + char c = beforeUsername.charAt(startingSearch-1); + if (c == ' ') continue; + if (c == ']') continue; + startingSearch ++; + break; + } + } + + if (startingSearch == -1) startingSearch = 0; + startingSearch += stIdx; + + if (color != null) + chatComponents = SurgicalReplacer.inject(chatComponents, + SurgicalReplacer.linearifyMoveColorCharToStyle( + new ChatComponentText(color+replacementContext.getUsername()) + .setChatStyle(SurgicalReplacer.getChatStyleAt(chatComponents, idx)) + ), + idx, replacementContext.getUsername().length()); + + if (prefix != null) + chatComponents = SurgicalReplacer.inject(chatComponents, + SurgicalReplacer.linearifyMoveColorCharToStyle( + new ChatComponentText(prefix+" ") + .setChatStyle(SurgicalReplacer.getChatStyleAt(chatComponents, 0)) + ), + startingSearch, 0); } + clientChatReceivedEvent.message = SurgicalReplacer.combine(chatComponents); } catch (Throwable t) { System.out.println(clientChatReceivedEvent.message); t.printStackTrace(); diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CustomNetworkPlayerInfo.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CustomNetworkPlayerInfo.java index 783b255e..9686a855 100644 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CustomNetworkPlayerInfo.java +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/CustomNetworkPlayerInfo.java @@ -42,7 +42,7 @@ public class CustomNetworkPlayerInfo extends NetworkPlayerInfo { private IChatComponent displayName; - private String unformattedDisplayText; + private String formatted; private String playerNameWithoutColor; public IChatComponent getOriginalDisplayName() { @@ -53,15 +53,15 @@ public class CustomNetworkPlayerInfo extends NetworkPlayerInfo { public void setDisplayName(IChatComponent displayNameIn) { displayName = displayNameIn; if (displayName == null) { - unformattedDisplayText = null; + formatted = null; return; } - unformattedDisplayText = displayName.getUnformattedTextForChat(); + formatted = displayName.getFormattedText(); playerNameWithoutColor = ""; - for (String s : unformattedDisplayText.split(" ")) { + for (String s : formatted.split(" ")) { String stripped = TextUtils.stripColor(s); if (stripped.startsWith("[")) { continue; @@ -75,28 +75,19 @@ public class CustomNetworkPlayerInfo extends NetworkPlayerInfo { public IChatComponent getDisplayName() { - String rawPlayerString; - String actualName; + String rawPlayerString = + formatted != null ? formatted : ScorePlayerTeam.formatPlayerName(super.getPlayerTeam(), super.getGameProfile().getName());; + String actualName = null; List<ActiveCosmetic> activeCosmetics; - - // in case that the set player name is not called we do this - if (playerNameWithoutColor != null) { - activeCosmetics = DungeonsGuide.getDungeonsGuide().getCosmeticsManager().getActiveCosmeticByPlayerNameLowerCase().get(playerNameWithoutColor.toLowerCase()); - rawPlayerString = unformattedDisplayText; - actualName = this.playerNameWithoutColor; - } else { - rawPlayerString = ScorePlayerTeam.formatPlayerName(super.getPlayerTeam(), super.getGameProfile().getName()); - actualName = ""; - for (String s : rawPlayerString.split(" ")) { - String strippped = TextUtils.stripColor(s); - if (strippped.startsWith("[")) continue; - actualName = strippped; - break; - } - activeCosmetics = DungeonsGuide.getDungeonsGuide().getCosmeticsManager().getActiveCosmeticByPlayerNameLowerCase().get(actualName.toLowerCase()); + for (String s : rawPlayerString.split(" ")) { + String strippped = TextUtils.stripColor(s); + if (strippped.startsWith("[")) continue; + actualName = strippped; + break; } - - + // in case that the set player name is not called we do this + if (actualName == null) return displayName; + activeCosmetics = DungeonsGuide.getDungeonsGuide().getCosmeticsManager().getActiveCosmeticByPlayerNameLowerCase().get(actualName.toLowerCase()); if (activeCosmetics == null) return displayName; CosmeticData color=null; for (ActiveCosmetic activeCosmetic : activeCosmetics) { diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/README.md b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/README.md new file mode 100644 index 00000000..98d13246 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/README.md @@ -0,0 +1,5 @@ +2 Phase Cosmetic Injection +1. Gather data + - and instruct the thing to replace what near what pos +2. Actually replace + - aghhhhhh diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorFriendList.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorFriendList.java new file mode 100644 index 00000000..62eb16a7 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorFriendList.java @@ -0,0 +1,62 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.chatdetectors; + +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; +import net.minecraft.util.IChatComponent; + +import java.util.ArrayList; +import java.util.List; + +public class ChatDetectorFriendList implements IChatDetector { + @Override + public List<ReplacementContext> getReplacementContext(IChatComponent chatComponent) { + boolean friend = false; + int idx = 0; + for (IChatComponent iChatComponent : chatComponent) { + idx++; + if (iChatComponent.getUnformattedText().startsWith(" §6Friends ")) { + friend = true; + } + if (idx > 5 && !friend) return null; + } + + if (!friend) return null; + + String formatted = chatComponent.getFormattedText(); + String strip = TextUtils.stripColor(formatted); + int len = 0; + List<ReplacementContext> replacementContexts = new ArrayList<>(); + for (String s : strip.split("\n")) { + if (s.charAt(0) == '-' || s.charAt(0) == ' ') { + len += s.length()+1; + continue; + } + String username = s.split(" ")[0]; + System.out.println(username); + replacementContexts.add(new ReplacementContext( + len, username, null + )); + len += s.length()+1; + } + + return replacementContexts; + } +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorGuildPartyList.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorGuildPartyList.java new file mode 100644 index 00000000..7df0b62f --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorGuildPartyList.java @@ -0,0 +1,55 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.chatdetectors; + +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; +import net.minecraft.util.IChatComponent; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ChatDetectorGuildPartyList implements IChatDetector { + @Override + public List<ReplacementContext> getReplacementContext(IChatComponent chatComponent) { + String formatted = chatComponent.getFormattedText(); + if (!(formatted.contains("§c ● ") || formatted.contains("§a ● ") || + formatted.contains("§c●") || formatted.contains("§a●"))) { + return null; + } +// + String strip = TextUtils.stripColor(formatted); +// String last = strip.split(": "); + + List<String> players = new ArrayList<>(); + for (String s : strip.split(" ")) { + if (s.contains("●")) continue; + if (s.startsWith("[")) continue; + if (s.endsWith(":")) continue; + + if (!(formatted.contains(s+"§r") || formatted.contains(s+" §r") )) continue; + players.add(s); + } + + return players.stream().map(a -> new ReplacementContext( + strip.indexOf(a), a, null + )).collect(Collectors.toList()); + } +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorJoinLeave.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorJoinLeave.java new file mode 100644 index 00000000..9de4be0a --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorJoinLeave.java @@ -0,0 +1,79 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.chatdetectors; + +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; +import net.minecraft.util.IChatComponent; + +import java.util.Collections; +import java.util.List; + +public class ChatDetectorJoinLeave implements IChatDetector { + @Override + public List<ReplacementContext> getReplacementContext(IChatComponent chatComponent) { + String formatted = chatComponent.getFormattedText(); + if (formatted.startsWith("§aFriend > §r")) { + if (formatted.endsWith("§r§eleft.§r") || formatted.endsWith("§r§ejoined.§r")) { + String strip = TextUtils.stripColor(formatted); + String username = strip.substring(9, strip.indexOf(' ', 9)); + return Collections.singletonList(new ReplacementContext( + 9, username, null + )); + } + } else if (formatted.endsWith("§6joined the lobby!§r")) { + String[] messageSplit = TextUtils.stripColor(formatted).split(" "); + String oldLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + oldLeader = s; + break; + } + if (oldLeader != null) + return Collections.singletonList(new ReplacementContext( + TextUtils.stripColor(formatted).indexOf(oldLeader), oldLeader, null + )); + } else if (formatted.endsWith("§6joined the lobby!§r §a<§c<§b<§r")) { + String[] messageSplit = TextUtils.stripColor(formatted.substring(15)).split(" "); + String oldLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + oldLeader = s; + break; + } + if (oldLeader != null) + return Collections.singletonList(new ReplacementContext( + TextUtils.stripColor(formatted).indexOf(oldLeader), oldLeader, null + )); + } else if (formatted.startsWith("§b✦ ") && formatted.contains("§r§7found a ") && formatted.endsWith("§r§bMystery Box§r§7!§r")) { + String[] messageSplit = TextUtils.stripColor(formatted.substring(4)).split(" "); + String oldLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + oldLeader = s; + break; + } + if (oldLeader != null) + return Collections.singletonList(new ReplacementContext( + TextUtils.stripColor(formatted).indexOf(oldLeader), oldLeader, null + )); + } + return null; + } +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorPartyMessages.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorPartyMessages.java new file mode 100644 index 00000000..6637b7a9 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorPartyMessages.java @@ -0,0 +1,192 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.chatdetectors; + +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import kr.syeyoung.dungeonsguide.mod.party.PartyManager; +import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; +import net.minecraft.util.IChatComponent; + +import java.util.ArrayList; +import java.util.List; + +public class ChatDetectorPartyMessages implements IChatDetector { + @Override + public List<ReplacementContext> getReplacementContext(IChatComponent chatComponent) { + String str = chatComponent.getFormattedText(); + String strip = TextUtils.stripColor(str); + + List<ReplacementContext> detectors = new ArrayList<>(); + if (str.endsWith("§aenabled All Invite§r") || str.endsWith("§cdisabled All Invite§r") || str.endsWith("§ejoined the party.§r") || str.endsWith("§ehas been removed from the party.§r") || str.endsWith("§ehas left the party.§r")) { + String username = null; + for (String s : TextUtils.stripColor(str).split(" ")) { + if (s.startsWith("[")) continue; + username = s; + break; + } + if (username != null) + detectors.add(new ReplacementContext( + strip.indexOf(username), username, null + )); + } else if (str.endsWith(" They have §r§c60 §r§eseconds to accept.§r")) { + String username = null; + for (String s : TextUtils.stripColor(str).split(" ")) { + if (s.startsWith("[")) continue; + username = s; + break; + } + if (username != null) + detectors.add(new ReplacementContext( + strip.indexOf(username), username, null + )); + username = null; + for (String s : TextUtils.stripColor(str.substring(str.indexOf("§r§einvited ")+12)).split(" ")) { + if (s.startsWith("[")) continue; + username = s; + break; + } + if (username != null) + detectors.add(new ReplacementContext( + strip.indexOf(username), username, null + )); + } else if (str.startsWith("§eThe party was transferred to ")) { + String[] messageSplit = TextUtils.stripColor(str.substring(31)).split(" "); + String newLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + newLeader = s; + break; + } + String oldLeader; + if (str.endsWith("§r§eleft§r")) { + oldLeader = messageSplit[messageSplit.length - 2]; + } else { + oldLeader = messageSplit[messageSplit.length - 1]; + } + + if (oldLeader != null) + detectors.add(new ReplacementContext( + strip.indexOf(oldLeader), oldLeader, null + )); + if (newLeader != null) + detectors.add(new ReplacementContext( + strip.indexOf(newLeader), newLeader, null + )); + } else if (str.endsWith("§eto Party Leader§r")) { + String[] messageSplit = TextUtils.stripColor(str).split(" "); + String oldLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + oldLeader = s; + break; + } + messageSplit = TextUtils.stripColor(str.substring(str.indexOf("has promoted") + 13)).split(" "); + String newLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + newLeader = s; + break; + } + if (oldLeader != null) + detectors.add(new ReplacementContext( + strip.indexOf(oldLeader), oldLeader, null + )); + if (newLeader != null) + detectors.add(new ReplacementContext( + strip.indexOf(newLeader), newLeader, null + )); + } else if (str.endsWith("§r§eto Party Moderator§r")) { + String[] messageSplit = TextUtils.stripColor(str).split(" "); + String oldLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + oldLeader = s; + break; + } + messageSplit = TextUtils.stripColor(str.substring(str.indexOf("has promoted") + 13)).split(" "); + String newModerator = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + newModerator = s; + break; + } + if (oldLeader != null) + detectors.add(new ReplacementContext( + strip.indexOf(oldLeader), oldLeader, null + )); + if (newModerator != null) + detectors.add(new ReplacementContext( + strip.indexOf(newModerator), newModerator, null + )); + + } else if (str.endsWith("§r§eto Party Member§r")) { + String[] messageSplit = TextUtils.stripColor(str).split(" "); + String oldLeader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + oldLeader = s; + break; + } + messageSplit = TextUtils.stripColor(str.substring(str.indexOf("has demoted") + 12)).split(" "); + String newMember = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + newMember = s; + break; + } + if (oldLeader != null) + detectors.add(new ReplacementContext( + strip.indexOf(oldLeader), oldLeader, null + )); + if (newMember != null) + detectors.add(new ReplacementContext( + strip.indexOf(newMember), newMember, null + )); + + } else if (str.startsWith("§eYou have joined ")) { + String[] messageSplit = strip.split(" "); + String leader = null; + for (String s : messageSplit) { + if (s.startsWith("[")) continue; + leader = s; + break; + } + if (leader == null) return null; + String username = leader.substring(0, leader.length()-2); + detectors.add(new ReplacementContext( + strip.indexOf(username), username, null + )); + } else if (str.startsWith("§eYou'll be partying with: ")) { + String[] players = strip.split(" "); + for (String player : players) { + if (player.startsWith("[")) continue; + // player + detectors.add(new ReplacementContext( + strip.indexOf(player), player, null + )); + } + } else if (str.contains("§r§ejoined the dungeon group! (§r§b")) { + String username = TextUtils.stripColor(str).split(" ")[3]; + detectors.add(new ReplacementContext( + strip.indexOf(username), username, null + )); + } + return detectors; + } +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorProbablyUniversal.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorProbablyUniversal.java new file mode 100644 index 00000000..fc3188be --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/ChatDetectorProbablyUniversal.java @@ -0,0 +1,76 @@ +/* + * 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.mod.cosmetics.chatdetectors; + +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.event.ClickEvent; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.IChatComponent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ChatDetectorProbablyUniversal implements IChatDetector { + @Override + public List<ReplacementContext> getReplacementContext(IChatComponent chatComponent) { + String formatted = chatComponent.getFormattedText(); + if (!formatted.contains(": ")) return null; + + + boolean correspondingEvFound = false; + for (IChatComponent iChatComponent : chatComponent) { + ClickEvent ev = iChatComponent.getChatStyle().getChatClickEvent(); + if (ev != null) { + if (ev.getValue().startsWith("/msg")) correspondingEvFound = true; + if (ev.getValue().startsWith("/socialoptions")) correspondingEvFound = true; + if (ev.getValue().startsWith("/viewprofile")) correspondingEvFound = true; + } + } + + formatted = formatted.substring(0, formatted.indexOf(": ")); + String name = TextUtils.stripColor(formatted); + + String[] splited = name.split(" "); + int backLen = 0; + label: for (int i = splited.length - 1; i >= 0; i--) { + String potentialName = splited[i]; + backLen += potentialName.length() + 1; + for (char c : potentialName.toCharArray()) { + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '_' || c == '-')) { + continue; + } + continue label; + } + if (potentialName.equalsIgnoreCase(Minecraft.getMinecraft().thePlayer.getName())) + return Collections.singletonList(new ReplacementContext( + name.length() - backLen, potentialName, null + )); + else if (correspondingEvFound) + return Collections.singletonList(new ReplacementContext( + name.length() - backLen, potentialName, null + )); + else return null; + } + return null; + } +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/IChatDetector.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/IChatDetector.java new file mode 100644 index 00000000..2b1d47b5 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatdetectors/IChatDetector.java @@ -0,0 +1,29 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.chatdetectors; + +import kr.syeyoung.dungeonsguide.mod.cosmetics.surgical.ReplacementContext; +import net.minecraft.util.IChatComponent; +import net.minecraftforge.client.event.ClientChatReceivedEvent; + +import java.util.List; + +public interface IChatDetector { + List<ReplacementContext> getReplacementContext(IChatComponent chatComponent); +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerChatByMe.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerChatByMe.java deleted file mode 100644 index 6f8bcb11..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerChatByMe.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.mod.cosmetics.chatreplacers; - -import kr.syeyoung.dungeonsguide.mod.cosmetics.ActiveCosmetic; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticData; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticsManager; -import kr.syeyoung.dungeonsguide.mod.cosmetics.IChatReplacer; -import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.ChatStyle; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.Tuple; -import net.minecraftforge.client.event.ClientChatReceivedEvent; - -import java.util.ArrayList; -import java.util.List; - -public class ChatReplacerChatByMe implements IChatReplacer { - @Override - public boolean isAcceptable(ClientChatReceivedEvent event) { - for (IChatComponent sibling : event.message.getSiblings()) { - if (sibling.getUnformattedTextForChat().startsWith(": ")) return true; - } - return false; - } - - @Override - public void translate(ClientChatReceivedEvent event, CosmeticsManager cosmeticsManager) { - List<Tuple<IChatComponent, IChatComponent>> replacements = new ArrayList<>(); - List<IChatComponent> iChatComponents = new ArrayList<>( event.message.getSiblings() ); - List<IChatComponent> hasMsg = new ArrayList<>(); - for (IChatComponent sibling : iChatComponents) { - if (sibling.getUnformattedTextForChat().startsWith(": ")) break; - hasMsg.add(sibling); - } - iChatComponents.removeAll(hasMsg); - - ChatComponentText chatComponents = new ChatComponentText(""); - chatComponents.getSiblings().addAll(hasMsg); - ChatStyle origStyle = hasMsg.get(0).getChatStyle(); - String name = chatComponents.getFormattedText(); - - String[] split = name.split(" "); - String actualName = split[split.length-1]; - - List<ActiveCosmetic> cData = cosmeticsManager.getActiveCosmeticByPlayerNameLowerCase().get(TextUtils.stripColor(actualName).toLowerCase()); - if (cData == null) return; - CosmeticData color=null, prefix=null; - for (ActiveCosmetic activeCosmetic : cData) { - CosmeticData cosmeticData = cosmeticsManager.getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); - if (cosmeticData !=null && cosmeticData.getCosmeticType().equals("color")) { - color = cosmeticData; - } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { - prefix = cosmeticData; - } - } - - String building = ""; - if (prefix != null) building += prefix.getData().replace("&", "§") + " "; - for (int i = 0; i < split.length-1; i++) { - building += split[i] +" "; - } - - if (color != null) { - String nick = split[split.length-1]; - building += color.getData().replace("&","§"); - boolean foundLegitChar = false; - boolean foundColor = false; - for (char c : nick.toCharArray()) { - if (foundColor) { - foundColor = false; continue; - } - if (c == '§' && !foundLegitChar) foundColor = true; - else { - foundLegitChar = true; - building += c; - } - } - } else { - building += split[split.length-1] ; - } - - ChatComponentText chatComponents1 = new ChatComponentText(building); - chatComponents1.setChatStyle(origStyle); - event.message.getSiblings().clear(); - event.message.getSiblings().add(chatComponents1); - event.message.getSiblings().addAll(iChatComponents); - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerCoop.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerCoop.java deleted file mode 100644 index 6522b4b2..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerCoop.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.mod.cosmetics.chatreplacers; - -import kr.syeyoung.dungeonsguide.mod.cosmetics.ActiveCosmetic; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticData; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticsManager; -import kr.syeyoung.dungeonsguide.mod.cosmetics.IChatReplacer; -import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.Tuple; -import net.minecraftforge.client.event.ClientChatReceivedEvent; - -import java.util.ArrayList; -import java.util.List; - -public class ChatReplacerCoop implements IChatReplacer { - @Override - public boolean isAcceptable(ClientChatReceivedEvent event) { - for (IChatComponent sibling : event.message.getSiblings()) { - if (sibling.getUnformattedTextForChat().startsWith("§bCo-op > ")) return true; - } - return false; - } - - @Override - public void translate(ClientChatReceivedEvent event, CosmeticsManager cosmeticsManager) { - List<Tuple<IChatComponent, IChatComponent>> replacements = new ArrayList<>(); - List<IChatComponent> iChatComponents = new ArrayList<>( event.message.getSiblings() ); - IChatComponent sibling = iChatComponents.get(0); iChatComponents.remove(sibling); - - - String[] splitInto = sibling.getUnformattedTextForChat().split(" "); - int lastValidNickname = -1; - int lastPrefix = -1; - for (int i = 0; i < splitInto.length; i++) { - String s = TextUtils.stripColor(splitInto[i]); - char c = s.charAt(0); - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-') { - lastValidNickname = i; - } - } - if (lastValidNickname == -1) return; - - if (lastValidNickname -1 >= 0 && TextUtils.stripColor(splitInto[lastValidNickname - 1]).charAt(0) == '[') lastPrefix = lastValidNickname -1; - else lastPrefix = lastValidNickname; - - - List<ActiveCosmetic> cData = cosmeticsManager.getActiveCosmeticByPlayerNameLowerCase().get(TextUtils.stripColor(splitInto[lastValidNickname].substring(0, splitInto[lastValidNickname].length()-1)).toLowerCase()); - - if (cData == null) return; - - CosmeticData color = null, prefix = null; - for (ActiveCosmetic activeCosmetic : cData) { - CosmeticData cosmeticData = cosmeticsManager.getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); - if (cosmeticData != null && cosmeticData.getCosmeticType().equals("color")) { - color = cosmeticData; - } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { - prefix = cosmeticData; - } - } - - String building = ""; - for (int i = 0; i < lastPrefix; i++) { - building += splitInto[i] +" "; - } - if (prefix != null) building += prefix.getData().replace("&", "§") + " "; - for (int i = lastPrefix; i < lastValidNickname; i++) { - building += splitInto[i] +" "; - } - if (color != null) { - String nick = splitInto[lastValidNickname]; - building += color.getData().replace("&","§"); - boolean foundLegitChar = false; - boolean foundColor = false; - for (char c : nick.toCharArray()) { - if (foundColor) { - foundColor = false; continue; - } - if (c == '§' && !foundLegitChar) foundColor = true; - else { - foundLegitChar = true; - building += c; - } - } - building += " "; - } else { - building += splitInto[lastValidNickname] + " "; - } - for (int i = lastValidNickname+1; i<splitInto.length; i++) { - building += splitInto[i] + " "; - } - if (sibling.getUnformattedTextForChat().charAt(sibling.getUnformattedText().length()-1) != ' ') - building = building.substring(0, building.length() - 1); - - ChatComponentText chatComponents1 = new ChatComponentText(building); - chatComponents1.setChatStyle(sibling.getChatStyle()); - event.message.getSiblings().clear(); - event.message.getSiblings().add(chatComponents1); - event.message.getSiblings().addAll(iChatComponents); - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerMessage.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerMessage.java deleted file mode 100644 index db59f878..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerMessage.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.mod.cosmetics.chatreplacers; - -import kr.syeyoung.dungeonsguide.mod.cosmetics.ActiveCosmetic; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticData; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticsManager; -import kr.syeyoung.dungeonsguide.mod.cosmetics.IChatReplacer; -import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.ChatStyle; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.Tuple; -import net.minecraftforge.client.event.ClientChatReceivedEvent; - -import java.util.ArrayList; -import java.util.List; - -public class ChatReplacerMessage implements IChatReplacer { - @Override - public boolean isAcceptable(ClientChatReceivedEvent event) { - for (IChatComponent sibling : event.message.getSiblings()) { - if (sibling.getChatStyle() != null && sibling.getChatStyle().getChatClickEvent() != null && sibling.getChatStyle().getChatClickEvent().getValue().startsWith("/msg")) return true; - } - return false; - } - - @Override - public void translate(ClientChatReceivedEvent event, CosmeticsManager cosmeticsManager) { - List<Tuple<IChatComponent, IChatComponent>> replacements = new ArrayList<>(); - List<IChatComponent> iChatComponents = new ArrayList<>( event.message.getSiblings() ); - List<IChatComponent> hasMsg = new ArrayList<>(); - for (IChatComponent sibling : iChatComponents) { - if (sibling.getChatStyle() != null && sibling.getChatStyle().getChatClickEvent() != null && sibling.getChatStyle().getChatClickEvent().getValue().startsWith("/msg")) { - hasMsg.add(sibling); - } - } - iChatComponents.removeAll(hasMsg); - - ChatComponentText chatComponents = new ChatComponentText(""); - chatComponents.getSiblings().addAll(hasMsg); - ChatStyle origStyle = hasMsg.get(0).getChatStyle(); - String name = chatComponents.getFormattedText(); - - - String[] split = name.split(" "); - String actualName = split[split.length-1]; - - List<ActiveCosmetic> cData = cosmeticsManager.getActiveCosmeticByPlayerNameLowerCase().get(TextUtils.stripColor(actualName).toLowerCase()); - if (cData == null || split.length > 2) return; - CosmeticData color=null, prefix=null; - for (ActiveCosmetic activeCosmetic : cData) { - CosmeticData cosmeticData = cosmeticsManager.getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); - if (cosmeticData !=null && cosmeticData.getCosmeticType().equals("color")) { - color = cosmeticData; - } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { - prefix = cosmeticData; - } - } - - String building = ""; - if (prefix != null) building += prefix.getData().replace("&", "§") + " "; - if (split.length == 2) building += split[0] +" "; - - if (color != null) { - String nick = split[1]; - building += color.getData().replace("&","§"); - boolean foundLegitChar = false; - boolean foundColor = false; - for (char c : nick.toCharArray()) { - if (foundColor) { - foundColor = false; continue; - } - if (c == '§' && !foundLegitChar) foundColor = true; - else { - foundLegitChar = true; - building += c; - } - } - } else { - building += split[1] ; - } - - ChatComponentText chatComponents1 = new ChatComponentText(building); - chatComponents1.setChatStyle(origStyle); - event.message.getSiblings().clear(); - event.message.getSiblings().add(chatComponents1); - event.message.getSiblings().addAll(iChatComponents); - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerPV.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerPV.java deleted file mode 100644 index 2f1c8da9..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerPV.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.mod.cosmetics.chatreplacers; - -import kr.syeyoung.dungeonsguide.mod.cosmetics.ActiveCosmetic; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticData; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticsManager; -import kr.syeyoung.dungeonsguide.mod.cosmetics.IChatReplacer; -import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; -import net.minecraft.util.ChatComponentText; -import net.minecraftforge.client.event.ClientChatReceivedEvent; - -import java.util.List; - - -// Bug 289 - mod conflict with NEU (Replaces /socialoptions with /pv) -public class ChatReplacerPV implements IChatReplacer { - @Override - public boolean isAcceptable(ClientChatReceivedEvent event) { - if (event.message.getChatStyle() != null && event.message.getChatStyle().getChatClickEvent() != null && event.message.getChatStyle().getChatClickEvent().getValue().startsWith("/pv")) return true; - return false; - } - - @Override - public void translate(ClientChatReceivedEvent event, CosmeticsManager cosmeticsManager) { - if (event.message.getChatStyle() != null && event.message.getChatStyle().getChatClickEvent() != null && event.message.getChatStyle().getChatClickEvent().getValue().startsWith("/pv")) { - String username = event.message.getChatStyle().getChatClickEvent().getValue().split(" ")[1]; - List<ActiveCosmetic> cData = cosmeticsManager.getActiveCosmeticByPlayerNameLowerCase().get(username.toLowerCase()); - - if (cData != null) { - CosmeticData color=null, prefix=null; - for (ActiveCosmetic activeCosmetic : cData) { - CosmeticData cosmeticData = cosmeticsManager.getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); - if (cosmeticData !=null && cosmeticData.getCosmeticType().equals("color")) { - color = cosmeticData; - } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { - prefix = cosmeticData; - } - } - - String[] splitInto = event.message.getUnformattedTextForChat().split(" "); - int lastValidNickname = -1; - int lastPrefix = -1; - for (int i = 0; i < splitInto.length; i++) { - String s = splitInto[i]; - if (s.startsWith("§7")) s = s.substring(2); - char c = s.charAt(0); - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-') { - lastValidNickname = i; - if (i >= 1) { - String str = TextUtils.stripColor(splitInto[i-1]); - if (str.startsWith("[") && str.endsWith("]"))break; - } - } - } - if (lastValidNickname == -1) return; - - if (lastValidNickname -1 >= 0 && TextUtils.stripColor(splitInto[lastValidNickname - 1]).charAt(0) == '[') lastPrefix = lastValidNickname -1; - else lastPrefix = lastValidNickname; - - String building = ""; - for (int i = 0; i < lastPrefix; i++) { - building += splitInto[i] +" "; - } - if (prefix != null) building += prefix.getData().replace("&", "§") + " "; - for (int i = lastPrefix; i < lastValidNickname; i++) { - building += splitInto[i] +" "; - } - if (color != null) { - String nick = splitInto[lastValidNickname]; - building += color.getData().replace("&","§"); - boolean foundLegitChar = false; - boolean foundColor = false; - for (char c : nick.toCharArray()) { - if (foundColor) { - foundColor = false; continue; - } - if (c == '§' && !foundLegitChar) foundColor = true; - else { - foundLegitChar = true; - building += c; - } - } - building += " "; - } else { - building += splitInto[lastValidNickname] + " "; - } - for (int i = lastValidNickname+1; i<splitInto.length; i++) { - building += splitInto[i] + " "; - } - if (event.message.getUnformattedTextForChat().charAt(event.message.getUnformattedTextForChat().length()-1) != ' ') - building = building.substring(0, building.length() - 1); - - ChatComponentText newChatCompText = new ChatComponentText(building); - newChatCompText.setChatStyle(event.message.getChatStyle()); - newChatCompText.getSiblings().addAll(event.message.getSiblings()); - - event.message = newChatCompText; - } - } - - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerSocialOptions.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerSocialOptions.java deleted file mode 100644 index af855938..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerSocialOptions.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.mod.cosmetics.chatreplacers; - -import kr.syeyoung.dungeonsguide.mod.cosmetics.ActiveCosmetic; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticData; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticsManager; -import kr.syeyoung.dungeonsguide.mod.cosmetics.IChatReplacer; -import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; -import net.minecraft.util.ChatComponentText; -import net.minecraftforge.client.event.ClientChatReceivedEvent; - -import java.util.List; - -public class ChatReplacerSocialOptions implements IChatReplacer { - @Override - public boolean isAcceptable(ClientChatReceivedEvent event) { - if (event.message.getChatStyle() != null && event.message.getChatStyle().getChatClickEvent() != null && event.message.getChatStyle().getChatClickEvent().getValue().startsWith("/socialoptions")) return true; - return false; - } - - @Override - public void translate(ClientChatReceivedEvent event, CosmeticsManager cosmeticsManager) { - if (event.message.getChatStyle() != null && event.message.getChatStyle().getChatClickEvent() != null && event.message.getChatStyle().getChatClickEvent().getValue().startsWith("/socialoptions")) { - String username = event.message.getChatStyle().getChatClickEvent().getValue().split(" ")[1]; - List<ActiveCosmetic> cData = cosmeticsManager.getActiveCosmeticByPlayerNameLowerCase().get(username.toLowerCase()); - - if (cData != null) { - CosmeticData color=null, prefix=null; - for (ActiveCosmetic activeCosmetic : cData) { - CosmeticData cosmeticData = cosmeticsManager.getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); - if (cosmeticData !=null && cosmeticData.getCosmeticType().equals("color")) { - color = cosmeticData; - } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { - prefix = cosmeticData; - } - } - - String[] splitInto = event.message.getUnformattedTextForChat().split(" "); - int lastValidNickname = -1; - int lastPrefix = -1; - for (int i = 0; i < splitInto.length; i++) { - String s = splitInto[i]; - if (s.startsWith("§7")) s = s.substring(2); - char c = s.charAt(0); - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-') { - lastValidNickname = i; - if (i >= 1) { - String str = TextUtils.stripColor(splitInto[i-1]); - if (str.startsWith("[") && str.endsWith("]"))break; - } - } - } - if (lastValidNickname == -1) return; - - if (lastValidNickname -1 >= 0 && TextUtils.stripColor(splitInto[lastValidNickname - 1]).charAt(0) == '[') lastPrefix = lastValidNickname -1; - else lastPrefix = lastValidNickname; - - String building = ""; - for (int i = 0; i < lastPrefix; i++) { - building += splitInto[i] +" "; - } - if (prefix != null) building += prefix.getData().replace("&", "§") + " "; - for (int i = lastPrefix; i < lastValidNickname; i++) { - building += splitInto[i] +" "; - } - if (color != null) { - String nick = splitInto[lastValidNickname]; - building += color.getData().replace("&","§"); - boolean foundLegitChar = false; - boolean foundColor = false; - for (char c : nick.toCharArray()) { - if (foundColor) { - foundColor = false; continue; - } - if (c == '§' && !foundLegitChar) foundColor = true; - else { - foundLegitChar = true; - building += c; - } - } - building += " "; - } else { - building += splitInto[lastValidNickname] + " "; - } - for (int i = lastValidNickname+1; i<splitInto.length; i++) { - building += splitInto[i] + " "; - } - if (event.message.getUnformattedTextForChat().charAt(event.message.getUnformattedTextForChat().length()-1) != ' ') - building = building.substring(0, building.length() - 1); - - ChatComponentText newChatCompText = new ChatComponentText(building); - newChatCompText.setChatStyle(event.message.getChatStyle()); - newChatCompText.getSiblings().addAll(event.message.getSiblings()); - - event.message = newChatCompText; - } - } - - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerViewProfile.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerViewProfile.java deleted file mode 100644 index c2993258..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/chatreplacers/ChatReplacerViewProfile.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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.mod.cosmetics.chatreplacers; - -import kr.syeyoung.dungeonsguide.mod.cosmetics.ActiveCosmetic; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticData; -import kr.syeyoung.dungeonsguide.mod.cosmetics.CosmeticsManager; -import kr.syeyoung.dungeonsguide.mod.cosmetics.IChatReplacer; -import kr.syeyoung.dungeonsguide.mod.utils.TextUtils; -import net.minecraft.util.ChatComponentText; -import net.minecraft.util.IChatComponent; -import net.minecraft.util.Tuple; -import net.minecraftforge.client.event.ClientChatReceivedEvent; - -import java.util.ArrayList; -import java.util.List; - -public class ChatReplacerViewProfile implements IChatReplacer { - @Override - public boolean isAcceptable(ClientChatReceivedEvent event) { - for (IChatComponent sibling : event.message.getSiblings()) { - if (sibling.getChatStyle() != null && sibling.getChatStyle().getChatClickEvent() != null && sibling.getChatStyle().getChatClickEvent().getValue().startsWith("/viewprofile ")) return true; - } - return false; - } - - @Override - public void translate(ClientChatReceivedEvent event, CosmeticsManager cosmeticsManager) { - List<Tuple<IChatComponent, IChatComponent>> replacements = new ArrayList<>(); - for (IChatComponent sibling : event.message.getSiblings()) { - if (sibling.getChatStyle() != null && sibling.getChatStyle().getChatClickEvent() != null && sibling.getChatStyle().getChatClickEvent().getValue().startsWith("/viewprofile ")) { - String uid = sibling.getChatStyle().getChatClickEvent().getValue().split(" ")[1]; - // TODO: make cosmeticsManager handle usernames instead of uuids - // apparently now hypixel's /viewprofile command gives the nickname -// List<ActiveCosmetic> cData = cosmeticsManager.getActiveCosmeticByPlayer().get(UUID.fromString(uid)); - List<ActiveCosmetic> cData = null; - - if (cData != null) { - CosmeticData color=null, prefix=null; - for (ActiveCosmetic activeCosmetic : cData) { - CosmeticData cosmeticData = cosmeticsManager.getCosmeticDataMap().get(activeCosmetic.getCosmeticData()); - if (cosmeticData !=null && cosmeticData.getCosmeticType().equals("color")) { - color = cosmeticData; - } else if (cosmeticData != null && cosmeticData.getCosmeticType().equals("prefix")) { - prefix = cosmeticData; - } - } - - String[] splitInto = sibling.getUnformattedTextForChat().split(" "); - int lastValidNickname = -1; - int lastPrefix = -1; - for (int i = 0; i < splitInto.length; i++) { - String s = TextUtils.stripColor(splitInto[i]); - char c = s.charAt(0); - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-') { - lastValidNickname = i; - if (i >= 1) { - String str = TextUtils.stripColor(splitInto[i-1]); - if (str.startsWith("[") && str.endsWith("]"))break; - } - } - } - if (lastValidNickname == -1) continue; - - if (lastValidNickname -1 >= 0 && TextUtils.stripColor(splitInto[lastValidNickname - 1]).charAt(0) == '[') lastPrefix = lastValidNickname -1; - else lastPrefix = lastValidNickname; - - String building = ""; - for (int i = 0; i < lastPrefix; i++) { - building += splitInto[i] +" "; - } - if (prefix != null) building += prefix.getData().replace("&", "§") + " "; - for (int i = lastPrefix; i < lastValidNickname; i++) { - building += splitInto[i] +" "; - } - if (color != null) { - String nick = splitInto[lastValidNickname]; - building += color.getData().replace("&","§"); - boolean foundLegitChar = false; - boolean foundColor = false; - for (char c : nick.toCharArray()) { - if (foundColor) { - foundColor = false; continue; - } - if (c == '§' && !foundLegitChar) foundColor = true; - else { - foundLegitChar = true; - building += c; - } - } - building += " "; - } else { - building += splitInto[lastValidNickname] + " "; - } - for (int i = lastValidNickname+1; i<splitInto.length; i++) { - building += splitInto[i] + " "; - } - if (sibling.getUnformattedTextForChat().charAt(sibling.getUnformattedText().length()-1) != ' ') - building = building.substring(0, building.length() - 1); - - ChatComponentText newChatCompText = new ChatComponentText(building); - newChatCompText.setChatStyle(sibling.getChatStyle()); - replacements.add(new Tuple<>(sibling, newChatCompText)); - break; - } - } - } - - for (Tuple<IChatComponent, IChatComponent> replacement : replacements) { - int index = event.message.getSiblings().indexOf(replacement.getFirst()); - event.message.getSiblings().set(index, replacement.getSecond()); - } - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/surgical/ReplacementContext.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/surgical/ReplacementContext.java new file mode 100644 index 00000000..b1ebc717 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/surgical/ReplacementContext.java @@ -0,0 +1,31 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.surgical; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ReplacementContext { + private int nearIdx; + + private String username; + private String uuid; // aiding uuid. +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/surgical/SurgicalReplacer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/surgical/SurgicalReplacer.java new file mode 100644 index 00000000..475b10f9 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/cosmetics/surgical/SurgicalReplacer.java @@ -0,0 +1,190 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * 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.mod.cosmetics.surgical; + +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; + +import java.util.*; + +public class SurgicalReplacer { + + public static List<ChatComponentText> getChatStyleOf(String str, ChatStyle parentStyle) { + boolean randomStyle = parentStyle.getObfuscated(); + boolean boldStyle = parentStyle.getBold(); + boolean strikethroughStyle = parentStyle.getStrikethrough(); + boolean underlineStyle = parentStyle.getUnderlined(); + boolean italicStyle = parentStyle.getItalic(); + int colorStyle = parentStyle.getColor() == null ? 15 : parentStyle.getColor().getColorIndex(); + char[] charArr = str.toCharArray(); + + List<ChatComponentText> list = new LinkedList<>(); + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < charArr.length; i++) { + char c0 = charArr[i]; + if (c0 == 167 && i + 1 < charArr.length) { + if (stringBuilder.length() != 0) { + ChatComponentText chatComponents = new ChatComponentText(stringBuilder.toString()); + chatComponents.setChatStyle(new ChatStyle() + .setBold(boldStyle) + .setObfuscated(randomStyle) + .setStrikethrough(strikethroughStyle) + .setUnderlined(underlineStyle) + .setItalic(italicStyle) + .setColor(EnumChatFormatting.func_175744_a(colorStyle)) + .setChatHoverEvent(parentStyle.getChatHoverEvent()) + .setChatClickEvent(parentStyle.getChatClickEvent())); + list.add(chatComponents); + stringBuilder = new StringBuilder(); + } + int i1 = "0123456789abcdefklmnor".indexOf(Character.toLowerCase(charArr[i + 1])); + if (i1 < 16) { + randomStyle = false; + boldStyle = false; + strikethroughStyle = false; + underlineStyle = false; + italicStyle = false; + if (i1 < 0) { + i1 = 15; + } + colorStyle = i1; + } else if (i1 == 16) { + randomStyle = true; + } else if (i1 == 17) { + boldStyle = true; + } else if (i1 == 18) { + strikethroughStyle = true; + } else if (i1 == 19) { + underlineStyle = true; + } else if (i1 == 20) { + italicStyle = true; + } else { + randomStyle = false; + boldStyle = false; + strikethroughStyle = false; + underlineStyle = false; + italicStyle = false; + colorStyle = 15; + } + + ++i; + } else { + stringBuilder.append(c0); + } + } + ChatComponentText chatComponents = new ChatComponentText(stringBuilder.toString()); + chatComponents.setChatStyle(new ChatStyle() + .setBold(boldStyle) + .setObfuscated(randomStyle) + .setStrikethrough(strikethroughStyle) + .setUnderlined(underlineStyle) + .setItalic(italicStyle) + .setColor(EnumChatFormatting.func_175744_a(colorStyle)) + .setChatHoverEvent(parentStyle.getChatHoverEvent()) + .setChatClickEvent(parentStyle.getChatClickEvent())); + list.add(chatComponents); + return list; + } + + public static LinkedList<IChatComponent> linearifyMoveColorCharToStyle(IChatComponent iChatComponent) { + LinkedList<IChatComponent> chatComponents = new LinkedList<>(); + for (IChatComponent component : iChatComponent) { + if (component instanceof ChatComponentText) { + chatComponents.addAll(getChatStyleOf(((ChatComponentText) component).getChatComponentText_TextValue(), component.getChatStyle().createDeepCopy())); + } else { + IChatComponent neuCopy = component.createCopy(); + neuCopy.getSiblings().clear(); + chatComponents.add(neuCopy.setChatStyle(component.getChatStyle().createDeepCopy())); + } + } + return chatComponents; + } + + public static ChatStyle getChatStyleAt(List<IChatComponent> chatComponents, int idx) { + int i = 0; + for (IChatComponent chatComponent : chatComponents) { + i += chatComponent.getUnformattedTextForChat().length(); + if (i > idx) return chatComponent.getChatStyle().createDeepCopy(); + } + return null; + } + public static LinkedList<IChatComponent> inject(LinkedList<IChatComponent> linearified, List<IChatComponent> toInjectLinearified, int idx, int len) { + LinkedList<IChatComponent> clone = new LinkedList<>(); + int currLen = 0; + boolean injected = false; + while (!linearified.isEmpty()) { + if (currLen == idx && !injected) { + clone.addAll(toInjectLinearified); + injected = true; + } + + IChatComponent toProcess = linearified.poll(); + int procLen = toProcess.getUnformattedTextForChat().length(); + + if (currLen + procLen <= idx) { + clone.add(toProcess); + currLen += procLen; + continue; + } + + if (currLen + procLen > idx && currLen < idx) { + ChatComponentText chatComponents = new ChatComponentText( + toProcess.getUnformattedTextForChat().substring(0, idx - currLen) + ); + chatComponents.setChatStyle(toProcess.getChatStyle()); + + clone.add(chatComponents); + + ChatComponentText next = new ChatComponentText( + toProcess.getUnformattedTextForChat().substring(idx-currLen) + ); + next.setChatStyle(toProcess.getChatStyle()); + linearified.addFirst(next); + + currLen = idx; + continue; + } + + if (currLen + procLen <= idx + len) { + currLen += procLen; + continue; + } + if (currLen + procLen > idx + len && currLen < idx + len) { + ChatComponentText next = new ChatComponentText( + toProcess.getUnformattedTextForChat().substring(idx + len - currLen) + ); + next.setChatStyle(toProcess.getChatStyle()); + clone.add(next); + currLen += procLen; + continue; + } + clone.add(toProcess); + currLen += procLen; + } + return clone; + } + public static IChatComponent combine(List<IChatComponent> components) { + ChatComponentText chatComponents = new ChatComponentText(""); + chatComponents.getSiblings().addAll(components); + return chatComponents; + } +} |