aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2024-05-18 01:24:05 -0400
committerGravatar Chris Xiong <chirs241097@gmail.com> 2024-05-18 01:24:05 -0400
commit04b4941e65693f8d6b55f924781d7dd7cd26b1d5 (patch)
treec78ff16924c246c3c0073c0f880a5582689c4a37
parent2b0c0f25b9e9ca690bb334479f798794fec6dd38 (diff)
downloadmeteor-trashy-addon-dev.tar.xz
commands: EntityHighlight, improved MapLocate.HEADdev
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/Addon.java6
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java157
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java472
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java626
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/EntityHighlightCommand.java81
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/MapLocateCommand.java69
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java161
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/commands/argument/ColorCodeArgumentType.java79
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/misc/AddonEntityOutlineShader.java41
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/misc/AddonPostProcessingShaders.java14
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/mixin/ClientWorldMixin.java13
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/mixin/EntityMixin.java25
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/mixin/WorldRendererMixin.java48
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/mixin/meteorclient/PostProcessShadersMixin.java41
-rw-r--r--src/main/resources/addon.mixins.json5
-rw-r--r--src/main/resources/fabric.mod.json3
-rw-r--r--src/main/resources/meteorclient_addon.mixins.json11
17 files changed, 1830 insertions, 22 deletions
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<Vec3d, List<? extends Entity>> ARBITRARY = (pos, entities) -> {
+ };
+ private static final TypeFilter<Entity, ?> PASSTHROUGH_FILTER = new TypeFilter<Entity, Entity>() {
+ public Entity downcast(Entity arg) {
+ return arg;
+ }
+
+ @Override
+ public Class<? extends Entity> getBaseClass() {
+ return Entity.class;
+ }
+ };
+ private final int limit;
+ private final boolean includesNonPlayers;
+ private final boolean localWorldOnly;
+ private final Predicate<Entity> basePredicate;
+ private final NumberRange.DoubleRange distance;
+ private final Function<Vec3d, Vec3d> positionOffset;
+ @Nullable
+ private final Box box;
+ private final BiConsumer<Vec3d, List<? extends Entity>> sorter;
+ private final boolean senderOnly;
+ @Nullable
+ private final String playerName;
+ @Nullable
+ private final UUID uuid;
+ private final TypeFilter<Entity, ?> entityFilter;
+ private final boolean usesAt;
+
+ public CEntitySelector(
+ int count,
+ boolean includesNonPlayers,
+ boolean localWorldOnly,
+ Predicate<Entity> basePredicate,
+ NumberRange.DoubleRange distance,
+ Function<Vec3d, Vec3d> positionOffset,
+ @Nullable Box box,
+ BiConsumer<Vec3d, List<? extends Entity>> 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<Entity, ?>)(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<Entity> getPositionPredicate(Vec3d pos) {
+ Predicate<Entity> 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<? extends Entity> getClientSideEntityMatches(ClientWorld cw, Vec3d sourcePos) throws CommandSyntaxException {
+ Predicate<Entity> p = this.getPositionPredicate(sourcePos);
+ List<Entity> 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 <T extends Entity> List<T> getEntities(Vec3d pos, List<T> 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<? extends Entity> 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<String, CEntitySelectorOptions.SelectorOption> 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<CEntitySelectorReader> 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<EntityType<?>> 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<String, NumberRange.IntRange> 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<String, NumberRange.IntRange> 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<Identifier, Predicate<AdvancementProgress>> 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<String, Predicate<CriterionProgress>> 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<String, Predicate<CriterionProgress>> 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<String, CEntitySelectorOptions.SelectorOption> 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<CEntitySelectorReader> 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<Vec3d, List<? extends Entity>> NEAREST = (pos, entities) -> entities.sort(
+ (entity1, entity2) -> Doubles.compare(entity1.squaredDistanceTo(pos), entity2.squaredDistanceTo(pos))
+ );
+ public static final BiConsumer<Vec3d, List<? extends Entity>> FURTHEST = (pos, entities) -> entities.sort(
+ (entity1, entity2) -> Doubles.compare(entity2.squaredDistanceTo(pos), entity1.squaredDistanceTo(pos))
+ );
+ public static final BiConsumer<Vec3d, List<? extends Entity>> RANDOM = (pos, entities) -> Collections.shuffle(entities);
+ public static final BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> 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<Entity> predicate = entity -> true;
+ private BiConsumer<Vec3d, List<? extends Entity>> sorter = CEntitySelector.ARBITRARY;
+ private boolean senderOnly;
+ @Nullable
+ private String playerName;
+ private int startCursor;
+ @Nullable
+ private UUID uuid;
+ private BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> 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<Vec3d, Vec3d> 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<Entity> rotationPredicate(FloatRangeArgument angleRange, ToDoubleFunction<Entity> 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<Entity> 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<Vec3d, List<? extends Entity>> getSorter() {
+ return this.sorter;
+ }
+
+ public void setSorter(BiConsumer<Vec3d, List<? extends Entity>> 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<Suggestions> suggestSelector(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ consumer.accept(builder);
+ if (this.atAllowed) {
+ suggestSelector(builder);
+ }
+
+ return builder.buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestNormal(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(this.startCursor);
+ consumer.accept(suggestionsBuilder2);
+ return builder.add(suggestionsBuilder2).buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestSelectorRest(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(builder.getStart() - 1);
+ suggestSelector(suggestionsBuilder2);
+ builder.add(suggestionsBuilder2);
+ return builder.buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestOpen(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ builder.suggest(String.valueOf('['));
+ return builder.buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestOptionOrEnd(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ builder.suggest(String.valueOf(']'));
+ CEntitySelectorOptions.suggestOptions(this, builder);
+ return builder.buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestOption(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ CEntitySelectorOptions.suggestOptions(this, builder);
+ return builder.buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestEndNext(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ builder.suggest(String.valueOf(','));
+ builder.suggest(String.valueOf(']'));
+ return builder.buildFuture();
+ }
+
+ private CompletableFuture<Suggestions> suggestDefinerNext(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+ builder.suggest(String.valueOf('='));
+ return builder.buildFuture();
+ }
+
+ public boolean isSenderOnly() {
+ return this.senderOnly;
+ }
+
+ public void setSuggestionProvider(BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> suggestionProvider) {
+ this.suggestionProvider = suggestionProvider;
+ }
+
+ public CompletableFuture<Suggestions> listSuggestions(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> 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 <target selector> set <color> : Highlight targeted entities with specified color
+eh <target selector> 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<UUID, Color> 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<? extends Entity> 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<? extends Entity> 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<CommandSource> 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<? extends Entity> 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<? extends Entity> 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 <id> [color] : highlight item frames with map #<id> 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<? extends Entity> l1 = esIF.getClientSideEntityMatches(mc.world, mc.player.getPos());
+ List<? extends Entity> 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<CommandSource> 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<CEntitySelector> {
+ private static final Collection<String> 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 <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> 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<String> collection = MinecraftClient.getInstance().getNetworkHandler().getPlayerList().stream().map(e -> e.getProfile().getName()).collect(Collectors.toSet());
+ Iterable<String> iterable = (Iterable<String>)(this.playersOnly ? collection : Iterables.concat(collection, lv.getEntitySuggestions()));
+ CommandSource.suggestMatching(iterable, builderx);
+ });
+ } else {
+ return Suggestions.empty();
+ }
+ }
+
+ public Collection<String> getExamples() {
+ return EXAMPLES;
+ }
+
+ public static class Serializer implements ArgumentSerializer<CEntityArgumentType, CEntityArgumentType.Serializer.Properties> {
+ 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<CEntityArgumentType> {
+ 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<CEntityArgumentType, ?> 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<Color> {
+ private static final Collection<String> 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 <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> 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<String> 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<EntityShader> 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<Entity> 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<Integer> 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<Boolean> 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
+ }
+}