From e8905d671a70eb83224f726c282b01b6451dad91 Mon Sep 17 00:00:00 2001 From: Soda5601 <62250232+ThebestkillerTBK@users.noreply.github.com> Date: Tue, 24 Jan 2023 23:49:55 +0800 Subject: [PATCH] Gamemode Notifier and Anti Vanish more modes (#211) closes https://github.com/AntiCope/meteor-rejects/issues/203 --- README.md | 1 + .../anticope/rejects/MeteorRejectsAddon.java | 1 + .../anticope/rejects/modules/AntiVanish.java | 209 ++++++++++-------- .../rejects/modules/GamemodeNotifier.java | 45 ++++ .../rejects/settings/GameModeListSetting.java | 86 +++++++ .../settings/GameModeListSettingScreen.java | 41 ++++ .../rejects/settings/RejectsSettings.java | 17 ++ 7 files changed, 305 insertions(+), 95 deletions(-) create mode 100644 src/main/java/anticope/rejects/modules/GamemodeNotifier.java create mode 100644 src/main/java/anticope/rejects/settings/GameModeListSetting.java create mode 100644 src/main/java/anticope/rejects/settings/GameModeListSettingScreen.java diff --git a/README.md b/README.md index 56570ab..e04b703 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ - Coord Logger (World events from [JexClient](https://github.com/DustinRepo/JexClient-main/blob/main/src/main/java/me/dustin/jex/feature/mod/impl/misc/CoordFinder.java)) - Custom Packets - Extra Elytra (Ported from [Wurst](https://github.com/Wurst-Imperium/Wurst7/tree)) +- Gamemode notifier - Ghost Mode (Taken from an [unmerged PR](https://github.com/MeteorDevelopment/meteor-client/pull/1932)) - Glide (Ported from [Wurst](https://github.com/Wurst-Imperium/Wurst7/tree)) - Insta Mine (Removed from Meteor in [62cd0](https://github.com/MeteorDevelopment/meteor-client/commit/62cd0461e48a6c50f040bf48de25be1fa4eba77e)) diff --git a/src/main/java/anticope/rejects/MeteorRejectsAddon.java b/src/main/java/anticope/rejects/MeteorRejectsAddon.java index 2a8c3a9..eea1ad8 100644 --- a/src/main/java/anticope/rejects/MeteorRejectsAddon.java +++ b/src/main/java/anticope/rejects/MeteorRejectsAddon.java @@ -58,6 +58,7 @@ public class MeteorRejectsAddon extends MeteorAddon { modules.add(new CoordLogger()); modules.add(new CustomPackets()); modules.add(new ExtraElytra()); + modules.add(new GamemodeNotifier()); modules.add(new GhostMode()); modules.add(new Glide()); modules.add(new InstaMine()); diff --git a/src/main/java/anticope/rejects/modules/AntiVanish.java b/src/main/java/anticope/rejects/modules/AntiVanish.java index cb5c3e0..1c35b51 100644 --- a/src/main/java/anticope/rejects/modules/AntiVanish.java +++ b/src/main/java/anticope/rejects/modules/AntiVanish.java @@ -1,127 +1,146 @@ package anticope.rejects.modules; import anticope.rejects.MeteorRejectsAddon; -import com.google.gson.JsonArray; -import meteordevelopment.orbit.EventHandler; -import meteordevelopment.meteorclient.events.game.GameLeftEvent; +import com.mojang.brigadier.suggestion.Suggestion; +import meteordevelopment.meteorclient.events.game.ReceiveMessageEvent; import meteordevelopment.meteorclient.events.packets.PacketEvent; import meteordevelopment.meteorclient.events.world.TickEvent; +import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.widgets.WWidget; +import meteordevelopment.meteorclient.gui.widgets.containers.WVerticalList; +import meteordevelopment.meteorclient.settings.*; import meteordevelopment.meteorclient.systems.modules.Module; -import meteordevelopment.meteorclient.utils.network.Http; +import meteordevelopment.orbit.EventHandler; +import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket; +import net.minecraft.network.packet.s2c.play.CommandSuggestionsS2CPacket; -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; - -import java.util.Queue; -import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; public class AntiVanish extends Module { - - private final Queue toLookup = new ConcurrentLinkedQueue(); - private long lastTick = 0; - + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + + private final Setting interval = sgGeneral.add(new IntSetting.Builder() + .name("interval") + .description("Vanish check interval.") + .defaultValue(100) + .min(0) + .sliderMax(300) + .build() + ); + + private final Setting mode = sgGeneral.add(new EnumSetting.Builder() + .name("mode") + .defaultValue(Mode.LeaveMessage) + .build() + ); + + private final Setting command = sgGeneral.add(new StringSetting.Builder() + .name("command") + .description("The completion command.") + .defaultValue("minecraft:msg") + .visible(() -> mode.get() == Mode.RealJoinMessage) + .build() + ); + + private Map playerCache = new HashMap<>(); + private final List messageCache = new ArrayList<>(); + + private final Random random = new Random(); + private final List completionIDs = new ArrayList<>(); + private List completionPlayerCache = new ArrayList<>(); + + private int timer = 0; + public AntiVanish() { super(MeteorRejectsAddon.CATEGORY, "anti-vanish", "Notifies user when a admin uses /vanish"); } - + @Override - public void onDeactivate() { - toLookup.clear(); + public void onActivate() { + timer = 0; + completionIDs.clear(); + messageCache.clear(); } - @EventHandler - public void onLeave(GameLeftEvent event) { - toLookup.clear(); + + @Override + public WWidget getWidget(GuiTheme theme) { + WVerticalList l = theme.verticalList(); + l.add(theme.label("LeaveMessage: If client didn't receive a quit game message (like essentials).")); + l.add(theme.label("RealJoinMessage: Tell whether the player is really left.")); + return l; } @EventHandler - public void onPacket(PacketEvent.Receive event) { - if (event.packet instanceof PlayerListS2CPacket packet) { - if (packet.getActions().contains(PlayerListS2CPacket.Action.UPDATE_LATENCY)) { - try { - for (PlayerListS2CPacket.Entry entry : packet.getEntries()) { - if (mc.getNetworkHandler().getPlayerListEntry(entry.profileId()) != null) - continue; - toLookup.add(entry.profileId()); + private void onPacket(PacketEvent.Receive event) { + if (mode.get() == Mode.RealJoinMessage && event.packet instanceof CommandSuggestionsS2CPacket packet) { + if (completionIDs.contains(packet.getCompletionId())) { + var lastUsernames = completionPlayerCache.stream().toList(); + + completionPlayerCache = packet.getSuggestions().getList().stream() + .map(Suggestion::getText) + .toList(); + + if (lastUsernames.isEmpty()) return; + + Predicate joinedOrQuit = playerName -> lastUsernames.contains(playerName) != completionPlayerCache.contains(playerName); + + for (String playerName : completionPlayerCache) { + if (Objects.equals(playerName, mc.player.getName().getString())) continue; + if (joinedOrQuit.test(playerName)) { + info("Player joined: " + playerName); } - } catch (Exception ignore) {} + } + + for (String playerName : lastUsernames) { + if (Objects.equals(playerName, mc.player.getName().getString())) continue; + if (joinedOrQuit.test(playerName)) { + info("Player left: " + playerName); + } + } + + completionIDs.remove(Integer.valueOf(packet.getCompletionId())); + event.cancel(); } } } @EventHandler - public void onTick(TickEvent.Post event) { - long time = mc.world.getTime(); - UUID lookup; - - if (Math.abs(lastTick - time) > 100 && (lookup = toLookup.poll()) != null) { - try { - String name = getPlayerNameFromUUID(lookup); - if (name != null) { - warning(name + " has gone into vanish."); - } - } catch (Exception ignore) {} - lastTick = time; - } + private void onReceiveMessage(ReceiveMessageEvent event) { + messageCache.add(event.getMessage().getString()); } - public String getPlayerNameFromUUID(UUID id) { - try { - final NameLookup process = new NameLookup(id, mc); - final Thread thread = new Thread(process); - thread.start(); - thread.join(); - return process.getName(); - } catch (Exception ignored) { - return null; - } - } + @EventHandler + private void onTick(TickEvent.Post event) { + timer++; + if (timer < interval.get()) return; - public static class NameLookup implements Runnable { - private final String uuidstr; - private final UUID uuid; - private final MinecraftClient mc; - private volatile String name; + switch (mode.get()) { + case LeaveMessage -> { + Map oldPlayers = Map.copyOf(playerCache); + playerCache = mc.getNetworkHandler().getPlayerList().stream().collect(Collectors.toMap(e -> e.getProfile().getId(), e -> e.getProfile().getName())); - public NameLookup(final String input, MinecraftClient mc) { - this.uuidstr = input; - this.uuid = UUID.fromString(input); - this.mc = mc; - } - - public NameLookup(final UUID input, MinecraftClient mc) { - this.uuid = input; - this.uuidstr = input.toString(); - this.mc = mc; - } - - @Override - public void run() { - name = this.lookUpName(); - } - - public String lookUpName() { - PlayerEntity player = null; - if (mc.world != null) { - player = mc.world.getPlayerByUuid(uuid); - } - if (player == null) { - final String url = "https://api.mojang.com/user/profiles/" + uuidstr.replace("-", "") + "/names"; - try { - JsonArray res = Http.get(url).sendJson(JsonArray.class); - return res.get(res.size() - 1).getAsJsonObject().get("name").getAsString(); - } catch (Exception e) { - return uuidstr; + for (UUID uuid : oldPlayers.keySet()) { + if (playerCache.containsKey(uuid)) continue; + String name = oldPlayers.get(uuid); + if (messageCache.stream().noneMatch(s -> s.contains(name))) { + warning(name + " has gone into vanish."); + } } } - return player.getName().getString(); - } - - public String getName() { - return this.name; + case RealJoinMessage -> { + int id = random.nextInt(200); + completionIDs.add(id); + mc.getNetworkHandler().sendPacket(new RequestCommandCompletionsC2SPacket(id, command.get() + " ")); + } } + timer = 0; + messageCache.clear(); } -} - + public enum Mode { + LeaveMessage, + RealJoinMessage//https://github.com/xtrm-en/meteor-antistaff/blob/main/src/main/java/me/xtrm/meteorclient/antistaff/modules/AntiStaff.java + } +} \ No newline at end of file diff --git a/src/main/java/anticope/rejects/modules/GamemodeNotifier.java b/src/main/java/anticope/rejects/modules/GamemodeNotifier.java new file mode 100644 index 0000000..0a42362 --- /dev/null +++ b/src/main/java/anticope/rejects/modules/GamemodeNotifier.java @@ -0,0 +1,45 @@ +package anticope.rejects.modules; + +import anticope.rejects.MeteorRejectsAddon; +import anticope.rejects.settings.GameModeListSetting; +import meteordevelopment.meteorclient.events.packets.PacketEvent; +import meteordevelopment.meteorclient.settings.Setting; +import meteordevelopment.meteorclient.settings.SettingGroup; +import meteordevelopment.meteorclient.systems.modules.Module; +import meteordevelopment.orbit.EventHandler; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; +import net.minecraft.world.GameMode; + +import java.util.List; + +public class GamemodeNotifier extends Module { + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + private final Setting> gamemodes = sgGeneral.add(new GameModeListSetting.Builder() + .name("gamemode") + .description("Which gamemodes to notify.") + .build() + ); + + public GamemodeNotifier() { + super(MeteorRejectsAddon.CATEGORY, "gamemode-notifier", "Notifies user a player's gamemode was changed."); + } + + @EventHandler + public void onPacket(PacketEvent.Receive event) { + if (event.packet instanceof PlayerListS2CPacket packet) { + for (PlayerListS2CPacket.Entry entry : packet.getEntries()) { + if (!packet.getActions().contains(PlayerListS2CPacket.Action.UPDATE_GAME_MODE)) continue; + PlayerListEntry entry1 = mc.getNetworkHandler().getPlayerListEntry(entry.profileId()); + if (entry1 == null) continue; + GameMode gameMode = entry.gameMode(); + if (entry1.getGameMode() != gameMode) { + if (!gamemodes.get().contains(gameMode)) continue; + info("Player %s changed gamemode to %s", entry1.getProfile().getName(), entry.gameMode()); + } + } + } + } +} + + diff --git a/src/main/java/anticope/rejects/settings/GameModeListSetting.java b/src/main/java/anticope/rejects/settings/GameModeListSetting.java new file mode 100644 index 0000000..f3d2353 --- /dev/null +++ b/src/main/java/anticope/rejects/settings/GameModeListSetting.java @@ -0,0 +1,86 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package anticope.rejects.settings; + +import meteordevelopment.meteorclient.settings.IVisible; +import meteordevelopment.meteorclient.settings.Setting; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.world.GameMode; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class GameModeListSetting extends Setting> { + public GameModeListSetting(String name, String description, List defaultValue, Consumer> onChanged, Consumer>> onModuleActivated, IVisible visible) { + super(name, description, defaultValue, onChanged, onModuleActivated, visible); + } + + @Override + protected List parseImpl(String str) { + String[] values = str.split(","); + List modes = new ArrayList<>(values.length); + for (String s : values) { + GameMode mode = GameMode.byName(s); + if (mode != null) modes.add(mode); + } + return modes; + } + + @Override + protected boolean isValueValid(List value) { + return true; + } + + @Override + protected void resetImpl() { + value = new ArrayList<>(); + } + + @Override + public NbtCompound save(NbtCompound tag) { + NbtList valueTag = new NbtList(); + for (GameMode mode : get()) { + valueTag.add(NbtString.of(mode.getName())); + } + tag.put("value", valueTag); + + return tag; + } + + @Override + public List load(NbtCompound tag) { + get().clear(); + + NbtList valueTag = tag.getList("value", 8); + for (NbtElement tagI : valueTag) { + GameMode mode = GameMode.byName(tagI.asString()); + if (mode != null) + get().add(mode); + } + + return get(); + } + + public static class Builder extends SettingBuilder, GameModeListSetting> { + public Builder() { + super(new ArrayList<>(0)); + } + + public Builder defaultValue(List map) { + this.defaultValue = map; + return this; + } + + @Override + public GameModeListSetting build() { + return new GameModeListSetting(name, description, defaultValue, onChanged, onModuleActivated, visible); + } + } +} diff --git a/src/main/java/anticope/rejects/settings/GameModeListSettingScreen.java b/src/main/java/anticope/rejects/settings/GameModeListSettingScreen.java new file mode 100644 index 0000000..9067230 --- /dev/null +++ b/src/main/java/anticope/rejects/settings/GameModeListSettingScreen.java @@ -0,0 +1,41 @@ +package anticope.rejects.settings; + +import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.WindowScreen; +import meteordevelopment.meteorclient.gui.widgets.containers.WTable; +import meteordevelopment.meteorclient.gui.widgets.pressable.WCheckbox; +import meteordevelopment.meteorclient.utils.Utils; +import net.minecraft.world.GameMode; + +import java.util.List; + +public class GameModeListSettingScreen extends WindowScreen { + private final GameModeListSetting setting; + private final WTable table; + + public GameModeListSettingScreen(GuiTheme theme, GameModeListSetting setting) { + super(theme, "Select Gamemodes"); + this.setting = setting; + table = super.add(theme.table()).expandX().widget(); + } + + @Override + public void initWidgets() { + List gms = setting.get(); + for (GameMode gameMode : GameMode.values()) { + table.add(theme.label(Utils.nameToTitle(gameMode.getName()))).expandCellX(); + + boolean contains = setting.get().contains(gameMode); + WCheckbox checkbox = table.add(theme.checkbox(contains)).widget(); + checkbox.action = () -> { + if (contains) { + gms.remove(gameMode); + } else { + gms.add(gameMode); + } + }; + + table.row(); + } + } +} diff --git a/src/main/java/anticope/rejects/settings/RejectsSettings.java b/src/main/java/anticope/rejects/settings/RejectsSettings.java index 4da4cfd..6fb7a57 100644 --- a/src/main/java/anticope/rejects/settings/RejectsSettings.java +++ b/src/main/java/anticope/rejects/settings/RejectsSettings.java @@ -1,11 +1,19 @@ package anticope.rejects.settings; +import meteordevelopment.meteorclient.gui.DefaultSettingsWidgetFactory; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.renderer.GuiRenderer; +import meteordevelopment.meteorclient.gui.screens.settings.EntityTypeListSettingScreen; import meteordevelopment.meteorclient.gui.utils.SettingsWidgetFactory; +import meteordevelopment.meteorclient.gui.widgets.containers.WContainer; +import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList; import meteordevelopment.meteorclient.gui.widgets.containers.WTable; +import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; import java.util.Map; +import static meteordevelopment.meteorclient.MeteorClient.mc; + public class RejectsSettings { private final Map, SettingsWidgetFactory.Factory> factories; @@ -18,10 +26,19 @@ public class RejectsSettings { public void addSettings() { factories.put(StringMapSetting.class, (table, setting) -> stringMapW(table, (StringMapSetting) setting)); + factories.put(GameModeListSetting.class, (table, setting) -> gameModeListW(table, (GameModeListSetting) setting)); } private void stringMapW(WTable table, StringMapSetting setting) { WTable wtable = table.add(theme.table()).expandX().widget(); StringMapSetting.fillTable(theme, wtable, setting); } + + private void gameModeListW(WTable table, GameModeListSetting setting) { + WButton button = table.add(theme.button("Select")).expandCellX().widget(); + button.action = () -> mc.setScreen(new GameModeListSettingScreen(theme, setting)); + + WButton reset = table.add(theme.button(GuiRenderer.RESET)).widget(); + reset.action = setting::reset; + } }