diff --git a/build.gradle b/build.gradle index 171f86f4..7fafea2e 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,14 @@ repositories { maven { url 'https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1' } } +tasks.withType(JavaExec).configureEach { + System.properties.each { k, v -> + if (k.startsWith('run.')) { + systemProperty k - 'run.', v + } + } +} + configurations { shadeOnly shade diff --git a/src/main/java/io/github/solclient/client/SolClient.java b/src/main/java/io/github/solclient/client/SolClient.java index e8353364..147dd29d 100644 --- a/src/main/java/io/github/solclient/client/SolClient.java +++ b/src/main/java/io/github/solclient/client/SolClient.java @@ -253,8 +253,10 @@ private void prepare(ModInfo info) { private static Gson getGson(Mod mod) { GsonBuilder builder = new GsonBuilder(); - if (mod != null) - builder.registerTypeAdapter(mod.getClass(), (InstanceCreator) (type) -> mod); + if (mod != null) { + builder.registerTypeAdapter(mod.getClass(), (InstanceCreator) (type) -> mod); + mod.registerOtherTypeAdapters(builder); + } return builder.excludeFieldsWithoutExposeAnnotation().create(); } diff --git a/src/main/java/io/github/solclient/client/event/impl/ReceiveChatMessageEvent.java b/src/main/java/io/github/solclient/client/event/impl/ReceiveChatMessageEvent.java index f7232cfb..38cb8c2d 100644 --- a/src/main/java/io/github/solclient/client/event/impl/ReceiveChatMessageEvent.java +++ b/src/main/java/io/github/solclient/client/event/impl/ReceiveChatMessageEvent.java @@ -19,16 +19,19 @@ package io.github.solclient.client.event.impl; import lombok.RequiredArgsConstructor; +import net.minecraft.text.Text; @RequiredArgsConstructor public class ReceiveChatMessageEvent { public final boolean actionBar; - public final String message; + public final String originalMessage; + public final Text formattedMessage; /** * Whether the event is fired from the replay mod. */ public final boolean replay; public boolean cancelled; + public Text newMessage = null; } diff --git a/src/main/java/io/github/solclient/client/mod/Mod.java b/src/main/java/io/github/solclient/client/mod/Mod.java index 78b62d65..0dce8214 100644 --- a/src/main/java/io/github/solclient/client/mod/Mod.java +++ b/src/main/java/io/github/solclient/client/mod/Mod.java @@ -337,6 +337,10 @@ public void setPinned(boolean pinned) { } } + public void registerOtherTypeAdapters(GsonBuilder builder) { + + } + void notifyUnpin() { pinned = false; } diff --git a/src/main/java/io/github/solclient/client/mod/impl/core/mixins/client/ClientPlayNetworkHandlerMixin.java b/src/main/java/io/github/solclient/client/mod/impl/core/mixins/client/ClientPlayNetworkHandlerMixin.java index e5c9c378..d10d06e6 100644 --- a/src/main/java/io/github/solclient/client/mod/impl/core/mixins/client/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/io/github/solclient/client/mod/impl/core/mixins/client/ClientPlayNetworkHandlerMixin.java @@ -18,6 +18,7 @@ package io.github.solclient.client.mod.impl.core.mixins.client; +import net.minecraft.text.LiteralText; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -56,16 +57,20 @@ public void handleEntityStatus(EntityStatusS2CPacket packet, CallbackInfo callba @Redirect(method = "onChatMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;)V")) public void handleChat(ChatHud instance, Text message) { - if (!EventBus.INSTANCE.post( - new ReceiveChatMessageEvent(false, Formatting.strip(message.asUnformattedString()), false)).cancelled) { - instance.addMessage(message); + ReceiveChatMessageEvent event = new ReceiveChatMessageEvent(false, Formatting.strip(message.asUnformattedString()), message, false); + if (!EventBus.INSTANCE.post(event).cancelled) { + if (event.newMessage != null) { + instance.addMessage(event.newMessage); + } else { + instance.addMessage(message); + } } } @Redirect(method = "onChatMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;setOverlayMessage(Lnet/minecraft/text/Text;Z)V")) public void handleActionBar(InGameHud instance, Text text, boolean tinted) { if (!EventBus.INSTANCE.post( - new ReceiveChatMessageEvent(true, Formatting.strip(text.asUnformattedString()), false)).cancelled) { + new ReceiveChatMessageEvent(true, Formatting.strip(text.asUnformattedString()), text, false)).cancelled) { instance.setOverlayMessage(text, tinted); } } diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/PaperDollMod.java b/src/main/java/io/github/solclient/client/mod/impl/hud/PaperDollMod.java new file mode 100644 index 00000000..e4fb3a89 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/PaperDollMod.java @@ -0,0 +1,142 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud; + +import com.google.gson.annotations.Expose; +import com.mojang.blaze3d.platform.GLX; +import com.mojang.blaze3d.platform.GlStateManager; +import io.github.solclient.client.event.EventHandler; +import io.github.solclient.client.event.impl.PlayerHeadRotateEvent; +import io.github.solclient.client.event.impl.PreTickEvent; +import io.github.solclient.client.mod.impl.SolClientHudMod; +import io.github.solclient.client.mod.option.ModOption; +import io.github.solclient.client.mod.option.ModOptionStorage; +import io.github.solclient.client.mod.option.annotation.AbstractTranslationKey; +import io.github.solclient.client.mod.option.annotation.Option; +import io.github.solclient.client.mod.option.impl.SliderOption; +import io.github.solclient.client.util.MinecraftUtils; +import io.github.solclient.client.util.data.Position; +import io.github.solclient.client.util.data.Rectangle; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.DiffuseLighting; +import net.minecraft.client.render.entity.EntityRenderDispatcher; +import net.minecraft.client.resource.language.I18n; + +import java.util.List; +import java.util.Optional; + +// Originally from KronHUD +// Added here by DarkKronicle :) +// https://github.com/DarkKronicle/KronHUD/blob/master/src/main/java/io/github/darkkronicle/kronhud/gui/hud/PlayerHud.java +@AbstractTranslationKey("sol_client.mod.paperdoll") +public class PaperDollMod extends SolClientHudMod { + + @Expose + @Option + private boolean dynamicRotation = true; + + @Expose + private float rotation = 0; + + private float lastYawOffset = 0; + private float yawOffset = 0; + + @Override + public Rectangle getBounds(Position position) { + return new Rectangle(position.getX(), position.getY(), 62, 94); + } + + @Override + public String getDetail() { + return I18n.translate("sol_client.mod.screen.by", "DarkKronicle"); // maybe also add original creator + } + + @Override + protected List> createOptions() { + List> options = super.createOptions(); + Optional format = Optional.empty(); + options.add( + new SliderOption( + "sol_client.mod.paperdoll.option.rotation", + ModOptionStorage.of(Number.class, () -> rotation, (value) -> rotation = value.floatValue()), + format, 0, 360, 1 + ) + ); + return options; + } + + public void renderPlayer(double x, double y, float delta) { + if (mc.player == null) { + return; + } + + float deltaYaw = mc.player.prevYaw + (mc.player.yaw - mc.player.prevYaw) * delta; + if (dynamicRotation) { + deltaYaw -= (lastYawOffset + ((yawOffset - lastYawOffset) * delta)); + } + + GlStateManager.enableLighting(); + GlStateManager.color(1, 1, 1, 1); + GlStateManager.enableColorMaterial(); + GlStateManager.pushMatrix(); + GlStateManager.translate((float) x, (float) y, 500.0F); + GlStateManager.scale((float) (-40), (float) 40, (float) 40); + GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F); + + GlStateManager.rotate(135.0F, 0.0F, 1.0F, 0.0F); + DiffuseLighting.enableNormally(); + GlStateManager.rotate(-135.0F, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(deltaYaw + rotation, 0.0F, 1.0F, 0.0F); + GlStateManager.translate(0.0F, 0.0F, 0.0F); + + EntityRenderDispatcher entityRenderDispatcher = MinecraftClient.getInstance().getEntityRenderManager(); + entityRenderDispatcher.setYaw(0); + entityRenderDispatcher.setRenderShadows(false); + entityRenderDispatcher.render(mc.player, 0.0, 0.0, 0.0, 0.0F, MinecraftUtils.getTickDelta()); + entityRenderDispatcher.setRenderShadows(true); + + GlStateManager.popMatrix(); + DiffuseLighting.disable(); + GlStateManager.disableRescaleNormal(); + GlStateManager.activeTexture(GLX.lightmapTextureUnit); + GlStateManager.disableTexture(); + GlStateManager.activeTexture(GLX.textureUnit); + + } + + @EventHandler + public void onPlayerRotate(PlayerHeadRotateEvent event) { + if (event.yaw == 0 && event.pitch == 0) { + return; + } + yawOffset += (event.yaw * .15) / 2; + } + + @Override + public void render(Position position, boolean editMode) { + renderPlayer(position.getX() + 31, position.getY() + 86, MinecraftUtils.getTickDelta()); + } + + @EventHandler + public void onTick(PreTickEvent event) { + lastYawOffset = yawOffset; + yawOffset *= .93f; + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsDeathType.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsDeathType.java new file mode 100644 index 00000000..fde69f58 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsDeathType.java @@ -0,0 +1,59 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@AllArgsConstructor +public enum BedwarsDeathType { + COMBAT("rekt", BedwarsMessages.COMBAT_KILL), + VOID("yeeted into void", BedwarsMessages.VOID_KILL), + PROJECTILE("shot", BedwarsMessages.PROJECTILE_KILL), + FALL("fall", BedwarsMessages.FALL_KILL), + GOLEM("golem moment", BedwarsMessages.GOLEM_KILL), + SELF_VOID("voided", new Pattern[]{BedwarsMessages.SELF_VOID}), + SELF_UNKNOWN("died", new Pattern[]{BedwarsMessages.SELF_UNKNOWN}), + ; + + @Getter + private final String inner; + + @Getter + private final Pattern[] patterns; + + public static boolean getDeath(String rawMessage, BedwarsDeathMatch ifPresent) { + for (BedwarsDeathType type : values()) { + if (BedwarsMessages.matched(type.getPatterns(), rawMessage, m -> ifPresent.onMatch(type, m))) { + return true; + } + } + return false; + } + + public interface BedwarsDeathMatch { + + void onMatch(BedwarsDeathType type, Matcher matcher); + + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsGame.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsGame.java new file mode 100644 index 00000000..338be7e2 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsGame.java @@ -0,0 +1,475 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import io.github.solclient.client.event.impl.ReceiveChatMessageEvent; +import io.github.solclient.client.event.impl.ScoreboardRenderEvent; +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades.BedwarsTeamUpgrades; +import lombok.Getter; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.scoreboard.ScoreboardPlayerScore; +import net.minecraft.scoreboard.Team; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; + +public class BedwarsGame { + + private static final int DIAMOND_START = 30; + private static final int DIAMOND_1 = 30; + private static final int DIAMOND_2 = 23; + private static final int DIAMOND_3 = 16; + private static final int EMERALD_START = 30; + private static final int EMERALD_1 = 65; + private static final int EMERALD_2 = 50; + private static final int EMERALD_3 = 35; + + private int diamondsTimer = DIAMOND_START; + private int emeraldsTimer = EMERALD_START; + + private BedwarsTeam won = null; + private int wonTick = -1; + private int seconds = 0; + private Text topBarText = new LiteralText(""); + private Text bottomBarText = new LiteralText(""); + + + private BedwarsPlayer me = null; + + private final Map players = new HashMap<>(16); + private final Map playersById = new HashMap<>(16); + private final MinecraftClient mc; + @Getter + private boolean started = false; + private final BedwarsMod mod; + @Getter + private final BedwarsTeamUpgrades upgrades = new BedwarsTeamUpgrades(); + + + public BedwarsGame(BedwarsMod mod) { + mc = MinecraftClient.getInstance(); + this.mod = mod; + } + + public void onStart() { + mod.upgradesOverlay.onStart(upgrades); + players.clear(); + playersById.clear(); + Map> teamPlayers = new HashMap<>(); + for (PlayerListEntry player : mc.player.networkHandler.getPlayerList()) { + String name = mc.inGameHud.getPlayerListWidget().getPlayerName(player).replaceAll("§.", ""); + if (name.charAt(1) != ' ') { + continue; + } + BedwarsTeam team = BedwarsTeam.fromPrefix(name.charAt(0)).orElse(null); + if (team == null) { + continue; + } + teamPlayers.compute(team, (t, entries) -> { + if (entries == null) { + List players = new ArrayList<>(); + players.add(player); + return players; + } + entries.add(player); + return entries; + }); + } + for (Map.Entry> teamPlayerList : teamPlayers.entrySet()) { + teamPlayerList.getValue().sort(Comparator.comparing(p -> p.getProfile().getName())); + List value = teamPlayerList.getValue(); + for (int i = 0; i < value.size(); i++) { + PlayerListEntry e = value.get(i); + BedwarsPlayer p = new BedwarsPlayer(teamPlayerList.getKey(), e, i + 1); + if (mc.player.getGameProfile().getName().equals(e.getProfile().getName())) { + me = p; + } + players.put(e.getProfile().getName(), p); + playersById.put(e.getProfile().getId(), p); + } + } + this.started = true; + } + + public Text getTopBarText() { + return topBarText; + } + + public Text getBottomBarText() { + return bottomBarText; + } + + private String calculateTopBarText() { + return getFormattedTime(); + } + + private String calculateBottomBarText() { + return "§bDiamonds - " + diamondsTimer + " §8| " + "§aEmeralds - " + emeraldsTimer; + } + + public String getFormattedTime() { + int minute = seconds / 60; + int second = seconds % 60; + String time = minute + ":"; + if (second < 10) { + time += "0" + second; + } else { + time += second; + } + return time; + } + + public Optional getPlayer(UUID uuid) { + return Optional.ofNullable(playersById.getOrDefault(uuid, null)); + } + + public Optional getPlayer(String name) { + return Optional.ofNullable(players.getOrDefault(name, null)); + } + + private void debug(String message) { + mc.inGameHud.getChatHud().addMessage(new LiteralText("§b§lINFO:§8 " + message)); + } + + private void died(ReceiveChatMessageEvent event, BedwarsPlayer player, @Nullable BedwarsPlayer killer, BedwarsDeathType type, boolean finalDeath) { + player.died(); + if (killer != null) { + killer.killed(finalDeath); + } + if (mod.overrideMessages) { + event.newMessage = new LiteralText(formatDeath(player, killer, type, finalDeath)); + } + } + + private String formatDisconnect(BedwarsPlayer disconnected) { + String playerFormatted = getPlayerFormatted(disconnected); + return playerFormatted + " §7§o/disconnected/"; + } + + private String formatReconnect(BedwarsPlayer reconnected) { + String playerFormatted = getPlayerFormatted(reconnected); + return playerFormatted + " §7§o/reconnected/"; + } + + private String formatEliminated(BedwarsTeam team) { + StringBuilder message = new StringBuilder( + "§6§l§oTEAM ELIMINATED §8§l> " + team.getColorSection() + team.getName() + " Team §7/eliminated/ "); + for (BedwarsPlayer p : players.values().stream() + .filter(b -> b.getTeam() == team) + .sorted(Comparator.comparingInt(BedwarsPlayer::getNumber)) + .collect(Collectors.toList())) + { + BedwarsPlayerStats stats = p.getStats(); + if (stats == null) { + continue; + } + message.append("\n") + .append("§b") + .append(stats.getStars()) + .append(" ") + .append(p.getColoredName()) + .append("§7 Beds: §f") + .append(stats.getBedsBroken()) + .append("§7 Finals: §f") + .append(stats.getFinalKills()) + .append("§7 FKDR: §f") + .append(String.format("%.2f", stats.getFKDR())) + .append("§7 BBLR: §f") + .append(String.format("%.2f", stats.getBBLR())); + } + return message.toString(); + } + + private String formatBed(BedwarsTeam team, BedwarsPlayer breaker) { + String playerFormatted = getPlayerFormatted(breaker); + return "§6§l§oBED BROKEN §8§l> " + team.getColorSection() + team.getName() + " Bed §7/broken/ " + playerFormatted + + (breaker.getStats() == null || breaker.getTeam() != me.getTeam() ? "" : " §6" + breaker.getStats().getBedsBroken()); + } + + private String formatDeath(BedwarsPlayer player, @Nullable BedwarsPlayer killer, BedwarsDeathType type, boolean finalDeath) { + String inner = type.getInner(); + if (finalDeath) { + inner = "§6§l/" + inner.toUpperCase(Locale.ROOT) + "/"; + } else { + inner = "§7/" + inner + "/"; + } + String playerFormatted = getPlayerFormatted(player); + if (killer == null) { + return playerFormatted + " " + inner; + } + String killerFormatted = getPlayerFormatted(killer); + if (finalDeath && killer.getStats() != null && killer.getTeam() == me.getTeam()) { + killerFormatted += " §6" + killer.getStats().getFinalKills(); + } + return playerFormatted + " " + inner + " " + killerFormatted; + } + + private String getPlayerFormatted(BedwarsPlayer player) { + return player.getColoredTeamNumber() + " " + player.getProfile().getProfile().getName(); + } + + public boolean isTeamEliminated(BedwarsTeam team) { + return players.values().stream().filter(b -> b.getTeam() == team).allMatch(BedwarsPlayer::isFinalKilled); + } + + public void onChatMessage(String rawMessage, ReceiveChatMessageEvent event) { + try { + if (mod.removeAnnoyingMessages && BedwarsMessages.matched(BedwarsMessages.ANNOYING_MESSAGES, rawMessage).isPresent()) { + event.cancelled = true; + return; + } + if (BedwarsDeathType.getDeath(rawMessage, (type, m) -> { + died(m, rawMessage, event, type); + })) { + return; + } + if (BedwarsMessages.matched(BedwarsMessages.BED_DESTROY, rawMessage, m -> { + BedwarsPlayer player = BedwarsMessages.matched(BedwarsMessages.BED_BREAK, rawMessage).flatMap(m1 -> getPlayer(m1.group(1))).orElse(null); + BedwarsTeam team = BedwarsTeam.fromName(m.group(1)).orElse(me.getTeam()); + bedDestroyed(event, team, player); + })) { + return; + } + if (BedwarsMessages.matched(BedwarsMessages.DISCONNECT, rawMessage, m -> getPlayer(m.group(1)).ifPresent(p -> disconnected(event, p)))) { + return; + } + if (BedwarsMessages.matched(BedwarsMessages.RECONNECT, rawMessage, m -> getPlayer(m.group(1)).ifPresent(p -> reconnected(event, p)))) { + return; + } + if (BedwarsMessages.matched(BedwarsMessages.GAME_END, rawMessage, m -> { + BedwarsTeam win = players.values().stream().filter(p -> !p.isFinalKilled()).findFirst().map(BedwarsPlayer::getTeam).orElse(null); + this.won = win; + this.wonTick = mc.inGameHud.getTicks() + 10; + })) { + return; + } + if (BedwarsMessages.matched(BedwarsMessages.TEAM_ELIMINATED, rawMessage, m -> BedwarsTeam.fromName(m.group(1)).ifPresent(t -> teamEliminated(event, t)))) { + return; + } + upgrades.onMessage(rawMessage); + } catch (Exception e) { + debug("Error: " + e); + } + } + + private void died(Matcher m, String rawMessage, ReceiveChatMessageEvent event, BedwarsDeathType type) { + BedwarsPlayer killed = getPlayer(m.group(1)).orElse(null); + BedwarsPlayer killer = null; + if (type != BedwarsDeathType.SELF_UNKNOWN && type != BedwarsDeathType.SELF_VOID) { + killer = getPlayer(m.group(2)).orElse(null); + } + if (killed == null) { + debug("Player " + m.group(1) + " was not found"); + return; + } + died(event, killed, killer, type, BedwarsMessages.matched(BedwarsMessages.FINAL_KILL, rawMessage).isPresent()); + } + + private void gameEnd(BedwarsTeam win) { + if (me == null) { + BedwarsMod.instance.gameEnd(); + return; + } + + mc.inGameHud.getChatHud().addMessage( + new LiteralText("§8§m----------[§7Winstreaks§8]----------") + ); + for (BedwarsPlayer p : players.values()) { + if (p.getStats() != null && p.getStats().getWinstreak() > 0) { + boolean winner = p.getTeam().equals(win); + int before = p.getStats().getWinstreak(); + int after = winner ? before + 1 : 0; + mc.inGameHud.getChatHud().addMessage( + new LiteralText( + getPlayerFormatted(p) + "§8: §7" + before + " §8 -> §" + (winner ? "a" : "c") + after + )); + } + } + + BedwarsMod.instance.gameEnd(); + } + + private void teamEliminated(ReceiveChatMessageEvent event, BedwarsTeam team) { + // Make sure everyone is dead, just in case + players.values().stream().filter(b -> b.getTeam() == team).forEach(b -> { + b.setBed(false); + b.died(); + }); + if (mod.overrideMessages) { + event.newMessage = new LiteralText(formatEliminated(team)); + } + } + + private void bedDestroyed(ReceiveChatMessageEvent event, BedwarsTeam team, @Nullable BedwarsPlayer breaker) { + players.values().stream().filter(b -> b.getTeam() == team).forEach(b -> b.setBed(false)); + if (breaker != null && breaker.getStats() != null) { + breaker.getStats().addBed(); + } + if (mod.overrideMessages) { + event.newMessage = new LiteralText(formatBed(team, breaker)); + } + } + + private void disconnected(ReceiveChatMessageEvent event, BedwarsPlayer player) { + player.disconnected(); + if (mod.overrideMessages) { + event.newMessage = new LiteralText(formatDisconnect(player)); + } + } + + + private void reconnected(ReceiveChatMessageEvent event, BedwarsPlayer player) { + player.reconnected(); + if (mod.overrideMessages) { + event.newMessage = new LiteralText(formatReconnect(player)); + } + } + + public void onScoreboardRender(ScoreboardRenderEvent event) { + Scoreboard scoreboard = event.objective.getScoreboard(); + Collection scores = scoreboard.getAllPlayerScores(event.objective); + List filteredScores = scores.stream() + .filter(p_apply_1_ -> p_apply_1_.getPlayerName() != null && !p_apply_1_.getPlayerName().startsWith("#")) + .collect(Collectors.toList()); + Collections.reverse(filteredScores); + if (filteredScores.size() < 3) { + return; + } + ScoreboardPlayerScore score = filteredScores.get(2); + Team team = scoreboard.getPlayerTeam(score.getPlayerName()); + String timer = Team.decorateName(team, score.getPlayerName()); + if (!timer.contains(":")) { + return; + } + int seconds; + try { + seconds = Integer.parseInt(timer.split(":")[1].substring(0, 2)); + } catch (Exception e) { + e.printStackTrace(); + return; + } + int target = (60 - seconds) % 60; + if (this.seconds % 60 != target) { + // Update seconds + while (this.seconds % 60 != target) { + updateClock(); + } + topBarText = new LiteralText(calculateTopBarText()); + bottomBarText = new LiteralText(calculateBottomBarText()); + } + } + + private int getDiamondTimerTier(int tier) { + if (tier <= 1) { + return DIAMOND_1; + } + if (tier == 2) { + return DIAMOND_2; + } + return DIAMOND_3; + } + + private int getEmeraldTier(int tier) { + if (tier <= 1) { + return EMERALD_1; + } + if (tier == 2) { + return EMERALD_2; + } + return EMERALD_3; + } + + private void updateClock() { + // This just straight up doesn't work. I think it's because hypixel doesn't follow strict timings + // Also the math on this is just wrong somewhere + this.seconds++; + int minutes = seconds / 60; + int diamondTier = Math.min((minutes + 6) / 12 + 1, 3); + int emeraldTier = Math.min(minutes / 12 + 1, 3); + diamondsTimer--; + emeraldsTimer--; + if ((seconds % 60 == 0) && (minutes < 24 && minutes % 6 == 0)) { + if ((minutes % 12) / 6 == 1) { + // Diamonds + diamondsTimer = 0; + } else { + // Emeralds + emeraldsTimer = 0; + } + } + if (diamondsTimer <= 0) { + int secondsTillUpgrade = (((minutes) / 12 + 1)) * 12 * 60 - 6 * 60 - seconds; + diamondsTimer = Math.min(getDiamondTimerTier(diamondTier), secondsTillUpgrade); + } + if (emeraldsTimer <= 0) { + int secondsTillUpgrade = ((minutes / 12 + 1)) * 12 * 60 - seconds; + emeraldsTimer = Math.min(getEmeraldTier(emeraldTier), secondsTillUpgrade); + } + } + + public void tick() { + int currentTick = mc.inGameHud.getTicks(); + if (won != null && currentTick >= wonTick) { + gameEnd(won); + } + players.values().forEach(p -> p.tick(currentTick)); + } + + public void updateEntries(List entries) { + // Update latencies and other information for entries + entries.forEach(entry -> + getPlayer(entry.getProfile().getName()).ifPresent(player -> player.updateListEntry(entry)) + ); + } + + public List getTabPlayerList(List original) { + updateEntries(original); + return players.values().stream().filter(b -> !b.isFinalKilled()).sorted((b1, b2) -> { + if (b1.getTeam() == b2.getTeam()) { + return Integer.compare(b1.getNumber(), b2.getNumber()); + } + return Integer.compare(b1.getTeam().ordinal(), b2.getTeam().ordinal()); + }).map(BedwarsPlayer::getProfile).collect(Collectors.toList()); + } + + public BedwarsPlayer getSelf() { + return me; + } + + public String getLevelHead(AbstractClientPlayerEntity entity) { + BedwarsPlayer player = getPlayer(entity.getUuid()).orElse(null); + if (player == null) { + return null; + } + BedwarsPlayerStats stats = player.getStats(); + if (stats == null) { + return null; + } + return "§7Kills: §f" + stats.getGameKills() + " §7Deaths: §f" + stats.getGameDeaths(); + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMessages.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMessages.java new file mode 100644 index 00000000..565e4345 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMessages.java @@ -0,0 +1,304 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BedwarsMessages { + + public final static Pattern[] COMBAT_KILL = convert( + "{killed} was struck down by {player}.", + "{killed} was filled full of lead by {player}.", + "{killed} died in close combat to {player}.", + "{killed} was given the cold shoulder by {player}.", + "{killed} was glazed in BBQ sauce by {player}.", + "{killed} was bitten by {player}.", + "{killed} was wrapped into a gift by {player}.", + "{killed} was hunted down by {player}.", + "{killed} was oinked by {player}.", + "{killed} was chewed up by {player}.", + "{killed} was buzzed to death by {player}.", + "{killed} was trampled by {player}.", + "{killed} be sent to Davy Jones' locker by {player}.", + "{killed} got rekt by {player}.", + "{killed} was locked outside during a snow storm by {player}.", + "{killed} was painted pretty by {player}.", + "{killed} was wrapped up by {player}.", + "{killed} was stomped by {player}.", + "{killed} was {player}'s final #{number}", + "{killed} was spooked by {player}.", + "{killed} was tragically backstabbed by {player}.", + "{killed} was crushed by {player}.", + "{killed} was {player}'s final #{number}.", + "{killed} was whacked with a party balloon by {player}.", + "{killed} was crushed into moon dust by {player}.", + "{killed} was smothered in holiday cheer by {player}.", + "{killed} was ripped to shreds by {player}.", + "{killed} was bested by {player}.", + "{killed} was {player}'s final #{number}.", + "{killed} had a small brain moment while fighting {player}.", + "{killed} was too shy to meet {player}.", + "{killed} was yelled at by {player}.", + "{killed} was killed by {player}." + ); + + public final static Pattern[] VOID_KILL = convert( + "{killed} was turned to dust by {player}.", + "{killed} met their end by {player}.", + "{killed} fought to the edge with {player}.", + "{killed} was hit off by a love bomb from {player}.", + "{killed} slipped in BBQ sauce off the edge spilled by {player}.", + "{killed} howled into the void for {player}.", + "{killed} hit the hard-wood floor because of {player}.", + "{killed} stumbled on a trap set by {player}.", + "{killed} slipped into void for {player}.", + "{killed} was scared into the void by {player}.", + "{killed} was bzzz'd into the void by {player}.", + "{killed} was back kicked into the void by {player}.", + "{killed} be cannonballed to death by {player}.", + "{killed} took the L to {player}.", + "{killed} was pushed into a snowbank by {player}.", + "{killed} was deviled into the void by {player}.", + "{killed} was tied into a bow by {player}.", + "{killed} was thrown down a pit by {player}.", + "{killed} was spooked off the map by {player}.", + "{killed} was heartlessly let go by {player}.", + "{killed} was dominated by {player}.", + "{killed} was popped into the void by {player}.", + "{killed} was sent the wrong way by {player}.", + "{killed} was banished into the ether by {player}'s holiday spirit.", + "{killed} was charged by {player}.", + "{killed} was knocked into the void by {player}.", + "{killed} was not able to block clutch against {player}.", + "{killed} didn't distance themselves properly from {player}.", + "{killed} was thrown off the lawn by {player}.", + "{killed} was turned to dust by {player}." + ); + + public final static Pattern[] PROJECTILE_KILL = convert( + "{killed} was melted by {player}.", + "{killed} was killed with dynamite by {player}.", + "{killed} fell to the great marksmanship of {player}.", + "{killed} was struck with Cupid's arrow by {player}.", + "{killed} was thrown chili powder at by {player}.", + "{killed} caught the ball thrown by {player}.", + "{killed} was put on the naughty list by {player}.", + "{killed} got skewered by {player}.", + "{killed} got attacked by a carrot from {player}.", + "{killed} stepped in a mouse trap placed by {player}.", + "{killed} was startled by {player}.", + "{killed} was impaled from a distance by {player}.", + "{killed} be shot and killed by {player}.", + "{killed} got smacked by {player}.", + "{killed} was hit with a snowball from {player}.", + "{killed} slipped into a pan placed by {player}.", + "{killed} was glued up by {player}.", + "{killed} was shot by {player}.", + "{killed} was remotely spooked by {player}.", + "{killed}'s heart was pierced by {player}.", + "{killed} was assassinated by {player}.", + "{killed} was shot with a roman candle by {player}.", + "{killed} was hit by an asteroid from {player}.", + "{killed} was sniped by a missile of festivity by {player}.", + "{killed} was pounced on by {player}.", + "{killed} was shot by {player}.", + "{killed} got 360 no-scoped by {player}.", + "{killed} was coughed at by {player}.", + "{killed} was accidentally spit on by {player}." + ); + + public final static Pattern[] FALL_KILL = convert( + "{killed} was turned to ash by {player}.", + "{killed} lost a drinking contest with {player}.", + "{killed} stumbled off a ledge with help by {player}.", + "{killed} was out of the league of {player}.", + "{killed} was not spicy enough for {player}.", + "{killed} was distracted by a puppy placed by {player}.", + "{killed} was pushed down a slope by {player}.", + "{killed} was thrown into a volcano by {player}.", + "{killed} was distracted by a piglet from {player}.", + "{killed} was distracted by a rat dragging pizza from {player}.", + "{killed} was stung off the edge by {player}.", + "{killed} was headbutted off a cliff by {player}.", + "{killed} be killed with magic by {player}.", + "{killed} got roasted by {player}.", + "{killed} was shoved down an icy slope by {player}.", + "{killed} was flipped off the edge by {player}.", + "{killed} tripped over a present placed by {player}.", + "{killed} was thrown to the ground by {player}.", + "{killed} was totally spooked by {player}.", + "{killed} was delivered into nothingness by {player}.", + "{killed} was thrown off their high horse by {player}.", + "{killed} was launched like a firework by {player}.", + "{killed} was blasted to the moon by {player}.", + "{killed} was pushed by {player}'s holiday spirit.", + "{killed} was ripped and thrown by {player}.", + "{killed} was knocked off an edge by {player}.", + "{killed} was knocked off a cliff by {player}.", + "{killed} forgot how many blocks they had left while fighting {player}.", + "{killed} tripped while trying to run away from {player}.", + "{killed} slipped on the fake teeth of {player}.", + "{killed} was knocked into the void by {player}." + ); + + public final static Pattern[] GOLEM_KILL = convert( + "{killed} was fried by {player}'s Golem.", + "{killed} lost the draw to {player}'s Golem.", + "{killed} tangoed with {player}'s Golem.", + "{killed} was no match for {player}'s Golem.", + "{killed} was sliced up by {player}'s Golem.", + "{killed} played too rough with {player}'s Golem.", + "{killed} was turned to gingerbread by {player}'s Golem.", + "{killed} was mauled by {player}'s Golem.", + "{killed} was oinked by {player}'s Golem.", + "{killed} squeaked around with {player}'s Golem.", + "{killed} was bee'd by {player}'s Golem.", + "{killed} was trampled by {player}'s Golem.", + "{killed} be killed with metal by {player}'s Golem.", + "{killed} got bamboozled by {player}'s Golem.", + "{killed} got snowed in by {player}'s Golem.", + "{killed} was made sunny side up by {player}'s Golem.", + "{killed} was taped together by {player}'s Golem.", + "{killed} was outclassed by {player}'s Golem.", + "{killed} was spooked by {player}'s Golem.", + "{killed} was dismembered by {player}'s Golem.", + "{killed} was degraded by {player}'s Golem.", + "{killed} was lit up by {player}'s Golem.", + "{killed} was blown up by {player}'s Golem.", + "{killed} was sung holiday tunes to by {player}'s Golem.", + "{killed} was ripped to shreds by {player}'s Golem.", + "{killed} was bested by {player}'s Golem.", + "{killed} got absolutely destroyed by {player}'s Golem.", + "{killed} got too close to {player}'s Golem.", + "{killed} was chased away by {player}'s Golem." + ); + + public final static Pattern[] BED_BREAK = { + Pattern.compile(formatPlaceholder("Bed was broken by {player}")), + Pattern.compile(formatPlaceholder("Bed was incinerated by {player}")), + Pattern.compile(formatPlaceholder("Bed was iced by {player}")), + Pattern.compile(formatPlaceholder("Bed had to raise the white flag to {player}")), + Pattern.compile(formatPlaceholder("Bed was dismantled by {player}")), + Pattern.compile(formatPlaceholder("Bed was deep fried by {player}")), + Pattern.compile(formatPlaceholder("Bed was ripped apart by {player}")), + Pattern.compile(formatPlaceholder("Bed was traded in for milk and cookies by {player}")), + Pattern.compile(formatPlaceholder("Bed was sacrificed by {player}")), + Pattern.compile(formatPlaceholder("Bed was gulped by {player}")), + Pattern.compile(formatPlaceholder("Bed was gulped by {player}")), + Pattern.compile(formatPlaceholder("Bed was squeaked apart by {player}")), + Pattern.compile(formatPlaceholder("Bed was stung by {player}")), + Pattern.compile(formatPlaceholder("Bed was impaled by {player}")), + Pattern.compile(formatPlaceholder("Bed be shot with cannon by {player}")), + Pattern.compile(formatPlaceholder("Bed got memed by {player}")), + Pattern.compile(formatPlaceholder("Bed was made into a snowman by {player}")), + Pattern.compile(formatPlaceholder("Bed was scrambled by {player}")), + Pattern.compile(formatPlaceholder("Bed was stuffed with tissue paper by {player}")), + Pattern.compile(formatPlaceholder("Bed was scrambled by {player}")), + Pattern.compile(formatPlaceholder("Bed was bed #{number} destroyed by {player}")), + Pattern.compile(formatPlaceholder("Bed was spooked by {player}")), + Pattern.compile(formatPlaceholder("Bed was dreadfully corrupted by {player}")), + Pattern.compile(formatPlaceholder("Bed was bed #{number} destroyed by {player}")), + Pattern.compile(formatPlaceholder("Bed exploded from a firework by {player}")), + Pattern.compile(formatPlaceholder("Bed was blasted to dust by {player}")), + Pattern.compile(formatPlaceholder("Bed was melted by {player}'s holiday spirit")), + Pattern.compile(formatPlaceholder("Bed was ripped to shreds by {player}")), + Pattern.compile(formatPlaceholder("Bed has left the game after seeing {player}")), + Pattern.compile(formatPlaceholder("Bed was spooked by {player}")), + Pattern.compile(formatPlaceholder("Bed was contaminated by {player}")), + Pattern.compile(formatPlaceholder("Bed was sold in a garage sale by {player}")), + Pattern.compile(formatPlaceholder("Bed was destroyed by {player}")), + }; + + public final static Pattern DISCONNECT = Pattern.compile("(\\b[A-Za-z0-9_§]{3,16}\\b) disconnected\\.$"); + public final static Pattern RECONNECT = Pattern.compile("(\\b[A-Za-z0-9_§]{3,16}\\b) reconnected\\.$"); + public final static Pattern FINAL_KILL = Pattern.compile("FINAL KILL!"); + public final static Pattern BED_DESTROY = Pattern.compile("^\\s*?BED DESTRUCTION > (\\w+) Bed"); + public final static Pattern TEAM_ELIMINATED = Pattern.compile("^\\s*?TEAM ELIMINATED > (\\w+) Team"); + + public final static Pattern GAME_END = Pattern.compile("^ +1st Killer - ?\\[?\\w*\\+*\\]? \\w+ - \\d+(?: Kills?)?$"); + + public final static Pattern SELF_VOID = Pattern.compile(formatPlaceholder("^{killed} fell into the void.(?: FINAL KILL!)?\\s*?")); + public final static Pattern SELF_UNKNOWN = Pattern.compile(formatPlaceholder("^{killed} died.(?: FINAL KILL!)?\\s*?")); + + public final static Pattern[] ANNOYING_MESSAGES = { + Pattern.compile("^You will respawn in \\d* seconds!$"), + Pattern.compile("^You will respawn in \\d* second!$"), + Pattern.compile("^You purchased Wool$"), + Pattern.compile("^Cross-teaming is not allowed"), + Pattern.compile("^\\+\\d+ Coins!"), + Pattern.compile("^\\+\\d+ coins!"), + Pattern.compile("^Coins just earned DOUBLE"), + Pattern.compile("^\\+\\d+ Bed Wars Experience"), + Pattern.compile("^You have respawned"), + Pattern.compile("^If you get disconnected use /rejoin to join back in the game\\.$"), + }; + + private static Pattern[] convert(String... input) { + return Arrays.stream(input).map(str -> Pattern.compile("^" + formatPlaceholder(str) + "(?: FINAL KILL!)?\\s*?")).toArray(Pattern[]::new); + } + + private static String formatPlaceholder(String input) { + return input + .replace("{killed}", "(\\b[A-Za-z0-9_§]{3,16}\\b)") + .replace("{player}", "(\\b[A-Za-z0-9_§]{3,16}\\b)") + .replace("{number}", "[0-9,]+"); + } + + public static boolean matched(Pattern pattern, String input, Consumer consumer) { + Optional matcher = matched(pattern, input); + if (!matcher.isPresent()) { + return false; + } + consumer.accept(matcher.get()); + return true; + } + + public static boolean matched(Pattern[] pattern, String input, Consumer consumer) { + Optional matcher = matched(pattern, input); + if (!matcher.isPresent()) { + return false; + } + consumer.accept(matcher.get()); + return true; + } + + public static Optional matched(Pattern[] pattern, String input) { + for (Pattern p : pattern) { + Optional m = matched(p, input); + if (m.isPresent()) { + return m; + } + } + return Optional.empty(); + } + + public static Optional matched(Pattern pattern, String input) { + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return Optional.of(matcher); + } + return Optional.empty(); + } + + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMod.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMod.java new file mode 100644 index 00000000..03430741 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMod.java @@ -0,0 +1,234 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import com.google.gson.GsonBuilder; +import com.google.gson.InstanceCreator; +import com.google.gson.annotations.Expose; +import io.github.solclient.client.event.EventHandler; +import io.github.solclient.client.event.impl.PreTickEvent; +import io.github.solclient.client.event.impl.ReceiveChatMessageEvent; +import io.github.solclient.client.event.impl.ScoreboardRenderEvent; +import io.github.solclient.client.event.impl.WorldLoadEvent; +import io.github.solclient.client.mod.hud.HudElement; +import io.github.solclient.client.mod.impl.*; +import io.github.solclient.client.mod.option.ModOption; +import io.github.solclient.client.mod.option.annotation.AbstractTranslationKey; +import io.github.solclient.client.mod.option.annotation.Option; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.scoreboard.ScoreboardPlayerScore; +import net.minecraft.scoreboard.Team; +import net.minecraft.text.LiteralText; +import net.minecraft.util.Formatting; + + +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@AbstractTranslationKey("sol_client.mod.bedwars") +public final class BedwarsMod extends StandardMod { + + private final static Pattern[] GAME_START = { + Pattern.compile("^\\s*?Protect your bed and destroy the enemy beds\\.\\s*?$"), + Pattern.compile("^\\s*?Bed Wars Lucky Blocks\\s*?$"), + Pattern.compile("^\\s*?Bed Wars Swappage\\s*?$") + }; + + public static BedwarsMod instance; + + @Expose + @Option + public boolean hardcoreHearts = true; + + @Expose + @Option + public boolean showHunger = false; + + @Expose + @Option + public boolean displayArmor = true; + + @Expose + @Option + public boolean bedwarsLevelHead = true; + + protected BedwarsGame currentGame = null; + + @Expose + protected final TeamUpgradesOverlay upgradesOverlay; + + @Expose + @Option + protected boolean removeAnnoyingMessages = true; + + @Expose + @Option + private boolean tabRenderLatencyIcon = false; + + @Expose + @Option + protected boolean showChatTime = true; + + @Expose + @Option + protected boolean overrideMessages = true; + private int targetTick = -1; + private boolean waiting = false; + + public BedwarsMod() { + upgradesOverlay = new TeamUpgradesOverlay(this); + } + + @Override + protected List> createOptions() { + List> options = super.createOptions(); + options.addAll(upgradesOverlay.createOptions()); + return options; + } + + @Override + public String getDetail() { + return I18n.translate("sol_client.mod.screen.by", "DarkKronicle") + I18n.translate("sol_client.mod.screen.textures_by", "Sybillian"); + } + + @Override + public void init() { + super.init(); + instance = this; + } + + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + if (currentGame != null) { + gameEnd(); + } + } + + public boolean isWaiting() { + if (inGame()) { + waiting = false; + } + return waiting; + } + + @EventHandler + public void onMessage(ReceiveChatMessageEvent event) { + // Remove formatting + String rawMessage = event.originalMessage.replaceAll("§.", ""); + if (currentGame != null) { + currentGame.onChatMessage(rawMessage, event); + String time = "§7" + currentGame.getFormattedTime() + " "; + if (!event.cancelled && showChatTime) { + // Add time to every message received in game + if (event.newMessage != null) { + event.newMessage = new LiteralText(time).append(event.newMessage); + } else { + event.newMessage = new LiteralText(time).append(event.formattedMessage); + } + } + } else if (targetTick < 0 && BedwarsMessages.matched(GAME_START, rawMessage).isPresent()) { + // Give time for Hypixel to sync + targetTick = mc.inGameHud.getTicks() + 10; + } + } + + public Optional getGame() { + return currentGame == null ? Optional.empty() : Optional.of(currentGame); + } + + @EventHandler + public void onTick(PreTickEvent event) { + if (currentGame != null) { + waiting = false; + if (currentGame.isStarted()) { + // Trigger setting the header + mc.inGameHud.getPlayerListWidget().setHeader(null); + currentGame.tick(); + } else { + if (checkReady()) { + currentGame.onStart(); + } + } + } else { + if (targetTick > 0 && mc.inGameHud.getTicks() > targetTick) { + currentGame = new BedwarsGame(this); + targetTick = -1; + } + } + } + + private boolean checkReady() { + for (PlayerListEntry player : mc.player.networkHandler.getPlayerList()) { + String name = mc.inGameHud.getPlayerListWidget().getPlayerName(player).replaceAll("§.", ""); + if (name.charAt(1) == ' ') { + return true; + } + } + return false; + } + + @Override + public List getHudElements() { + return Arrays.asList(upgradesOverlay); + } + + public boolean inGame() { + return currentGame != null && currentGame.isStarted(); + } + + @EventHandler + public void onScoreboardRender(ScoreboardRenderEvent event) { + if (inGame()) { + waiting = false; + currentGame.onScoreboardRender(event); + return; + } + if (!Formatting.strip(event.objective.getDisplayName()).contains("BED WARS")) { + return; + } + Scoreboard scoreboard = event.objective.getScoreboard(); + Collection scores = scoreboard.getAllPlayerScores(event.objective); + List filteredScores = scores.stream() + .filter(p_apply_1_ -> p_apply_1_.getPlayerName() != null && !p_apply_1_.getPlayerName().startsWith("#")) + .collect(Collectors.toList()); + waiting = filteredScores.stream().anyMatch(score -> { + Team team = scoreboard.getPlayerTeam(score.getPlayerName()); + String format = Formatting.strip(Team.decorateName(team, score.getPlayerName())).replaceAll("[^A-z0-9 .:]", ""); + return format.contains("Waiting...") || format.contains("Starting in"); + }); + } + + public void gameEnd() { + upgradesOverlay.onEnd(); + currentGame = null; + } + + @Override + public void registerOtherTypeAdapters(GsonBuilder builder) { + builder.registerTypeAdapter(TeamUpgradesOverlay.class, (InstanceCreator) (type) -> upgradesOverlay); + } + + public boolean blockLatencyIcon() { + return !tabRenderLatencyIcon; + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMode.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMode.java new file mode 100644 index 00000000..114adcc5 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsMode.java @@ -0,0 +1,39 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import lombok.Getter; + + +public enum BedwarsMode { + SOLO(BedwarsTeam.values()), + DOUBLES(BedwarsTeam.values()), + THREES(BedwarsTeam.BLUE, BedwarsTeam.GREEN, BedwarsTeam.YELLOW, BedwarsTeam.RED), + FOURS(BedwarsTeam.BLUE, BedwarsTeam.GREEN, BedwarsTeam.YELLOW, BedwarsTeam.RED), + FOUR_V_FOUR(BedwarsTeam.BLUE, BedwarsTeam.RED) + ; + + @Getter + private final BedwarsTeam[] teams; + + BedwarsMode(BedwarsTeam... teams) { + this.teams = teams; + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsPlayer.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsPlayer.java new file mode 100644 index 00000000..4cd5d4b1 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsPlayer.java @@ -0,0 +1,168 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + + +import io.github.solclient.client.mod.impl.hypixeladditions.HypixelAPICache; +import lombok.Data; +import lombok.Getter; +import net.hypixel.api.reply.PlayerReply; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.PlayerListEntry; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +@Data +public class BedwarsPlayer { + + private final BedwarsTeam team; + @Getter + private PlayerListEntry profile; + private boolean alive = true; + private boolean disconnected = false; + private boolean bed = true; + private final int number; + private BedwarsPlayerStats stats = null; + private boolean triedStats = false; + private int tickAlive = -1; + + public BedwarsPlayer(BedwarsTeam team, PlayerListEntry profile, int number) { + this.team = team; + this.profile = profile; + this.number = number; + } + + public String getColoredTeamNumber(String format) { + return getTeam().getColorSection() + format + getTeam().getPrefix() + getNumber(); + } + + public String getColoredTeamNumber() { + return getTeam().getColorSection() + getTeam().getPrefix() + getNumber(); + } + + public String getName() { + return profile.getProfile().getName(); + } + + public String getColoredName() { + return team.getColorSection() + getName(); + } + + public String getTabListDisplay() { + if (alive) { + if (bed) { + return team.getColorSection() + "§l" + team.getPrefix() + number + " " + getColoredName(); + } + return team.getColorSection() + "§l" + team.getPrefix() + number + team.getColorSection() + "§o " + getName(); + } + if (disconnected) { + return team.getColorSection() + "§l§m" + team.getPrefix() + number + "§7 §o§n" + getName(); + } + return team.getColorSection() + "§l§m" + team.getPrefix() + number + "§7 §m" + getName(); + } + + public void updateListEntry(PlayerListEntry entry) { + this.profile = entry; + } + + public boolean isFinalKilled() { + return tickAlive < 0 && !bed && !alive || (!bed && isDisconnected()); + } + + public void tick(int currentTick) { + if (stats == null && !triedStats) { + triedStats = true; + Optional> future = HypixelAPICache.getInstance().getPlayerOrRequest(profile.getProfile().getId()); + if (!future.isPresent()) { + stats = BedwarsPlayerStats.generateFake(); + } else { + future.get().whenCompleteAsync((player, error) -> { + if (error != null) { + stats = BedwarsPlayerStats.generateFake(); + return; + } + try { + stats = BedwarsPlayerStats.fromAPI(player); + } catch (Exception e) { + stats = BedwarsPlayerStats.generateFake(); + } + }); + } + } + if (alive || tickAlive < 0) { + return; + } + if (currentTick >= tickAlive) { + alive = true; + tickAlive = -1; + } + } + + public void died() { + if (!alive) { + if (!bed) { + tickAlive = -1; + } + return; + } + if (stats != null) { + if (!bed) { + stats.addFinalDeath(); + } else { + stats.addDeath(); + } + } + alive = false; + if (!bed) { + tickAlive = -1; + return; + } + int currentTick = MinecraftClient.getInstance().inGameHud.getTicks(); + tickAlive = currentTick + 20 * 5; // 5 second respawn + } + + public void disconnected() { + if (stats != null) { + if (!bed) { + stats.addFinalDeath(); + } else { + stats.addDeath(); + } + } + disconnected = true; + tickAlive = -1; + alive = false; + } + + public void reconnected() { + disconnected = false; + int currentTick = MinecraftClient.getInstance().inGameHud.getTicks(); + tickAlive = currentTick + 20 * 10; // 10 second respawn + } + + public void killed(boolean finalKill) { + if (stats != null) { + if (finalKill) { + stats.addFinalKill(); + } + stats.addKill(); + } + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsPlayerStats.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsPlayerStats.java new file mode 100644 index 00000000..2e4cb3ea --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsPlayerStats.java @@ -0,0 +1,163 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.hypixel.api.reply.PlayerReply; +import org.jetbrains.annotations.Nullable; + + +@AllArgsConstructor +public class BedwarsPlayerStats { + + @Getter + private int finalKills; + @Getter + private int finalDeaths; + @Getter + private int bedsBroken; + @Getter + private int deaths; + @Getter + private int kills; + @Getter + private int gameFinalKills; + @Getter + private int gameFinalDeaths; + @Getter + private int gameBedsBroken; + @Getter + private int gameDeaths; + @Getter + private int gameKills; + @Getter + private final int losses; + @Getter + private final int wins; + @Getter + private final int winstreak; + @Getter + private final int stars; + + + public static BedwarsPlayerStats generateFake() { + return new BedwarsPlayerStats( + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 + ); + } + + @Nullable + public static BedwarsPlayerStats fromAPI(PlayerReply.Player player) { + JsonElement rawStats = player.getProperty("stats"); + if (rawStats == null || !rawStats.isJsonObject()) { + return null; + } + JsonObject stats = rawStats.getAsJsonObject(); + JsonObject bedwars = getObjectSafe(stats, "Bedwars"); + if (bedwars == null) { + return null; + } + int finalKills = getAsIntElse(bedwars, "final_kills_bedwars", 0); + int finalDeaths = getAsIntElse(bedwars, "final_deaths_bedwars", 0); + int bedsBroken = getAsIntElse(bedwars, "beds_broken_bedwars", 0); + int deaths = getAsIntElse(bedwars, "deaths_bedwars", 0); + int kills = getAsIntElse(bedwars, "kills_bedwars", 0); + int losses = getAsIntElse(bedwars, "losses_bedwars", 0); + int wins = getAsIntElse(bedwars, "wins_bedwars", 0); + int winstreak = getAsIntElse(bedwars, "winstreak", 0); + JsonObject achievements = getObjectSafe(player.getRaw().getAsJsonObject(), "achievements"); + int stars = 1; + if (achievements != null) { + stars = getAsIntElse(achievements, "bedwars_level", 1); + } + return new BedwarsPlayerStats(finalKills, finalDeaths, bedsBroken, deaths, kills, 0, 0, 0, 0, 0, losses, wins, winstreak, stars); + } + + public static int getAsIntElse(JsonObject obj, String key, int other) { + if (obj.has(key)) { + try { + return obj.get(key).getAsInt(); + } catch (NumberFormatException | UnsupportedOperationException | IllegalStateException e) { + // Not actually an int + } + } + return other; + } + + public static JsonObject getObjectSafe(JsonObject object, String key) { + if (!object.has(key)) { + return null; + } + JsonElement el = object.get(key); + if (!el.isJsonObject()) { + return null; + } + return el.getAsJsonObject(); + } + + public void addDeath() { + deaths++; + gameDeaths++; + } + + public void addFinalDeath() { + finalDeaths++; + gameFinalDeaths++; + } + + public void addKill() { + kills++; + gameKills++; + } + + public void addFinalKill() { + finalKills++; + gameFinalKills++; + } + + public void addBed() { + bedsBroken++; + gameBedsBroken++; + } + + public float getFKDR() { + return (float) finalKills / finalDeaths; + } + + public float getBBLR() { + return (float) bedsBroken / losses; + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsTeam.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsTeam.java new file mode 100644 index 00000000..ee6f29dc --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/BedwarsTeam.java @@ -0,0 +1,73 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Locale; +import java.util.Optional; + + +@AllArgsConstructor +public enum BedwarsTeam { + RED('c', 'R'), + BLUE('9', 'B'), + GREEN('a', 'G'), + YELLOW('e', 'Y'), + AQUA('b', 'A'), + WHITE('f', 'W'), + PINK('d', 'P'), + GRAY('8', 'S'), + ; + + @Getter + private final char code; + + @Getter + private final char prefix; + + public String getColorSection() { + return "§" + code; + } + + public static Optional fromPrefix(char prefix) { + for (BedwarsTeam t : values()) { + if (t.getPrefix() == prefix) { + return Optional.of(t); + } + } + return Optional.empty(); + } + + public static Optional fromName(String name) { + for (BedwarsTeam t : values()) { + if (name.equalsIgnoreCase(t.name())) { + return Optional.of(t); + } + } + return Optional.empty(); + } + + public String getName() { + return name().substring(0, 1).toUpperCase(Locale.ROOT) + name().substring(1).toLowerCase(Locale.ROOT); + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/TeamUpgradesOverlay.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/TeamUpgradesOverlay.java new file mode 100644 index 00000000..dd2e6c41 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/TeamUpgradesOverlay.java @@ -0,0 +1,167 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay; + +import com.google.gson.annotations.Expose; +import com.mojang.blaze3d.platform.GlStateManager; +import io.github.solclient.client.mod.Mod; +import io.github.solclient.client.mod.hud.HudElement; +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades.BedwarsTeamUpgrades; +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades.TeamUpgrade; +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades.TrapUpgrade; +import io.github.solclient.client.mod.option.ModOption; +import io.github.solclient.client.mod.option.ModOptionStorage; +import io.github.solclient.client.mod.option.annotation.Option; +import io.github.solclient.client.mod.option.impl.FieldOptions; +import io.github.solclient.client.mod.option.impl.SliderOption; +import io.github.solclient.client.mod.option.impl.ToggleOption; +import io.github.solclient.client.util.data.Position; +import io.github.solclient.client.util.data.Rectangle; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TeamUpgradesOverlay implements HudElement { + + private final static String TRANSLATION_KEY = "bedwars.teamupgrades"; + + private BedwarsTeamUpgrades upgrades = null; + private final BedwarsMod mod; + private final MinecraftClient mc; + private final static String[] trapEdit = {"trap/minerfatigue", "trap/itsatrap"}; + + @Expose + private boolean enabled = false; + + @Expose + private Position position = new Position(100, 100); + + @Expose + private float scale = 100; + + public TeamUpgradesOverlay(BedwarsMod mod) { + this.mod = mod; + this.mc = MinecraftClient.getInstance(); + } + + public void onStart(BedwarsTeamUpgrades newUpgrades) { + upgrades = newUpgrades; + } + + public void onEnd() { + upgrades = null; + } + + @Override + public float getScale() { + return scale / 100; + } + + @Override + public Position getConfiguredPosition() { + return this.position; + } + + @Override + public void setPosition(Position position) { + this.position = position; + } + + @Override + public boolean isVisible() { + return enabled; + } + + @Override + public Rectangle getBounds(Position position) { + return position.rectangle(60, 40); + } + + public List> createOptions() { + List> options = new ArrayList<>(); + options.add(new ToggleOption( + TRANSLATION_KEY + ".enabled", + ModOptionStorage.of(boolean.class, () -> enabled, (value) -> { + if (enabled != value) { + enabled = value; + } + }) + )); + options.add( + new SliderOption(TRANSLATION_KEY + ".option.scale", + ModOptionStorage.of(Number.class, () -> scale, (value) -> scale = value.floatValue()), + Optional.of("sol_client.slider.percent"), 50, 150, 1 + )); + try { + FieldOptions.visit(mod, this.getClass(), options::add); + } catch (IllegalAccessException error) { + throw new AssertionError(error); + } + return options; + } + + @Override + public void render(Position position, boolean editMode) { + if (upgrades == null && !editMode) { + return; + } + int x = position.getX() + 1; + int y = position.getY() + 2; + GlStateManager.color(1, 1, 1); + boolean normalUpgrades = false; + if (upgrades != null) { + for (TeamUpgrade u : upgrades.upgrades) { + if (!u.isPurchased()) { + continue; + } + if (u instanceof TrapUpgrade) { + continue; + } + String texture = u.getTexture()[0]; + mc.getTextureManager().bindTexture(new Identifier("sol_client", "textures/bedwars/" + texture + ".png")); + DrawableHelper.drawTexture(x, y, 0, 0, 16, 16, 16, 16); + x += 17; + normalUpgrades = true; + } + } + x = position.getX() + 1; + if (normalUpgrades) { + y += 17; + } + for (String texture : (editMode ? trapEdit : upgrades.trap.getTexture())) { + mc.getTextureManager().bindTexture(new Identifier("sol_client", "textures/bedwars/" + texture + ".png")); + DrawableHelper.drawTexture(x, y, 0, 0, 16, 16, 16, 16); + x += 17; + } + } + + @Override + public Mod getMod() { + return mod; + } + + @Override + public boolean isShownInReplay() { + return false; + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/InGameHudMixin.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/InGameHudMixin.java new file mode 100644 index 00000000..73e2fb3f --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/InGameHudMixin.java @@ -0,0 +1,60 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.mixins; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMod; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.entity.Entity; +import net.minecraft.entity.vehicle.MinecartEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(InGameHud.class) +public class InGameHudMixin { + + private static final Entity noHungerEntityTM = new MinecartEntity(null); + + @ModifyVariable( + method = "renderStatusBars", + at = @At( + value="STORE" + ), + ordinal = 18 + ) + public int displayHardcoreHearts(int offset) { + boolean hardcore = BedwarsMod.instance.isEnabled() && BedwarsMod.instance.inGame() && BedwarsMod.instance.hardcoreHearts && !BedwarsMod.instance.getGame().get().getSelf().isBed(); + return hardcore ? 5 : 0; + } + + @ModifyVariable( + method = "renderStatusBars", + at = @At( + value="STORE" + ), + ordinal = 0 + ) + public Entity dontHunger(Entity normal) { + if (normal == null && BedwarsMod.instance.isEnabled() && BedwarsMod.instance.inGame() && !BedwarsMod.instance.showHunger) { + return noHungerEntityTM; + } + return normal; + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/PlayerEntityMixin.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/PlayerEntityMixin.java new file mode 100644 index 00000000..6a313224 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/PlayerEntityMixin.java @@ -0,0 +1,44 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.mixins; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMod; +import net.minecraft.entity.player.PlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(PlayerEntity.class) +public class PlayerEntityMixin { + + @Inject( + method = "getArmorProtectionValue", + at = @At( + "HEAD" + ), + cancellable = true + ) + public void disableArmor(CallbackInfoReturnable ci) { + if (BedwarsMod.instance.isEnabled() && BedwarsMod.instance.inGame() && !BedwarsMod.instance.displayArmor) { + ci.setReturnValue(0); + } + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/PlayerListHudMixin.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/PlayerListHudMixin.java new file mode 100644 index 00000000..aa4eeb5d --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/mixins/PlayerListHudMixin.java @@ -0,0 +1,209 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.mixins; + +import java.util.List; +import java.util.Optional; + +import com.google.gson.JsonObject; +import io.github.solclient.client.mod.impl.hypixeladditions.HypixelAPICache; +import io.github.solclient.client.mod.impl.hypixeladditions.HypixelAdditionsMod; +import io.github.solclient.client.util.data.Colour; +import net.hypixel.api.reply.PlayerReply; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.scoreboard.ScoreboardObjective; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.*; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.*; +import net.minecraft.client.gui.hud.PlayerListHud; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.text.Text; + +@Mixin(PlayerListHud.class) +public class PlayerListHudMixin { + + @Shadow private Text header; + + @Shadow private Text footer; + + @Shadow @Final private MinecraftClient client; + + @Inject( + method = "render", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/hud/PlayerListHud;renderLatencyIcon(IIILnet/minecraft/client/network/PlayerListEntry;)V" + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + public void renderWithoutObjective( + int width, Scoreboard scoreboard, ScoreboardObjective playerListScoreboardObjective, CallbackInfo ci, + ClientPlayNetworkHandler clientPlayNetworkHandler, List list, int i, int j, int l, int m, int k, boolean bl, int n, int o, + int p, int q, int r, List list2, int t, int u, int s, int v, int y, PlayerListEntry playerListEntry2 + ) { + if (!BedwarsMod.instance.isEnabled() || !BedwarsMod.instance.isWaiting()) { + return; + } + int startX = v + i + 1; + int endX = startX + n; + String render; + try { + render = HypixelAdditionsMod.instance.getLevelhead( + false, playerListEntry2.getProfile().getName(), playerListEntry2.getProfile().getId()); + } catch (Exception e) { + return; + } + if (render == null) { + return; + } + this.client.textRenderer.drawWithShadow( + render, + (float)(endX - this.client.textRenderer.getStringWidth(render)) + 20, + (float) y, + -1 + ); + } + + @Inject( + method = "renderLatencyIcon", + at = @At("HEAD"), + cancellable = true + ) + public void cancelLatencyIcon(int width, int x, int y, PlayerListEntry playerEntry, CallbackInfo ci) { + if (BedwarsMod.instance.isEnabled() && BedwarsMod.instance.blockLatencyIcon() && (BedwarsMod.instance.isWaiting() || BedwarsMod.instance.inGame())) { + ci.cancel(); + } + } + + @Inject( + method = "renderScoreboardObjective", + at = @At( + value="INVOKE", target = "Lnet/minecraft/client/font/TextRenderer;drawWithShadow(Ljava/lang/String;FFI)I", ordinal=1 + ), + cancellable = true + ) + public void renderCustomScoreboardObjective( + ScoreboardObjective objective, int y, String player, int startX, int endX, PlayerListEntry playerEntry, CallbackInfo ci + ) { + if (!BedwarsMod.instance.isEnabled()) { + return; + } + + BedwarsGame game = BedwarsMod.instance.getGame().orElse(null); + if (game == null) { + return; + } + BedwarsPlayer bedwarsPlayer = game.getPlayer(playerEntry.getProfile().getName()).orElse(null); + if (bedwarsPlayer == null) { + return; + } + ci.cancel(); + String render; + int color; + if (!bedwarsPlayer.isAlive()) { + if (bedwarsPlayer.isDisconnected()) { + return; + } + int tickTillLive = Math.max(0, bedwarsPlayer.getTickAlive() - this.client.inGameHud.getTicks()); + float secondsTillLive = tickTillLive / 20f; + render = String.format("%.1f", secondsTillLive) + "s"; + color = new Colour(200, 200, 200).getValue(); + } else { + int health = objective.getScoreboard().getPlayerScore(player, objective).getScore(); + color = new Colour(255,255,255).lerp(new Colour(215, 0, 64), 1 - (health / 20f)).getValue(); + render = String.valueOf(health); + } + // Health + this.client.textRenderer.drawWithShadow( + render, + (float)(endX - this.client.textRenderer.getStringWidth(render)), + (float) y, + color + ); + + } + + @ModifyVariable( + method = "render", + at = @At( + value="STORE" + ), + ordinal = 7 + ) + public int changeWidth(int value) { + if (BedwarsMod.instance.isEnabled() && BedwarsMod.instance.blockLatencyIcon() && (BedwarsMod.instance.isWaiting() || BedwarsMod.instance.inGame())) { + value -= 9; + } + if (BedwarsMod.instance.isEnabled() && BedwarsMod.instance.isWaiting()) { + value += 20; + } + return value; + } + + @Inject(method = "getPlayerName", at = @At("HEAD"), cancellable = true) + public void getPlayerName(PlayerListEntry playerEntry, CallbackInfoReturnable cir) { + if (!BedwarsMod.instance.isEnabled()) { + return; + } + BedwarsGame game = BedwarsMod.instance.getGame().orElse(null); + if (game == null || !game.isStarted()) { + return; + } + BedwarsPlayer player = game.getPlayer(playerEntry.getProfile().getName()).orElse(null); + if (player == null) { + return; + } + cir.setReturnValue(player.getTabListDisplay()); + } + + @ModifyVariable(method = "render", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Ordering;sortedCopy(Ljava/lang/Iterable;)Ljava/util/List;")) + public List overrideSortedPlayers(List original) { + if (!BedwarsMod.instance.inGame()) { + return original; + } + List players = BedwarsMod.instance.getGame().get().getTabPlayerList(original); + if (players == null) { + return original; + } + return players; + } + + @Inject(method = "setHeader", at = @At("HEAD"), cancellable = true) + public void changeHeader(Text header, CallbackInfo ci) { + if (!BedwarsMod.instance.inGame()) { + return; + } + this.header = BedwarsMod.instance.getGame().get().getTopBarText(); + ci.cancel(); + } + + @Inject(method = "setFooter", at = @At("HEAD"), cancellable = true) + public void changeFooter(Text header, CallbackInfo ci) { + if (!BedwarsMod.instance.inGame()) { + return; + } + this.footer = BedwarsMod.instance.getGame().get().getBottomBarText(); + ci.cancel(); + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/stats/LobbyStatsHud.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/stats/LobbyStatsHud.java new file mode 100644 index 00000000..98cce546 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/stats/LobbyStatsHud.java @@ -0,0 +1,76 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.stats; + +import io.github.solclient.client.mod.Mod; +import io.github.solclient.client.mod.hud.HudElement; +import io.github.solclient.client.util.data.Position; +import io.github.solclient.client.util.data.Rectangle; + + +public class LobbyStatsHud implements HudElement { + + public LobbyStatsHud() { + + } + + public void update() { + + } + + @Override + public float getScale() { + return 0; + } + + @Override + public Position getConfiguredPosition() { + return null; + } + + @Override + public void setPosition(Position position) { + + } + + @Override + public boolean isVisible() { + return false; + } + + @Override + public Rectangle getBounds(Position position) { + return null; + } + + @Override + public void render(Position position, boolean editMode) { + + } + + @Override + public Mod getMod() { + return null; + } + + @Override + public boolean isShownInReplay() { + return false; + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/BedwarsTeamUpgrades.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/BedwarsTeamUpgrades.java new file mode 100644 index 00000000..3fcc4d5c --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/BedwarsTeamUpgrades.java @@ -0,0 +1,72 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades; + + +import java.util.regex.Pattern; + +public class BedwarsTeamUpgrades { + + public final TrapUpgrade trap = new TrapUpgrade(); + + public final TeamUpgrade sharpness = new BinaryUpgrade( + "sharp", Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased Sharpened Swords"), + 8, 4 + ); + + public final TeamUpgrade dragonBuff = new BinaryUpgrade( + "dragonbuff", Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased Dragon Buff\\s*$"), + 5, 5 + ); + + public final TeamUpgrade healPool = new BinaryUpgrade( + "healpool", Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased Heal Pool\\s*$"), + 3, 1 + ); + + public final TeamUpgrade protection = new TieredUpgrade( + "prot", Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased Reinforced Armor .{1,3}\\s*$"), + new int[]{5, 10, 20, 30}, new int[]{2, 4, 8, 16} + ); + + public final TeamUpgrade maniacMiner = new TieredUpgrade( + "haste", Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased Maniac Miner .{1,3}\\s*$"), + new int[]{2, 4}, new int[]{4, 6} + ); + + public final TeamUpgrade forge = new TieredUpgrade( + "forge", Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased (?:Iron|Golden|Emerald|Molten) Forge\\s*$"), + new int[]{2, 4}, new int[]{4, 6} + ); + + public final TeamUpgrade[] upgrades = {trap, sharpness, dragonBuff, healPool, protection, maniacMiner, forge}; + + public BedwarsTeamUpgrades() { + + } + + public void onMessage(String rawMessage) { + for (TeamUpgrade upgrade : upgrades) { + if (upgrade.match(rawMessage)) { + return; + } + } + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/BinaryUpgrade.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/BinaryUpgrade.java new file mode 100644 index 00000000..ac226f0c --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/BinaryUpgrade.java @@ -0,0 +1,61 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMode; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BinaryUpgrade extends TeamUpgrade { + + private boolean purchased = false; + + private final int foursPrice; + private final int doublesPrice; + + public BinaryUpgrade(String name, Pattern regex, int foursPrice, int doublesPrice) { + super(name, regex); + this.foursPrice = foursPrice; + this.doublesPrice = doublesPrice; + } + @Override + protected void onMatch(TeamUpgrade upgrade, Matcher matcher) { + purchased = true; + } + + @Override + public String[] getTexture() { + return new String[]{name + "_" + (purchased ? "1" : "0")}; + } + + @Override + public boolean isPurchased() { + return purchased; + } + + @Override + public int getPrice(BedwarsMode mode) { + if (mode.getTeams().length == 8) { + return doublesPrice; + } + return foursPrice; + } + +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TeamUpgrade.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TeamUpgrade.java new file mode 100644 index 00000000..e50c3cff --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TeamUpgrade.java @@ -0,0 +1,60 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMessages; +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMode; +import lombok.Getter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class TeamUpgrade { + @Getter + protected final String name; + protected final Pattern[] regex; + + public TeamUpgrade(String name, Pattern pattern) { + this(name, new Pattern[]{pattern}); + } + + public TeamUpgrade(String name, Pattern[] pattern) { + this.name = name; + this.regex = pattern; + } + + public boolean match(String unformatedMessage) { + return BedwarsMessages.matched(regex, unformatedMessage, matcher -> onMatch(this, matcher)); + } + + public abstract String[] getTexture(); + + public boolean isMultiUpgrade() { + // Basically only trap + return false; + } + + protected abstract void onMatch(TeamUpgrade upgrade, Matcher matcher); + + public abstract int getPrice(BedwarsMode mode); + + + public abstract boolean isPurchased(); +} + diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TieredUpgrade.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TieredUpgrade.java new file mode 100644 index 00000000..d0c2ff19 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TieredUpgrade.java @@ -0,0 +1,69 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMode; +import lombok.Getter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TieredUpgrade extends TeamUpgrade { + + private final int[] doublesPrice; + private final int[] foursPrice; + @Getter + private int level = 0; + + public TieredUpgrade(String name, Pattern regex, int[] foursPrice, int[] doublesPrice) { + super(name, regex); + this.foursPrice = foursPrice; + this.doublesPrice = doublesPrice; + } + + @Override + public String[] getTexture() { + return new String[]{name + "_" + level}; + } + + @Override + public boolean isPurchased() { + return level > 0; + } + + @Override + protected void onMatch(TeamUpgrade upgrade, Matcher matcher) { + level += 1; + } + + public boolean isMaxedOut(BedwarsMode mode) { + if (mode.getTeams().length == 8) { + return level >= doublesPrice.length; + } + return level >= foursPrice.length; + } + + @Override + public int getPrice(BedwarsMode mode) { + if (mode.getTeams().length == 8) { + return doublesPrice[level]; + } + return foursPrice[level]; + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TrapUpgrade.java b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TrapUpgrade.java new file mode 100644 index 00000000..da48edfd --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/bedwarsoverlay/upgrades/TrapUpgrade.java @@ -0,0 +1,121 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hud.bedwarsoverlay.upgrades; + +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TrapUpgrade extends TeamUpgrade { + + private final static Pattern[] REGEX = { + Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased (.+) Trap\\s*$"), + Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased (.+) Trap\\s*$"), + Pattern.compile("^\\b[A-Za-z0-9_§]{3,16}\\b purchased (.+) Trap\\s*$"), + Pattern.compile("Trap was set (off)!"), + }; + + private final List traps = new ArrayList<>(3); + + public TrapUpgrade() { + super("trap", REGEX); + } + + @Override + protected void onMatch(TeamUpgrade upgrade, Matcher matcher) { + if (matcher.group(1).equals("off")) { + // Trap went off + traps.remove(0); + return; + } + traps.add(TrapType.getFuzzy(matcher.group(1))); + } + + public boolean canPurchase() { + return traps.size() < 3; + } + + @Override + public int getPrice(BedwarsMode mode) { + switch (traps.size()) { + case 0: + return 1; + case 1: + return 2; + case 2: + return 4; + }; + return 0; + } + + @Override + public boolean isPurchased() { + return traps.size() > 0; + } + + @Override + public String[] getTexture() { + if (traps.size() == 0) { + return new String[]{"trap/empty"}; + } + String[] trapTextures = new String[traps.size()]; + for (int i = 0; i < traps.size(); i++) { + TrapType type = traps.get(i); + trapTextures[i] = "trap/" + type.getTextureName(); + } + return trapTextures; + } + + @Override + public boolean isMultiUpgrade() { + return true; + } + + @AllArgsConstructor + public enum TrapType { + ITS_A_TRAP("itsatrap"), + COUNTER_OFFENSIVE("counteroffensive"), + ALARM("alarm"), + MINER_FATIGUE("minerfatigue") + ; + + @Getter + private final String textureName; + + public static TrapType getFuzzy(String s) { + s = s.toLowerCase(Locale.ROOT); + if (s.contains("miner")) { + return MINER_FATIGUE; + } + if (s.contains("alarm")) { + return ALARM; + } + if (s.contains("counter")) { + return COUNTER_OFFENSIVE; + } + return ITS_A_TRAP; + } + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hud/chat/ChatMod.java b/src/main/java/io/github/solclient/client/mod/impl/hud/chat/ChatMod.java index 144ec8a7..45297c4b 100644 --- a/src/main/java/io/github/solclient/client/mod/impl/hud/chat/ChatMod.java +++ b/src/main/java/io/github/solclient/client/mod/impl/hud/chat/ChatMod.java @@ -86,7 +86,7 @@ public class ChatMod extends SolClientHudMod { public boolean colours = true; @Expose @Option - @Slider(min = 40, max = 320, step = 1) + @Slider(min = 40, max = 500, step = 1) public float width = 320; @Expose @Option @@ -224,7 +224,7 @@ public void onReceiveChatMessage(ReceiveChatMessageEvent event) { return; } - String message = strip(event.message); + String message = strip(event.originalMessage); for (String word : filteredWords) { word = strip(word); diff --git a/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAPICache.java b/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAPICache.java new file mode 100644 index 00000000..a6fe4d52 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAPICache.java @@ -0,0 +1,123 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.hypixeladditions; + +import io.github.solclient.client.util.ApacheHttpClient; +import io.github.solclient.client.util.MinecraftUtils; +import net.hypixel.api.HypixelAPI; +import net.hypixel.api.reply.PlayerReply; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class HypixelAPICache { + + private static final HypixelAPICache INSTANCE = new HypixelAPICache(); + + private HypixelAPI api = null; + private final Map playerCache = new HashMap<>(); + private final Map> responseCache = new HashMap<>(); + private final Map levelCache = new HashMap<>(); + + public static HypixelAPICache getInstance() { + return INSTANCE; + } + + private HypixelAPICache() {} + + public Optional getPlayerFromCache(UUID uuid) { + return Optional.ofNullable(playerCache.get(uuid)); + } + + + public Optional> getPlayerOrRequest(UUID uuid) { + PlayerReply.Player cached = playerCache.get(uuid); + if (cached != null) { + return Optional.of(CompletableFuture.completedFuture(cached)); + } + CompletableFuture cachedResponse = responseCache.get(uuid); + if (cachedResponse != null) { + return Optional.of(cachedResponse); + } + if (api == null) { + return Optional.empty(); + } + CompletableFuture reply = api.getPlayerByUuid(uuid).thenApplyAsync(playerReply -> { + responseCache.remove(uuid); + if (!playerReply.isSuccess()) { + return null; + } + playerCache.put(uuid, playerReply.getPlayer()); + return playerReply.getPlayer(); + }); + responseCache.put(uuid, reply); + return Optional.of(reply); + } + + + public String getLevelHead(UUID id) { + + if (levelCache.containsKey(id)) { + // If it exists in cache then we are computing it/have the result + Integer result = levelCache.get(id); + if (result < 0) { + return null; + } + return String.valueOf(result); + } + + if (api == null) { + return null; + } + + // Put here first because we are waiting for computation to come back + levelCache.put(id, -1); + getPlayerOrRequest(id).ifPresent(c -> c.whenCompleteAsync((player, error) -> { + if (player == null || error != null) { + return; + } + + if (player.exists()) { + levelCache.put(id, (int) player.getNetworkLevel()); + } else { + // At this stage, the player is either nicked, or an NPC, but all NPCs and fake + // players I've tested do not get to this stage. + levelCache.put(id, MinecraftUtils.randomInt(120, 280)); + // Based on looking at YouTubers' Hypixel levels. It won't + // actually be the true level, and may not look quite right, + // but it's more plausible than a Level 1 god bridger. + } + })); + return null; + } + + public void setAPIKey(String apiKey) { + api = new HypixelAPI(new ApacheHttpClient(UUID.fromString(apiKey))); + } + + public void clear() { + responseCache.forEach((k, v) -> v.obtrudeValue(null)); + responseCache.clear(); + playerCache.clear(); + levelCache.clear(); + } +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAdditionsMod.java b/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAdditionsMod.java index cdc85f9e..35c0a2ea 100644 --- a/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAdditionsMod.java +++ b/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/HypixelAdditionsMod.java @@ -37,7 +37,6 @@ import io.github.solclient.client.mod.option.annotation.*; import io.github.solclient.client.util.*; import io.github.solclient.client.util.data.*; -import net.hypixel.api.HypixelAPI; import net.minecraft.item.ItemStack; import net.minecraft.text.*; import net.minecraft.text.ClickEvent.Action; @@ -107,10 +106,8 @@ public class HypixelAdditionsMod extends StandardMod { @Expose @Option public boolean levelhead; - private final Map levelCache = new HashMap<>(); @Expose private String apiKey; - private HypixelAPI api; private HypixelLocationData locationData; private final Pattern locrawTrigger = Pattern.compile("\\{(\".*\":\".*\",)?+\".*\":\".*\"\\}"); @@ -120,36 +117,7 @@ public String getLevelhead(boolean isMainPlayer, String name, UUID id) { return null; } - if (levelCache.containsKey(id)) { - String result = levelCache.get(id); - if (result.isEmpty()) { - return null; - } - return result; - } - - else if (api != null) { - levelCache.put(id, ""); - api.getPlayerByUuid(id).whenCompleteAsync((response, error) -> { - if (!response.isSuccess() || error != null) { - return; - } - - if (response.getPlayer().exists()) { - levelCache.put(id, Integer.toString((int) response.getPlayer().getNetworkLevel())); - } else { - // At this stage, the player is either nicked, or an NPC, but all NPCs and fake - // players I've tested do not get to this stage. - levelCache.put(id, Integer.toString(MinecraftUtils.randomInt(180, 280))); // Based on looking at YouTubers' - // Hypixel levels. It won't - // actually be the true level, - // and may not look quite right, - // but it's more plausible than - // a Level 1 god bridger. - } - }); - } - return null; + return HypixelAPICache.getInstance().getLevelHead(id); } public boolean isLobby() { @@ -221,7 +189,7 @@ private void updateState() { public void setApiKey(String apiKey) { this.apiKey = apiKey; if (apiKey != null) { - api = new HypixelAPI(new ApacheHttpClient(UUID.fromString(apiKey))); + HypixelAPICache.getInstance().setAPIKey(apiKey); } } @@ -254,7 +222,7 @@ public void onServerChange(ServerConnectEvent event) { @EventHandler public void onWorldLoad(WorldLoadEvent event) { donegg = donegl = false; - levelCache.clear(); + HypixelAPICache.getInstance().clear(); if (!isHypixel()) { return; @@ -270,10 +238,10 @@ public void onMessage(ReceiveChatMessageEvent event) { return; } - if (locrawTrigger.matcher(event.message).matches()) { + if (locrawTrigger.matcher(event.originalMessage).matches()) { try { event.cancelled = true; - locationData = new Gson().fromJson(event.message, HypixelLocationData.class); + locationData = new Gson().fromJson(event.originalMessage, HypixelLocationData.class); return; } catch (Throwable error) { logger.warn("Could not detect location", error); @@ -282,19 +250,19 @@ public void onMessage(ReceiveChatMessageEvent event) { if (hidegg) { for (Pattern pattern : hideggTriggers) { - if (pattern.matcher(event.message).matches()) { + if (pattern.matcher(event.originalMessage).matches()) { event.cancelled = true; return; } } } - if (hidegl && hideglTrigger.matcher(event.message).matches()) { + if (hidegl && hideglTrigger.matcher(event.originalMessage).matches()) { event.cancelled = true; return; } - if (hideChannelMessageTrigger.matcher(event.message).matches()) { + if (hideChannelMessageTrigger.matcher(event.originalMessage).matches()) { event.cancelled = true; return; } @@ -303,13 +271,13 @@ public void onMessage(ReceiveChatMessageEvent event) { return; } - if (event.actionBar && isHousing() && event.message.startsWith("Now playing:")) { + if (event.actionBar && isHousing() && event.originalMessage.startsWith("Now playing:")) { event.cancelled = true; return; } if (popupEvents) { - for (String line : event.message.split("\\n")) { + for (String line : event.originalMessage.split("\\n")) { Popup popup = HypixelPopupType.popupFromMessage(line); if (popup != null) { PopupsApiMod.instance.add(popup); @@ -320,7 +288,7 @@ public void onMessage(ReceiveChatMessageEvent event) { if (autogg && !donegg) { for (Pattern pattern : autoggTriggers) { - if (pattern.matcher(event.message).matches()) { + if (pattern.matcher(event.originalMessage).matches()) { donegg = true; mc.player.sendChatMessage("/achat " + autoggMessage); return; @@ -328,12 +296,12 @@ public void onMessage(ReceiveChatMessageEvent event) { } } - if (autogl && !donegl && event.message.equals(autoglTrigger)) { + if (autogl && !donegl && event.originalMessage.equals(autoglTrigger)) { ticksUntilAutogl = 20; return; } - Matcher apiKeyMatcher = apiKeyMessageTrigger.matcher(event.message); + Matcher apiKeyMatcher = apiKeyMessageTrigger.matcher(event.originalMessage); if (apiKeyMatcher.matches()) { setApiKey(apiKeyMatcher.group(1)); } diff --git a/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/mixins/PlayerEntityRendererMixin.java b/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/mixins/PlayerEntityRendererMixin.java index f26785eb..1665d7b5 100644 --- a/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/mixins/PlayerEntityRendererMixin.java +++ b/src/main/java/io/github/solclient/client/mod/impl/hypixeladditions/mixins/PlayerEntityRendererMixin.java @@ -18,6 +18,7 @@ package io.github.solclient.client.mod.impl.hypixeladditions.mixins; +import io.github.solclient.client.mod.impl.hud.bedwarsoverlay.BedwarsMod; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -38,14 +39,22 @@ protected PlayerEntityRendererMixin(EntityRenderDispatcher dispatcher) { @Inject(method = "method_10209(Lnet/minecraft/client/network/AbstractClientPlayerEntity;DDDLjava/lang/String;FD)V", at = @At("RETURN")) public void renderLevelhead(AbstractClientPlayerEntity entityIn, double x, double y, double z, String str, float p_177069_9_, double p_177069_10_, CallbackInfo callback) { - if (HypixelAdditionsMod.isEffective()) { + if (BedwarsMod.instance.isEnabled() && BedwarsMod.instance.inGame() && BedwarsMod.instance.bedwarsLevelHead) { + String levelhead = BedwarsMod.instance.getGame().get().getLevelHead(entityIn); + if (levelhead != null) { + renderLabelIfPresent(entityIn, Formatting.GRAY + levelhead, x, + y + (getFontRenderer().fontHeight * 1.15F * p_177069_9_), z, 64 + ); + } + } else if (HypixelAdditionsMod.isEffective()) { String levelhead = HypixelAdditionsMod.instance.getLevelhead( entityIn == MinecraftClient.getInstance().player, entityIn.getName().asFormattedString(), entityIn.getUuid()); - - if (levelhead != null) - renderLabelIfPresent(entityIn, Formatting.AQUA + "Level: " + Formatting.YELLOW + levelhead, x, - y + (getFontRenderer().fontHeight * 1.15F * p_177069_9_), z, 64); + if (levelhead != null) { + renderLabelIfPresent(entityIn, Formatting.AQUA + "Level: " + Formatting.YELLOW + levelhead, x, + y + (getFontRenderer().fontHeight * 1.15F * p_177069_9_), z, 64 + ); + } } } diff --git a/src/main/java/io/github/solclient/client/mod/impl/replay/mixins/PacketListenerMixin.java b/src/main/java/io/github/solclient/client/mod/impl/replay/mixins/PacketListenerMixin.java index d555e1a3..2b17bf84 100644 --- a/src/main/java/io/github/solclient/client/mod/impl/replay/mixins/PacketListenerMixin.java +++ b/src/main/java/io/github/solclient/client/mod/impl/replay/mixins/PacketListenerMixin.java @@ -38,8 +38,8 @@ public void handleChat(Packet packet, CallbackInfo callback) { if (packet instanceof ChatMessageS2CPacket) { String messageString = Formatting.strip(((ChatMessageS2CPacket) packet).getMessage().asUnformattedString()); - if (EventBus.INSTANCE.post(new ReceiveChatMessageEvent( - ((ChatMessageS2CPacket) packet).getType() == 2, messageString, true)).cancelled) + if (EventBus.INSTANCE.post(new ReceiveChatMessageEvent(((ChatMessageS2CPacket) packet).getType() == 2, + messageString, ((ChatMessageS2CPacket) packet).getMessage(), true)).cancelled) callback.cancel(); } } diff --git a/src/main/java/io/github/solclient/client/mod/option/impl/FieldOptions.java b/src/main/java/io/github/solclient/client/mod/option/impl/FieldOptions.java index a1295bf9..abf52818 100644 --- a/src/main/java/io/github/solclient/client/mod/option/impl/FieldOptions.java +++ b/src/main/java/io/github/solclient/client/mod/option/impl/FieldOptions.java @@ -46,7 +46,7 @@ public void visit(Mod mod, Consumer> visitor) throws IllegalAccessE visit(mod, mod.getClass(), visitor); } - private void visit(Mod mod, Class clazz, Consumer> visitor) + public void visit(Mod mod, Class clazz, Consumer> visitor) throws IllegalAccessException { if (clazz == null) return; diff --git a/src/main/resources/assets/sol_client/lang/en_US.lang b/src/main/resources/assets/sol_client/lang/en_US.lang index 9c3bbd64..09fab8e0 100644 --- a/src/main/resources/assets/sol_client/lang/en_US.lang +++ b/src/main/resources/assets/sol_client/lang/en_US.lang @@ -349,6 +349,10 @@ sol_client.mod.hypixel_util.option.autoglMessage=GL message sol_client.mod.hypixel_util.option.hidegl=Hide GL messages sol_client.mod.hypixel_util.option.levelhead=Show player levels +sol_client.mod.bedwars.name=Bedwars +sol_client.mod.bedwars.description=Various improvements and hud for bedwars. + + sol_client.mod.tnt_timer.name=TNT Timer sol_client.mod.tnt_timer.description=Display the explosion time of TNT. @@ -479,6 +483,11 @@ sol_client.mod.discord_integration.option.speakingColour=Speaking colour sol_client.mod.discord_integration.multiplayer=Multiplayer: %s +sol_client.mod.paperdoll.name=Paper Doll +sol_client.mod.paperdoll.description=Renders a small version of you on screen. +sol_client.mod.paperdoll.option.dynamicRotation=Dynamic Rotation +sol_client.mod.paperdoll.option.rotation=Rotation Offset + sol_client.mod.screen.title=Modules sol_client.mod.screen.search=Type to search sol_client.mod.screen.apply_to_all=Apply to All @@ -487,6 +496,7 @@ sol_client.mod.screen.modified_from=, using code by %s sol_client.mod.screen.originally_by=, originally by %s sol_client.mod.screen.inspired_by=, inspired by %s sol_client.mod.screen.by=, by %s +sol_client.mod.screen.textures_by=; textures by %s sol_client.mod.screen.about=About Sol Client sol_client.mod.screen.license=Licensed under the GPL v3 license. diff --git a/src/main/resources/assets/sol_client/textures/bedwars/dragonbuff_0.png b/src/main/resources/assets/sol_client/textures/bedwars/dragonbuff_0.png new file mode 100644 index 00000000..a9cf7905 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/dragonbuff_0.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/dragonbuff_1.png b/src/main/resources/assets/sol_client/textures/bedwars/dragonbuff_1.png new file mode 100644 index 00000000..c9b22218 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/dragonbuff_1.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/forge_0.png b/src/main/resources/assets/sol_client/textures/bedwars/forge_0.png new file mode 100644 index 00000000..089a9471 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/forge_0.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/forge_1.png b/src/main/resources/assets/sol_client/textures/bedwars/forge_1.png new file mode 100644 index 00000000..ba87ef2a Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/forge_1.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/forge_2.png b/src/main/resources/assets/sol_client/textures/bedwars/forge_2.png new file mode 100644 index 00000000..c4d14eea Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/forge_2.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/forge_3.png b/src/main/resources/assets/sol_client/textures/bedwars/forge_3.png new file mode 100644 index 00000000..8d6e083a Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/forge_3.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/forge_4.png b/src/main/resources/assets/sol_client/textures/bedwars/forge_4.png new file mode 100644 index 00000000..c346b66a Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/forge_4.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/haste_0.png b/src/main/resources/assets/sol_client/textures/bedwars/haste_0.png new file mode 100644 index 00000000..f10edaaf Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/haste_0.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/haste_1.png b/src/main/resources/assets/sol_client/textures/bedwars/haste_1.png new file mode 100644 index 00000000..0ee6ba27 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/haste_1.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/haste_2.png b/src/main/resources/assets/sol_client/textures/bedwars/haste_2.png new file mode 100644 index 00000000..23c562ad Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/haste_2.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/healpool_0.png b/src/main/resources/assets/sol_client/textures/bedwars/healpool_0.png new file mode 100644 index 00000000..efb9c84d Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/healpool_0.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/healpool_1.png b/src/main/resources/assets/sol_client/textures/bedwars/healpool_1.png new file mode 100644 index 00000000..44d0885d Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/healpool_1.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/prot_0.png b/src/main/resources/assets/sol_client/textures/bedwars/prot_0.png new file mode 100644 index 00000000..18cfb129 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/prot_0.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/prot_1.png b/src/main/resources/assets/sol_client/textures/bedwars/prot_1.png new file mode 100644 index 00000000..184eda3a Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/prot_1.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/prot_2.png b/src/main/resources/assets/sol_client/textures/bedwars/prot_2.png new file mode 100644 index 00000000..559047b3 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/prot_2.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/prot_3.png b/src/main/resources/assets/sol_client/textures/bedwars/prot_3.png new file mode 100644 index 00000000..aaa5ce0f Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/prot_3.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/prot_4.png b/src/main/resources/assets/sol_client/textures/bedwars/prot_4.png new file mode 100644 index 00000000..d2715e82 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/prot_4.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/sharp_0.png b/src/main/resources/assets/sol_client/textures/bedwars/sharp_0.png new file mode 100644 index 00000000..1662e97d Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/sharp_0.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/sharp_1.png b/src/main/resources/assets/sol_client/textures/bedwars/sharp_1.png new file mode 100644 index 00000000..880201af Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/sharp_1.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/trap/alarm.png b/src/main/resources/assets/sol_client/textures/bedwars/trap/alarm.png new file mode 100644 index 00000000..05518f0d Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/trap/alarm.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/trap/counteroffensive.png b/src/main/resources/assets/sol_client/textures/bedwars/trap/counteroffensive.png new file mode 100644 index 00000000..1ec03a52 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/trap/counteroffensive.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/trap/empty.png b/src/main/resources/assets/sol_client/textures/bedwars/trap/empty.png new file mode 100644 index 00000000..5029b747 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/trap/empty.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/trap/itsatrap.png b/src/main/resources/assets/sol_client/textures/bedwars/trap/itsatrap.png new file mode 100644 index 00000000..3d79a94a Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/trap/itsatrap.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/trap/menu.png b/src/main/resources/assets/sol_client/textures/bedwars/trap/menu.png new file mode 100644 index 00000000..4786a83f Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/trap/menu.png differ diff --git a/src/main/resources/assets/sol_client/textures/bedwars/trap/minerfatigue.png b/src/main/resources/assets/sol_client/textures/bedwars/trap/minerfatigue.png new file mode 100644 index 00000000..3eb866b7 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/bedwars/trap/minerfatigue.png differ diff --git a/src/main/resources/standard-mods.json b/src/main/resources/standard-mods.json index b5cf2e09..11276bdd 100644 --- a/src/main/resources/standard-mods.json +++ b/src/main/resources/standard-mods.json @@ -96,6 +96,11 @@ "main": "hud.PotionEffectsMod", "category": "hud" }, + { + "id": "paperdoll", + "main": "hud.PaperDollMod", + "category": "hud" + }, { "id": "armour", "main": "hud.armour.ArmourMod", @@ -254,6 +259,12 @@ "mixins": "@hypixeladditions.mixins", "category": "integration" }, + { + "id": "bedwars", + "main": "hud.bedwarsoverlay.BedwarsMod", + "mixins": "@hud.bedwarsoverlay.mixins", + "category": "hud" + }, { "id": "quickplay", "main": "quickplay.QuickPlayMod",