Gamemode Notifier and Anti Vanish more modes (#211)

closes https://github.com/AntiCope/meteor-rejects/issues/203
This commit is contained in:
Soda5601
2023-01-24 23:49:55 +08:00
committed by GitHub
parent db004341cd
commit e8905d671a
7 changed files with 305 additions and 95 deletions

View File

@@ -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))

View File

@@ -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());

View File

@@ -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<UUID> toLookup = new ConcurrentLinkedQueue<UUID>();
private long lastTick = 0;
private final SettingGroup sgGeneral = settings.getDefaultGroup();
private final Setting<Integer> interval = sgGeneral.add(new IntSetting.Builder()
.name("interval")
.description("Vanish check interval.")
.defaultValue(100)
.min(0)
.sliderMax(300)
.build()
);
private final Setting<Mode> mode = sgGeneral.add(new EnumSetting.Builder<Mode>()
.name("mode")
.defaultValue(Mode.LeaveMessage)
.build()
);
private final Setting<String> command = sgGeneral.add(new StringSetting.Builder()
.name("command")
.description("The completion command.")
.defaultValue("minecraft:msg")
.visible(() -> mode.get() == Mode.RealJoinMessage)
.build()
);
private Map<UUID, String> playerCache = new HashMap<>();
private final List<String> messageCache = new ArrayList<>();
private final Random random = new Random();
private final List<Integer> completionIDs = new ArrayList<>();
private List<String> 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<String> 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<UUID, String> 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
}
}

View File

@@ -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<List<GameMode>> 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());
}
}
}
}
}

View File

@@ -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<List<GameMode>> {
public GameModeListSetting(String name, String description, List<GameMode> defaultValue, Consumer<List<GameMode>> onChanged, Consumer<Setting<List<GameMode>>> onModuleActivated, IVisible visible) {
super(name, description, defaultValue, onChanged, onModuleActivated, visible);
}
@Override
protected List<GameMode> parseImpl(String str) {
String[] values = str.split(",");
List<GameMode> 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<GameMode> 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<GameMode> 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<Builder, List<GameMode>, GameModeListSetting> {
public Builder() {
super(new ArrayList<>(0));
}
public Builder defaultValue(List<GameMode> map) {
this.defaultValue = map;
return this;
}
@Override
public GameModeListSetting build() {
return new GameModeListSetting(name, description, defaultValue, onChanged, onModuleActivated, visible);
}
}
}

View File

@@ -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<GameMode> 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();
}
}
}

View File

@@ -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<Class<?>, 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;
}
}