From 04b4941e65693f8d6b55f924781d7dd7cd26b1d5 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Sat, 18 May 2024 01:24:05 -0400 Subject: commands: EntityHighlight, improved MapLocate. --- src/main/java/org/chrisoft/trashyaddon/Addon.java | 6 + .../trashyaddon/commands/CEntitySelector.java | 157 ++++++ .../commands/CEntitySelectorOptions.java | 472 ++++++++++++++++ .../commands/CEntitySelectorReader.java | 626 +++++++++++++++++++++ .../commands/EntityHighlightCommand.java | 81 +++ .../trashyaddon/commands/MapLocateCommand.java | 69 ++- .../commands/argument/CEntityArgumentType.java | 161 ++++++ .../commands/argument/ColorCodeArgumentType.java | 79 +++ .../trashyaddon/misc/AddonEntityOutlineShader.java | 41 ++ .../misc/AddonPostProcessingShaders.java | 14 + .../trashyaddon/mixin/ClientWorldMixin.java | 13 + .../chrisoft/trashyaddon/mixin/EntityMixin.java | 25 + .../trashyaddon/mixin/WorldRendererMixin.java | 48 ++ .../meteorclient/PostProcessShadersMixin.java | 41 ++ src/main/resources/addon.mixins.json | 5 +- src/main/resources/fabric.mod.json | 3 +- src/main/resources/meteorclient_addon.mixins.json | 11 + 17 files changed, 1830 insertions(+), 22 deletions(-) create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/EntityHighlightCommand.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/argument/ColorCodeArgumentType.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/misc/AddonEntityOutlineShader.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/misc/AddonPostProcessingShaders.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/mixin/ClientWorldMixin.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/mixin/EntityMixin.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/mixin/WorldRendererMixin.java create mode 100644 src/main/java/org/chrisoft/trashyaddon/mixin/meteorclient/PostProcessShadersMixin.java create mode 100644 src/main/resources/meteorclient_addon.mixins.json (limited to 'src') diff --git a/src/main/java/org/chrisoft/trashyaddon/Addon.java b/src/main/java/org/chrisoft/trashyaddon/Addon.java index 00f0869..427b479 100644 --- a/src/main/java/org/chrisoft/trashyaddon/Addon.java +++ b/src/main/java/org/chrisoft/trashyaddon/Addon.java @@ -1,6 +1,9 @@ package org.chrisoft.trashyaddon; import org.chrisoft.trashyaddon.commands.*; +import org.chrisoft.trashyaddon.misc.AddonEntityOutlineShader; +import org.chrisoft.trashyaddon.misc.AddonPostProcessingShaders; +import org.chrisoft.trashyaddon.mixin.meteorclient.PostProcessShadersMixin; import org.chrisoft.trashyaddon.modules.*; import com.mojang.logging.LogUtils; import meteordevelopment.meteorclient.addons.MeteorAddon; @@ -16,6 +19,8 @@ public class Addon extends MeteorAddon { @Override public void onInitialize() { LOG.info("Initializing Meteor Trash Addons"); + AddonPostProcessingShaders.registerPostProcessShader(new AddonEntityOutlineShader()); + CEntitySelectorOptions.register(); // Modules Modules.get().add(new AutoTrade()); @@ -27,6 +32,7 @@ public class Addon extends MeteorAddon { Commands.add(new MapDumpCommand()); Commands.add(new MapTallyCommand()); Commands.add(new MapLocateCommand()); + Commands.add(new EntityHighlightCommand()); // HUD //Hud.get().register(HudExample.INFO); diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java new file mode 100644 index 0000000..8bfa233 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java @@ -0,0 +1,157 @@ +package org.chrisoft.trashyaddon.commands; + +import com.google.common.collect.Lists; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.command.argument.EntityArgumentType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.predicate.NumberRange; +import net.minecraft.text.Text; +import net.minecraft.text.Texts; +import net.minecraft.util.TypeFilter; +import net.minecraft.util.function.LazyIterationConsumer; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; +import org.chrisoft.trashyaddon.mixin.ClientWorldMixin; +import org.jetbrains.annotations.Nullable; + +public class CEntitySelector { + public static final int MAX_VALUE = Integer.MAX_VALUE; + public static final BiConsumer> ARBITRARY = (pos, entities) -> { + }; + private static final TypeFilter PASSTHROUGH_FILTER = new TypeFilter() { + public Entity downcast(Entity arg) { + return arg; + } + + @Override + public Class getBaseClass() { + return Entity.class; + } + }; + private final int limit; + private final boolean includesNonPlayers; + private final boolean localWorldOnly; + private final Predicate basePredicate; + private final NumberRange.DoubleRange distance; + private final Function positionOffset; + @Nullable + private final Box box; + private final BiConsumer> sorter; + private final boolean senderOnly; + @Nullable + private final String playerName; + @Nullable + private final UUID uuid; + private final TypeFilter entityFilter; + private final boolean usesAt; + + public CEntitySelector( + int count, + boolean includesNonPlayers, + boolean localWorldOnly, + Predicate basePredicate, + NumberRange.DoubleRange distance, + Function positionOffset, + @Nullable Box box, + BiConsumer> sorter, + boolean senderOnly, + @Nullable String playerName, + @Nullable UUID uuid, + @Nullable EntityType type, + boolean usesAt + ) { + this.limit = count; + this.includesNonPlayers = includesNonPlayers; + this.localWorldOnly = localWorldOnly; + this.basePredicate = basePredicate; + this.distance = distance; + this.positionOffset = positionOffset; + this.box = box; + this.sorter = sorter; + this.senderOnly = senderOnly; + this.playerName = playerName; + this.uuid = uuid; + this.entityFilter = (TypeFilter)(type == null ? PASSTHROUGH_FILTER : type); + this.usesAt = usesAt; + } + + public int getLimit() { + return this.limit; + } + + public boolean includesNonPlayers() { + return this.includesNonPlayers; + } + + public boolean isSenderOnly() { + return this.senderOnly; + } + + public boolean isLocalWorldOnly() { + return this.localWorldOnly; + } + + public boolean usesAt() { + return this.usesAt; + } + + private int getAppendLimit() { + return this.sorter == ARBITRARY ? this.limit : Integer.MAX_VALUE; + } + + private Predicate getPositionPredicate(Vec3d pos) { + Predicate predicate = this.basePredicate; + if (this.box != null) { + Box lv = this.box.offset(pos); + predicate = predicate.and(entity -> lv.intersects(entity.getBoundingBox())); + } + + if (!this.distance.isDummy()) { + predicate = predicate.and(entity -> this.distance.testSqrt(entity.squaredDistanceTo(pos))); + } + + return predicate; + } + public List getClientSideEntityMatches(ClientWorld cw, Vec3d sourcePos) throws CommandSyntaxException { + Predicate p = this.getPositionPredicate(sourcePos); + List ret = new ArrayList<>(); + int limit = this.getAppendLimit(); + if (this.box != null) + cw.collectEntitiesByType(this.entityFilter, this.box.offset(sourcePos), p, ret, limit); + else { + ((ClientWorldMixin)cw).invokeGetEntityLookup().forEach(this.entityFilter, (e) -> { + if (p.test(e)) { + ret.add(e); + if (ret.size() >= limit) + return LazyIterationConsumer.NextIteration.ABORT; + } + return LazyIterationConsumer.NextIteration.CONTINUE; + }); + } + return ret; + } + + private List getEntities(Vec3d pos, List entities) { + if (entities.size() > 1) { + this.sorter.accept(pos, entities); + } + + return entities.subList(0, Math.min(this.limit, entities.size())); + } + + public static Text getNames(List entities) { + return Texts.join(entities, Entity::getDisplayName); + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java new file mode 100644 index 0000000..cb419f0 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java @@ -0,0 +1,472 @@ +package org.chrisoft.trashyaddon.commands; + +import com.google.common.collect.Maps; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; +import java.util.function.Predicate; + +import meteordevelopment.meteorclient.utils.entity.EntityUtils; +import net.minecraft.advancement.AdvancementProgress; +import net.minecraft.advancement.criterion.CriterionProgress; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.command.CommandSource; +import net.minecraft.command.FloatRangeArgument; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.predicate.NumberRange; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.scoreboard.AbstractTeam; +import net.minecraft.scoreboard.ReadableScoreboardScore; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.scoreboard.ScoreboardObjective; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.GameMode; + +public class CEntitySelectorOptions { + private static final Map OPTIONS = Maps.newHashMap(); + public static final DynamicCommandExceptionType UNKNOWN_OPTION_EXCEPTION = new DynamicCommandExceptionType( + option -> Text.stringifiedTranslatable("argument.entity.options.unknown", option) + ); + public static final DynamicCommandExceptionType INAPPLICABLE_OPTION_EXCEPTION = new DynamicCommandExceptionType( + option -> Text.stringifiedTranslatable("argument.entity.options.inapplicable", option) + ); + public static final SimpleCommandExceptionType NEGATIVE_DISTANCE_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.options.distance.negative") + ); + public static final SimpleCommandExceptionType NEGATIVE_LEVEL_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.options.level.negative") + ); + public static final SimpleCommandExceptionType TOO_SMALL_LEVEL_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.options.limit.toosmall") + ); + public static final DynamicCommandExceptionType IRREVERSIBLE_SORT_EXCEPTION = new DynamicCommandExceptionType( + sortType -> Text.stringifiedTranslatable("argument.entity.options.sort.irreversible", sortType) + ); + public static final DynamicCommandExceptionType INVALID_MODE_EXCEPTION = new DynamicCommandExceptionType( + gameMode -> Text.stringifiedTranslatable("argument.entity.options.mode.invalid", gameMode) + ); + public static final DynamicCommandExceptionType INVALID_TYPE_EXCEPTION = new DynamicCommandExceptionType( + entity -> Text.stringifiedTranslatable("argument.entity.options.type.invalid", entity) + ); + + private static void putOption(String id, CEntitySelectorOptions.SelectorHandler handler, Predicate condition, Text description) { + OPTIONS.put(id, new CEntitySelectorOptions.SelectorOption(handler, condition, description)); + } + + public static void register() { + if (OPTIONS.isEmpty()) { + putOption("name", reader -> { + int i = reader.getReader().getCursor(); + boolean bl = reader.readNegationCharacter(); + String string = reader.getReader().readString(); + if (reader.excludesName() && !bl) { + reader.getReader().setCursor(i); + throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "name"); + } else { + if (bl) { + reader.setExcludesName(true); + } else { + reader.setSelectsName(true); + } + + reader.setPredicate(readerx -> readerx.getName().getString().equals(string) != bl); + } + }, reader -> !reader.selectsName(), Text.translatable("argument.entity.options.name.description")); + putOption("distance", reader -> { + int i = reader.getReader().getCursor(); + NumberRange.DoubleRange lv = NumberRange.DoubleRange.parse(reader.getReader()); + if ((!lv.min().isPresent() || !(lv.min().get() < 0.0)) && (!lv.max().isPresent() || !(lv.max().get() < 0.0))) { + reader.setDistance(lv); + reader.setLocalWorldOnly(); + } else { + reader.getReader().setCursor(i); + throw NEGATIVE_DISTANCE_EXCEPTION.createWithContext(reader.getReader()); + } + }, reader -> reader.getDistance().isDummy(), Text.translatable("argument.entity.options.distance.description")); + putOption("level", reader -> { + int i = reader.getReader().getCursor(); + NumberRange.IntRange lv = NumberRange.IntRange.parse(reader.getReader()); + if ((!lv.min().isPresent() || lv.min().get() >= 0) && (!lv.max().isPresent() || lv.max().get() >= 0)) { + reader.setLevelRange(lv); + reader.setIncludesNonPlayers(false); + } else { + reader.getReader().setCursor(i); + throw NEGATIVE_LEVEL_EXCEPTION.createWithContext(reader.getReader()); + } + }, reader -> reader.getLevelRange().isDummy(), Text.translatable("argument.entity.options.level.description")); + putOption("x", reader -> { + reader.setLocalWorldOnly(); + reader.setX(reader.getReader().readDouble()); + }, reader -> reader.getX() == null, Text.translatable("argument.entity.options.x.description")); + putOption("y", reader -> { + reader.setLocalWorldOnly(); + reader.setY(reader.getReader().readDouble()); + }, reader -> reader.getY() == null, Text.translatable("argument.entity.options.y.description")); + putOption("z", reader -> { + reader.setLocalWorldOnly(); + reader.setZ(reader.getReader().readDouble()); + }, reader -> reader.getZ() == null, Text.translatable("argument.entity.options.z.description")); + putOption("dx", reader -> { + reader.setLocalWorldOnly(); + reader.setDx(reader.getReader().readDouble()); + }, reader -> reader.getDx() == null, Text.translatable("argument.entity.options.dx.description")); + putOption("dy", reader -> { + reader.setLocalWorldOnly(); + reader.setDy(reader.getReader().readDouble()); + }, reader -> reader.getDy() == null, Text.translatable("argument.entity.options.dy.description")); + putOption("dz", reader -> { + reader.setLocalWorldOnly(); + reader.setDz(reader.getReader().readDouble()); + }, reader -> reader.getDz() == null, Text.translatable("argument.entity.options.dz.description")); + putOption( + "x_rotation", + reader -> reader.setPitchRange(FloatRangeArgument.parse(reader.getReader(), true, MathHelper::wrapDegrees)), + reader -> reader.getPitchRange() == FloatRangeArgument.ANY, + Text.translatable("argument.entity.options.x_rotation.description") + ); + putOption( + "y_rotation", + reader -> reader.setYawRange(FloatRangeArgument.parse(reader.getReader(), true, MathHelper::wrapDegrees)), + reader -> reader.getYawRange() == FloatRangeArgument.ANY, + Text.translatable("argument.entity.options.y_rotation.description") + ); + putOption("limit", reader -> { + int i = reader.getReader().getCursor(); + int j = reader.getReader().readInt(); + if (j < 1) { + reader.getReader().setCursor(i); + throw TOO_SMALL_LEVEL_EXCEPTION.createWithContext(reader.getReader()); + } else { + reader.setLimit(j); + reader.setHasLimit(true); + } + }, reader -> !reader.isSenderOnly() && !reader.hasLimit(), Text.translatable("argument.entity.options.limit.description")); + putOption( + "sort", + reader -> { + int i = reader.getReader().getCursor(); + String string = reader.getReader().readUnquotedString(); + reader.setSuggestionProvider( + (builder, consumer) -> CommandSource.suggestMatching(Arrays.asList("nearest", "furthest", "random", "arbitrary"), builder) + ); + + reader.setSorter(switch (string) { + case "nearest" -> CEntitySelectorReader.NEAREST; + case "furthest" -> CEntitySelectorReader.FURTHEST; + case "random" -> CEntitySelectorReader.RANDOM; + case "arbitrary" -> CEntitySelector.ARBITRARY; + default -> { + reader.getReader().setCursor(i); + throw IRREVERSIBLE_SORT_EXCEPTION.createWithContext(reader.getReader(), string); + } + }); + reader.setHasSorter(true); + }, + reader -> !reader.isSenderOnly() && !reader.hasSorter(), + Text.translatable("argument.entity.options.sort.description") + ); + putOption("gamemode", reader -> { + reader.setSuggestionProvider((builder, consumer) -> { + String stringx = builder.getRemaining().toLowerCase(Locale.ROOT); + boolean blx = !reader.excludesGameMode(); + boolean bl2 = true; + if (!stringx.isEmpty()) { + if (stringx.charAt(0) == '!') { + blx = false; + stringx = stringx.substring(1); + } else { + bl2 = false; + } + } + + for (GameMode lvx : GameMode.values()) { + if (lvx.getName().toLowerCase(Locale.ROOT).startsWith(stringx)) { + if (bl2) { + builder.suggest("!" + lvx.getName()); + } + + if (blx) { + builder.suggest(lvx.getName()); + } + } + } + + return builder.buildFuture(); + }); + int i = reader.getReader().getCursor(); + boolean bl = reader.readNegationCharacter(); + if (reader.excludesGameMode() && !bl) { + reader.getReader().setCursor(i); + throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "gamemode"); + } else { + String string = reader.getReader().readUnquotedString(); + GameMode lv = GameMode.byName(string, null); + if (lv == null) { + reader.getReader().setCursor(i); + throw INVALID_MODE_EXCEPTION.createWithContext(reader.getReader(), string); + } else { + reader.setIncludesNonPlayers(false); + reader.setPredicate(entity -> { + if (!(entity instanceof ClientPlayerEntity)) { + return false; + } else { + GameMode lvx = EntityUtils.getGameMode((ClientPlayerEntity)entity); + return bl ? lvx != lv : lvx == lv; + } + }); + if (bl) { + reader.setExcludesGameMode(true); + } else { + reader.setSelectsGameMode(true); + } + } + } + }, reader -> !reader.selectsGameMode(), Text.translatable("argument.entity.options.gamemode.description")); + putOption("team", reader -> { + boolean bl = reader.readNegationCharacter(); + String string = reader.getReader().readUnquotedString(); + reader.setPredicate(entity -> { + if (!(entity instanceof LivingEntity)) { + return false; + } else { + AbstractTeam lv = entity.getScoreboardTeam(); + String string2 = lv == null ? "" : lv.getName(); + return string2.equals(string) != bl; + } + }); + if (bl) { + reader.setExcludesTeam(true); + } else { + reader.setSelectsTeam(true); + } + }, reader -> !reader.selectsTeam(), Text.translatable("argument.entity.options.team.description")); + putOption("type", reader -> { + reader.setSuggestionProvider((builder, consumer) -> { + CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.getIds(), builder, String.valueOf('!')); + CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.streamTags().map(TagKey::id), builder, "!#"); + if (!reader.excludesEntityType()) { + CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.getIds(), builder); + CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.streamTags().map(TagKey::id), builder, String.valueOf('#')); + } + + return builder.buildFuture(); + }); + int i = reader.getReader().getCursor(); + boolean bl = reader.readNegationCharacter(); + if (reader.excludesEntityType() && !bl) { + reader.getReader().setCursor(i); + throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "type"); + } else { + if (bl) { + reader.setExcludesEntityType(); + } + + if (reader.readTagCharacter()) { + TagKey> lv = TagKey.of(RegistryKeys.ENTITY_TYPE, Identifier.fromCommandInput(reader.getReader())); + reader.setPredicate(entity -> entity.getType().isIn(lv) != bl); + } else { + Identifier lv2 = Identifier.fromCommandInput(reader.getReader()); + EntityType lv3 = Registries.ENTITY_TYPE.getOrEmpty(lv2).orElseThrow(() -> { + reader.getReader().setCursor(i); + return INVALID_TYPE_EXCEPTION.createWithContext(reader.getReader(), lv2.toString()); + }); + if (Objects.equals(EntityType.PLAYER, lv3) && !bl) { + reader.setIncludesNonPlayers(false); + } + + reader.setPredicate(entity -> Objects.equals(lv3, entity.getType()) != bl); + if (!bl) { + reader.setEntityType(lv3); + } + } + } + }, reader -> !reader.selectsEntityType(), Text.translatable("argument.entity.options.type.description")); + putOption("tag", reader -> { + boolean bl = reader.readNegationCharacter(); + String string = reader.getReader().readUnquotedString(); + reader.setPredicate(entity -> "".equals(string) ? entity.getCommandTags().isEmpty() != bl : entity.getCommandTags().contains(string) != bl); + }, reader -> true, Text.translatable("argument.entity.options.tag.description")); + putOption("nbt", reader -> { + boolean bl = reader.readNegationCharacter(); + NbtCompound lv = new StringNbtReader(reader.getReader()).parseCompound(); + reader.setPredicate(entity -> { + NbtCompound lvx = entity.writeNbt(new NbtCompound()); + if (entity instanceof ClientPlayerEntity) { + ItemStack lv2 = ((ClientPlayerEntity)entity).getInventory().getMainHandStack(); + if (!lv2.isEmpty()) { + lvx.put("SelectedItem", lv2.writeNbt(new NbtCompound())); + } + } + + return NbtHelper.matches(lv, lvx, true) != bl; + }); + }, reader -> true, Text.translatable("argument.entity.options.nbt.description")); + putOption("scores", reader -> { + StringReader stringReader = reader.getReader(); + Map map = Maps.newHashMap(); + stringReader.expect('{'); + stringReader.skipWhitespace(); + + while (stringReader.canRead() && stringReader.peek() != '}') { + stringReader.skipWhitespace(); + String string = stringReader.readUnquotedString(); + stringReader.skipWhitespace(); + stringReader.expect('='); + stringReader.skipWhitespace(); + NumberRange.IntRange lv = NumberRange.IntRange.parse(stringReader); + map.put(string, lv); + stringReader.skipWhitespace(); + if (stringReader.canRead() && stringReader.peek() == ',') { + stringReader.skip(); + } + } + + stringReader.expect('}'); + if (!map.isEmpty()) { + reader.setPredicate(entity -> { + Scoreboard lvx = entity.getServer().getScoreboard(); + + for (Entry entry : map.entrySet()) { + ScoreboardObjective lv2 = lvx.getNullableObjective(entry.getKey()); + if (lv2 == null) { + return false; + } + + ReadableScoreboardScore lv3 = lvx.getScore(entity, lv2); + if (lv3 == null) { + return false; + } + + if (!entry.getValue().test(lv3.getScore())) { + return false; + } + } + + return true; + }); + } + + reader.setSelectsScores(true); + }, reader -> !reader.selectsScores(), Text.translatable("argument.entity.options.scores.description")); + putOption("advancements", reader -> { + StringReader stringReader = reader.getReader(); + Map> map = Maps.newHashMap(); + stringReader.expect('{'); + stringReader.skipWhitespace(); + + while (stringReader.canRead() && stringReader.peek() != '}') { + stringReader.skipWhitespace(); + Identifier lv = Identifier.fromCommandInput(stringReader); + stringReader.skipWhitespace(); + stringReader.expect('='); + stringReader.skipWhitespace(); + if (stringReader.canRead() && stringReader.peek() == '{') { + Map> map2 = Maps.newHashMap(); + stringReader.skipWhitespace(); + stringReader.expect('{'); + stringReader.skipWhitespace(); + + while (stringReader.canRead() && stringReader.peek() != '}') { + stringReader.skipWhitespace(); + String string = stringReader.readUnquotedString(); + stringReader.skipWhitespace(); + stringReader.expect('='); + stringReader.skipWhitespace(); + boolean bl = stringReader.readBoolean(); + map2.put(string, criterionProgress -> criterionProgress.isObtained() == bl); + stringReader.skipWhitespace(); + if (stringReader.canRead() && stringReader.peek() == ',') { + stringReader.skip(); + } + } + + stringReader.skipWhitespace(); + stringReader.expect('}'); + stringReader.skipWhitespace(); + map.put(lv, advancementProgress -> { + for (Entry> entry : map2.entrySet()) { + CriterionProgress lvx = advancementProgress.getCriterionProgress(entry.getKey()); + if (lvx == null || !entry.getValue().test(lvx)) { + return false; + } + } + + return true; + }); + } else { + boolean bl2 = stringReader.readBoolean(); + map.put(lv, advancementProgress -> advancementProgress.isDone() == bl2); + } + + stringReader.skipWhitespace(); + if (stringReader.canRead() && stringReader.peek() == ',') { + stringReader.skip(); + } + } + + stringReader.expect('}'); + if (!map.isEmpty()) { + reader.setPredicate(entity -> false); // no support + reader.setIncludesNonPlayers(false); + } + + reader.setSelectsAdvancements(true); + }, reader -> !reader.selectsAdvancements(), Text.translatable("argument.entity.options.advancements.description")); + putOption( + "predicate", + reader -> { + reader.setPredicate(entity -> false); // no support + }, + reader -> true, + Text.translatable("argument.entity.options.predicate.description") + ); + } + } + + public static CEntitySelectorOptions.SelectorHandler getHandler(CEntitySelectorReader reader, String option, int restoreCursor) throws CommandSyntaxException { + CEntitySelectorOptions.SelectorOption lv = OPTIONS.get(option); + if (lv != null) { + if (lv.condition.test(reader)) { + return lv.handler; + } else { + throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), option); + } + } else { + reader.getReader().setCursor(restoreCursor); + throw UNKNOWN_OPTION_EXCEPTION.createWithContext(reader.getReader(), option); + } + } + + public static void suggestOptions(CEntitySelectorReader reader, SuggestionsBuilder suggestionBuilder) { + String string = suggestionBuilder.getRemaining().toLowerCase(Locale.ROOT); + + for (Entry entry : OPTIONS.entrySet()) { + if (entry.getValue().condition.test(reader) && entry.getKey().toLowerCase(Locale.ROOT).startsWith(string)) { + suggestionBuilder.suggest(entry.getKey() + "=", entry.getValue().description); + } + } + } + + public interface SelectorHandler { + void handle(CEntitySelectorReader reader) throws CommandSyntaxException; + } + + static record SelectorOption(CEntitySelectorOptions.SelectorHandler handler, Predicate condition, Text description) { + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java new file mode 100644 index 0000000..c978d6e --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java @@ -0,0 +1,626 @@ +package org.chrisoft.trashyaddon.commands; + +import com.google.common.primitives.Doubles; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.ToDoubleFunction; + +import net.minecraft.command.FloatRangeArgument; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.predicate.NumberRange; +import net.minecraft.text.Text; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import org.jetbrains.annotations.Nullable; + +public class CEntitySelectorReader { + public static final char SELECTOR_PREFIX = '@'; + private static final char ARGUMENTS_OPENING = '['; + private static final char ARGUMENTS_CLOSING = ']'; + public static final char ARGUMENT_DEFINER = '='; + private static final char ARGUMENT_SEPARATOR = ','; + public static final char INVERT_MODIFIER = '!'; + public static final char TAG_MODIFIER = '#'; + private static final char NEAREST_PLAYER = 'p'; + private static final char ALL_PLAYERS = 'a'; + private static final char RANDOM_PLAYER = 'r'; + private static final char SELF = 's'; + private static final char ALL_ENTITIES = 'e'; + public static final SimpleCommandExceptionType INVALID_ENTITY_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.invalid")); + public static final DynamicCommandExceptionType UNKNOWN_SELECTOR_EXCEPTION = new DynamicCommandExceptionType( + selectorType -> Text.stringifiedTranslatable("argument.entity.selector.unknown", selectorType) + ); + public static final SimpleCommandExceptionType NOT_ALLOWED_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.selector.not_allowed") + ); + public static final SimpleCommandExceptionType MISSING_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.selector.missing")); + public static final SimpleCommandExceptionType UNTERMINATED_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.options.unterminated") + ); + public static final DynamicCommandExceptionType VALUELESS_EXCEPTION = new DynamicCommandExceptionType( + option -> Text.stringifiedTranslatable("argument.entity.options.valueless", option) + ); + public static final BiConsumer> NEAREST = (pos, entities) -> entities.sort( + (entity1, entity2) -> Doubles.compare(entity1.squaredDistanceTo(pos), entity2.squaredDistanceTo(pos)) + ); + public static final BiConsumer> FURTHEST = (pos, entities) -> entities.sort( + (entity1, entity2) -> Doubles.compare(entity2.squaredDistanceTo(pos), entity1.squaredDistanceTo(pos)) + ); + public static final BiConsumer> RANDOM = (pos, entities) -> Collections.shuffle(entities); + public static final BiFunction, CompletableFuture> DEFAULT_SUGGESTION_PROVIDER = (builder, consumer) -> builder.buildFuture(); + private final StringReader reader; + private final boolean atAllowed; + private int limit; + private boolean includesNonPlayers; + private boolean localWorldOnly; + private NumberRange.DoubleRange distance = NumberRange.DoubleRange.ANY; + private NumberRange.IntRange levelRange = NumberRange.IntRange.ANY; + @Nullable + private Double x; + @Nullable + private Double y; + @Nullable + private Double z; + @Nullable + private Double dx; + @Nullable + private Double dy; + @Nullable + private Double dz; + private FloatRangeArgument pitchRange = FloatRangeArgument.ANY; + private FloatRangeArgument yawRange = FloatRangeArgument.ANY; + private Predicate predicate = entity -> true; + private BiConsumer> sorter = CEntitySelector.ARBITRARY; + private boolean senderOnly; + @Nullable + private String playerName; + private int startCursor; + @Nullable + private UUID uuid; + private BiFunction, CompletableFuture> suggestionProvider = DEFAULT_SUGGESTION_PROVIDER; + private boolean selectsName; + private boolean excludesName; + private boolean hasLimit; + private boolean hasSorter; + private boolean selectsGameMode; + private boolean excludesGameMode; + private boolean selectsTeam; + private boolean excludesTeam; + @Nullable + private EntityType entityType; + private boolean excludesEntityType; + private boolean selectsScores; + private boolean selectsAdvancements; + private boolean usesAt; + + public CEntitySelectorReader(StringReader reader) { + this(reader, true); + } + + public CEntitySelectorReader(StringReader reader, boolean atAllowed) { + this.reader = reader; + this.atAllowed = atAllowed; + } + + public CEntitySelector build() { + Box lv; + if (this.dx == null && this.dy == null && this.dz == null) { + if (this.distance.max().isPresent()) { + double d = this.distance.max().get(); + lv = new Box(-d, -d, -d, d + 1.0, d + 1.0, d + 1.0); + } else { + lv = null; + } + } else { + lv = this.createBox(this.dx == null ? 0.0 : this.dx, this.dy == null ? 0.0 : this.dy, this.dz == null ? 0.0 : this.dz); + } + + Function function; + if (this.x == null && this.y == null && this.z == null) { + function = pos -> pos; + } else { + function = pos -> new Vec3d(this.x == null ? pos.x : this.x, this.y == null ? pos.y : this.y, this.z == null ? pos.z : this.z); + } + + return new CEntitySelector( + this.limit, + this.includesNonPlayers, + this.localWorldOnly, + this.predicate, + this.distance, + function, + lv, + this.sorter, + this.senderOnly, + this.playerName, + this.uuid, + this.entityType, + this.usesAt + ); + } + + private Box createBox(double x, double y, double z) { + boolean bl = x < 0.0; + boolean bl2 = y < 0.0; + boolean bl3 = z < 0.0; + double g = bl ? x : 0.0; + double h = bl2 ? y : 0.0; + double i = bl3 ? z : 0.0; + double j = (bl ? 0.0 : x) + 1.0; + double k = (bl2 ? 0.0 : y) + 1.0; + double l = (bl3 ? 0.0 : z) + 1.0; + return new Box(g, h, i, j, k, l); + } + + private void buildPredicate() { + if (this.pitchRange != FloatRangeArgument.ANY) { + this.predicate = this.predicate.and(this.rotationPredicate(this.pitchRange, Entity::getPitch)); + } + + if (this.yawRange != FloatRangeArgument.ANY) { + this.predicate = this.predicate.and(this.rotationPredicate(this.yawRange, Entity::getYaw)); + } + + if (!this.levelRange.isDummy()) { + this.predicate = this.predicate + .and(entity -> false); // no support for the xp predicate + } + } + + private Predicate rotationPredicate(FloatRangeArgument angleRange, ToDoubleFunction entityToAngle) { + double d = (double)MathHelper.wrapDegrees(angleRange.min() == null ? 0.0F : angleRange.min()); + double e = (double)MathHelper.wrapDegrees(angleRange.max() == null ? 359.0F : angleRange.max()); + return entity -> { + double f = MathHelper.wrapDegrees(entityToAngle.applyAsDouble(entity)); + return d > e ? f >= d || f <= e : f >= d && f <= e; + }; + } + + protected void readAtVariable() throws CommandSyntaxException { + this.usesAt = true; + this.suggestionProvider = this::suggestSelectorRest; + if (!this.reader.canRead()) { + throw MISSING_EXCEPTION.createWithContext(this.reader); + } else { + int i = this.reader.getCursor(); + char c = this.reader.read(); + if (c == 'p') { + this.limit = 1; + this.includesNonPlayers = false; + this.sorter = NEAREST; + this.setEntityType(EntityType.PLAYER); + } else if (c == 'a') { + this.limit = Integer.MAX_VALUE; + this.includesNonPlayers = false; + this.sorter = CEntitySelector.ARBITRARY; + this.setEntityType(EntityType.PLAYER); + } else if (c == 'r') { + this.limit = 1; + this.includesNonPlayers = false; + this.sorter = RANDOM; + this.setEntityType(EntityType.PLAYER); + } else if (c == 's') { + this.limit = 1; + this.includesNonPlayers = true; + this.senderOnly = true; + } else { + if (c != 'e') { + this.reader.setCursor(i); + throw UNKNOWN_SELECTOR_EXCEPTION.createWithContext(this.reader, "@" + c); + } + + this.limit = Integer.MAX_VALUE; + this.includesNonPlayers = true; + this.sorter = CEntitySelector.ARBITRARY; + this.predicate = Entity::isAlive; + } + + this.suggestionProvider = this::suggestOpen; + if (this.reader.canRead() && this.reader.peek() == '[') { + this.reader.skip(); + this.suggestionProvider = this::suggestOptionOrEnd; + this.readArguments(); + } + } + } + + protected void readRegular() throws CommandSyntaxException { + if (this.reader.canRead()) { + this.suggestionProvider = this::suggestNormal; + } + + int i = this.reader.getCursor(); + String string = this.reader.readString(); + + try { + this.uuid = UUID.fromString(string); + this.includesNonPlayers = true; + } catch (IllegalArgumentException var4) { + if (string.isEmpty() || string.length() > 16) { + this.reader.setCursor(i); + throw INVALID_ENTITY_EXCEPTION.createWithContext(this.reader); + } + + this.includesNonPlayers = false; + this.playerName = string; + } + + this.limit = 1; + } + + protected void readArguments() throws CommandSyntaxException { + this.suggestionProvider = this::suggestOption; + this.reader.skipWhitespace(); + + while (this.reader.canRead() && this.reader.peek() != ']') { + this.reader.skipWhitespace(); + int i = this.reader.getCursor(); + String string = this.reader.readString(); + CEntitySelectorOptions.SelectorHandler lv = CEntitySelectorOptions.getHandler(this, string, i); + this.reader.skipWhitespace(); + if (!this.reader.canRead() || this.reader.peek() != '=') { + this.reader.setCursor(i); + throw VALUELESS_EXCEPTION.createWithContext(this.reader, string); + } + + this.reader.skip(); + this.reader.skipWhitespace(); + this.suggestionProvider = DEFAULT_SUGGESTION_PROVIDER; + lv.handle(this); + this.reader.skipWhitespace(); + this.suggestionProvider = this::suggestEndNext; + if (this.reader.canRead()) { + if (this.reader.peek() != ',') { + if (this.reader.peek() != ']') { + throw UNTERMINATED_EXCEPTION.createWithContext(this.reader); + } + break; + } + + this.reader.skip(); + this.suggestionProvider = this::suggestOption; + } + } + + if (this.reader.canRead()) { + this.reader.skip(); + this.suggestionProvider = DEFAULT_SUGGESTION_PROVIDER; + } else { + throw UNTERMINATED_EXCEPTION.createWithContext(this.reader); + } + } + + public boolean readNegationCharacter() { + this.reader.skipWhitespace(); + if (this.reader.canRead() && this.reader.peek() == '!') { + this.reader.skip(); + this.reader.skipWhitespace(); + return true; + } else { + return false; + } + } + + public boolean readTagCharacter() { + this.reader.skipWhitespace(); + if (this.reader.canRead() && this.reader.peek() == '#') { + this.reader.skip(); + this.reader.skipWhitespace(); + return true; + } else { + return false; + } + } + + public StringReader getReader() { + return this.reader; + } + + public void setPredicate(Predicate predicate) { + this.predicate = this.predicate.and(predicate); + } + + public void setLocalWorldOnly() { + this.localWorldOnly = true; + } + + public NumberRange.DoubleRange getDistance() { + return this.distance; + } + + public void setDistance(NumberRange.DoubleRange distance) { + this.distance = distance; + } + + public NumberRange.IntRange getLevelRange() { + return this.levelRange; + } + + public void setLevelRange(NumberRange.IntRange levelRange) { + this.levelRange = levelRange; + } + + public FloatRangeArgument getPitchRange() { + return this.pitchRange; + } + + public void setPitchRange(FloatRangeArgument pitchRange) { + this.pitchRange = pitchRange; + } + + public FloatRangeArgument getYawRange() { + return this.yawRange; + } + + public void setYawRange(FloatRangeArgument yawRange) { + this.yawRange = yawRange; + } + + @Nullable + public Double getX() { + return this.x; + } + + @Nullable + public Double getY() { + return this.y; + } + + @Nullable + public Double getZ() { + return this.z; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public void setDx(double dx) { + this.dx = dx; + } + + public void setDy(double dy) { + this.dy = dy; + } + + public void setDz(double dz) { + this.dz = dz; + } + + @Nullable + public Double getDx() { + return this.dx; + } + + @Nullable + public Double getDy() { + return this.dy; + } + + @Nullable + public Double getDz() { + return this.dz; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public void setIncludesNonPlayers(boolean includesNonPlayers) { + this.includesNonPlayers = includesNonPlayers; + } + + public BiConsumer> getSorter() { + return this.sorter; + } + + public void setSorter(BiConsumer> sorter) { + this.sorter = sorter; + } + + public CEntitySelector read() throws CommandSyntaxException { + this.startCursor = this.reader.getCursor(); + this.suggestionProvider = this::suggestSelector; + if (this.reader.canRead() && this.reader.peek() == '@') { + if (!this.atAllowed) { + throw NOT_ALLOWED_EXCEPTION.createWithContext(this.reader); + } + + this.reader.skip(); + this.readAtVariable(); + } else { + this.readRegular(); + } + + this.buildPredicate(); + return this.build(); + } + + private static void suggestSelector(SuggestionsBuilder builder) { + builder.suggest("@p", Text.translatable("argument.entity.selector.nearestPlayer")); + builder.suggest("@a", Text.translatable("argument.entity.selector.allPlayers")); + builder.suggest("@r", Text.translatable("argument.entity.selector.randomPlayer")); + builder.suggest("@s", Text.translatable("argument.entity.selector.self")); + builder.suggest("@e", Text.translatable("argument.entity.selector.allEntities")); + } + + private CompletableFuture suggestSelector(SuggestionsBuilder builder, Consumer consumer) { + consumer.accept(builder); + if (this.atAllowed) { + suggestSelector(builder); + } + + return builder.buildFuture(); + } + + private CompletableFuture suggestNormal(SuggestionsBuilder builder, Consumer consumer) { + SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(this.startCursor); + consumer.accept(suggestionsBuilder2); + return builder.add(suggestionsBuilder2).buildFuture(); + } + + private CompletableFuture suggestSelectorRest(SuggestionsBuilder builder, Consumer consumer) { + SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(builder.getStart() - 1); + suggestSelector(suggestionsBuilder2); + builder.add(suggestionsBuilder2); + return builder.buildFuture(); + } + + private CompletableFuture suggestOpen(SuggestionsBuilder builder, Consumer consumer) { + builder.suggest(String.valueOf('[')); + return builder.buildFuture(); + } + + private CompletableFuture suggestOptionOrEnd(SuggestionsBuilder builder, Consumer consumer) { + builder.suggest(String.valueOf(']')); + CEntitySelectorOptions.suggestOptions(this, builder); + return builder.buildFuture(); + } + + private CompletableFuture suggestOption(SuggestionsBuilder builder, Consumer consumer) { + CEntitySelectorOptions.suggestOptions(this, builder); + return builder.buildFuture(); + } + + private CompletableFuture suggestEndNext(SuggestionsBuilder builder, Consumer consumer) { + builder.suggest(String.valueOf(',')); + builder.suggest(String.valueOf(']')); + return builder.buildFuture(); + } + + private CompletableFuture suggestDefinerNext(SuggestionsBuilder builder, Consumer consumer) { + builder.suggest(String.valueOf('=')); + return builder.buildFuture(); + } + + public boolean isSenderOnly() { + return this.senderOnly; + } + + public void setSuggestionProvider(BiFunction, CompletableFuture> suggestionProvider) { + this.suggestionProvider = suggestionProvider; + } + + public CompletableFuture listSuggestions(SuggestionsBuilder builder, Consumer consumer) { + return this.suggestionProvider.apply(builder.createOffset(this.reader.getCursor()), consumer); + } + + public boolean selectsName() { + return this.selectsName; + } + + public void setSelectsName(boolean selectsName) { + this.selectsName = selectsName; + } + + public boolean excludesName() { + return this.excludesName; + } + + public void setExcludesName(boolean excludesName) { + this.excludesName = excludesName; + } + + public boolean hasLimit() { + return this.hasLimit; + } + + public void setHasLimit(boolean hasLimit) { + this.hasLimit = hasLimit; + } + + public boolean hasSorter() { + return this.hasSorter; + } + + public void setHasSorter(boolean hasSorter) { + this.hasSorter = hasSorter; + } + + public boolean selectsGameMode() { + return this.selectsGameMode; + } + + public void setSelectsGameMode(boolean selectsGameMode) { + this.selectsGameMode = selectsGameMode; + } + + public boolean excludesGameMode() { + return this.excludesGameMode; + } + + public void setExcludesGameMode(boolean excludesGameMode) { + this.excludesGameMode = excludesGameMode; + } + + public boolean selectsTeam() { + return this.selectsTeam; + } + + public void setSelectsTeam(boolean selectsTeam) { + this.selectsTeam = selectsTeam; + } + + public boolean excludesTeam() { + return this.excludesTeam; + } + + public void setExcludesTeam(boolean excludesTeam) { + this.excludesTeam = excludesTeam; + } + + public void setEntityType(EntityType entityType) { + this.entityType = entityType; + } + + public void setExcludesEntityType() { + this.excludesEntityType = true; + } + + public boolean selectsEntityType() { + return this.entityType != null; + } + + public boolean excludesEntityType() { + return this.excludesEntityType; + } + + public boolean selectsScores() { + return this.selectsScores; + } + + public void setSelectsScores(boolean selectsScores) { + this.selectsScores = selectsScores; + } + + public boolean selectsAdvancements() { + return this.selectsAdvancements; + } + + public void setSelectsAdvancements(boolean selectsAdvancements) { + this.selectsAdvancements = selectsAdvancements; + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/EntityHighlightCommand.java b/src/main/java/org/chrisoft/trashyaddon/commands/EntityHighlightCommand.java new file mode 100644 index 0000000..c29dfa4 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/EntityHighlightCommand.java @@ -0,0 +1,81 @@ +package org.chrisoft.trashyaddon.commands; + +import meteordevelopment.meteorclient.commands.Command; +import meteordevelopment.meteorclient.utils.render.color.Color; +import net.minecraft.command.CommandSource; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.entity.Entity; +import org.chrisoft.trashyaddon.commands.argument.CEntityArgumentType; +import org.chrisoft.trashyaddon.commands.argument.ColorCodeArgumentType; + +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import static com.mojang.brigadier.Command.SINGLE_SUCCESS; +import static meteordevelopment.meteorclient.MeteorClient.mc; + +/* +Usage: +eh set : Highlight targeted entities with specified color +eh remove : Remove highlight from targeted entities +eh clear : Remove highlight from all entities +Note: This command pulls settings from your ESP module. + */ + +public class EntityHighlightCommand extends Command { + private final static HashMap entityColors = new HashMap<>(); + + public static void clearColors() { entityColors.clear(); } + public static void setEntityColor(Entity e, Color c) { + entityColors.put(e.getUuid(), c); + } + public static void setEntityColors(List entities, Color c) { + for (Entity e : entities) + setEntityColor(e, c); + } + public static void removeEntityColor(Entity e) { + entityColors.remove(e.getUuid()); + } + public static void removeEntityColors(List entities) { + for (Entity e : entities) + removeEntityColor(e); + } + public static Color getEntityColor(Entity e) { + return entityColors.get(e.getUuid()); + } + + public EntityHighlightCommand() { + super("eh", "Highlight selected entities."); + } + + @Override + public void build(LiteralArgumentBuilder builder) { + builder.then( + argument("match", CEntityArgumentType.entities()).then( + literal("set").then( + argument("color", ColorCodeArgumentType.colorCode()).executes(ctx -> { + CEntitySelector es = ctx.getArgument("match", CEntitySelector.class); + List entities = es.getClientSideEntityMatches(mc.world, mc.player.getPos()); + setEntityColors(entities, ctx.getArgument("color", Color.class)); + info("Successfully set highlight for " + entities.size() + " entity(ies)."); + return SINGLE_SUCCESS; + } + ))).then( + literal("remove").executes(ctx -> { + CEntitySelector es = ctx.getArgument("match", CEntitySelector.class); + List entities = es.getClientSideEntityMatches(mc.world, mc.player.getPos()); + removeEntityColors(entities); + info("Successfully removed highlight for " + entities.size() + " entity(ies)."); + return SINGLE_SUCCESS; + }) + ) + ).then( + literal("clear").executes(ctx -> { + clearColors(); + info("Successfully removed highlight for all entities."); + return SINGLE_SUCCESS; + }) + ); + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/MapLocateCommand.java b/src/main/java/org/chrisoft/trashyaddon/commands/MapLocateCommand.java index c965b1a..a96b52b 100644 --- a/src/main/java/org/chrisoft/trashyaddon/commands/MapLocateCommand.java +++ b/src/main/java/org/chrisoft/trashyaddon/commands/MapLocateCommand.java @@ -1,43 +1,72 @@ package org.chrisoft.trashyaddon.commands; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import meteordevelopment.meteorclient.commands.Command; +import meteordevelopment.meteorclient.utils.render.color.Color; import net.minecraft.command.CommandSource; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.decoration.ItemFrameEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.chrisoft.trashyaddon.commands.argument.CEntityArgumentType; +import org.chrisoft.trashyaddon.commands.argument.ColorCodeArgumentType; -import java.text.Format; import java.util.Formatter; -import java.util.OptionalInt; +import java.util.List; +import java.util.stream.Stream; import static com.mojang.brigadier.Command.SINGLE_SUCCESS; +/* +Usage: +ml [color] : highlight item frames with map # using [color] (defaults to bright magenta) +Note: Use .eh clear to remove highlight from all item frames. + */ + public class MapLocateCommand extends Command { private final MinecraftClient mc = MinecraftClient.getInstance(); public MapLocateCommand() { - super("ml", "Find item frames with a certain map in them. Will eventually be replaced with an enhanced ESP module."); + super("ml", "Locate item frames with a specific map."); + } + + private void exec(int id, Color color) { + try { + CEntityArgumentType dummyArgIF = CEntityArgumentType.entities(); + CEntitySelector esIF = dummyArgIF.parse(new StringReader(new Formatter().format("@e[type=minecraft:item_frame,nbt={Item:{tag:{map:%d}}}]", id).toString())); + CEntityArgumentType dummyArgGIF = CEntityArgumentType.entities(); + CEntitySelector esGIF = dummyArgGIF.parse(new StringReader(new Formatter().format("@e[type=minecraft:glow_item_frame,nbt={Item:{tag:{map:%d}}}]", id).toString())); + List l1 = esIF.getClientSideEntityMatches(mc.world, mc.player.getPos()); + List l2 = esGIF.getClientSideEntityMatches(mc.world, mc.player.getPos()); + if (l1.isEmpty() && l2.isEmpty()) { + info(Text.literal(new Formatter().format("Map #%d not found", id).toString()).setStyle(Style.EMPTY.withColor(Formatting.RED))); + return; + } + info(new Formatter().format("Map #%d found at:", id).toString()); + Stream.concat(l1.stream(), l2.stream()).forEach(e -> { + EntityHighlightCommand.setEntityColor(e, color); + info(new Formatter().format(" %.1f %.1f %.1f", e.getPos().x, e.getPos().y, e.getPos().z).toString()); + }); + } catch (CommandSyntaxException e) { + error("This should never happen. The author of this garbage is probably an idiot."); + } } @Override public void build(LiteralArgumentBuilder builder) { - builder.then(argument("id", IntegerArgumentType.integer()).executes(context -> { - int targetID = IntegerArgumentType.getInteger(context, "id"); - for (Entity e : mc.world.getEntities()) { - if (e.getType() != EntityType.ITEM_FRAME && e.getType() != EntityType.GLOW_ITEM_FRAME) - continue; - ItemFrameEntity ife = (ItemFrameEntity) e; - OptionalInt oid = ife.getMapId(); - if (oid.isEmpty()) - continue; - int id = oid.getAsInt(); - Formatter fmt = new Formatter(); - if (id == targetID) - info(fmt.format("Map frame found at %.2f %.2f %.2f", ife.getPos().x, ife.getPos().y, ife.getPos().z).toString()); - } + builder.then(argument("id", IntegerArgumentType.integer()).executes(ctx -> { + int targetID = IntegerArgumentType.getInteger(ctx, "id"); + Color color = new Color(0xff7fff); + exec(targetID, color); + return SINGLE_SUCCESS; + }).then(argument("color", ColorCodeArgumentType.colorCode()).executes(ctx -> { + int targetID = IntegerArgumentType.getInteger(ctx, "id"); + Color color = ctx.getArgument("color", Color.class); + exec(targetID, color); return SINGLE_SUCCESS; - })); + }))); } } diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java b/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java new file mode 100644 index 0000000..c13c08c --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java @@ -0,0 +1,161 @@ +package org.chrisoft.trashyaddon.commands.argument; + +import com.google.common.collect.Iterables; +import com.google.gson.JsonObject; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.CommandSource; +import org.chrisoft.trashyaddon.commands.CEntitySelector; +import org.chrisoft.trashyaddon.commands.CEntitySelectorReader; +import net.minecraft.command.argument.serialize.ArgumentSerializer; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.text.Text; + +public class CEntityArgumentType implements ArgumentType { + private static final Collection EXAMPLES = Arrays.asList("Player", "0123", "@e", "@e[type=foo]", "dd12be42-52a9-4a91-a8a1-11c01849e498"); + public static final SimpleCommandExceptionType TOO_MANY_ENTITIES_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.toomany")); + public static final SimpleCommandExceptionType TOO_MANY_PLAYERS_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.player.toomany")); + public static final SimpleCommandExceptionType PLAYER_SELECTOR_HAS_ENTITIES_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.player.entities") + ); + public static final SimpleCommandExceptionType ENTITY_NOT_FOUND_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.notfound.entity") + ); + public static final SimpleCommandExceptionType PLAYER_NOT_FOUND_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.notfound.player") + ); + public static final SimpleCommandExceptionType NOT_ALLOWED_EXCEPTION = new SimpleCommandExceptionType( + Text.translatable("argument.entity.selector.not_allowed") + ); + final boolean singleTarget; + final boolean playersOnly; + + protected CEntityArgumentType(boolean singleTarget, boolean playersOnly) { + this.singleTarget = singleTarget; + this.playersOnly = playersOnly; + } + + public static CEntityArgumentType entity() { + return new CEntityArgumentType(true, false); + } + + public static CEntityArgumentType entities() { + return new CEntityArgumentType(false, false); + } + + public static CEntityArgumentType player() { + return new CEntityArgumentType(true, true); + } + + public static CEntityArgumentType players() { + return new CEntityArgumentType(false, true); + } + + public CEntitySelector parse(StringReader stringReader) throws CommandSyntaxException { + int i = 0; + CEntitySelectorReader lv = new CEntitySelectorReader(stringReader); + CEntitySelector lv2 = lv.read(); + if (lv2.getLimit() > 1 && this.singleTarget) { + if (this.playersOnly) { + stringReader.setCursor(0); + throw TOO_MANY_PLAYERS_EXCEPTION.createWithContext(stringReader); + } else { + stringReader.setCursor(0); + throw TOO_MANY_ENTITIES_EXCEPTION.createWithContext(stringReader); + } + } else if (lv2.includesNonPlayers() && this.playersOnly && !lv2.isSenderOnly()) { + stringReader.setCursor(0); + throw PLAYER_SELECTOR_HAS_ENTITIES_EXCEPTION.createWithContext(stringReader); + } else { + return lv2; + } + } + + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + if (context.getSource() instanceof CommandSource lv) { + StringReader stringReader = new StringReader(builder.getInput()); + stringReader.setCursor(builder.getStart()); + CEntitySelectorReader lv2 = new CEntitySelectorReader(stringReader, lv.hasPermissionLevel(2)); + + try { + lv2.read(); + } catch (CommandSyntaxException var7) { + } + + return lv2.listSuggestions(builder, builderx -> { + Collection collection = MinecraftClient.getInstance().getNetworkHandler().getPlayerList().stream().map(e -> e.getProfile().getName()).collect(Collectors.toSet()); + Iterable iterable = (Iterable)(this.playersOnly ? collection : Iterables.concat(collection, lv.getEntitySuggestions())); + CommandSource.suggestMatching(iterable, builderx); + }); + } else { + return Suggestions.empty(); + } + } + + public Collection getExamples() { + return EXAMPLES; + } + + public static class Serializer implements ArgumentSerializer { + private static final byte SINGLE_FLAG = 1; + private static final byte PLAYERS_ONLY_FLAG = 2; + + public void writePacket(CEntityArgumentType.Serializer.Properties arg, PacketByteBuf arg2) { + int i = 0; + if (arg.single) { + i |= 1; + } + + if (arg.playersOnly) { + i |= 2; + } + + arg2.writeByte(i); + } + + public CEntityArgumentType.Serializer.Properties fromPacket(PacketByteBuf arg) { + byte b = arg.readByte(); + return new CEntityArgumentType.Serializer.Properties((b & 1) != 0, (b & 2) != 0); + } + + public void writeJson(CEntityArgumentType.Serializer.Properties arg, JsonObject jsonObject) { + jsonObject.addProperty("amount", arg.single ? "single" : "multiple"); + jsonObject.addProperty("type", arg.playersOnly ? "players" : "entities"); + } + + public CEntityArgumentType.Serializer.Properties getArgumentTypeProperties(CEntityArgumentType arg) { + return new CEntityArgumentType.Serializer.Properties(arg.singleTarget, arg.playersOnly); + } + + public final class Properties implements ArgumentSerializer.ArgumentTypeProperties { + final boolean single; + final boolean playersOnly; + + Properties(boolean single, boolean playersOnly) { + this.single = single; + this.playersOnly = playersOnly; + } + + public CEntityArgumentType createType(CommandRegistryAccess arg) { + return new CEntityArgumentType(this.single, this.playersOnly); + } + + @Override + public ArgumentSerializer getSerializer() { + return Serializer.this; + } + } + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/argument/ColorCodeArgumentType.java b/src/main/java/org/chrisoft/trashyaddon/commands/argument/ColorCodeArgumentType.java new file mode 100644 index 0000000..e56ade3 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/argument/ColorCodeArgumentType.java @@ -0,0 +1,79 @@ +package org.chrisoft.trashyaddon.commands.argument; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import meteordevelopment.meteorclient.utils.render.color.Color; +import net.minecraft.command.CommandSource; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +public class ColorCodeArgumentType implements ArgumentType { + private static final Collection EXAMPLES = Arrays.asList("#000", "#FFFFFF", "#AA667788", "red"); + private static final SimpleCommandExceptionType BAD_COLOR_CODE = new SimpleCommandExceptionType(Text.literal("Invalid color code.")); + private ColorCodeArgumentType() {} + public static ColorCodeArgumentType colorCode() { return new ColorCodeArgumentType(); } + + @Override + public Color parse(StringReader reader) throws CommandSyntaxException { + if (reader.peek() == '#') { + reader.read(); + String v = reader.readString(); + switch (v.length()) { + case 3: + try { + int iv = Integer.parseInt(v, 16); + int r = iv & 0xF00 >> 8; + int g = iv & 0xF0 >> 4; + int b = iv & 0xF; + return new Color((r << 4) | r, (g << 4) | g, (b << 4) | b); + } catch (NumberFormatException __) { + throw BAD_COLOR_CODE.create(); + } + case 6: + try { + int iv = Integer.parseInt(v, 16); + int p = 0xFF000000 | iv; + return new Color(p); + } catch (NumberFormatException __) { + throw BAD_COLOR_CODE.create(); + } + case 8: + try { + int iv = Integer.parseUnsignedInt(v, 16); + return new Color(iv); + } catch (NumberFormatException __) { + throw BAD_COLOR_CODE.create(); + } + default: throw BAD_COLOR_CODE.create(); + } + } + String str = reader.readUnquotedString(); + Formatting lv = Formatting.byName(str); + if (lv != null && lv.isColor()) { + return new Color(lv.getColorValue()).a(255); + } + throw BAD_COLOR_CODE.create(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + StringReader stringReader = new StringReader(builder.getRemaining()); + if (stringReader.canRead(1) && stringReader.peek() == '#') + return builder.buildFuture(); + return CommandSource.suggestMatching(Formatting.getNames(true, false).stream().filter(x -> !x.equals("reset")), builder); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/misc/AddonEntityOutlineShader.java b/src/main/java/org/chrisoft/trashyaddon/misc/AddonEntityOutlineShader.java new file mode 100644 index 0000000..af34df4 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/misc/AddonEntityOutlineShader.java @@ -0,0 +1,41 @@ +package org.chrisoft.trashyaddon.misc; + +import meteordevelopment.meteorclient.systems.modules.Modules; +import meteordevelopment.meteorclient.systems.modules.render.ESP; +import meteordevelopment.meteorclient.utils.render.postprocess.EntityShader; +import net.minecraft.entity.Entity; +import org.chrisoft.trashyaddon.commands.EntityHighlightCommand; + +public class AddonEntityOutlineShader extends EntityShader { + private static ESP esp; + + public AddonEntityOutlineShader() { + init("outline"); + } + + @Override + protected boolean shouldDraw() { + if (esp == null) esp = Modules.get().get(ESP.class); + //return esp.isShader(); + return true; + } + + @Override + public void endRender() { + super.endRender(); + } + + @Override + public boolean shouldDraw(Entity entity) { + if (!shouldDraw()) return false; + return EntityHighlightCommand.getEntityColor(entity) != null; + } + + @Override + protected void setUniforms() { + shader.set("u_Width", esp.outlineWidth.get()); + shader.set("u_FillOpacity", esp.fillOpacity.get()); + shader.set("u_ShapeMode", esp.shapeMode.get().ordinal()); + shader.set("u_GlowMultiplier", esp.glowMultiplier.get()); + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/misc/AddonPostProcessingShaders.java b/src/main/java/org/chrisoft/trashyaddon/misc/AddonPostProcessingShaders.java new file mode 100644 index 0000000..596f449 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/misc/AddonPostProcessingShaders.java @@ -0,0 +1,14 @@ +package org.chrisoft.trashyaddon.misc; + +import meteordevelopment.meteorclient.utils.render.postprocess.EntityShader; + +import java.util.ArrayList; +import java.util.List; + +public class AddonPostProcessingShaders { + public static final List entityShaders = new ArrayList<>(); + + public static void registerPostProcessShader(EntityShader es) { + AddonPostProcessingShaders.entityShaders.add(es); + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/mixin/ClientWorldMixin.java b/src/main/java/org/chrisoft/trashyaddon/mixin/ClientWorldMixin.java new file mode 100644 index 0000000..da7d36a --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/mixin/ClientWorldMixin.java @@ -0,0 +1,13 @@ +package org.chrisoft.trashyaddon.mixin; + +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.world.entity.EntityLookup; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ClientWorld.class) +public interface ClientWorldMixin { + @Invoker("getEntityLookup") + public EntityLookup invokeGetEntityLookup(); +} diff --git a/src/main/java/org/chrisoft/trashyaddon/mixin/EntityMixin.java b/src/main/java/org/chrisoft/trashyaddon/mixin/EntityMixin.java new file mode 100644 index 0000000..39f6bb6 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/mixin/EntityMixin.java @@ -0,0 +1,25 @@ +package org.chrisoft.trashyaddon.mixin; + +import com.mojang.logging.LogUtils; +import meteordevelopment.meteorclient.utils.render.color.Color; +import meteordevelopment.meteorclient.utils.render.postprocess.PostProcessShaders; +import net.minecraft.entity.Entity; +import org.chrisoft.trashyaddon.commands.EntityHighlightCommand; +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(value = Entity.class, priority = 1001) +public abstract class EntityMixin { + @Inject(method = "getTeamColorValue", at = @At("HEAD"), cancellable = true) + private void onGetTeamColorValue(CallbackInfoReturnable info) { + if (PostProcessShaders.rendering) { + Color color = EntityHighlightCommand.getEntityColor((Entity) (Object) this); + if (color != null) { + info.setReturnValue(color.getPacked()); + info.cancel(); + } + } + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/mixin/WorldRendererMixin.java b/src/main/java/org/chrisoft/trashyaddon/mixin/WorldRendererMixin.java new file mode 100644 index 0000000..f953e7e --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/mixin/WorldRendererMixin.java @@ -0,0 +1,48 @@ +package org.chrisoft.trashyaddon.mixin; + +import meteordevelopment.meteorclient.utils.render.color.Color; +import meteordevelopment.meteorclient.utils.render.postprocess.PostProcessShaders; +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import meteordevelopment.meteorclient.utils.render.postprocess.EntityShader; +import org.chrisoft.trashyaddon.commands.EntityHighlightCommand; +import org.chrisoft.trashyaddon.misc.AddonPostProcessingShaders; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = WorldRenderer.class, priority = 999) +public abstract class WorldRendererMixin { + @Shadow + private Framebuffer entityOutlinesFramebuffer; + @Shadow + protected abstract void renderEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers); + + @Inject(method = "renderEntity", at = @At("HEAD")) + private void renderEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, CallbackInfo info) { + for (EntityShader es : AddonPostProcessingShaders.entityShaders) { + drawShaded(entity, cameraX, cameraY, cameraZ, tickDelta, vertexConsumers, matrices, es, EntityHighlightCommand.getEntityColor(entity)); + } + } + + @Unique + private void drawShaded(Entity entity, double cameraX, double cameraY, double cameraZ, float tickDelta, VertexConsumerProvider vertexConsumers, MatrixStack matrices, EntityShader shader, Color color) { + if (shader.shouldDraw(entity) && !PostProcessShaders.isCustom(vertexConsumers) && color != null) { + Framebuffer prevBuffer = this.entityOutlinesFramebuffer; + this.entityOutlinesFramebuffer = shader.framebuffer; + PostProcessShaders.rendering = true; + + shader.vertexConsumerProvider.setColor(color.r, color.g, color.b, color.a); + renderEntity(entity, cameraX, cameraY, cameraZ, tickDelta, matrices, shader.vertexConsumerProvider); + + PostProcessShaders.rendering = false; + this.entityOutlinesFramebuffer = prevBuffer; + } + } +} diff --git a/src/main/java/org/chrisoft/trashyaddon/mixin/meteorclient/PostProcessShadersMixin.java b/src/main/java/org/chrisoft/trashyaddon/mixin/meteorclient/PostProcessShadersMixin.java new file mode 100644 index 0000000..565e670 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/mixin/meteorclient/PostProcessShadersMixin.java @@ -0,0 +1,41 @@ +package org.chrisoft.trashyaddon.mixin.meteorclient; + +import com.mojang.logging.LogUtils; +import meteordevelopment.meteorclient.utils.render.postprocess.EntityShader; +import net.minecraft.client.render.VertexConsumerProvider; +import org.chrisoft.trashyaddon.misc.AddonPostProcessingShaders; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(value = meteordevelopment.meteorclient.utils.render.postprocess.PostProcessShaders.class, remap = false) +public abstract class PostProcessShadersMixin { + @Inject(method = "beginRender()V", at = @At("TAIL")) + private static void beginRender(CallbackInfo __) { + for (EntityShader es : AddonPostProcessingShaders.entityShaders) + es.beginRender(); + } + + @Inject(method = "endRender()V", at = @At("TAIL")) + private static void endRender(CallbackInfo __) { + for (EntityShader es : AddonPostProcessingShaders.entityShaders) + es.endRender(); + } + + @Inject(method = "onResized(II)V", at = @At("TAIL")) + private static void onResized(int width, int height, CallbackInfo __) { + for (EntityShader es : AddonPostProcessingShaders.entityShaders) + es.onResized(width, height); + } + + @Inject(method = "isCustom(Lnet/minecraft/client/render/VertexConsumerProvider;)Z", at = @At("HEAD"), cancellable = true, remap = true) + private static void isCustom(VertexConsumerProvider vcp, CallbackInfoReturnable info) { + for (EntityShader es : AddonPostProcessingShaders.entityShaders) + if (vcp == es.vertexConsumerProvider) { + info.setReturnValue(true); + info.cancel(); + } + } +} diff --git a/src/main/resources/addon.mixins.json b/src/main/resources/addon.mixins.json index fa5b1a5..c17e853 100644 --- a/src/main/resources/addon.mixins.json +++ b/src/main/resources/addon.mixins.json @@ -4,7 +4,10 @@ "compatibilityLevel": "JAVA_17", "client": [ "MerchantScreenAccessor", - "HandledScreenMixin" + "HandledScreenMixin", + "ClientWorldMixin", + "EntityMixin", + "WorldRendererMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 68f8dd0..da62d20 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -18,7 +18,8 @@ ] }, "mixins": [ - "addon.mixins.json" + "addon.mixins.json", + "meteorclient_addon.mixins.json" ], "custom": { "meteor-client:color": "225,25,25" diff --git a/src/main/resources/meteorclient_addon.mixins.json b/src/main/resources/meteorclient_addon.mixins.json new file mode 100644 index 0000000..29b432f --- /dev/null +++ b/src/main/resources/meteorclient_addon.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "org.chrisoft.trashyaddon.mixin.meteorclient", + "compatibilityLevel": "JAVA_17", + "client": [ + "PostProcessShadersMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} -- cgit v1.2.3