From 303a775fc34f104b2374c750ef0672f0495c995e Mon Sep 17 00:00:00 2001 From: LexManos Date: Mon, 25 Sep 2017 12:56:09 -0700 Subject: [PATCH] Add new /forge entity list command for displaying a list of all entities in world. As well as tracking down chunks with large amounts of entities. --- .../minecraftforge/registries/GameData.java | 2 +- .../server/command/ForgeCommand.java | 100 +++++++++++++++++- .../resources/assets/forge/lang/en_US.lang | 7 ++ 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index bfab5dee2..111f2ec71 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -572,7 +572,7 @@ public class GameData RegistryEvent.MissingMappings event = reg.getMissingEvent(name, m.getValue()); MinecraftForge.EVENT_BUS.post(event); - List> lst = event.getAllMappings().stream().filter(e -> e.getAction() == MissingMappings.Action.DEFAULT).collect(Collectors.toList()); + List> lst = event.getAllMappings().stream().filter(e -> e.getAction() == MissingMappings.Action.DEFAULT).sorted((a, b) -> a.toString().compareTo(b.toString())).collect(Collectors.toList()); if (!lst.isEmpty()) { FMLLog.log.error("Unidentified mapping from registry {}", name); diff --git a/src/main/java/net/minecraftforge/server/command/ForgeCommand.java b/src/main/java/net/minecraftforge/server/command/ForgeCommand.java index edb8bf881..b03b66c3e 100644 --- a/src/main/java/net/minecraftforge/server/command/ForgeCommand.java +++ b/src/main/java/net/minecraftforge/server/command/ForgeCommand.java @@ -23,16 +23,30 @@ import java.text.DecimalFormat; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.collect.Maps; + import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; import net.minecraft.command.ICommandSender; import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.WorldWorkerManager; import net.minecraftforge.server.ForgeTimeTracker; @@ -86,6 +100,10 @@ public class ForgeCommand extends CommandBase { { handleGen(server, sender, args); } + else if ("entity".equals(args[0])) + { + handleEntity(server, sender, args); + } else { throw new WrongUsageException("commands.forge.usage"); @@ -96,7 +114,7 @@ public class ForgeCommand extends CommandBase { public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { if (args.length <= 1) - return getListOfStringsMatchingLastWord(args, new String[] {"help", "tps", "track", "gen"}); + return getListOfStringsMatchingLastWord(args, "help", "tps", "track", "gen", "entity"); switch (args[0].toLowerCase(Locale.ENGLISH)) { @@ -108,6 +126,12 @@ public class ForgeCommand extends CommandBase { if (args.length == 6) // Dimension, Add support for names? Get list of ids? Meh return Collections.emptyList(); break; + case "entity": + if (args.length == 2) + return getListOfStringsMatchingLastWord(args, "help", "list"); + if (args.length == 3) + return getListOfStringsMatchingLastWord(args,EntityList.getEntityNameList().stream().map(e -> e.toString()).sorted().toArray(String[]::new)); + break; } return Collections.emptyList(); } @@ -199,4 +223,78 @@ public class ForgeCommand extends CommandBase { return sum / values.length; } + + private void handleEntity(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException + { + if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help")) + throw new WrongUsageException("commands.forge.entity.usage"); + + switch (args[1].toLowerCase(Locale.ENGLISH)) + { + case "list": + String filter = "*"; + if (args.length > 2) + { + if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) + throw new WrongUsageException("commands.forge.entity.list.usage"); + filter = args[2]; + } + final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?"); + Set names = EntityList.getEntityNameList().stream().filter(n -> n.toString().matches(cleanfilter)).collect(Collectors.toSet()); + + if (names.isEmpty()) + throw new WrongUsageException("commands.forge.entity.list.invalid"); + + int dim = args.length > 3 ? parseInt(args[3]) : sender.getEntityWorld().provider.getDimension(); + + Map>> list = Maps.newHashMap(); + WorldServer world = DimensionManager.getWorld(dim); + if (world == null) + throw new WrongUsageException("commands.forge.entity.list.invalidworld", dim); + + List entities = world.getLoadedEntityList(); + entities.forEach(e -> { + ResourceLocation key = EntityList.getKey(e); + + MutablePair> info = list.get(key); + if (info == null) + { + info = MutablePair.of(0, Maps.newHashMap()); + list.put(key, info); + } + ChunkPos chunk = new ChunkPos(e.getPosition()); + info.left++; + info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1); + }); + + if (names.size() == 1) + { + ResourceLocation name = names.iterator().next(); + Pair> info = list.get(name); + if (info == null) + throw new WrongUsageException("commands.forge.entity.list.none"); + sender.sendMessage(new TextComponentTranslation("commands.forge.entity.list.single.header", name, info.getLeft())); + info.getRight().entrySet().stream() + .sorted((a,b) -> a.getValue() != b.getValue() ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString())) + .limit(10).forEach(e -> sender.sendMessage(new TextComponentString(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z))); + } + else + { + + List> info = list.entrySet().stream() + .filter(e -> names.contains(e.getKey())) + .map(e -> Pair.of(e.getKey(), e.getValue().left)) + .sorted((a, b) -> a.getRight() != b.getRight() ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString())) + .collect(Collectors.toList()); + + if (info == null || info.size() == 0) + throw new WrongUsageException("commands.forge.entity.list.none"); + + int count = info.stream().mapToInt(a -> a.getRight()).sum(); + sender.sendMessage(new TextComponentTranslation("commands.forge.entity.list.multiple.header", count)); + info.forEach(e -> sender.sendMessage(new TextComponentString(" " + e.getValue() + ": " + e.getKey()))); + } + break; + } + } } diff --git a/src/main/resources/assets/forge/lang/en_US.lang b/src/main/resources/assets/forge/lang/en_US.lang index c4e063bca..4d20cb91e 100644 --- a/src/main/resources/assets/forge/lang/en_US.lang +++ b/src/main/resources/assets/forge/lang/en_US.lang @@ -6,6 +6,13 @@ commands.forge.gen.dim_fail=Failed to load world for dimension %d, Task terminat commands.forge.gen.progress=Generation Progress: %d/%d commands.forge.gen.complete=Finished generating %d new chunks (out of %d) for dimension %d. commands.forge.gen.start=Starting to generate %d chunks in a spiral around %d, %d in dimension %d. +commands.forge.entity.usage=Use /forge entity [list] help for more infomration on a specific command. +commands.forge.entity.list.usage=Use /forge entity list [filter] [dim] to get entity info that matches the optional filter. +commands.forge.entity.list.invalid=Invalid filter, does not match any entities. Use /forge entity list for a proper list +commands.forge.entity.list.invalidworld=Could not load world for dimension %d. Please select a valid dimension. +commands.forge.entity.list.none=No entities found. +commands.forge.entity.list.single.header=Entity: %s Total: %d +commands.forge.entity.list.multiple.header=Total: %d commands.forge.tracking.te.enabled=Tile Entity tracking enabled for %d seconds. commands.tree_base.invalid_cmd=Invalid subcommand '%s'!