From 5b887ced8cecaa54045e9fc0b8f162e2d20306fa Mon Sep 17 00:00:00 2001 From: Vonr Date: Mon, 21 Oct 2024 22:07:43 +0800 Subject: [PATCH] feat: command to list dropless mobs the root command is quite cheap to run and thus does not require op, the "simulate" subcommand can get expensive and thus does require op. --- .../common/command/DroplessMobsCommand.java | 98 +++++++++++++++++++ .../arsnouveau/common/event/EventHandler.java | 1 + .../resources/META-INF/accesstransformer.cfg | 1 + 3 files changed, 100 insertions(+) create mode 100644 src/main/java/com/hollingsworth/arsnouveau/common/command/DroplessMobsCommand.java diff --git a/src/main/java/com/hollingsworth/arsnouveau/common/command/DroplessMobsCommand.java b/src/main/java/com/hollingsworth/arsnouveau/common/command/DroplessMobsCommand.java new file mode 100644 index 000000000..672b4d5ec --- /dev/null +++ b/src/main/java/com/hollingsworth/arsnouveau/common/command/DroplessMobsCommand.java @@ -0,0 +1,98 @@ +package com.hollingsworth.arsnouveau.common.command; + +import com.hollingsworth.arsnouveau.api.ANFakePlayer; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.server.ReloadableServerRegistries; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.LootTable; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + +import java.util.stream.Collectors; + +public class DroplessMobsCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("ars-dropless").executes(c -> { + // The root command is accessible to everyone as it isn't too expensive to run. + c.getSource().sendSystemMessage(Component.literal(listDroplessEntities(c.getSource().getServer().reloadableRegistries(), c.getSource().getLevel(), 0).stream().map(e -> BuiltInRegistries.ENTITY_TYPE.getKey(e).toString()).collect(Collectors.joining(",")))); + c.getSource().sendSystemMessage(Component.literal("Listed mobs that produce no drops under naive filtering, open your logs if you want to copy them.")); + + return 1; + }).then(Commands.literal("simulate").requires(c -> c.hasPermission(2)).then(Commands.argument("times", IntegerArgumentType.integer(1)).executes(c -> { + // This subcommand requires op as with enough simulations and entity types, it can get quite slow. + int times = IntegerArgumentType.getInteger(c, "times"); + c.getSource().sendSystemMessage(Component.literal(listDroplessEntities(c.getSource().getServer().reloadableRegistries(), c.getSource().getLevel(), times).stream().map(e -> BuiltInRegistries.ENTITY_TYPE.getKey(e).toString()).collect(Collectors.joining(",")))); + c.getSource().sendSystemMessage(Component.literal("Listed mobs that produced no drops after " + times + " simulations, open your logs if you want to copy them.")); + + return 1; + })))); + } + + public static ObjectArrayList> listDroplessEntities(ReloadableServerRegistries.Holder registries, ServerLevel level, int simulations) { + ANFakePlayer fakePlayer = simulations <= 0 ? null : ANFakePlayer.getPlayer(level); + DamageSource damageSource = simulations <= 0 ? null : level.damageSources().playerAttack(fakePlayer); + ObjectArrayList stacks = simulations <= 0 ? null : new ObjectArrayList<>(); + + ObjectArrayList> types = new ObjectArrayList<>(); + outer: for (EntityType ty : BuiltInRegistries.ENTITY_TYPE) { + Entity e; + try { + e = ty.create(level); + if (e == null) { + continue; + } + + if (!(e instanceof LivingEntity)) { + e.discard(); + continue; + } + } catch (Throwable ignored) { + continue; + } + + LootTable table = registries.getLootTable(e.getType().getDefaultLootTable()); + if (!table.pools.isEmpty()) { + e.discard(); + continue; + } + + if (simulations <= 0) { + types.add(e.getType()); + continue; + } + + LootParams.Builder ctx = (new LootParams.Builder(level)) + .withParameter(LootContextParams.THIS_ENTITY, e).withParameter(LootContextParams.ORIGIN, e.position()) + .withParameter(LootContextParams.DAMAGE_SOURCE, damageSource) + .withOptionalParameter(LootContextParams.ATTACKING_ENTITY, fakePlayer) + .withOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY, damageSource.getDirectEntity()); + ctx = ctx.withParameter(LootContextParams.LAST_DAMAGE_PLAYER, fakePlayer) + .withLuck(fakePlayer.getLuck()); + + for (int i = 0; i < simulations; i++) { + stacks.clear(); + stacks.addAll(table.getRandomItems(ctx.create(LootContextParamSets.ENTITY))); + if (!stacks.isEmpty()) { + e.discard(); + continue outer; + } + } + + types.add(e.getType()); + } + + return types; + } +} diff --git a/src/main/java/com/hollingsworth/arsnouveau/common/event/EventHandler.java b/src/main/java/com/hollingsworth/arsnouveau/common/event/EventHandler.java index 25545c5ea..0e161ce24 100644 --- a/src/main/java/com/hollingsworth/arsnouveau/common/event/EventHandler.java +++ b/src/main/java/com/hollingsworth/arsnouveau/common/event/EventHandler.java @@ -344,6 +344,7 @@ public static void commandRegister(RegisterCommandsEvent event) { SummonAnimHeadCommand.register(event.getDispatcher()); LearnGlyphCommand.register(event.getDispatcher()); AdoptCommand.register(event.getDispatcher()); + DroplessMobsCommand.register(event.getDispatcher()); } @SubscribeEvent diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index eb14d2eea..bd9f91a40 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -66,6 +66,7 @@ public net.minecraft.world.entity.Entity maxUpStep # maxUpStep public net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProviderType (Lcom/mojang/serialization/MapCodec;)V # BlockStateProviderType public net.minecraft.world.item.crafting.RecipeManager byKeyTyped(Lnet/minecraft/world/item/crafting/RecipeType;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/world/item/crafting/RecipeHolder; # byKeyTyped public net.minecraft.client.multiplayer.ClientChunkCache$Storage chunks # chunks +public net.minecraft.world.level.storage.loot.LootTable pools # pools # Geckolib ATs, do not delete public net.minecraft.client.model.geom.ModelPart$Cube public net.minecraft.client.model.geom.ModelPart cubes # cubes