From f9677dfc01f5b8942c55815ef5fbd09c8886ac16 Mon Sep 17 00:00:00 2001 From: Fallen_Breath Date: Mon, 21 Jun 2021 17:47:29 +0800 Subject: [PATCH] Added lifetime tracker ported from Carpet TIS Addition 1.20.0 --- carpetmodSrc/carpet/CarpetServer.java | 2 + carpetmodSrc/carpet/CarpetSettings.java | 7 +- .../carpet/commands/CarpetCommands.java | 2 + .../commands/lifetime/AbstractTracker.java | 214 ++++++++++++++++ .../commands/lifetime/CommandLifeTime.java | 150 +++++++++++ .../commands/lifetime/LifeTimeTracker.java | 179 +++++++++++++ .../lifetime/LifeTimeWorldTracker.java | 235 ++++++++++++++++++ .../lifetime/removal/DeathRemovalReason.java | 49 ++++ .../removal/LiteralRemovalReason.java | 48 ++++ .../removal/MobPickupRemovalReason.java | 40 +++ .../lifetime/removal/RemovalReason.java | 7 + .../removal/TransDimensionRemovalReason.java | 42 ++++ .../spawning/LiteralSpawningReason.java | 33 +++ .../spawning/MobDropSpawningReason.java | 40 +++ .../lifetime/spawning/SpawningReason.java | 7 + .../TransDimensionSpawningReason.java | 42 ++++ .../trackeddata/BasicTrackedData.java | 188 ++++++++++++++ .../trackeddata/ExperienceOrbTrackedData.java | 19 ++ .../trackeddata/ExtraCountTrackedData.java | 69 +++++ .../lifetime/trackeddata/ItemTrackedData.java | 19 ++ .../lifetime/utils/AbstractReason.java | 8 + .../commands/lifetime/utils/CounterUtil.java | 45 ++++ .../commands/lifetime/utils/GameUtil.java | 28 +++ .../lifetime/utils/LifeTimeStatistic.java | 163 ++++++++++++ .../lifetime/utils/LifeTimeTrackerUtil.java | 35 +++ .../lifetime/utils/SpecificDetailMode.java | 28 +++ .../commands/lifetime/utils/TextUtil.java | 69 +++++ carpetmodSrc/carpet/utils/Messenger.java | 5 + patches/net/minecraft/block/Block.java.patch | 22 +- .../minecraft/block/BlockPortal.java.patch | 26 +- .../command/server/CommandSummon.java.patch | 23 +- .../net/minecraft/entity/Entity.java.patch | 206 +++++++++++++-- .../minecraft/entity/EntityLiving.java.patch | 48 +++- .../entity/EntityLivingBase.java.patch | 66 +++-- .../entity/item/EntityExpBottle.java.patch | 22 ++ .../entity/item/EntityItem.java.patch | 61 ++++- .../entity/item/EntityXPOrb.java.patch | 34 ++- .../entity/monster/EntityGhast.java.patch | 27 +- .../entity/monster/EntityMob.java.patch | 21 +- .../entity/monster/EntitySlime.java.patch | 32 ++- .../entity/monster/EntityZombie.java.patch | 19 ++ .../entity/player/EntityPlayer.java.patch | 41 +-- .../item/ItemMonsterPlacer.java.patch | 19 ++ .../tileentity/TileEntityHopper.java.patch | 33 ++- .../world/WorldEntitySpawner.java.patch | 27 +- .../minecraft/world/WorldServer.java.patch | 78 +++--- .../chunk/storage/AnvilChunkLoader.java.patch | 30 ++- 47 files changed, 2448 insertions(+), 160 deletions(-) create mode 100644 carpetmodSrc/carpet/commands/lifetime/AbstractTracker.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/CommandLifeTime.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/LifeTimeTracker.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/LifeTimeWorldTracker.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/removal/DeathRemovalReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/removal/LiteralRemovalReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/removal/MobPickupRemovalReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/removal/RemovalReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/removal/TransDimensionRemovalReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/spawning/LiteralSpawningReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/spawning/MobDropSpawningReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/spawning/SpawningReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/spawning/TransDimensionSpawningReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/trackeddata/BasicTrackedData.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/trackeddata/ExperienceOrbTrackedData.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/trackeddata/ExtraCountTrackedData.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/trackeddata/ItemTrackedData.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/AbstractReason.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/CounterUtil.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/GameUtil.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeStatistic.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeTrackerUtil.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/SpecificDetailMode.java create mode 100644 carpetmodSrc/carpet/commands/lifetime/utils/TextUtil.java create mode 100644 patches/net/minecraft/entity/item/EntityExpBottle.java.patch create mode 100644 patches/net/minecraft/entity/monster/EntityZombie.java.patch create mode 100644 patches/net/minecraft/item/ItemMonsterPlacer.java.patch diff --git a/carpetmodSrc/carpet/CarpetServer.java b/carpetmodSrc/carpet/CarpetServer.java index 11b665aa..09f94d25 100644 --- a/carpetmodSrc/carpet/CarpetServer.java +++ b/carpetmodSrc/carpet/CarpetServer.java @@ -1,5 +1,6 @@ package carpet; +import carpet.commands.lifetime.LifeTimeTracker; import carpet.helpers.StackTraceDeobfuscator; import carpet.network.PluginChannelManager; import carpet.network.ToggleableChannelHandler; @@ -69,6 +70,7 @@ public static void onServerLoaded(MinecraftServer server) public static void onLoadAllWorlds(MinecraftServer server) { TickingArea.loadConfig(server); + LifeTimeTracker.attachServer(server); for (WorldServer world : server.worlds) { int dim = world.provider.getDimensionType().getId(); try { diff --git a/carpetmodSrc/carpet/CarpetSettings.java b/carpetmodSrc/carpet/CarpetSettings.java index 216adfac..fb498820 100644 --- a/carpetmodSrc/carpet/CarpetSettings.java +++ b/carpetmodSrc/carpet/CarpetSettings.java @@ -99,7 +99,7 @@ public class CarpetSettings @Rule(desc = "Enables /lazychunkbehavior command", category = COMMANDS, extra = { "Makes a chunk act like a lazy chunk for entities and falling sand" }) - + public static boolean commandEntityInfo = true; @Rule(desc = "Enables /unload command to inspect chunk unloading order", category = COMMANDS) @@ -155,6 +155,11 @@ public class CarpetSettings @Rule(desc = "Reduces the permition level to kick players for everyone.", category = COMMANDS) public static boolean publicKick; + @Rule(desc = "Enables /lifetime for tracking entities lifetime etc.", category = COMMANDS, extra = { + "rule optimizedDespawnRange is suggested to be enabled to avoid 0gt immediately despawn spamming" + }) + public static boolean commandLifeTime = true; + // ===== CREATIVE TOOLS ===== // @Rule(desc = "Emerald ore receiving a block update will throw a StackOverflowError, simulating an update suppressor.", category = CREATIVE) diff --git a/carpetmodSrc/carpet/commands/CarpetCommands.java b/carpetmodSrc/carpet/commands/CarpetCommands.java index edc73117..48759b88 100644 --- a/carpetmodSrc/carpet/commands/CarpetCommands.java +++ b/carpetmodSrc/carpet/commands/CarpetCommands.java @@ -1,6 +1,7 @@ package carpet.commands; import carpet.CarpetServer; +import carpet.commands.lifetime.CommandLifeTime; import narcolepticfrog.rsmm.MeterCommand; import net.minecraft.command.CommandHandler; @@ -21,6 +22,7 @@ public static void register(CommandHandler handler) { handler.registerCommand(new CommandGrow()); handler.registerCommand(new CommandLagSpike()); handler.registerCommand(new CommandLazyChunkBehavior()); + handler.registerCommand(new CommandLifeTime()); handler.registerCommand(new CommandLight()); handler.registerCommand(new CommandLoadChunk()); handler.registerCommand(new CommandLog()); diff --git a/carpetmodSrc/carpet/commands/lifetime/AbstractTracker.java b/carpetmodSrc/carpet/commands/lifetime/AbstractTracker.java new file mode 100644 index 00000000..29bfc027 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/AbstractTracker.java @@ -0,0 +1,214 @@ +package carpet.commands.lifetime; + +import carpet.CarpetServer; +import carpet.commands.lifetime.utils.GameUtil; +import carpet.utils.Messenger; +import net.minecraft.command.ICommandSender; + +public abstract class AbstractTracker +{ + private final String name; + private boolean tracking; + private long startTick; + private long startMillis; + + public AbstractTracker(String name) + { + this.name = name; + } + + /* + * --------------------- + * tracker name things + * --------------------- + */ + + public String getName() + { + return this.name; + } + + public String getCommandPrefix() + { + return this.name.toLowerCase(); + } + + // Xxx + public String getTranslatedName() + { + return this.name; + } + + // Xxx Tracker + public String getTranslatedNameFull() + { + return String.format("%s Tracker", this.getTranslatedName()); + } + + /* + * ----------------------- + * status / info getters + * ----------------------- + */ + + public boolean isTracking() + { + return this.tracking; + } + + public long getStartMillis() + { + return this.startMillis; + } + + public long getStartTick() + { + return this.startTick; + } + + /* + * ------------------------ + * for command executions + * ------------------------ + */ + + public int startTracking(ICommandSender source, boolean showFeedback) + { + if (this.isTracking()) + { + if (showFeedback) + { + Messenger.m(source, Messenger.c( + "r " + String.format("%s is already running", this.getTranslatedNameFull()) + )); + } + return 1; + } + this.tracking = true; + this.startTick = GameUtil.getGameTime(); + this.startMillis = System.currentTimeMillis(); + if (showFeedback) + { + Messenger.m(source, Messenger.c( + "w " + String.format("%s started", this.getTranslatedNameFull()) + )); + } + this.initTracker(); + return 1; + } + + public int stopTracking(ICommandSender source, boolean showFeedback) + { + if (source != null) + { + if (this.isTracking()) + { + this.reportTracking(source, false); + if (showFeedback) + { + Messenger.m(source, Messenger.c( + "w \n", + "w " + String.format("%s stopped", this.getTranslatedNameFull()) + )); + } + } + else if (showFeedback) + { + Messenger.m(source, Messenger.c( + "r " + String.format("%s has not started", this.getTranslatedNameFull()) + )); + } + } + this.tracking = false; + return 1; + } + + public int restartTracking(ICommandSender source) + { + boolean wasTracking = this.isTracking(); + this.stopTracking(source, false); + this.startTracking(source, false); + if (wasTracking) + { + source.sendMessage(Messenger.s(null, " ")); + } + Messenger.m(source, Messenger.s(null, String.format("%s restarted", this.getTranslatedNameFull()))); + return 1; + } + + protected int doWhenTracking(ICommandSender source, Runnable runnable) + { + if (this.isTracking()) + { + runnable.run(); + } + else + { + Messenger.m(source, Messenger.c( + "r " + String.format("%s has not started", this.getTranslatedNameFull()) + )); + } + return 1; + } + + public int reportTracking(ICommandSender source, boolean realtime) + { + return this.doWhenTracking(source, () -> this.printTrackingResult(source, realtime)); + } + + /* + * ------- + * Utils + * ------- + */ + + protected long getTrackedTick(boolean realtime) + { + return Math.max(1, realtime ? (System.currentTimeMillis() - this.getStartMillis()) / 50 : GameUtil.getGameTime() - this.getStartTick()); + } + + // send general header for tracking report and return the processed "ticks" + protected long sendTrackedTime(ICommandSender source, boolean realtime) + { + long ticks = this.getTrackedTick(realtime); + source.sendMessage(Messenger.c( + "w \n", + "g ----------- ", + "w " + this.getTranslatedNameFull(), + "g -----------\n", + String.format( + "w Tracked %.2f min (%s)", + (double)ticks / (20 * 60), + realtime ? "real time" : "in game" + ) + )); + return ticks; + } + + /* + * ------------ + * Interfaces + * ------------ + */ + +// /** +// * Stop tracking, call this when server stops +// * e.g. inside {@link carpet.CarpetServer#onServerClosed} +// */ +// public void stop() +// { +// this.stopTracking(null, false); +// } + + /** + * Called when the tracker starts tracking + * Go initialize necessary statistics + */ + protected abstract void initTracker(); + + /** + * Show tracking result to the command source + * @param realtime use real time or not. if not, use in-game time + */ + protected abstract void printTrackingResult(ICommandSender source, boolean realtime); +} diff --git a/carpetmodSrc/carpet/commands/lifetime/CommandLifeTime.java b/carpetmodSrc/carpet/commands/lifetime/CommandLifeTime.java new file mode 100644 index 00000000..cd0d6469 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/CommandLifeTime.java @@ -0,0 +1,150 @@ +package carpet.commands.lifetime; + +import carpet.CarpetSettings; +import carpet.commands.CommandCarpetBase; +import carpet.commands.lifetime.utils.SpecificDetailMode; +import carpet.utils.Messenger; +import com.google.common.collect.Lists; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class CommandLifeTime extends CommandCarpetBase +{ + private static final String NAME = "lifetime"; + private static final LifeTimeTracker tracker = LifeTimeTracker.getInstance(); + + @Override + public String getName() + { + return NAME; + } + + @Override + public String getUsage(ICommandSender sender) + { + return "/lifetime tracking []\n" + + "/lifetime [ []]"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException + { + if (!command_enabled("commandLifeTime", sender)) + { + return; + } + if (args.length == 0) + { + Messenger.s(sender, getUsage(sender)); + } + else + { + switch (args[0]) + { + case "tracking": + this.executeTracking(server, sender, args); + break; + case "help": + tracker.showHelp(sender); + break; + default: + this.executeDisplayResult(server, sender, args); + break; + } + } + } + + private void executeTracking(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException + { + if (args.length == 1) // lifetime tracking + { + tracker.reportTracking(sender, false); + return; + } + switch (args[1]) + { + case "start": + tracker.startTracking(sender, true); + break; + case "stop": + tracker.stopTracking(sender, true); + break; + case "restart": + tracker.restartTracking(sender); + break; + case "realtime": + tracker.reportTracking(sender, true); + break; + default: + throw new WrongUsageException("unknown command: " + args[1]); + } + } + + private void executeDisplayResult(MinecraftServer server, ICommandSender sender, String[] args) + { + String entityTypeInput = args[0]; + if (args.length == 1) // lifetime creeper + { + tracker.printTrackingResultSpecific(sender, entityTypeInput, null, false); + } + else + { + if (args[1].equals("realtime")) // lifetime creeper realtime + { + tracker.printTrackingResultSpecific(sender, entityTypeInput, null, true); + } + else if (args.length == 2) // lifetime creeper xxx + { + tracker.printTrackingResultSpecific(sender, entityTypeInput, args[1], false); + } + else if (args.length == 3 && args[2].equals("realtime")) // lifetime creeper xxx realtime + { + tracker.printTrackingResultSpecific(sender, entityTypeInput, args[1], true); + } + } + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos pos) + { + if (!CarpetSettings.commandLifeTime) + { + return Collections.emptyList(); + } + Set entityTypes = tracker.getAvailableEntityType().collect(Collectors.toSet()); + if (args.length == 1) + { + List suggestions = Lists.newArrayList(entityTypes); + suggestions.add("tracking"); + suggestions.add("help"); + return getListOfStringsMatchingLastWord(args, suggestions); + } + else if (args.length == 2 && args[0].equals("tracking")) + { + return getListOfStringsMatchingLastWord(args, "start", "stop", "restart", "realtime"); + } + else if (args.length >= 2 && entityTypes.contains(args[0])) + { + List detailSuggestions = Lists.newArrayList(SpecificDetailMode.getSuggestion()); + if (args.length == 2) + { + detailSuggestions.add("realtime"); + return getListOfStringsMatchingLastWord(args, detailSuggestions); + } + else if (detailSuggestions.contains(args[1])) + { + return getListOfStringsMatchingLastWord(args, "realtime"); + } + } + return Collections.emptyList(); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/LifeTimeTracker.java b/carpetmodSrc/carpet/commands/lifetime/LifeTimeTracker.java new file mode 100644 index 00000000..a60452fb --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/LifeTimeTracker.java @@ -0,0 +1,179 @@ +package carpet.commands.lifetime; + +import carpet.commands.lifetime.utils.LifeTimeTrackerUtil; +import carpet.commands.lifetime.utils.SpecificDetailMode; +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.Entity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +public class LifeTimeTracker extends AbstractTracker +{ + private static boolean attachedServer = false; + private static final LifeTimeTracker INSTANCE = new LifeTimeTracker(); + + private int currentTrackId = 0; + + private final Map trackers = new Reference2ObjectArrayMap<>(); + + public LifeTimeTracker() + { + super("LifeTime"); + } + + public static LifeTimeTracker getInstance() + { + return INSTANCE; + } + + public LifeTimeWorldTracker getTracker(World world) + { + return world instanceof WorldServer ? this.trackers.get(world) : null; + } + + public static void attachServer(MinecraftServer minecraftServer) + { + attachedServer = true; + INSTANCE.trackers.clear(); + for (WorldServer world : minecraftServer.worlds) + { + INSTANCE.trackers.put(world, world.getLifeTimeWorldTracker()); + } + } + +// public static void detachServer() +// { +// attachedServer = false; +// INSTANCE.stop(); +// } + + public static boolean isActivated() + { + return attachedServer && INSTANCE.isTracking(); + } + + public boolean willTrackEntity(Entity entity) + { + return isActivated() && + entity.getTrackId() == this.getCurrentTrackId() && + LifeTimeTrackerUtil.isTrackedEntity(entity); + } + public Stream getAvailableEntityType() + { + if (!isActivated()) + { + return Stream.empty(); + } + return this.trackers.values().stream(). + flatMap( + tracker -> tracker.getDataMap().keySet(). + stream().map(LifeTimeTrackerUtil::getEntityTypeDescriptor) + ). + distinct(); + } + + public int getCurrentTrackId() + { + return this.currentTrackId; + } + + @Override + protected void initTracker() + { + this.currentTrackId++; + this.trackers.values().forEach(LifeTimeWorldTracker::initTracker); + } + + @Override + protected void printTrackingResult(ICommandSender source, boolean realtime) + { + try + { + long ticks = this.sendTrackedTime(source, realtime); + int count = this.trackers.values().stream(). + mapToInt(tracker -> tracker.print(source, ticks, null, null)). + sum(); + if (count == 0) + { + Messenger.m(source, Messenger.s(null, "No result yet")); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public void sendUnknownEntity(ICommandSender source, String entityTypeString) + { + Messenger.s(source, String.format("Unknown entity type \"%s\"", entityTypeString), "r"); + } + + private void printTrackingResultSpecificInner(ICommandSender source, String entityTypeString, String detailModeString, boolean realtime) + { + Optional> entityTypeOptional = LifeTimeTrackerUtil.getEntityTypeFromName(entityTypeString); + if (entityTypeOptional.isPresent()) + { + SpecificDetailMode detailMode = null; + if (detailModeString != null) + { + try + { + detailMode = SpecificDetailMode.fromString(detailModeString); + } + catch (IllegalArgumentException e) + { + Messenger.s(source, String.format("Invalid statistic detail \"%s\"", detailModeString), "r"); + return; + } + } + + long ticks = this.sendTrackedTime(source, realtime); + Class entityType = entityTypeOptional.get(); + Messenger.s(source, "Life time result for " + LifeTimeTrackerUtil.getEntityTypeDescriptor(entityType)); + SpecificDetailMode finalDetailMode = detailMode; + int count = this.trackers.values().stream(). + mapToInt(tracker -> tracker.print(source, ticks, entityType, finalDetailMode)). + sum(); + if (count == 0) + { + Messenger.s(source, "No result yet"); + } + } + else + { + this.sendUnknownEntity(source, entityTypeString); + } + } + + public int printTrackingResultSpecific(ICommandSender source, String entityTypeString, String detailModeString, boolean realtime) + { + return this.doWhenTracking(source, () -> this.printTrackingResultSpecificInner(source, entityTypeString, detailModeString, realtime)); + } + + protected int showHelp(ICommandSender source) + { + String docLink = "https://github.com/TISUnion/TISCarpet113/blob/TIS-Server/docs/Features.md#lifetime"; + source.sendMessage(Messenger.c( + String.format("wb %s\n", this.getTranslatedNameFull()), + "w A tracker to track lifetime and spawn / removal reasons from all newly spawned and removed entities\n", + "w Complete doc ", + TextUtil.getFancyText( + null, + Messenger.s(null, "here", "ut"), + Messenger.s(null, docLink, "t"), + new ClickEvent(ClickEvent.Action.OPEN_URL, docLink) + ) + )); + return 1; + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/LifeTimeWorldTracker.java b/carpetmodSrc/carpet/commands/lifetime/LifeTimeWorldTracker.java new file mode 100644 index 00000000..79a49efd --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/LifeTimeWorldTracker.java @@ -0,0 +1,235 @@ +package carpet.commands.lifetime; + +import carpet.commands.lifetime.removal.RemovalReason; +import carpet.commands.lifetime.spawning.SpawningReason; +import carpet.commands.lifetime.trackeddata.BasicTrackedData; +import carpet.commands.lifetime.trackeddata.ExperienceOrbTrackedData; +import carpet.commands.lifetime.trackeddata.ItemTrackedData; +import carpet.commands.lifetime.utils.LifeTimeTrackerUtil; +import carpet.commands.lifetime.utils.SpecificDetailMode; +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.item.EntityXPOrb; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.world.WorldServer; + +import java.util.*; + +public class LifeTimeWorldTracker +{ + private final WorldServer world; + private final Map, BasicTrackedData> dataMap = Maps.newHashMap(); + // a counter which accumulates when spawning stage occurs + // it's used to determine life time + private long spawnStageCounter; + + public LifeTimeWorldTracker(WorldServer world) + { + this.world = world; + } + + public Map, BasicTrackedData> getDataMap() + { + return this.dataMap; + } + + public void initTracker() + { + this.dataMap.clear(); + } + + private Optional getTrackedData(Entity entity) + { + if (LifeTimeTracker.getInstance().willTrackEntity(entity)) + { + return Optional.of(this.dataMap.computeIfAbsent(entity.getClass(), (e -> { + if (entity instanceof EntityItem) + { + return new ItemTrackedData(); + } + if (entity instanceof EntityXPOrb) + { + return new ExperienceOrbTrackedData(); + } + return new BasicTrackedData(); + }))); + } + return Optional.empty(); + } + + public void onEntitySpawn(Entity entity, SpawningReason reason) + { + this.getTrackedData(entity).ifPresent(data -> data.updateSpawning(entity, reason)); + } + + public void onEntityRemove(Entity entity, RemovalReason reason) + { + this.getTrackedData(entity).ifPresent(data -> data.updateRemoval(entity, reason)); + } + + public void increaseSpawnStageCounter() + { + this.spawnStageCounter++; + } + + public long getSpawnStageCounter() + { + return this.spawnStageCounter; + } + + private List addIfEmpty(List list, ITextComponent text) + { + if (list.isEmpty()) + { + list.add(text); + } + return list; + } + + protected int print(ICommandSender source, long ticks, Class specificType, SpecificDetailMode detailMode) + { + // existence check + BasicTrackedData specificData = this.dataMap.get(specificType); + if (this.dataMap.isEmpty() || (specificType != null && specificData == null)) + { + return 0; + } + + // dimension name header + // Overworld (overworld) + List result = Lists.newArrayList(); + result.add(Messenger.s(null, " ")); + ITextComponent dimText = TextUtil.getDimensionNameText(this.world.provider.getDimensionType()); + dimText.getStyle().setColor(TextFormatting.GOLD).setBold(true); + result.add(Messenger.c( + dimText, + String.format("g (%s)", this.world.provider.getDimensionType().getName()) + )); + + if (specificType == null) + { + this.printAll(ticks, result); + } + else + { + this.printSpecific(ticks, specificType, specificData, detailMode, result); + } + Messenger.send(source, result); + return 1; + } + + private void printAll(long ticks, List result) + { + // sorted by spawn count + // will being sorting by avg life time better? + this.dataMap.entrySet().stream(). + sorted(Collections.reverseOrder(Comparator.comparingLong(a -> a.getValue().getSpawningCount()))). + forEach((entry) -> { + Class entityType = entry.getKey(); + BasicTrackedData data = entry.getValue(); + List spawningReasons = data.getSpawningReasonsTexts(ticks, true); + List removalReasons = data.getRemovalReasonsTexts(ticks, true); + String currentCommandBase = String.format("/%s %s", LifeTimeTracker.getInstance().getCommandPrefix(), LifeTimeTrackerUtil.getEntityTypeDescriptor(entityType)); + // [Creeper] S/R: 21/8, L: 145/145/145.00 (gt) + result.add(Messenger.c( + "g - [", + TextUtil.getFancyText( + null, + Messenger.s(null, LifeTimeTrackerUtil.getEntityTypeDescriptor(entityType)), + Messenger.s(null, "Click to show detail"), + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, currentCommandBase) + ), + "g ] ", + TextUtil.getFancyText( + null, + Messenger.c("e S", "g /", "r R"), + Messenger.c("e Spawn Count", "g / ", "r Removal Count"), + null + ), + "g : ", + TextUtil.getFancyText( + null, + Messenger.c("e " + data.getSpawningCount()), + Messenger.s(null, + Messenger.c( + data.getSpawningCountText(ticks), + "w " + (spawningReasons.isEmpty() ? "" : "\n"), + Messenger.c(spawningReasons.toArray(new Object[0])) + ).getUnformattedText() // to reduce network load + ), + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, String.format("%s %s", currentCommandBase, SpecificDetailMode.SPAWNING)) + ), + "g /", + TextUtil.getFancyText( + null, + Messenger.c("r " + data.getRemovalCount()), + Messenger.s(null, + Messenger.c( + data.getRemovalCountText(ticks), + "w " + (removalReasons.isEmpty() ? "" : "\n"), + Messenger.c(removalReasons.toArray(new Object[0])) + ).getUnformattedText() // to reduce network load + ), + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, String.format("%s %s", currentCommandBase, SpecificDetailMode.REMOVAL)) + ), + "g , ", + TextUtil.getFancyText( + null, + Messenger.c( + "q L", "g : ", + data.lifeTimeStatistic.getCompressedResult(true) + ), + Messenger.s(null, + Messenger.c( + "q Life Time Overview\n", + data.lifeTimeStatistic.getResult("", true) + ).getUnformattedText() // to reduce network load + ), + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, String.format("%s %s", currentCommandBase, SpecificDetailMode.LIFE_TIME)) + ) + )); + }); + } + + private void printSpecific(long ticks, Class specificType, BasicTrackedData specificData, SpecificDetailMode detailMode, List result) + { + boolean showLifeTime = detailMode == null || detailMode == SpecificDetailMode.LIFE_TIME; + boolean showSpawning = detailMode == null || detailMode == SpecificDetailMode.SPAWNING; + boolean showRemoval = detailMode == null || detailMode == SpecificDetailMode.REMOVAL; + if (showSpawning) + { + result.add(specificData.getSpawningCountText(ticks)); + } + if (showRemoval) + { + result.add(specificData.getRemovalCountText(ticks)); + } + if (showLifeTime) + { + result.add(TextUtil.getFancyText( + "q", + Messenger.s(null, "Life Time Overview"), + Messenger.s(null, "The amount of spawning stage passing between entity spawning and entity removal"), + null + )); + result.add(specificData.lifeTimeStatistic.getResult("", false)); + } + if (showSpawning) + { + result.add(Messenger.s(null, "Reasons for spawning", "e")); + result.addAll(this.addIfEmpty(specificData.getSpawningReasonsTexts(ticks, false), Messenger.s(null, " N/A", "g"))); + } + if (showRemoval) + { + result.add(Messenger.s(null, "Reasons for removal", "r")); + result.addAll(this.addIfEmpty(specificData.getRemovalReasonsTexts(ticks, false), Messenger.s(null, " N/A", "g"))); + } + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/removal/DeathRemovalReason.java b/carpetmodSrc/carpet/commands/lifetime/removal/DeathRemovalReason.java new file mode 100644 index 00000000..9b50ab90 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/removal/DeathRemovalReason.java @@ -0,0 +1,49 @@ +package carpet.commands.lifetime.removal; + +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import net.minecraft.util.DamageSource; +import net.minecraft.util.text.ITextComponent; + +import java.util.Objects; + +public class DeathRemovalReason extends RemovalReason +{ + private final String damageSourceName; + + public DeathRemovalReason(DamageSource damageSource) + { + this.damageSourceName = damageSource.getDamageType(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (!(o instanceof DeathRemovalReason)) return false; + DeathRemovalReason that = (DeathRemovalReason) o; + return Objects.equals(this.damageSourceName, that.damageSourceName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.damageSourceName); + } + + @Override + public ITextComponent toText() + { + return Messenger.c( + "w Death", + "g (", + TextUtil.getFancyText( + null, + Messenger.s(null, this.damageSourceName), + Messenger.s(null, "Damage source"), + null + ), + "g )" + ); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/removal/LiteralRemovalReason.java b/carpetmodSrc/carpet/commands/lifetime/removal/LiteralRemovalReason.java new file mode 100644 index 00000000..40cf3e53 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/removal/LiteralRemovalReason.java @@ -0,0 +1,48 @@ +package carpet.commands.lifetime.removal; + +import carpet.utils.Messenger; +import net.minecraft.util.text.ITextComponent; + +public class LiteralRemovalReason extends RemovalReason +{ + // 32m ~ 128m randomly despawn + public static final LiteralRemovalReason DESPAWN_RANDOMLY = new LiteralRemovalReason("despawn.randomly", "Randomly despawn"); + // > 128m immediately despawn + public static final LiteralRemovalReason DESPAWN_IMMEDIATELY = new LiteralRemovalReason("despawn.immediately", "Immediately despawn"); + // difficulty peaceful + public static final LiteralRemovalReason DESPAWN_DIFFICULTY = new LiteralRemovalReason("despawn.difficulty", "Despawn for difficulty"); + // item/xp orb timeout + public static final LiteralRemovalReason DESPAWN_TIMEOUT = new LiteralRemovalReason("despawn.timeout", "Despawn for timeout"); + + // when the persistent tag set to true, treat it as removed since it doesn't count towards mobcaps anymore + public static final LiteralRemovalReason PERSISTENT = new LiteralRemovalReason("persistent", "Becomes persistent"); + + // the fallback reason + public static final LiteralRemovalReason OTHER = new LiteralRemovalReason("other", "Other"); + + // for item entity and xp orb entity + public static final LiteralRemovalReason MERGE = new LiteralRemovalReason("merge", "Entity merging"); +// public static final LiteralRemovalReason PICKUP = new LiteralRemovalReason("pickup", "Picked up by player"); + + // for item entity + // the item count display is correct only when the hopper captures the whole item in 1 try + public static final LiteralRemovalReason HOPPER = new LiteralRemovalReason("hopper", "Collected by hopper"); + + // fall down to y=-64 and below + public static final LiteralRemovalReason VOID = new LiteralRemovalReason("void", "Entering void"); + + private final String name; + private final String translationKey; + + private LiteralRemovalReason(String translationKey, String name) + { + this.translationKey = translationKey; + this.name = name; + } + + @Override + public ITextComponent toText() + { + return Messenger.s(null, this.name); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/removal/MobPickupRemovalReason.java b/carpetmodSrc/carpet/commands/lifetime/removal/MobPickupRemovalReason.java new file mode 100644 index 00000000..a645f1ac --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/removal/MobPickupRemovalReason.java @@ -0,0 +1,40 @@ +package carpet.commands.lifetime.removal; + +import carpet.commands.lifetime.utils.LifeTimeTrackerUtil; +import carpet.utils.Messenger; +import net.minecraft.entity.Entity; +import net.minecraft.util.text.ITextComponent; + +import java.util.Objects; + +// for item entity and xp orb entity +public class MobPickupRemovalReason extends RemovalReason +{ + private final Class pickerType; + + public MobPickupRemovalReason(Class pickerType) + { + this.pickerType = pickerType; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MobPickupRemovalReason that = (MobPickupRemovalReason) o; + return Objects.equals(pickerType, that.pickerType); + } + + @Override + public int hashCode() + { + return Objects.hash(pickerType); + } + + @Override + public ITextComponent toText() + { + return Messenger.c("w Picked up by " + LifeTimeTrackerUtil.getEntityTypeDescriptor(this.pickerType)); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/removal/RemovalReason.java b/carpetmodSrc/carpet/commands/lifetime/removal/RemovalReason.java new file mode 100644 index 00000000..084d80fd --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/removal/RemovalReason.java @@ -0,0 +1,7 @@ +package carpet.commands.lifetime.removal; + +import carpet.commands.lifetime.utils.AbstractReason; + +public abstract class RemovalReason extends AbstractReason +{ +} diff --git a/carpetmodSrc/carpet/commands/lifetime/removal/TransDimensionRemovalReason.java b/carpetmodSrc/carpet/commands/lifetime/removal/TransDimensionRemovalReason.java new file mode 100644 index 00000000..e5c5accc --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/removal/TransDimensionRemovalReason.java @@ -0,0 +1,42 @@ +package carpet.commands.lifetime.removal; + +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.DimensionType; + +import java.util.Objects; + +public class TransDimensionRemovalReason extends RemovalReason +{ + private final DimensionType newDimension; + + public TransDimensionRemovalReason(DimensionType newDimension) + { + this.newDimension = Objects.requireNonNull(newDimension); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TransDimensionRemovalReason that = (TransDimensionRemovalReason) o; + return Objects.equals(this.newDimension, that.newDimension); + } + + @Override + public int hashCode() + { + return Objects.hash(this.newDimension); + } + + @Override + public ITextComponent toText() + { + ITextComponent dimText = TextUtil.getDimensionNameText(this.newDimension); + dimText.getStyle().setColor(TextFormatting.GRAY); + return Messenger.c("w Trans-dimension", "g (to ", dimText, "g )"); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/spawning/LiteralSpawningReason.java b/carpetmodSrc/carpet/commands/lifetime/spawning/LiteralSpawningReason.java new file mode 100644 index 00000000..79f57d82 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/spawning/LiteralSpawningReason.java @@ -0,0 +1,33 @@ +package carpet.commands.lifetime.spawning; + +import carpet.utils.Messenger; +import net.minecraft.util.text.ITextComponent; + +public class LiteralSpawningReason extends SpawningReason +{ + public static final LiteralSpawningReason NATURAL = new LiteralSpawningReason("natural", "Natural spawning"); + public static final LiteralSpawningReason PORTAL_PIGMAN = new LiteralSpawningReason("portal_pigman", "Nether portal pigman spawning"); + public static final LiteralSpawningReason COMMAND = new LiteralSpawningReason("command", "Summon command"); + public static final LiteralSpawningReason ITEM = new LiteralSpawningReason("item", "Spawned by item"); + public static final LiteralSpawningReason SLIME = new LiteralSpawningReason("slime", "Slime division"); + public static final LiteralSpawningReason ZOMBIE_REINFORCE = new LiteralSpawningReason("zombie_reinforce", "Zombie Reinforce"); + public static final LiteralSpawningReason SPAWNER = new LiteralSpawningReason("spawner", "Spawned by spawner"); + + // item only + public static final LiteralSpawningReason BLOCK_DROP = new LiteralSpawningReason("block_drop", "Block drop"); + + private final String translationKey; + private final String name; + + private LiteralSpawningReason(String translationKey, String name) + { + this.translationKey = translationKey; + this.name = name; + } + + @Override + public ITextComponent toText() + { + return Messenger.s(null, this.name); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/spawning/MobDropSpawningReason.java b/carpetmodSrc/carpet/commands/lifetime/spawning/MobDropSpawningReason.java new file mode 100644 index 00000000..f5d838e0 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/spawning/MobDropSpawningReason.java @@ -0,0 +1,40 @@ +package carpet.commands.lifetime.spawning; + +import carpet.commands.lifetime.utils.LifeTimeTrackerUtil; +import carpet.utils.Messenger; +import net.minecraft.entity.Entity; +import net.minecraft.util.text.ITextComponent; + +import java.util.Objects; + +// for item entity and xp orb entity +public class MobDropSpawningReason extends SpawningReason +{ + private final Class providerType; + + public MobDropSpawningReason(Class providerType) + { + this.providerType = providerType; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MobDropSpawningReason that = (MobDropSpawningReason) o; + return Objects.equals(providerType, that.providerType); + } + + @Override + public int hashCode() + { + return Objects.hash(providerType); + } + + @Override + public ITextComponent toText() + { + return Messenger.c("w Dropped by " + LifeTimeTrackerUtil.getEntityTypeDescriptor(this.providerType)); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/spawning/SpawningReason.java b/carpetmodSrc/carpet/commands/lifetime/spawning/SpawningReason.java new file mode 100644 index 00000000..91f32131 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/spawning/SpawningReason.java @@ -0,0 +1,7 @@ +package carpet.commands.lifetime.spawning; + +import carpet.commands.lifetime.utils.AbstractReason; + +public abstract class SpawningReason extends AbstractReason +{ +} diff --git a/carpetmodSrc/carpet/commands/lifetime/spawning/TransDimensionSpawningReason.java b/carpetmodSrc/carpet/commands/lifetime/spawning/TransDimensionSpawningReason.java new file mode 100644 index 00000000..caa5d8d6 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/spawning/TransDimensionSpawningReason.java @@ -0,0 +1,42 @@ +package carpet.commands.lifetime.spawning; + +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.DimensionType; + +import java.util.Objects; + +public class TransDimensionSpawningReason extends SpawningReason +{ + private final DimensionType oldDimension; + + public TransDimensionSpawningReason(DimensionType oldDimension) + { + this.oldDimension = Objects.requireNonNull(oldDimension); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TransDimensionSpawningReason that = (TransDimensionSpawningReason) o; + return Objects.equals(this.oldDimension, that.oldDimension); + } + + @Override + public int hashCode() + { + return Objects.hash(this.oldDimension); + } + + @Override + public ITextComponent toText() + { + ITextComponent dimText = TextUtil.getDimensionNameText(this.oldDimension); + dimText.getStyle().setColor(TextFormatting.GRAY); + return Messenger.c("w Trans-dimension", "g (from ", dimText, "g )"); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/trackeddata/BasicTrackedData.java b/carpetmodSrc/carpet/commands/lifetime/trackeddata/BasicTrackedData.java new file mode 100644 index 00000000..0f753e90 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/trackeddata/BasicTrackedData.java @@ -0,0 +1,188 @@ +package carpet.commands.lifetime.trackeddata; + +import carpet.commands.lifetime.removal.RemovalReason; +import carpet.commands.lifetime.spawning.SpawningReason; +import carpet.commands.lifetime.utils.AbstractReason; +import carpet.commands.lifetime.utils.CounterUtil; +import carpet.commands.lifetime.utils.LifeTimeStatistic; +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.entity.Entity; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.event.HoverEvent; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * A lifetime tracking tracked data per mob type + */ +public class BasicTrackedData +{ + public final Map spawningReasons = Maps.newHashMap(); + public final Map removalReasons = Maps.newHashMap(); + public final LifeTimeStatistic lifeTimeStatistic = new LifeTimeStatistic(); + + public void updateSpawning(Entity entity, SpawningReason reason) + { + this.spawningReasons.put(reason, this.spawningReasons.getOrDefault(reason, 0L) + 1); + } + + public void updateRemoval(Entity entity, RemovalReason reason) + { + this.lifeTimeStatistic.update(entity); + this.removalReasons.computeIfAbsent(reason, r -> new LifeTimeStatistic()).update(entity); + } + + protected static long getLongMapSum(Map longMap) + { + return longMap.values().stream().mapToLong(v -> v).sum(); + } + + public long getSpawningCount() + { + return getLongMapSum(this.spawningReasons); + } + + public long getRemovalCount() + { + return this.removalReasons.values().stream().mapToLong(stat -> stat.count).sum(); + } + + /** + * Spawn Count: xxxxx + */ + public ITextComponent getSpawningCountText(long ticks) + { + return Messenger.c( + "q Spawn Count", + "g : ", + CounterUtil.ratePerHourText(this.getSpawningCount(), ticks, "wgg") + ); + } + + /** + * Removal Count: xxxxx + */ + public ITextComponent getRemovalCountText(long ticks) + { + return Messenger.c( + "q Removal Count", + "g : ", + CounterUtil.ratePerHourText(this.getRemovalCount(), ticks, "wgg") + ); + } + + /** + * - AAA: 50, (100/h) 25% + * @param reason spawning reason or removal reason + */ + private static ITextComponent getReasonWithRate(AbstractReason reason, long ticks, long count, long total) + { + double percentage = 100.0D * count / total; + return Messenger.c( + "g - ", + reason.toText(), + "g : ", + CounterUtil.ratePerHourText(count, ticks, "wgg"), + "w ", + TextUtil.attachHoverText(Messenger.s(null, String.format("%.1f%%", percentage)), Messenger.s(null, String.format("%.6f%%", percentage))) + ); + } + + protected ITextComponent getSpawningReasonWithRate(SpawningReason reason, long ticks, long count, long total) + { + return getReasonWithRate(reason, ticks, count, total); + } + + protected ITextComponent getRemovalReasonWithRate(RemovalReason reason, long ticks, long count, long total) + { + return getReasonWithRate(reason, ticks, count, total); + } + + /** + * Reasons for spawning + * - AAA: 50, (100/h) 25% + * - BBB: 150, (300/h) 75% + * + * @param hoverMode automatically insert a new line text between lines or not for hover text display + * @return might be a empty list + */ + public List getSpawningReasonsTexts(long ticks, boolean hoverMode) + { + List result = Lists.newArrayList(); + List> entryList = Lists.newArrayList(this.spawningReasons.entrySet()); + entryList.sort(Collections.reverseOrder(Comparator.comparingLong(Map.Entry::getValue))); + + // Title for hover mode + if (!entryList.isEmpty() && hoverMode) + { + result.add(Messenger.s(null, "Reasons for spawning", "e")); + } + + entryList.forEach(entry -> { + SpawningReason reason = entry.getKey(); + Long statistic = entry.getValue(); + + // added to upper result which will be sent by Messenger.send + // so each element will be in a separate line + if (hoverMode) + { + result.add(Messenger.s(null, "\n")); + } + + result.add(this.getSpawningReasonWithRate(reason, ticks, statistic, this.getSpawningCount())); + }); + return result; + } + + /** + * Reasons for removal + * - AAA: 50, (100/h) 25% + * - Minimum life time: xx1 gt + * - Maximum life time: yy1 gt + * - Average life time: zz1 gt + * - BBB: 150, (300/h) 75% + * - Minimum life time: xx2 gt + * - Maximum life time: yy2 gt + * - Average life time: zz2 gt + * + * @param hoverMode automatically insert a new line text between lines or not for hover text display + * @return might be a empty list + */ + public List getRemovalReasonsTexts(long ticks, boolean hoverMode) + { + List result = Lists.newArrayList(); + List> entryList = Lists.newArrayList(this.removalReasons.entrySet()); + entryList.sort(Collections.reverseOrder(Comparator.comparingLong(a -> a.getValue().count))); + + // Title for hover mode + if (!entryList.isEmpty() && hoverMode) + { + result.add(Messenger.s(null, "Reasons for removal", "r")); + } + + entryList.forEach(entry -> { + RemovalReason reason = entry.getKey(); + LifeTimeStatistic statistic = entry.getValue(); + + // added to upper result which will be sent by Messenger.send + // so each element will be in a separate line + if (hoverMode) + { + result.add(Messenger.s(null, "\n")); + } + + result.add(Messenger.c( + this.getRemovalReasonWithRate(reason, ticks, statistic.count, this.lifeTimeStatistic.count), + "w \n", + statistic.getResult(" ", hoverMode) + )); + }); + return result; + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/trackeddata/ExperienceOrbTrackedData.java b/carpetmodSrc/carpet/commands/lifetime/trackeddata/ExperienceOrbTrackedData.java new file mode 100644 index 00000000..2d89a7c0 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/trackeddata/ExperienceOrbTrackedData.java @@ -0,0 +1,19 @@ +package carpet.commands.lifetime.trackeddata; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityXPOrb; + +public class ExperienceOrbTrackedData extends ExtraCountTrackedData +{ + @Override + protected long getExtraCount(Entity entity) + { + return entity instanceof EntityXPOrb ? ((EntityXPOrb)entity).getXpValue() : 0L; + } + + @Override + protected String getCountDisplayString() + { + return "Experience Amount"; + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/trackeddata/ExtraCountTrackedData.java b/carpetmodSrc/carpet/commands/lifetime/trackeddata/ExtraCountTrackedData.java new file mode 100644 index 00000000..34d7a6f9 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/trackeddata/ExtraCountTrackedData.java @@ -0,0 +1,69 @@ +package carpet.commands.lifetime.trackeddata; + +import carpet.commands.lifetime.removal.RemovalReason; +import carpet.commands.lifetime.spawning.SpawningReason; +import carpet.commands.lifetime.utils.CounterUtil; +import carpet.commands.lifetime.utils.TextUtil; +import carpet.utils.Messenger; +import com.google.common.collect.Maps; +import net.minecraft.entity.Entity; +import net.minecraft.util.text.ITextComponent; + +import java.util.Map; + +public abstract class ExtraCountTrackedData extends BasicTrackedData +{ + public final Map spawningExtraCountMap = Maps.newHashMap(); + public final Map removalExtraCountMap = Maps.newHashMap(); + + protected abstract long getExtraCount(Entity entity); + + @Override + public void updateSpawning(Entity entity, SpawningReason reason) + { + super.updateSpawning(entity, reason); + this.spawningExtraCountMap.put(reason, this.spawningExtraCountMap.getOrDefault(reason, 0L) + this.getExtraCount(entity)); + } + + @Override + public void updateRemoval(Entity entity, RemovalReason reason) + { + super.updateRemoval(entity, reason); + this.removalExtraCountMap.put(reason, this.removalExtraCountMap.getOrDefault(reason, 0L) + this.getExtraCount(entity)); + } + + protected abstract String getCountDisplayString(); + + private ITextComponent attachExtraCountHoverText(ITextComponent text, long extraCount, long ticks) + { + return TextUtil.attachHoverText(text, Messenger.c( + "w " + this.getCountDisplayString(), + "g : ", + CounterUtil.ratePerHourText(extraCount, ticks, "wgg") + )); + } + + @Override + public ITextComponent getSpawningCountText(long ticks) + { + return this.attachExtraCountHoverText(super.getSpawningCountText(ticks), getLongMapSum(this.spawningExtraCountMap), ticks); + } + + @Override + public ITextComponent getRemovalCountText(long ticks) + { + return this.attachExtraCountHoverText(super.getRemovalCountText(ticks), getLongMapSum(this.removalExtraCountMap), ticks); + } + + @Override + protected ITextComponent getSpawningReasonWithRate(SpawningReason reason, long ticks, long count, long total) + { + return this.attachExtraCountHoverText(super.getSpawningReasonWithRate(reason, ticks, count, total), this.spawningExtraCountMap.getOrDefault(reason, 0L), ticks); + } + + @Override + protected ITextComponent getRemovalReasonWithRate(RemovalReason reason, long ticks, long count, long total) + { + return this.attachExtraCountHoverText(super.getRemovalReasonWithRate(reason, ticks, count, total), this.removalExtraCountMap.getOrDefault(reason, 0L), ticks); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/trackeddata/ItemTrackedData.java b/carpetmodSrc/carpet/commands/lifetime/trackeddata/ItemTrackedData.java new file mode 100644 index 00000000..55d0f1ab --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/trackeddata/ItemTrackedData.java @@ -0,0 +1,19 @@ +package carpet.commands.lifetime.trackeddata; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; + +public class ItemTrackedData extends ExtraCountTrackedData +{ + @Override + protected long getExtraCount(Entity entity) + { + return entity instanceof EntityItem ? ((EntityItem)entity).getItem().getCount() : 0L; + } + + @Override + protected String getCountDisplayString() + { + return "Item Count"; + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/AbstractReason.java b/carpetmodSrc/carpet/commands/lifetime/utils/AbstractReason.java new file mode 100644 index 00000000..d7e43818 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/AbstractReason.java @@ -0,0 +1,8 @@ +package carpet.commands.lifetime.utils; + +import net.minecraft.util.text.ITextComponent; + +public abstract class AbstractReason +{ + public abstract ITextComponent toText(); +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/CounterUtil.java b/carpetmodSrc/carpet/commands/lifetime/utils/CounterUtil.java new file mode 100644 index 00000000..e62e9fd5 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/CounterUtil.java @@ -0,0 +1,45 @@ +package carpet.commands.lifetime.utils; + +import carpet.utils.Messenger; +import net.minecraft.util.text.ITextComponent; + +public class CounterUtil +{ + private static double getRatePerHourValue(long rate, long ticks) + { + return (double)rate / ticks * (20 * 60 * 60); + } + + public static String ratePerHour(long rate, long ticks) + { + return String.format("%d, (%.1f/h)", rate, getRatePerHourValue(rate, ticks)); + } + + public static String ratePerHour(int rate, long ticks) + { + return ratePerHour((long)rate, ticks); + } + + /** + * @param fmt a carpet color formatting string with 3 chars, for example "wgg" + * for a result of "%d, (%.1f/h)" + * "%d" uses fmt[0] + * ",", "(", "/h)" uses fmt[1] + * "%.1f uses fmt[2] + */ + public static ITextComponent ratePerHourText(long rate, long ticks, String fmt) + { + assert fmt.length() == 3; + return Messenger.c( + fmt.charAt(0) + " " + rate, + fmt.charAt(1) + " , (", + String.format("%s %.1f", fmt.charAt(2), getRatePerHourValue(rate, ticks)), + fmt.charAt(1) + " /h)" + ); + } + + public static ITextComponent ratePerHourText(int rate, long ticks, String fmt) + { + return ratePerHourText((long)rate, ticks, fmt); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/GameUtil.java b/carpetmodSrc/carpet/commands/lifetime/utils/GameUtil.java new file mode 100644 index 00000000..e9a6906a --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/GameUtil.java @@ -0,0 +1,28 @@ +package carpet.commands.lifetime.utils; + +import carpet.CarpetServer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; + +public class GameUtil +{ + public static long getGameTime() + { + return CarpetServer.minecraft_server.getWorld(0).getWorldTime(); + } + + public static boolean isOnServerThread() + { + return CarpetServer.minecraft_server != null && CarpetServer.minecraft_server.isCallingFromMinecraftThread(); + } + + public static boolean countsTowardsMobcap(Entity entity) + { + if (entity instanceof EntityLiving) + { + EntityLiving entityLiving = (EntityLiving)entity; + return !entityLiving.isNoDespawnRequired(); + } + return false; + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeStatistic.java b/carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeStatistic.java new file mode 100644 index 00000000..facee514 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeStatistic.java @@ -0,0 +1,163 @@ +package carpet.commands.lifetime.utils; + +import carpet.utils.Messenger; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.world.DimensionType; + +public class LifeTimeStatistic +{ + public static final String COLOR_MIN_TIME = "q "; + public static final String COLOR_MAX_TIME = "c "; + public static final String COLOR_AVG_TIME = "p "; + + public StatisticElement minTimeElement; + public StatisticElement maxTimeElement; + public long count; + public long timeSum; + + public LifeTimeStatistic() + { + this.clear(); + } + + public void clear() + { + this.count = 0; + this.timeSum = 0; + this.minTimeElement = new StatisticElement(Integer.MAX_VALUE, null, null, null); + this.maxTimeElement = new StatisticElement(Integer.MIN_VALUE, null, null, null); + } + + public boolean isValid() + { + return this.count > 0; + } + + public void update(Entity entity) + { + long time = entity.getLifeTime(); + this.count++; + this.timeSum += time; + StatisticElement element = new StatisticElement(time, entity.getEntityWorld().provider.getDimensionType(), entity.getSpawningPosition(), entity.getRemovalPosition()); + if (time < this.minTimeElement.time) + { + this.minTimeElement = element; + } + if (time > this.maxTimeElement.time) + { + this.maxTimeElement = element; + } + } + + /** + * - Minimum life time: xx gt + * - Maximum life time: yy gt + * - Average life time: zz gt + * + * @param indentString spaces for indent + */ + public ITextComponent getResult(String indentString, boolean hoverMode) + { + ITextComponent indent = Messenger.s(null, indentString, "g"); + ITextComponent newLine = Messenger.s(null, "\n"); + if (!this.isValid()) + { + return Messenger.c(indent, "g N/A"); + } + indent = Messenger.c(indent, "g - "); + return Messenger.c( + indent, + this.minTimeElement.getTimeWithPos("Minimum life time", COLOR_MIN_TIME, hoverMode), + newLine, + indent, + this.maxTimeElement.getTimeWithPos("Maximum life time", COLOR_MAX_TIME, hoverMode), + newLine, + indent, + "w Average life time", + "g : ", + COLOR_AVG_TIME + String.format("%.4f", (double)this.timeSum / this.count), + "g gt" + ); + } + + public ITextComponent getCompressedResult(boolean showGtSuffix) + { + if (!this.isValid()) + { + return Messenger.s(null, "N/A", "g"); + } + ITextComponent text = Messenger.c( + COLOR_MIN_TIME + this.minTimeElement.time, + "g /", + COLOR_MAX_TIME + this.maxTimeElement.time, + "g /", + COLOR_AVG_TIME + String.format("%.2f", (double)this.timeSum / this.count) + ); + if (showGtSuffix) + { + text.appendSibling(Messenger.c("g (gt)")); + } + return text; + } + + private static class StatisticElement + { + private final long time; + private final DimensionType dimensionType; + private final Vec3d spawningPos; + private final Vec3d removalPos; + + private StatisticElement(long time, DimensionType dimensionType, Vec3d spawningPos, Vec3d removalPos) + { + this.time = time; + this.dimensionType = dimensionType; + this.spawningPos = spawningPos; + this.removalPos = removalPos; + } + + /** + * [hint]: 123 gt + * [hint]: 123 gt [S] [R] + */ + private ITextComponent getTimeWithPos(String hint, String fmt, boolean hoverMode) + { + ITextComponent text = Messenger.c( + "w " + hint, + "g : ", + fmt + this.time, + "g gt" + ); + if (!hoverMode) + { + text.appendSibling(Messenger.c( + "w ", + TextUtil.getFancyText( + null, + Messenger.s(null, "[S]", "e"), + Messenger.c( + "w Spawning Position", + "g : ", + "w " + TextUtil.getCoordinateString(this.spawningPos) + ), + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, TextUtil.getTeleportCommand(this.spawningPos, this.dimensionType)) + ), + "w ", + TextUtil.getFancyText( + null, + Messenger.s(null, "[R]", "r"), + Messenger.c( + "w Removal Position", + "g : ", + "w " + TextUtil.getCoordinateString(this.removalPos) + ), + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, TextUtil.getTeleportCommand(this.removalPos, this.dimensionType)) + ) + )); + } + return text; + } + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeTrackerUtil.java b/carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeTrackerUtil.java new file mode 100644 index 00000000..613f6e7c --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/LifeTimeTrackerUtil.java @@ -0,0 +1,35 @@ +package carpet.commands.lifetime.utils; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.item.EntityXPOrb; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +import java.util.Optional; + +public class LifeTimeTrackerUtil +{ + public static boolean isTrackedEntity(Entity entity) + { + return entity instanceof EntityLiving || entity instanceof EntityItem || entity instanceof EntityXPOrb; + } + + public static String getEntityTypeDescriptor(Class entityType) + { + if (EntityPlayer.class.isAssignableFrom(entityType)) + { + return "player"; + } + ResourceLocation resourceLocation = EntityList.REGISTRY.getNameForObject(entityType); + return resourceLocation != null ? resourceLocation.getPath() : entityType.getSimpleName(); + } + + public static Optional> getEntityTypeFromName(String name) + { + ResourceLocation resourcelocation = new ResourceLocation(name); + return Optional.ofNullable(EntityList.REGISTRY.getObject(resourcelocation)); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/SpecificDetailMode.java b/carpetmodSrc/carpet/commands/lifetime/utils/SpecificDetailMode.java new file mode 100644 index 00000000..95b7b0e6 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/SpecificDetailMode.java @@ -0,0 +1,28 @@ +package carpet.commands.lifetime.utils; + +import java.util.Arrays; + +public enum SpecificDetailMode +{ + LIFE_TIME, + SPAWNING, + REMOVAL; + + private static final String[] SUGGESTIONS = Arrays.stream(values()).map(SpecificDetailMode::toString).toArray(String[]::new); + + public static String[] getSuggestion() + { + return SUGGESTIONS; + } + + @Override + public String toString() + { + return super.toString().toLowerCase(); + } + + public static SpecificDetailMode fromString(String str) + { + return valueOf(str.toUpperCase()); + } +} diff --git a/carpetmodSrc/carpet/commands/lifetime/utils/TextUtil.java b/carpetmodSrc/carpet/commands/lifetime/utils/TextUtil.java new file mode 100644 index 00000000..a0fee863 --- /dev/null +++ b/carpetmodSrc/carpet/commands/lifetime/utils/TextUtil.java @@ -0,0 +1,69 @@ +package carpet.commands.lifetime.utils; + +import carpet.utils.Messenger; +import com.google.common.collect.Maps; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; +import net.minecraft.world.DimensionType; + +import java.util.Map; + +public class TextUtil +{ + public static ITextComponent attachHoverEvent(ITextComponent text, HoverEvent hoverEvent) + { + text.getStyle().setHoverEvent(hoverEvent); + return text; + } + + public static ITextComponent attachHoverText(ITextComponent text, ITextComponent hoverText) + { + return attachHoverEvent(text, new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverText)); + } + + private static final Map DIMENSION_NAME = Maps.newHashMap(); + + static + { + DIMENSION_NAME.put(DimensionType.OVERWORLD, new TextComponentTranslation("createWorld.customize.preset.overworld")); + DIMENSION_NAME.put(DimensionType.NETHER, new TextComponentTranslation("advancements.nether.root.title")); + DIMENSION_NAME.put(DimensionType.THE_END, new TextComponentTranslation("advancements.end.root.title")); + } + + public static String getTeleportCommand(Vec3d pos, DimensionType dimensionType) + { + // no "execute in " in 1.12 + return String.format("/tp %s %s %s", pos.x, pos.y, pos.z); + } + + public static ITextComponent getFancyText(String style, ITextComponent displayText, ITextComponent hoverText, ClickEvent clickEvent) + { + ITextComponent text = displayText.createCopy(); + if (style != null) + { + text.setStyle(Messenger.c(style + " ").getSiblings().get(0).getStyle()); + } + if (hoverText != null) + { + text.getStyle().setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverText)); + } + if (clickEvent != null) + { + text.getStyle().setClickEvent(clickEvent); + } + return text; + } + + public static String getCoordinateString(Vec3d pos) + { + return String.format("[%.1f, %.1f, %.1f]", pos.x, pos.y, pos.z); + } + + public static ITextComponent getDimensionNameText(DimensionType dim) + { + return DIMENSION_NAME.getOrDefault(dim, Messenger.s(null, dim.toString())).createCopy(); + } +} diff --git a/carpetmodSrc/carpet/utils/Messenger.java b/carpetmodSrc/carpet/utils/Messenger.java index bd0cff9f..24593e40 100644 --- a/carpetmodSrc/carpet/utils/Messenger.java +++ b/carpetmodSrc/carpet/utils/Messenger.java @@ -238,6 +238,11 @@ public static ITextComponent m(ICommandSender receiver, Object ... fields) return message; } + public static ITextComponent c(Object ... fields) + { + return m(null, fields); + } + public static ITextComponent s(ICommandSender receiver,String text) { return s(receiver,text,""); diff --git a/patches/net/minecraft/block/Block.java.patch b/patches/net/minecraft/block/Block.java.patch index 81bc4606..3a424f21 100644 --- a/patches/net/minecraft/block/Block.java.patch +++ b/patches/net/minecraft/block/Block.java.patch @@ -1,10 +1,11 @@ --- ../src-base/minecraft/net/minecraft/block/Block.java +++ ../src-work/minecraft/net/minecraft/block/Block.java -@@ -1,11 +1,16 @@ +@@ -1,11 +1,17 @@ package net.minecraft.block; +import carpet.CarpetServer; +import carpet.CarpetSettings; ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; +import carpet.logging.LoggerRegistry; import com.google.common.collect.Sets; import com.google.common.collect.UnmodifiableIterator; @@ -17,7 +18,7 @@ import net.minecraft.block.material.EnumPushReaction; import net.minecraft.block.material.MapColor; import net.minecraft.block.material.Material; -@@ -23,20 +28,16 @@ +@@ -23,20 +29,16 @@ import net.minecraft.init.Blocks; import net.minecraft.init.Enchantments; import net.minecraft.init.Items; @@ -42,7 +43,7 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; -@@ -48,6 +49,10 @@ +@@ -48,6 +50,10 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; @@ -53,7 +54,7 @@ public class Block { private static final ResourceLocation field_176230_a = new ResourceLocation("air"); -@@ -74,7 +79,7 @@ +@@ -74,7 +80,7 @@ public float field_149765_K; protected final BlockStateContainer field_176227_L; private IBlockState field_176228_M; @@ -62,7 +63,7 @@ public static int func_149682_b(Block p_149682_0_) { -@@ -236,7 +241,7 @@ +@@ -236,7 +242,7 @@ return this; } @@ -71,7 +72,7 @@ { this.field_149786_r = p_149713_1_; return this; -@@ -328,7 +333,7 @@ +@@ -328,7 +334,7 @@ return this.field_149782_v; } @@ -80,7 +81,7 @@ { this.field_149789_z = p_149675_1_; return this; -@@ -400,7 +405,10 @@ +@@ -400,7 +406,10 @@ public void func_180645_a(World p_180645_1_, BlockPos p_180645_2_, IBlockState p_180645_3_, Random p_180645_4_) { @@ -91,7 +92,7 @@ } public void func_180650_b(World p_180650_1_, BlockPos p_180650_2_, IBlockState p_180650_3_, Random p_180650_4_) -@@ -489,8 +497,17 @@ +@@ -489,8 +498,18 @@ double d1 = (double)(p_180635_0_.field_73012_v.nextFloat() * 0.5F) + 0.25D; double d2 = (double)(p_180635_0_.field_73012_v.nextFloat() * 0.5F) + 0.25D; EntityItem entityitem = new EntityItem(p_180635_0_, (double)p_180635_1_.func_177958_n() + d0, (double)p_180635_1_.func_177956_o() + d1, (double)p_180635_1_.func_177952_p() + d2, p_180635_2_); @@ -103,13 +104,14 @@ + } + } entityitem.func_174869_p(); ++ entityitem.recordSpawning(LiteralSpawningReason.BLOCK_DROP); // CM lifetime tracker p_180635_0_.func_72838_d(entityitem); + if (CapturedDrops.isCapturingDrops()) + CapturedDrops.captureDrop(entityitem); } } -@@ -594,7 +611,10 @@ +@@ -594,7 +613,10 @@ public void func_180657_a(World p_180657_1_, EntityPlayer p_180657_2_, BlockPos p_180657_3_, IBlockState p_180657_4_, @Nullable TileEntity p_180657_5_, ItemStack p_180657_6_) { @@ -121,7 +123,7 @@ p_180657_2_.func_71020_j(0.005F); if (this.func_149700_E() && EnchantmentHelper.func_77506_a(Enchantments.field_185306_r, p_180657_6_) > 0) -@@ -849,7 +869,8 @@ +@@ -849,7 +871,8 @@ func_176219_a(32, "deadbush", (new BlockDeadBush()).func_149711_c(0.0F).func_149672_a(SoundType.field_185850_c).func_149663_c("deadbush")); func_176219_a(33, "piston", (new BlockPistonBase(false)).func_149663_c("pistonBase")); func_176219_a(34, "piston_head", (new BlockPistonExtension()).func_149663_c("pistonBase")); diff --git a/patches/net/minecraft/block/BlockPortal.java.patch b/patches/net/minecraft/block/BlockPortal.java.patch index 1b810d60..8925e941 100644 --- a/patches/net/minecraft/block/BlockPortal.java.patch +++ b/patches/net/minecraft/block/BlockPortal.java.patch @@ -1,6 +1,13 @@ --- ../src-base/minecraft/net/minecraft/block/BlockPortal.java +++ ../src-work/minecraft/net/minecraft/block/BlockPortal.java -@@ -24,6 +24,12 @@ +@@ -1,5 +1,6 @@ + package net.minecraft.block; + ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import com.google.common.cache.LoadingCache; + import java.util.Random; + import javax.annotation.Nullable; +@@ -24,6 +25,12 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; @@ -13,7 +20,16 @@ public class BlockPortal extends BlockBreakable { public static final PropertyEnum field_176550_a = PropertyEnum.func_177706_a("axis", EnumFacing.Axis.class, EnumFacing.Axis.X, EnumFacing.Axis.Z); -@@ -108,6 +114,12 @@ +@@ -72,6 +79,8 @@ + + if (entity != null) + { ++ entity.recordSpawning(LiteralSpawningReason.PORTAL_PIGMAN); // CM lifetime tracker ++ + entity.field_71088_bW = entity.func_82147_ab(); + } + } +@@ -108,6 +117,12 @@ if (blockportal$size.func_150860_b() && blockportal$size.field_150864_e == 0) { blockportal$size.func_150859_c(); @@ -26,7 +42,7 @@ return true; } else -@@ -117,6 +129,12 @@ +@@ -117,6 +132,12 @@ if (blockportal$size1.func_150860_b() && blockportal$size1.field_150864_e == 0) { blockportal$size1.func_150859_c(); @@ -39,7 +55,7 @@ return true; } else -@@ -137,6 +155,12 @@ +@@ -137,6 +158,12 @@ if (!blockportal$size.func_150860_b() || blockportal$size.field_150864_e < blockportal$size.field_150868_h * blockportal$size.field_150862_g) { p_189540_2_.func_175656_a(p_189540_3_, Blocks.field_150350_a.func_176223_P()); @@ -52,7 +68,7 @@ } } else if (enumfacing$axis == EnumFacing.Axis.Z) -@@ -146,6 +170,12 @@ +@@ -146,6 +173,12 @@ if (!blockportal$size1.func_150860_b() || blockportal$size1.field_150864_e < blockportal$size1.field_150868_h * blockportal$size1.field_150862_g) { p_189540_2_.func_175656_a(p_189540_3_, Blocks.field_150350_a.func_176223_P()); diff --git a/patches/net/minecraft/command/server/CommandSummon.java.patch b/patches/net/minecraft/command/server/CommandSummon.java.patch index 676877b4..38ecf52b 100644 --- a/patches/net/minecraft/command/server/CommandSummon.java.patch +++ b/patches/net/minecraft/command/server/CommandSummon.java.patch @@ -1,6 +1,15 @@ --- ../src-base/minecraft/net/minecraft/command/server/CommandSummon.java +++ ../src-work/minecraft/net/minecraft/command/server/CommandSummon.java -@@ -22,6 +22,13 @@ +@@ -3,6 +3,8 @@ + import java.util.Collections; + import java.util.List; + import javax.annotation.Nullable; ++ ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import net.minecraft.command.CommandBase; + import net.minecraft.command.CommandException; + import net.minecraft.command.ICommandSender; +@@ -22,6 +24,13 @@ import net.minecraft.world.World; import net.minecraft.world.chunk.storage.AnvilChunkLoader; @@ -14,7 +23,7 @@ public class CommandSummon extends CommandBase { public String func_71517_b() -@@ -63,6 +70,8 @@ +@@ -63,6 +72,8 @@ } World world = p_184881_2_.func_130014_f_(); @@ -23,7 +32,7 @@ if (!world.func_175667_e(blockpos)) { -@@ -70,7 +79,33 @@ +@@ -70,7 +81,33 @@ } else if (EntityList.field_191307_a.equals(new ResourceLocation(s))) { @@ -58,7 +67,13 @@ func_152373_a(p_184881_2_, this, "commands.summon.success", new Object[0]); } else -@@ -108,6 +143,8 @@ +@@ -104,10 +141,14 @@ + { + entity.func_70012_b(d0, d1, d2, entity.field_70177_z, entity.field_70125_A); + ++ entity.recordSpawning(LiteralSpawningReason.COMMAND); // CM lifetime tracker ++ + if (!flag && entity instanceof EntityLiving) { ((EntityLiving)entity).func_180482_a(world.func_175649_E(new BlockPos(entity)), (IEntityLivingData)null); } diff --git a/patches/net/minecraft/entity/Entity.java.patch b/patches/net/minecraft/entity/Entity.java.patch index f44b8b1d..59a6fef2 100644 --- a/patches/net/minecraft/entity/Entity.java.patch +++ b/patches/net/minecraft/entity/Entity.java.patch @@ -1,14 +1,23 @@ --- ../src-base/minecraft/net/minecraft/entity/Entity.java +++ ../src-work/minecraft/net/minecraft/entity/Entity.java -@@ -1,5 +1,7 @@ +@@ -1,5 +1,16 @@ package net.minecraft.entity; +import carpet.carpetclient.CarpetClientChunkLogger; ++import carpet.commands.lifetime.LifeTimeTracker; ++import carpet.commands.lifetime.removal.DeathRemovalReason; ++import carpet.commands.lifetime.removal.LiteralRemovalReason; ++import carpet.commands.lifetime.removal.RemovalReason; ++import carpet.commands.lifetime.removal.TransDimensionRemovalReason; ++import carpet.commands.lifetime.spawning.MobDropSpawningReason; ++import carpet.commands.lifetime.spawning.SpawningReason; ++import carpet.commands.lifetime.spawning.TransDimensionSpawningReason; ++import carpet.commands.lifetime.utils.GameUtil; +import carpet.patches.EntityPlayerMPFake; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -@@ -81,6 +83,10 @@ +@@ -81,6 +92,10 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -19,7 +28,7 @@ public abstract class Entity implements ICommandSender { private static final Logger field_184243_a = LogManager.getLogger(); -@@ -136,7 +142,7 @@ +@@ -136,7 +151,7 @@ private int field_190534_ay; protected boolean field_70171_ac; public int field_70172_ad; @@ -28,7 +37,7 @@ protected boolean field_70178_ae; protected EntityDataManager field_70180_af; protected static final DataParameter field_184240_ax = EntityDataManager.func_187226_a(Entity.class, DataSerializers.field_187191_a); -@@ -168,6 +174,9 @@ +@@ -168,6 +183,9 @@ private final double[] field_191505_aI; private long field_191506_aJ; @@ -38,7 +47,36 @@ public Entity(World p_i1582_1_) { this.field_145783_c = field_70152_a++; -@@ -278,6 +287,14 @@ +@@ -201,6 +219,9 @@ + this.field_70180_af.func_187214_a(field_184234_aB, Boolean.valueOf(false)); + this.field_70180_af.func_187214_a(field_189655_aD, Boolean.valueOf(false)); + this.func_70088_a(); ++ ++ // CM lifetime tracker ++ this.initLifeTimeTracker(); + } + + public int func_145782_y() +@@ -267,6 +288,18 @@ + + public void func_70106_y() + { ++ // CM lifetime tracker ++ DamageSource source = this.getDeathDamageSource(); ++ if (source != null) ++ { ++ this.recordRemoval(new DeathRemovalReason(source)); ++ } ++ else ++ { ++ this.recordRemoval(LiteralRemovalReason.OTHER); ++ } ++ // CM lifetime tracker ends ++ + this.field_70128_L = true; + } + +@@ -278,6 +311,14 @@ { if (p_70105_1_ != this.field_70130_N || p_70105_2_ != this.field_70131_O) { @@ -53,7 +91,7 @@ float f = this.field_70130_N; this.field_70130_N = p_70105_1_; this.field_70131_O = p_70105_2_; -@@ -299,7 +316,8 @@ +@@ -299,7 +340,8 @@ } } @@ -63,7 +101,16 @@ { this.field_70177_z = p_70101_1_ % 360.0F; this.field_70125_A = p_70101_2_ % 360.0F; -@@ -508,6 +526,12 @@ +@@ -492,6 +534,8 @@ + + protected void func_70076_C() + { ++ this.recordRemoval(LiteralRemovalReason.VOID); // CM lifetime tracker ++ + this.func_70106_y(); + } + +@@ -508,6 +552,12 @@ public void func_70091_d(MoverType p_70091_1_, double p_70091_2_, double p_70091_4_, double p_70091_6_) { @@ -76,7 +123,7 @@ if (this.field_70145_X) { this.func_174826_a(this.func_174813_aQ().func_72317_d(p_70091_2_, p_70091_4_, p_70091_6_)); -@@ -654,12 +678,28 @@ +@@ -654,12 +704,28 @@ } } @@ -106,7 +153,7 @@ for (int l = list1.size(); k < l; ++k) { -@@ -672,6 +712,9 @@ +@@ -672,6 +738,9 @@ if (p_70091_2_ != 0.0D) { int j5 = 0; @@ -116,7 +163,7 @@ for (int l5 = list1.size(); j5 < l5; ++j5) { -@@ -687,6 +730,9 @@ +@@ -687,6 +756,9 @@ if (p_70091_6_ != 0.0D) { int k5 = 0; @@ -126,7 +173,7 @@ for (int i6 = list1.size(); k5 < i6; ++k5) { -@@ -1140,6 +1186,8 @@ +@@ -1140,6 +1212,8 @@ public boolean func_70072_I() { @@ -135,7 +182,7 @@ if (this.func_184187_bx() instanceof EntityBoat) { this.field_70171_ac = false; -@@ -1159,6 +1207,7 @@ +@@ -1159,6 +1233,7 @@ { this.field_70171_ac = false; } @@ -143,7 +190,7 @@ return this.field_70171_ac; } -@@ -1533,6 +1582,9 @@ +@@ -1533,6 +1608,9 @@ if (!this.field_70128_L && s != null && !this.func_184218_aH()) { @@ -153,7 +200,7 @@ p_70039_1_.func_74778_a("id", s); this.func_189511_e(p_70039_1_); return true; -@@ -1543,6 +1595,19 @@ +@@ -1543,6 +1621,19 @@ } } @@ -173,7 +220,7 @@ public static void func_190533_a(DataFixer p_190533_0_) { p_190533_0_.func_188258_a(FixTypes.ENTITY, new IDataWalker() -@@ -1619,6 +1684,11 @@ +@@ -1619,6 +1710,11 @@ p_189511_1_.func_74782_a("Tags", nbttaglist); } @@ -185,7 +232,7 @@ this.func_70014_b(p_189511_1_); if (this.func_184207_aI()) -@@ -1663,19 +1733,19 @@ +@@ -1663,19 +1759,19 @@ this.field_70181_x = nbttaglist2.func_150309_d(1); this.field_70179_y = nbttaglist2.func_150309_d(2); @@ -216,7 +263,7 @@ } this.field_70165_t = nbttaglist.func_150309_d(0); -@@ -1744,6 +1814,17 @@ +@@ -1744,6 +1840,17 @@ { this.func_70107_b(this.field_70165_t, this.field_70163_u, this.field_70161_v); } @@ -234,7 +281,15 @@ } catch (Throwable throwable) { -@@ -1908,7 +1989,12 @@ +@@ -1817,6 +1924,7 @@ + { + EntityItem entityitem = new EntityItem(this.field_70170_p, this.field_70165_t, this.field_70163_u + (double)p_70099_2_, this.field_70161_v, p_70099_1_); + entityitem.func_174869_p(); ++ entityitem.recordSpawning(new MobDropSpawningReason(this.getClass())); // CM lifetime tracker + this.field_70170_p.func_72838_d(entityitem); + return entityitem; + } +@@ -1908,7 +2016,12 @@ public double func_70042_X() { @@ -247,7 +302,7 @@ } public boolean func_184220_m(Entity p_184220_1_) -@@ -1966,6 +2052,18 @@ +@@ -1966,6 +2079,18 @@ } } @@ -266,7 +321,15 @@ protected void func_184200_o(Entity p_184200_1_) { if (p_184200_1_.func_184187_bx() != this) -@@ -2398,7 +2496,10 @@ +@@ -2367,6 +2492,7 @@ + this.field_71093_bK = 0; + } + ++ this.recordRemoval(new TransDimensionRemovalReason(worldserver1.field_73011_w.func_186058_p())); // CM lifetime tracker + this.field_70170_p.func_72900_e(this); + this.field_70128_L = false; + this.field_70170_p.field_72984_F.func_76320_a("reposition"); +@@ -2398,7 +2524,10 @@ float f = this.field_70177_z; this.func_70012_b(d0, this.field_70163_u, d1, 90.0F, 0.0F); Teleporter teleporter = worldserver1.func_85176_s(); @@ -277,7 +340,15 @@ blockpos = new BlockPos(this); } -@@ -2578,6 +2679,10 @@ +@@ -2420,6 +2549,7 @@ + entity.func_174828_a(blockpos, entity.field_70177_z, entity.field_70125_A); + } + ++ entity.recordSpawning(new TransDimensionSpawningReason(worldserver.field_73011_w.func_186058_p())); // CM lifetime tracker + boolean flag = entity.field_98038_p; + entity.field_98038_p = true; + worldserver1.func_72838_d(entity); +@@ -2578,6 +2708,10 @@ public EnumFacing func_174811_aO() { @@ -288,7 +359,7 @@ return EnumFacing.func_176731_b(MathHelper.func_76128_c((double)(this.field_70177_z * 4.0F / 360.0F) + 0.5D) & 3); } -@@ -2886,4 +2991,14 @@ +@@ -2886,4 +3020,105 @@ { return 1; } @@ -302,4 +373,95 @@ + public void postLoad() + { + } ++ ++ //////////////////////////////// ++ // CM lifetime tracker starts // ++ // Author: Fallen_Breath // ++ //////////////////////////////// ++ ++ private long spawnTime; ++ private boolean doLifeTimeTracking; ++ private boolean recordedSpawning; ++ private boolean recordedRemoval; ++ private Vec3d spawningPos; ++ private Vec3d removalPos; ++ private DamageSource deathDamageSource; ++ private int trackId; ++ ++ private void initLifeTimeTracker() ++ { ++ this.doLifeTimeTracking = false; ++ this.recordedSpawning = false; ++ this.recordedRemoval = false; ++ if (this.field_70170_p instanceof WorldServer) ++ { ++ this.spawnTime = ((WorldServer)this.field_70170_p).getLifeTimeWorldTracker().getSpawnStageCounter(); ++ this.trackId = LifeTimeTracker.getInstance().getCurrentTrackId(); ++ this.doLifeTimeTracking = LifeTimeTracker.getInstance().willTrackEntity(this); ++ } ++ else ++ { ++ this.trackId = -1; ++ this.doLifeTimeTracking = false; ++ } ++ this.deathDamageSource = null; ++ } ++ ++ public int getTrackId() ++ { ++ return this.trackId; ++ } ++ ++ public long getLifeTime() ++ { ++ return ((WorldServer)this.field_70170_p).getLifeTimeWorldTracker().getSpawnStageCounter() - this.spawnTime; ++ } ++ ++ public Vec3d getSpawningPosition() ++ { ++ return this.spawningPos; ++ } ++ ++ public Vec3d getRemovalPosition() ++ { ++ return this.removalPos; ++ } ++ ++ public void recordSpawning(SpawningReason reason) ++ { ++ if (this.doLifeTimeTracking && !this.recordedSpawning && this.field_70170_p instanceof WorldServer) ++ { ++ if (this instanceof EntityLiving && !GameUtil.countsTowardsMobcap(this)) ++ { ++ return; ++ } ++ this.recordedSpawning = true; ++ this.spawningPos = this.func_174791_d(); ++ ((WorldServer)this.field_70170_p).getLifeTimeWorldTracker().onEntitySpawn(this, reason); ++ } ++ } ++ ++ public void recordRemoval(RemovalReason reason) ++ { ++ if (this.doLifeTimeTracking && this.recordedSpawning && this.spawningPos != null && !this.recordedRemoval) ++ { ++ this.recordedRemoval = true; ++ this.removalPos = this.func_174791_d(); ++ ((WorldServer)this.field_70170_p).getLifeTimeWorldTracker().onEntityRemove((Entity)(Object)this, reason); ++ } ++ } ++ ++ public DamageSource getDeathDamageSource() ++ { ++ return this.deathDamageSource; ++ } ++ ++ public void setDeathDamageSource(DamageSource source) ++ { ++ this.deathDamageSource = source; ++ } ++ ++ ////////////////////////////// ++ // CM lifetime tracker ends // ++ ////////////////////////////// } diff --git a/patches/net/minecraft/entity/EntityLiving.java.patch b/patches/net/minecraft/entity/EntityLiving.java.patch index 556a2329..8c8bcc9a 100644 --- a/patches/net/minecraft/entity/EntityLiving.java.patch +++ b/patches/net/minecraft/entity/EntityLiving.java.patch @@ -1,6 +1,14 @@ --- ../src-base/minecraft/net/minecraft/entity/EntityLiving.java +++ ../src-work/minecraft/net/minecraft/entity/EntityLiving.java -@@ -56,6 +56,9 @@ +@@ -1,5 +1,7 @@ + package net.minecraft.entity; + ++import carpet.commands.lifetime.removal.LiteralRemovalReason; ++import carpet.commands.lifetime.removal.MobPickupRemovalReason; + import com.google.common.collect.Maps; + import java.util.Arrays; + import java.util.Map; +@@ -56,6 +58,9 @@ import net.minecraft.world.storage.loot.LootContext; import net.minecraft.world.storage.loot.LootTable; @@ -10,7 +18,7 @@ public abstract class EntityLiving extends EntityLivingBase { private static final DataParameter field_184654_a = EntityDataManager.func_187226_a(EntityLiving.class, DataSerializers.field_187191_a); -@@ -192,11 +195,19 @@ +@@ -192,11 +197,19 @@ public void func_70642_aH() { @@ -34,7 +42,7 @@ } } -@@ -408,6 +419,10 @@ +@@ -408,6 +421,10 @@ p_70014_1_.func_74782_a("Leash", nbttagcompound2); } @@ -45,7 +53,39 @@ p_70014_1_.func_74757_a("LeftHanded", this.func_184638_cS()); -@@ -1414,4 +1429,33 @@ +@@ -669,6 +686,9 @@ + this.field_184655_bs[entityequipmentslot.func_188454_b()] = 2.0F; + } + ++ this.recordRemoval(LiteralRemovalReason.PERSISTENT); // CM lifetime tracker ++ p_175445_1_.recordRemoval(new MobPickupRemovalReason(this.getClass())); // CM lifetime tracker ++ + this.field_82179_bU = true; + this.func_71001_a(p_175445_1_, itemstack.func_190916_E()); + p_175445_1_.func_70106_y(); +@@ -704,11 +724,13 @@ + + if (this.func_70692_ba() && d3 > 16384.0D) + { ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_IMMEDIATELY); // CM lifetime tracker + this.func_70106_y(); + } + + if (this.field_70708_bq > 600 && this.field_70146_Z.nextInt(800) == 0 && d3 > 1024.0D && this.func_70692_ba()) + { ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_RANDOMLY); // CM lifetime tracker + this.func_70106_y(); + } + else if (d3 < 1024.0D) +@@ -1140,6 +1162,7 @@ + + public void func_110163_bv() + { ++ this.recordRemoval(LiteralRemovalReason.PERSISTENT); // CM lifetime tracker + this.field_82179_bU = true; + } + +@@ -1414,4 +1437,33 @@ IN_AIR, IN_WATER; } diff --git a/patches/net/minecraft/entity/EntityLivingBase.java.patch b/patches/net/minecraft/entity/EntityLivingBase.java.patch index 0464fb59..ff31d233 100644 --- a/patches/net/minecraft/entity/EntityLivingBase.java.patch +++ b/patches/net/minecraft/entity/EntityLivingBase.java.patch @@ -1,6 +1,13 @@ --- ../src-base/minecraft/net/minecraft/entity/EntityLivingBase.java +++ ../src-work/minecraft/net/minecraft/entity/EntityLivingBase.java -@@ -75,6 +75,9 @@ +@@ -1,5 +1,6 @@ + package net.minecraft.entity; + ++import carpet.commands.lifetime.spawning.MobDropSpawningReason; + import com.google.common.base.Objects; + import com.google.common.collect.Maps; + import java.util.Collection; +@@ -75,6 +76,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -10,7 +17,7 @@ public abstract class EntityLivingBase extends Entity { private static final Logger field_190632_a = LogManager.getLogger(); -@@ -151,6 +154,10 @@ +@@ -151,6 +155,10 @@ private DamageSource field_189750_bF; private long field_189751_bG; @@ -21,7 +28,7 @@ public void func_174812_G() { this.func_70097_a(DamageSource.field_76380_i, Float.MAX_VALUE); -@@ -168,6 +175,8 @@ +@@ -168,6 +176,8 @@ this.field_70177_z = (float)(Math.random() * (Math.PI * 2D)); this.field_70759_as = this.field_70177_z; this.field_70138_W = 0.6F; @@ -30,7 +37,21 @@ } protected void func_70088_a() -@@ -401,6 +410,7 @@ +@@ -383,7 +393,12 @@ + { + int j = EntityXPOrb.func_70527_a(i); + i -= j; +- this.field_70170_p.func_72838_d(new EntityXPOrb(this.field_70170_p, this.field_70165_t, this.field_70163_u, this.field_70161_v, j)); ++ ++ // CM lifetime tracker ++// this.world.spawnEntity(new EntityXPOrb(this.world, this.posX, this.posY, this.posZ, j)); ++ EntityXPOrb entityXPOrb = new EntityXPOrb(this.field_70170_p, this.field_70165_t, this.field_70163_u, this.field_70161_v, j); ++ entityXPOrb.recordSpawning(new MobDropSpawningReason(this.getClass())); ++ this.field_70170_p.func_72838_d(entityXPOrb); + } + } + +@@ -401,6 +416,7 @@ protected boolean func_146066_aG() { @@ -38,7 +59,7 @@ return !this.func_70631_g_(); } -@@ -741,8 +751,16 @@ +@@ -741,8 +757,16 @@ } else { @@ -57,7 +78,7 @@ } } } -@@ -813,6 +831,11 @@ +@@ -813,6 +837,11 @@ { p_70688_1_.func_188419_a().func_111187_a(this, this.func_110140_aT(), p_70688_1_.func_76458_c()); } @@ -69,7 +90,7 @@ } public void func_70691_i(float p_70691_1_) -@@ -847,14 +870,20 @@ +@@ -847,14 +876,20 @@ } else { @@ -91,7 +112,7 @@ return false; } else -@@ -864,6 +893,7 @@ +@@ -864,6 +899,7 @@ if ((p_70097_1_ == DamageSource.field_82728_o || p_70097_1_ == DamageSource.field_82729_p) && !this.func_184582_a(EntityEquipmentSlot.HEAD).func_190926_b()) { this.func_184582_a(EntityEquipmentSlot.HEAD).func_77972_a((int)(p_70097_2_ * 4.0F + this.field_70146_Z.nextFloat() * p_70097_2_ * 2.0F), this); @@ -99,7 +120,7 @@ p_70097_2_ *= 0.75F; } -@@ -872,6 +902,7 @@ +@@ -872,6 +908,7 @@ if (p_70097_2_ > 0.0F && this.func_184583_d(p_70097_1_)) { this.func_184590_k(p_70097_2_); @@ -107,7 +128,7 @@ p_70097_2_ = 0.0F; if (!p_70097_1_.func_76352_a()) -@@ -894,9 +925,10 @@ +@@ -894,9 +931,10 @@ { if (p_70097_2_ <= this.field_110153_bc) { @@ -119,7 +140,7 @@ this.func_70665_d(p_70097_1_, p_70097_2_ - this.field_110153_bc); this.field_110153_bc = p_70097_2_; flag1 = false; -@@ -1009,6 +1041,11 @@ +@@ -1009,6 +1047,11 @@ { this.func_184581_c(p_70097_1_); } @@ -131,7 +152,16 @@ boolean flag2 = !flag || p_70097_2_ > 0.0F; -@@ -1345,6 +1382,7 @@ +@@ -1145,6 +1188,8 @@ + { + if (!this.field_70729_aU) + { ++ this.setDeathDamageSource(p_70645_1_); // CM lifetime tracker ++ + Entity entity = p_70645_1_.func_76346_g(); + EntityLivingBase entitylivingbase = this.func_94060_bK(); + +@@ -1345,6 +1390,7 @@ int i = (this.func_70660_b(MobEffects.field_76429_m).func_76458_c() + 1) * 5; int j = 25 - i; float f = p_70672_2_ * (float)j; @@ -139,7 +169,7 @@ p_70672_2_ = f / 25.0F; } -@@ -1358,7 +1396,10 @@ +@@ -1358,7 +1404,10 @@ if (k > 0) { @@ -150,7 +180,7 @@ } return p_70672_2_; -@@ -1370,15 +1411,21 @@ +@@ -1370,15 +1419,21 @@ { if (!this.func_180431_b(p_70665_1_)) { @@ -172,7 +202,7 @@ this.func_70606_j(f1 - p_70665_2_); this.func_110142_aN().func_94547_a(p_70665_1_, f1, p_70665_2_); this.func_110149_m(this.func_110139_bj() - p_70665_2_); -@@ -1828,7 +1875,10 @@ +@@ -1828,7 +1883,10 @@ } } @@ -183,7 +213,7 @@ if (this.field_70123_F && this.func_70617_f_()) { -@@ -2306,6 +2356,9 @@ +@@ -2306,6 +2364,9 @@ protected void func_85033_bc() { @@ -193,7 +223,7 @@ List list = this.field_70170_p.func_175674_a(this, this.func_174813_aQ(), EntitySelectors.func_188442_a(this)); if (!list.isEmpty()) -@@ -2330,8 +2383,13 @@ +@@ -2330,8 +2391,13 @@ } } @@ -208,7 +238,7 @@ Entity entity = list.get(l); this.func_82167_n(entity); } -@@ -2366,6 +2424,11 @@ +@@ -2366,6 +2432,11 @@ { this.field_70703_bu = p_70637_1_; } diff --git a/patches/net/minecraft/entity/item/EntityExpBottle.java.patch b/patches/net/minecraft/entity/item/EntityExpBottle.java.patch new file mode 100644 index 00000000..ae0c21b5 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityExpBottle.java.patch @@ -0,0 +1,22 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityExpBottle.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityExpBottle.java +@@ -1,5 +1,6 @@ + package net.minecraft.entity.item; + ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import net.minecraft.entity.EntityLivingBase; + import net.minecraft.entity.projectile.EntityThrowable; + import net.minecraft.init.PotionTypes; +@@ -47,7 +48,11 @@ + { + int j = EntityXPOrb.func_70527_a(i); + i -= j; +- this.field_70170_p.func_72838_d(new EntityXPOrb(this.field_70170_p, this.field_70165_t, this.field_70163_u, this.field_70161_v, j)); ++ // CM lifetime tracker ++// this.world.spawnEntity(new EntityXPOrb(this.world, this.posX, this.posY, this.posZ, j)); ++ EntityXPOrb entityXPOrb = new EntityXPOrb(this.field_70170_p, this.field_70165_t, this.field_70163_u, this.field_70161_v, j); ++ entityXPOrb.recordSpawning(LiteralSpawningReason.ITEM); ++ this.field_70170_p.func_72838_d(entityXPOrb); + } + + this.func_70106_y(); diff --git a/patches/net/minecraft/entity/item/EntityItem.java.patch b/patches/net/minecraft/entity/item/EntityItem.java.patch index 267a5533..6edcc774 100644 --- a/patches/net/minecraft/entity/item/EntityItem.java.patch +++ b/patches/net/minecraft/entity/item/EntityItem.java.patch @@ -1,12 +1,14 @@ --- ../src-base/minecraft/net/minecraft/entity/item/EntityItem.java +++ ../src-work/minecraft/net/minecraft/entity/item/EntityItem.java -@@ -1,10 +1,17 @@ +@@ -1,10 +1,19 @@ package net.minecraft.entity.item; import javax.annotation.Nullable; + +import carpet.CarpetServer; +import carpet.CarpetSettings; ++import carpet.commands.lifetime.removal.LiteralRemovalReason; ++import carpet.commands.lifetime.removal.MobPickupRemovalReason; +import carpet.helpers.HopperCounter; +import carpet.logging.LoggerRegistry; +import carpet.logging.logHelpers.ItemLogHelper; @@ -18,7 +20,7 @@ import net.minecraft.init.Items; import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; -@@ -13,7 +20,6 @@ +@@ -13,7 +22,6 @@ import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.EntityDataManager; @@ -26,7 +28,7 @@ import net.minecraft.util.DamageSource; import net.minecraft.util.datafix.DataFixer; import net.minecraft.util.datafix.FixTypes; -@@ -25,6 +31,8 @@ +@@ -25,6 +33,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,7 +37,7 @@ public class EntityItem extends Entity { private static final Logger field_145803_d = LogManager.getLogger(); -@@ -35,6 +43,9 @@ +@@ -35,6 +45,9 @@ private String field_145801_f; private String field_145802_g; public float field_70290_d; @@ -45,7 +47,7 @@ public EntityItem(World p_i1709_1_, double p_i1709_2_, double p_i1709_4_, double p_i1709_6_) { -@@ -47,6 +58,12 @@ +@@ -47,6 +60,12 @@ this.field_70159_w = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D)); this.field_70181_x = 0.20000000298023224D; this.field_70179_y = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D)); @@ -58,7 +60,7 @@ } public EntityItem(World p_i1710_1_, double p_i1710_2_, double p_i1710_4_, double p_i1710_6_, ItemStack p_i1710_8_) -@@ -165,8 +182,20 @@ +@@ -165,8 +184,21 @@ } } @@ -75,11 +77,12 @@ + if (LoggerRegistry.__items && logHelper != null) { + logHelper.onFinish("Despawn Timer"); + } ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_TIMEOUT); // CM lifetime tracker + // ----- Carpet End ----- // this.func_70106_y(); } } -@@ -221,8 +250,23 @@ +@@ -221,14 +253,45 @@ } else if (itemstack1.func_190916_E() + itemstack.func_190916_E() > itemstack1.func_77976_d()) { @@ -90,6 +93,14 @@ + p_70289_1_.field_145804_b = Math.max(p_70289_1_.field_145804_b, this.field_145804_b); + p_70289_1_.field_70292_b = Math.min(p_70289_1_.field_70292_b, this.field_70292_b); + p_70289_1_.func_92058_a(itemstack1); ++ ++ // CM lifetime tracker ++ int stackCount = itemstack.func_190916_E(); ++ itemstack.func_190920_e(0); // temporarily set the stack to 0 ++ this.recordRemoval(LiteralRemovalReason.MERGE); ++ itemstack.func_190920_e(stackCount); ++ // CM lifetime tracker ends ++ + this.func_70106_y(); + return true; + } @@ -103,7 +114,21 @@ else { itemstack1.func_190917_f(itemstack.func_190916_E()); -@@ -256,6 +300,8 @@ + p_70289_1_.field_145804_b = Math.max(p_70289_1_.field_145804_b, this.field_145804_b); + p_70289_1_.field_70292_b = Math.min(p_70289_1_.field_70292_b, this.field_70292_b); + p_70289_1_.func_92058_a(itemstack1); ++ ++ // CM lifetime tracker ++ int stackCount = itemstack.func_190916_E(); ++ itemstack.func_190920_e(0); // temporarily set the stack to 0 ++ this.recordRemoval(LiteralRemovalReason.MERGE); ++ itemstack.func_190920_e(stackCount); ++ // CM lifetime tracker ends ++ + this.func_70106_y(); + return true; + } +@@ -256,6 +319,8 @@ public boolean func_70072_I() { @@ -112,7 +137,7 @@ if (this.field_70170_p.func_72918_a(this.func_174813_aQ(), Material.field_151586_h, this)) { if (!this.field_70171_ac && !this.field_70148_d) -@@ -269,7 +315,8 @@ +@@ -269,7 +334,8 @@ { this.field_70171_ac = false; } @@ -122,7 +147,7 @@ return this.field_70171_ac; } -@@ -295,6 +342,15 @@ +@@ -295,6 +361,16 @@ if (this.field_70291_e <= 0) { @@ -134,11 +159,12 @@ + if (LoggerRegistry.__items && logHelper != null) { + logHelper.onFinish(p_70097_1_.func_76355_l()); + } ++ this.setDeathDamageSource(p_70097_1_); // CM lifetime tracker + // ----- Carpet End ----- // this.func_70106_y(); } -@@ -366,6 +422,10 @@ +@@ -366,18 +442,33 @@ Item item = itemstack.func_77973_b(); int i = itemstack.func_190916_E(); @@ -149,7 +175,16 @@ if (this.field_145804_b == 0 && (this.field_145802_g == null || 6000 - this.field_70292_b <= 200 || this.field_145802_g.equals(p_70100_1_.func_70005_c_())) && p_70100_1_.field_71071_by.func_70441_a(itemstack)) { p_70100_1_.func_71001_a(this, i); -@@ -376,8 +436,13 @@ + + if (itemstack.func_190926_b()) + { ++ // CM lifetime tracker ++ int stackCount = itemstack.func_190916_E(); ++ itemstack.func_190920_e(i); // for recording the item amount ++ this.recordRemoval(new MobPickupRemovalReason(p_70100_1_.getClass())); ++ itemstack.func_190920_e(stackCount); ++ + this.func_70106_y(); itemstack.func_190920_e(i); } @@ -164,7 +199,7 @@ } } -@@ -470,4 +535,9 @@ +@@ -470,4 +561,9 @@ this.func_174871_r(); this.field_70292_b = 5999; } diff --git a/patches/net/minecraft/entity/item/EntityXPOrb.java.patch b/patches/net/minecraft/entity/item/EntityXPOrb.java.patch index de58bf39..830e758d 100644 --- a/patches/net/minecraft/entity/item/EntityXPOrb.java.patch +++ b/patches/net/minecraft/entity/item/EntityXPOrb.java.patch @@ -1,6 +1,14 @@ --- ../src-base/minecraft/net/minecraft/entity/item/EntityXPOrb.java +++ ../src-work/minecraft/net/minecraft/entity/item/EntityXPOrb.java -@@ -14,16 +14,24 @@ +@@ -1,5 +1,7 @@ + package net.minecraft.entity.item; + ++import carpet.commands.lifetime.removal.LiteralRemovalReason; ++import carpet.commands.lifetime.removal.MobPickupRemovalReason; + import net.minecraft.block.material.Material; + import net.minecraft.enchantment.EnchantmentHelper; + import net.minecraft.entity.Entity; +@@ -14,16 +16,24 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; @@ -26,7 +34,7 @@ public EntityXPOrb(World p_i1585_1_, double p_i1585_2_, double p_i1585_4_, double p_i1585_6_, int p_i1585_8_) { super(p_i1585_1_); -@@ -34,8 +42,26 @@ +@@ -34,8 +44,26 @@ this.field_70181_x = (double)((float)(Math.random() * 0.2D) * 2.0F); this.field_70179_y = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F); this.field_70530_e = p_i1585_8_; @@ -53,7 +61,7 @@ protected boolean func_70041_e_() { return false; -@@ -113,6 +139,20 @@ +@@ -113,6 +141,20 @@ } this.func_70091_d(MoverType.SELF, this.field_70159_w, this.field_70181_x, this.field_70179_y); @@ -74,7 +82,23 @@ float f = 0.98F; if (this.field_70122_E) -@@ -186,7 +226,8 @@ +@@ -134,6 +176,7 @@ + + if (this.field_70531_b >= 6000) + { ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_TIMEOUT); // CM lifetime tracker + this.func_70106_y(); + } + } +@@ -161,6 +204,7 @@ + + if (this.field_70529_d <= 0) + { ++ this.setDeathDamageSource(p_70097_1_); // CM lifetime tracker + this.func_70106_y(); + } + +@@ -186,8 +230,10 @@ { if (!this.field_70170_p.field_72995_K) { @@ -82,5 +106,7 @@ + //CM added ORed condition + if (this.field_70532_c == 0 && (p_70100_1_.field_71090_bL == 0 || CarpetSettings.xpNoCooldown) ) { ++ this.recordRemoval(new MobPickupRemovalReason(p_70100_1_.getClass())); // CM lifetime tracker p_70100_1_.field_71090_bL = 2; p_70100_1_.func_71001_a(this, 1); + ItemStack itemstack = EnchantmentHelper.func_92099_a(Enchantments.field_185296_A, p_70100_1_); diff --git a/patches/net/minecraft/entity/monster/EntityGhast.java.patch b/patches/net/minecraft/entity/monster/EntityGhast.java.patch index 6e31715b..a06a1168 100644 --- a/patches/net/minecraft/entity/monster/EntityGhast.java.patch +++ b/patches/net/minecraft/entity/monster/EntityGhast.java.patch @@ -1,6 +1,15 @@ --- ../src-base/minecraft/net/minecraft/entity/monster/EntityGhast.java +++ ../src-work/minecraft/net/minecraft/entity/monster/EntityGhast.java -@@ -29,6 +29,13 @@ +@@ -2,6 +2,8 @@ + + import java.util.Random; + import javax.annotation.Nullable; ++ ++import carpet.commands.lifetime.removal.LiteralRemovalReason; + import net.minecraft.entity.EntityFlying; + import net.minecraft.entity.EntityLiving; + import net.minecraft.entity.EntityLivingBase; +@@ -29,6 +31,13 @@ import net.minecraft.world.World; import net.minecraft.world.storage.loot.LootTableList; @@ -14,7 +23,7 @@ public class EntityGhast extends EntityFlying implements IMob { private static final DataParameter field_184683_a = EntityDataManager.func_187226_a(EntityGhast.class, DataSerializers.field_187198_h); -@@ -48,7 +55,9 @@ +@@ -48,7 +57,9 @@ this.field_70714_bg.func_75776_a(5, new EntityGhast.AIRandomFly(this)); this.field_70714_bg.func_75776_a(7, new EntityGhast.AILookAround(this)); this.field_70714_bg.func_75776_a(7, new EntityGhast.AIFireballAttack(this)); @@ -25,7 +34,15 @@ } public void func_175454_a(boolean p_175454_1_) -@@ -168,6 +177,47 @@ +@@ -67,6 +78,7 @@ + + if (!this.field_70170_p.field_72995_K && this.field_70170_p.func_175659_aa() == EnumDifficulty.PEACEFUL) + { ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_DIFFICULTY); // CM lifetime tracker + this.func_70106_y(); + } + } +@@ -168,6 +180,47 @@ return 2.6F; } @@ -73,7 +90,7 @@ static class AIFireballAttack extends EntityAIBase { private final EntityGhast field_179470_b; -@@ -180,7 +230,28 @@ +@@ -180,7 +233,28 @@ public boolean func_75250_a() { @@ -103,7 +120,7 @@ } public void func_75249_e() -@@ -191,6 +262,11 @@ +@@ -191,6 +265,11 @@ public void func_75251_c() { this.field_179470_b.func_175454_a(false); diff --git a/patches/net/minecraft/entity/monster/EntityMob.java.patch b/patches/net/minecraft/entity/monster/EntityMob.java.patch index f8cdd3ce..cf9e79dd 100644 --- a/patches/net/minecraft/entity/monster/EntityMob.java.patch +++ b/patches/net/minecraft/entity/monster/EntityMob.java.patch @@ -1,6 +1,13 @@ --- ../src-base/minecraft/net/minecraft/entity/monster/EntityMob.java +++ ../src-work/minecraft/net/minecraft/entity/monster/EntityMob.java -@@ -19,6 +19,8 @@ +@@ -1,5 +1,6 @@ + package net.minecraft.entity.monster; + ++import carpet.commands.lifetime.removal.LiteralRemovalReason; + import net.minecraft.enchantment.EnchantmentHelper; + import net.minecraft.entity.Entity; + import net.minecraft.entity.EntityCreature; +@@ -19,6 +20,8 @@ import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.World; @@ -9,7 +16,7 @@ public abstract class EntityMob extends EntityCreature implements IMob { public EntityMob(World p_i1738_1_) -@@ -32,6 +34,9 @@ +@@ -32,6 +35,9 @@ return SoundCategory.HOSTILE; } @@ -19,7 +26,15 @@ public void func_70636_d() { this.func_82168_bl(); -@@ -90,10 +95,16 @@ +@@ -51,6 +57,7 @@ + + if (!this.field_70170_p.field_72995_K && this.field_70170_p.func_175659_aa() == EnumDifficulty.PEACEFUL) + { ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_DIFFICULTY); // CM lifetime tracker + this.func_70106_y(); + } + } +@@ -90,10 +97,16 @@ float f = (float)this.func_110148_a(SharedMonsterAttributes.field_111264_e).func_111126_e(); int i = 0; diff --git a/patches/net/minecraft/entity/monster/EntitySlime.java.patch b/patches/net/minecraft/entity/monster/EntitySlime.java.patch index c5531f6f..5cf8b244 100644 --- a/patches/net/minecraft/entity/monster/EntitySlime.java.patch +++ b/patches/net/minecraft/entity/monster/EntitySlime.java.patch @@ -1,6 +1,16 @@ --- ../src-base/minecraft/net/minecraft/entity/monster/EntitySlime.java +++ ../src-work/minecraft/net/minecraft/entity/monster/EntitySlime.java -@@ -36,6 +36,8 @@ +@@ -1,6 +1,9 @@ + package net.minecraft.entity.monster; + + import javax.annotation.Nullable; ++ ++import carpet.commands.lifetime.removal.LiteralRemovalReason; ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import net.minecraft.entity.Entity; + import net.minecraft.entity.EntityLiving; + import net.minecraft.entity.EntityLivingBase; +@@ -36,6 +39,8 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.storage.loot.LootTableList; @@ -9,7 +19,23 @@ public class EntitySlime extends EntityLiving implements IMob { private static final DataParameter field_184711_bt = EntityDataManager.func_187226_a(EntitySlime.class, DataSerializers.field_187192_b); -@@ -251,7 +253,10 @@ +@@ -127,6 +132,7 @@ + { + if (!this.field_70170_p.field_72995_K && this.field_70170_p.func_175659_aa() == EnumDifficulty.PEACEFUL && this.func_70809_q() > 0) + { ++ this.recordRemoval(LiteralRemovalReason.DESPAWN_DIFFICULTY); // CM lifetime tracker + this.field_70128_L = true; + } + +@@ -222,6 +228,7 @@ + + entityslime.func_70799_a(i / 2, true); + entityslime.func_70012_b(this.field_70165_t + (double)f, this.field_70163_u + 0.5D, this.field_70161_v + (double)f1, this.field_70146_Z.nextFloat() * 360.0F, 0.0F); ++ entityslime.recordSpawning(LiteralSpawningReason.SLIME); // CM lifetime tracker + this.field_70170_p.func_72838_d(entityslime); + } + } +@@ -251,7 +258,10 @@ { int i = this.func_70809_q(); @@ -21,7 +47,7 @@ { this.func_184185_a(SoundEvents.field_187870_fk, 1.0F, (this.field_70146_Z.nextFloat() - this.field_70146_Z.nextFloat()) * 0.2F + 1.0F); this.func_174815_a(this, p_175451_1_); -@@ -273,6 +278,12 @@ +@@ -273,6 +283,12 @@ return this.func_70809_q(); } diff --git a/patches/net/minecraft/entity/monster/EntityZombie.java.patch b/patches/net/minecraft/entity/monster/EntityZombie.java.patch new file mode 100644 index 00000000..a21361e8 --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityZombie.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityZombie.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityZombie.java +@@ -4,6 +4,8 @@ + import java.util.List; + import java.util.UUID; + import javax.annotation.Nullable; ++ ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import net.minecraft.block.Block; + import net.minecraft.entity.Entity; + import net.minecraft.entity.EntityLiving; +@@ -250,6 +252,7 @@ + + if (!this.field_70170_p.func_175636_b((double)i1, (double)j1, (double)k1, 7.0D) && this.field_70170_p.func_72917_a(entityzombie.func_174813_aQ(), entityzombie) && this.field_70170_p.func_184144_a(entityzombie, entityzombie.func_174813_aQ()).isEmpty() && !this.field_70170_p.func_72953_d(entityzombie.func_174813_aQ())) + { ++ entityzombie.recordSpawning(LiteralSpawningReason.ZOMBIE_REINFORCE); // CM lifetime tracker + this.field_70170_p.func_72838_d(entityzombie); + entityzombie.func_70624_b(entitylivingbase); + entityzombie.func_180482_a(this.field_70170_p.func_175649_E(new BlockPos(entityzombie)), (IEntityLivingData)null); diff --git a/patches/net/minecraft/entity/player/EntityPlayer.java.patch b/patches/net/minecraft/entity/player/EntityPlayer.java.patch index 89db2233..3ae25bbe 100644 --- a/patches/net/minecraft/entity/player/EntityPlayer.java.patch +++ b/patches/net/minecraft/entity/player/EntityPlayer.java.patch @@ -1,6 +1,13 @@ --- ../src-base/minecraft/net/minecraft/entity/player/EntityPlayer.java +++ ../src-work/minecraft/net/minecraft/entity/player/EntityPlayer.java -@@ -91,6 +91,14 @@ +@@ -1,5 +1,6 @@ + package net.minecraft.entity.player; + ++import carpet.commands.lifetime.spawning.MobDropSpawningReason; + import com.google.common.base.Predicate; + import com.google.common.collect.Lists; + import com.mojang.authlib.GameProfile; +@@ -91,6 +92,14 @@ import net.minecraft.world.World; import net.minecraft.world.WorldServer; @@ -15,7 +22,7 @@ public abstract class EntityPlayer extends EntityLivingBase { private static final DataParameter field_184829_a = EntityDataManager.func_187226_a(EntityPlayer.class, DataSerializers.field_187193_c); -@@ -364,6 +372,15 @@ +@@ -364,6 +373,15 @@ public int func_82145_z() { @@ -31,7 +38,7 @@ return this.field_71075_bZ.field_75102_a ? 1 : 80; } -@@ -530,7 +547,9 @@ +@@ -530,7 +548,9 @@ this.func_192028_j(this.func_192023_dk()); this.func_192028_j(this.func_192025_dl()); @@ -42,7 +49,7 @@ { this.func_192030_dh(); } -@@ -692,7 +711,7 @@ +@@ -692,12 +712,13 @@ { if (!itemstack.func_190926_b()) { @@ -51,7 +58,13 @@ } this.func_71029_a(StatList.field_75952_v); -@@ -908,10 +927,23 @@ + } + ++ entityitem.recordSpawning(new MobDropSpawningReason(this.getClass())); // CM lifetime tracker + return entityitem; + } + } +@@ -908,10 +929,23 @@ this.func_70999_a(true, true, false); } @@ -76,7 +89,7 @@ if (this.field_70170_p.func_175659_aa() == EnumDifficulty.PEACEFUL) { p_70097_2_ = 0.0F; -@@ -926,6 +958,7 @@ +@@ -926,6 +960,7 @@ { p_70097_2_ = p_70097_2_ * 3.0F / 2.0F; } @@ -84,7 +97,7 @@ } return p_70097_2_ == 0.0F ? false : super.func_70097_a(p_70097_1_, p_70097_2_); -@@ -1008,16 +1041,22 @@ +@@ -1008,16 +1043,22 @@ { if (!this.func_180431_b(p_70665_1_)) { @@ -107,7 +120,7 @@ this.func_70606_j(this.func_110143_aJ() - p_70665_2_); this.func_110142_aN().func_94547_a(p_70665_1_, f1, p_70665_2_); -@@ -1027,6 +1066,12 @@ +@@ -1027,6 +1068,12 @@ } } } @@ -120,7 +133,7 @@ } public void func_175141_a(TileEntitySign p_175141_1_) -@@ -1227,19 +1272,33 @@ +@@ -1227,19 +1274,33 @@ { float f3 = 1.0F + EnchantmentHelper.func_191527_a(this) * f; @@ -154,7 +167,7 @@ if (p_71059_1_ instanceof EntityPlayerMP && p_71059_1_.field_70133_I) { ((EntityPlayerMP)p_71059_1_).field_71135_a.func_147359_a(new SPacketEntityVelocity(p_71059_1_)); -@@ -1333,6 +1392,13 @@ +@@ -1333,6 +1394,13 @@ } } } @@ -168,7 +181,7 @@ } } -@@ -1911,6 +1977,10 @@ +@@ -1911,6 +1979,10 @@ { return false; } @@ -179,7 +192,7 @@ else { BlockPos blockpos = p_175151_1_.func_177972_a(p_175151_2_.func_176734_d()); -@@ -2044,6 +2114,20 @@ +@@ -2044,6 +2116,20 @@ this.func_192031_i(new NBTTagCompound()); } @@ -200,7 +213,7 @@ private void func_192026_k(@Nullable NBTTagCompound p_192026_1_) { if (!this.field_70170_p.field_72995_K && !p_192026_1_.func_82582_d()) -@@ -2307,6 +2391,13 @@ +@@ -2307,6 +2393,13 @@ { return this.field_71075_bZ.field_75098_d && this.func_70003_b(2, ""); } @@ -214,7 +227,7 @@ public static enum EnumChatVisibility { -@@ -2333,11 +2424,11 @@ +@@ -2333,11 +2426,11 @@ } } diff --git a/patches/net/minecraft/item/ItemMonsterPlacer.java.patch b/patches/net/minecraft/item/ItemMonsterPlacer.java.patch new file mode 100644 index 00000000..01dba7ef --- /dev/null +++ b/patches/net/minecraft/item/ItemMonsterPlacer.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemMonsterPlacer.java ++++ ../src-work/minecraft/net/minecraft/item/ItemMonsterPlacer.java +@@ -3,6 +3,8 @@ + import java.util.List; + import java.util.UUID; + import javax.annotation.Nullable; ++ ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import net.minecraft.block.Block; + import net.minecraft.block.BlockLiquid; + import net.minecraft.block.state.IBlockState; +@@ -235,6 +237,7 @@ + entityliving.field_70759_as = entityliving.field_70177_z; + entityliving.field_70761_aq = entityliving.field_70177_z; + entityliving.func_180482_a(p_77840_0_.func_175649_E(new BlockPos(entityliving)), (IEntityLivingData)null); ++ entity.recordSpawning(LiteralSpawningReason.ITEM); // CM lifetime tracker + p_77840_0_.func_72838_d(entity); + entityliving.func_70642_aH(); + } diff --git a/patches/net/minecraft/tileentity/TileEntityHopper.java.patch b/patches/net/minecraft/tileentity/TileEntityHopper.java.patch index 3d24dcc3..7562c3a6 100644 --- a/patches/net/minecraft/tileentity/TileEntityHopper.java.patch +++ b/patches/net/minecraft/tileentity/TileEntityHopper.java.patch @@ -1,16 +1,17 @@ --- ../src-base/minecraft/net/minecraft/tileentity/TileEntityHopper.java +++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityHopper.java -@@ -2,6 +2,9 @@ +@@ -2,6 +2,10 @@ import java.util.List; import javax.annotation.Nullable; + +import carpet.carpetclient.CarpetClientChunkLogger; ++import carpet.commands.lifetime.removal.LiteralRemovalReason; +import carpet.helpers.TileEntityCraftingTable; import net.minecraft.block.Block; import net.minecraft.block.BlockChest; import net.minecraft.block.BlockHopper; -@@ -28,12 +31,21 @@ +@@ -28,12 +32,21 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; @@ -33,7 +34,7 @@ public static void func_189683_a(DataFixer p_189683_0_) { p_189683_0_.func_188258_a(FixTypes.BLOCK_ENTITY, new ItemStackDataLists(TileEntityHopper.class, new String[] {"Items"})); -@@ -132,14 +144,44 @@ +@@ -132,14 +145,44 @@ { boolean flag = false; @@ -83,7 +84,7 @@ } if (flag) -@@ -191,10 +233,42 @@ +@@ -191,10 +234,42 @@ private boolean func_145883_k() { @@ -126,7 +127,7 @@ return false; } else -@@ -203,6 +277,10 @@ +@@ -203,6 +278,10 @@ if (this.func_174919_a(iinventory, enumfacing)) { @@ -137,7 +138,7 @@ return false; } else -@@ -297,7 +375,10 @@ +@@ -297,7 +376,10 @@ public static boolean func_145891_a(IHopper p_145891_0_) { @@ -148,7 +149,7 @@ if (iinventory != null) { -@@ -305,6 +386,14 @@ +@@ -305,6 +387,14 @@ if (func_174917_b(iinventory, enumfacing)) { @@ -163,7 +164,7 @@ return false; } -@@ -333,6 +422,13 @@ +@@ -333,6 +423,13 @@ } } } @@ -177,7 +178,7 @@ } else { -@@ -352,6 +448,11 @@ +@@ -352,6 +449,11 @@ { ItemStack itemstack = p_174915_1_.func_70301_a(p_174915_2_); @@ -189,7 +190,15 @@ if (!itemstack.func_190926_b() && func_174921_b(p_174915_1_, itemstack, p_174915_2_, p_174915_3_)) { ItemStack itemstack1 = itemstack.func_77946_l(); -@@ -499,6 +600,14 @@ +@@ -384,6 +486,7 @@ + + if (itemstack1.func_190926_b()) + { ++ p_145898_2_.recordRemoval(LiteralRemovalReason.HOPPER); // CM lifetime tracker + flag = true; + p_145898_2_.func_70106_y(); + } +@@ -499,6 +602,14 @@ return func_145893_b(this.func_145831_w(), this.func_96107_aA() + (double)enumfacing.func_82601_c(), this.func_96109_aB() + (double)enumfacing.func_96559_d(), this.func_96108_aC() + (double)enumfacing.func_82599_e()); } @@ -204,7 +213,7 @@ public static IInventory func_145884_b(IHopper p_145884_0_) { return func_145893_b(p_145884_0_.func_145831_w(), p_145884_0_.func_96107_aA(), p_145884_0_.func_96109_aB() + 1.0D, p_145884_0_.func_96108_aC()); -@@ -546,7 +655,7 @@ +@@ -546,7 +657,7 @@ return iinventory; } @@ -213,7 +222,7 @@ { if (p_145894_0_.func_77973_b() != p_145894_1_.func_77973_b()) { -@@ -611,4 +720,15 @@ +@@ -611,4 +722,15 @@ { return this.field_145900_a; } diff --git a/patches/net/minecraft/world/WorldEntitySpawner.java.patch b/patches/net/minecraft/world/WorldEntitySpawner.java.patch index 3ec7a489..d8409413 100644 --- a/patches/net/minecraft/world/WorldEntitySpawner.java.patch +++ b/patches/net/minecraft/world/WorldEntitySpawner.java.patch @@ -1,6 +1,13 @@ --- ../src-base/minecraft/net/minecraft/world/WorldEntitySpawner.java +++ ../src-work/minecraft/net/minecraft/world/WorldEntitySpawner.java -@@ -22,6 +22,12 @@ +@@ -1,5 +1,6 @@ + package net.minecraft.world; + ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; + import com.google.common.collect.Sets; + import java.util.List; + import java.util.Random; +@@ -22,6 +23,12 @@ import net.minecraft.world.biome.Biome; import net.minecraft.world.chunk.Chunk; @@ -13,7 +20,16 @@ public final class WorldEntitySpawner { private static final int field_180268_a = (int)Math.pow(17.0D, 2.0D); -@@ -72,19 +78,54 @@ +@@ -29,6 +36,8 @@ + + public int func_77192_a(WorldServer p_77192_1_, boolean p_77192_2_, boolean p_77192_3_, boolean p_77192_4_) + { ++ p_77192_1_.getLifeTimeWorldTracker().increaseSpawnStageCounter(); // CM lifetime tracker ++ + if (!p_77192_2_ && !p_77192_3_) + { + return 0; +@@ -72,19 +81,54 @@ } } @@ -70,7 +86,7 @@ label134: for (ChunkPos chunkpos1 : this.field_77193_b) -@@ -108,6 +149,8 @@ +@@ -108,6 +152,8 @@ Biome.SpawnListEntry biome$spawnlistentry = null; IEntityLivingData ientitylivingdata = null; int l3 = MathHelper.func_76143_f(Math.random() * 4.0D); @@ -79,7 +95,7 @@ for (int i4 = 0; i4 < l3; ++i4) { -@@ -153,7 +196,29 @@ +@@ -153,7 +199,30 @@ if (entityliving.func_70058_J()) { ++j2; @@ -103,6 +119,7 @@ + } + else + { ++ entityliving.recordSpawning(LiteralSpawningReason.NATURAL); // CM lifetime tracker + p_77192_1_.func_72838_d(entityliving); + } + //CM end @@ -110,7 +127,7 @@ } else { -@@ -173,7 +238,30 @@ +@@ -173,7 +242,30 @@ } } } diff --git a/patches/net/minecraft/world/WorldServer.java.patch b/patches/net/minecraft/world/WorldServer.java.patch index f784eba2..33916c6e 100644 --- a/patches/net/minecraft/world/WorldServer.java.patch +++ b/patches/net/minecraft/world/WorldServer.java.patch @@ -1,14 +1,15 @@ --- ../src-base/minecraft/net/minecraft/world/WorldServer.java +++ ../src-work/minecraft/net/minecraft/world/WorldServer.java -@@ -1,5 +1,7 @@ +@@ -1,5 +1,8 @@ package net.minecraft.world; ++import carpet.commands.lifetime.LifeTimeWorldTracker; +import carpet.helpers.NextTickListEntryFix; +import carpet.helpers.ScheduledBlockEventSerializer; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -@@ -14,7 +16,6 @@ +@@ -14,7 +17,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; @@ -16,7 +17,7 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; import net.minecraft.advancements.AdvancementManager; -@@ -60,6 +61,7 @@ +@@ -60,6 +62,7 @@ import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -24,7 +25,7 @@ import net.minecraft.village.VillageCollection; import net.minecraft.village.VillageSiege; import net.minecraft.world.biome.Biome; -@@ -80,14 +82,23 @@ +@@ -80,14 +83,23 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -51,7 +52,7 @@ private final Map field_175741_N = Maps.newHashMap(); public boolean field_73058_d; private boolean field_73068_P; -@@ -99,6 +110,13 @@ +@@ -99,6 +111,16 @@ private int field_147489_T; private final List field_94579_S = Lists.newArrayList(); @@ -61,11 +62,32 @@ + public boolean blockActionsProcessed; + public ScheduledBlockEventSerializer blockEventSerializer; + public static boolean loginMinecartFix = false; ++ ++ // CM lifetime tracker ++ private final LifeTimeWorldTracker lifetimeTracker; + public WorldServer(MinecraftServer p_i45921_1_, ISaveHandler p_i45921_2_, WorldInfo p_i45921_3_, int p_i45921_4_, Profiler p_i45921_5_) { super(p_i45921_2_, p_i45921_3_, DimensionType.func_186069_a(p_i45921_4_).func_186070_d(), p_i45921_5_, false); -@@ -159,11 +177,24 @@ +@@ -111,8 +133,17 @@ + this.func_72966_v(); + this.func_72947_a(); + this.func_175723_af().func_177725_a(p_i45921_1_.func_175580_aG()); ++ ++ // CM lifetime tracker ++ this.lifetimeTracker = new LifeTimeWorldTracker(this); + } + ++ // CM lifetime tracker ++ public LifeTimeWorldTracker getLifeTimeWorldTracker() ++ { ++ return this.lifetimeTracker; ++ } ++ + public World func_175643_b() + { + this.field_72988_C = new MapStorage(this.field_73019_z); +@@ -159,11 +190,24 @@ this.func_175723_af().func_177750_a(this.field_72986_A.func_176137_E()); } @@ -90,7 +112,7 @@ super.func_72835_b(); if (this.func_72912_H().func_76093_s() && this.func_175659_aa() != EnumDifficulty.HARD) -@@ -184,15 +215,26 @@ +@@ -184,15 +228,26 @@ this.func_73053_d(); } @@ -117,7 +139,7 @@ int j = this.func_72967_a(1.0F); if (j != this.func_175657_ab()) -@@ -200,26 +242,76 @@ +@@ -200,26 +255,76 @@ this.func_175692_b(j); } @@ -196,7 +218,7 @@ } @Nullable -@@ -255,8 +347,15 @@ +@@ -255,8 +360,15 @@ ++j; } } @@ -214,7 +236,7 @@ } } -@@ -287,6 +386,28 @@ +@@ -287,6 +399,28 @@ { if (this.field_73068_P && !this.field_72995_K) { @@ -243,7 +265,7 @@ for (EntityPlayer entityplayer : this.field_73010_i) { if (!entityplayer.func_175149_v() && !entityplayer.func_71026_bH()) -@@ -303,7 +424,7 @@ +@@ -303,7 +437,7 @@ } } @@ -252,7 +274,7 @@ { return this.func_72863_F().func_73149_a(p_175680_1_, p_175680_2_); } -@@ -344,6 +465,7 @@ +@@ -344,6 +478,7 @@ boolean flag = this.func_72896_J(); boolean flag1 = this.func_72911_I(); this.field_72984_F.func_76320_a("pollingChunks"); @@ -260,7 +282,7 @@ for (Iterator iterator = this.field_73063_M.func_187300_b(); iterator.hasNext(); this.field_72984_F.func_76319_b()) { -@@ -355,9 +477,14 @@ +@@ -355,9 +490,14 @@ chunk.func_76594_o(); this.field_72984_F.func_76318_c("tickChunk"); chunk.func_150804_b(false); @@ -276,7 +298,7 @@ { this.field_73005_l = this.field_73005_l * 3 + 1013904223; int l = this.field_73005_l >> 2; -@@ -385,7 +512,7 @@ +@@ -385,7 +525,7 @@ this.field_72984_F.func_76318_c("iceandsnow"); @@ -285,7 +307,7 @@ { this.field_73005_l = this.field_73005_l * 3 + 1013904223; int j2 = this.field_73005_l >> 2; -@@ -443,7 +570,7 @@ +@@ -443,7 +583,7 @@ } } @@ -294,7 +316,7 @@ { BlockPos blockpos = this.func_175725_q(p_175736_1_); AxisAlignedBB axisalignedbb = (new AxisAlignedBB(blockpos, new BlockPos(blockpos.func_177958_n(), this.func_72800_K(), blockpos.func_177952_p()))).func_186662_g(3.0D); -@@ -472,13 +599,25 @@ +@@ -472,13 +612,25 @@ public boolean func_175691_a(BlockPos p_175691_1_, Block p_175691_2_) { @@ -322,7 +344,7 @@ return this.field_73064_N.contains(nextticklistentry); } -@@ -503,15 +642,22 @@ +@@ -503,15 +655,22 @@ { iblockstate.func_177230_c().func_180650_b(this, p_175654_1_, iblockstate, this.field_73012_v); } @@ -347,7 +369,7 @@ if (this.func_175667_e(p_175654_1_)) { -@@ -531,7 +677,13 @@ +@@ -531,7 +690,13 @@ public void func_180497_b(BlockPos p_180497_1_, Block p_180497_2_, int p_180497_3_, int p_180497_4_) { @@ -362,7 +384,7 @@ nextticklistentry.func_82753_a(p_180497_4_); Material material = p_180497_2_.func_176223_P().func_185904_a(); -@@ -549,7 +701,8 @@ +@@ -549,7 +714,8 @@ public void func_72939_s() { @@ -372,7 +394,7 @@ { if (this.field_80004_Q++ >= 300) { -@@ -644,9 +797,18 @@ +@@ -644,9 +810,18 @@ } else { @@ -393,7 +415,7 @@ } this.field_72984_F.func_76320_a("cleaning"); -@@ -677,6 +839,8 @@ +@@ -677,6 +852,8 @@ if (this.func_175707_a(nextticklistentry1.field_180282_a.func_177982_a(0, 0, 0), nextticklistentry1.field_180282_a.func_177982_a(0, 0, 0))) { @@ -402,7 +424,7 @@ IBlockState iblockstate = this.func_180495_p(nextticklistentry1.field_180282_a); if (iblockstate.func_185904_a() != Material.field_151579_a && Block.func_149680_a(iblockstate.func_177230_c(), nextticklistentry1.func_151351_a())) -@@ -699,6 +863,7 @@ +@@ -699,6 +876,7 @@ this.func_175684_a(nextticklistentry1.field_180282_a, nextticklistentry1.func_151351_a(), 0); } } @@ -410,7 +432,7 @@ this.field_72984_F.func_76319_b(); this.field_94579_S.clear(); -@@ -950,11 +1115,18 @@ +@@ -950,11 +1128,18 @@ chunkproviderserver.func_186027_a(p_73044_1_); @@ -430,7 +452,7 @@ } } } -@@ -1033,9 +1205,15 @@ +@@ -1033,9 +1218,15 @@ } else { @@ -446,7 +468,7 @@ return false; } -@@ -1055,6 +1233,7 @@ +@@ -1055,6 +1246,7 @@ this.field_175729_l.func_76038_a(p_72923_1_.func_145782_y(), p_72923_1_); this.field_175741_N.put(p_72923_1_.func_110124_au(), p_72923_1_); Entity[] aentity = p_72923_1_.func_70021_al(); @@ -454,7 +476,7 @@ if (aentity != null) { -@@ -1139,6 +1318,7 @@ +@@ -1139,6 +1331,7 @@ } this.field_147490_S[this.field_147489_T].add(blockeventdata); @@ -462,7 +484,7 @@ } private void func_147488_Z() -@@ -1150,14 +1330,19 @@ +@@ -1150,14 +1343,19 @@ for (BlockEventData blockeventdata : this.field_147490_S[i]) { @@ -482,7 +504,7 @@ } private boolean func_147485_a(BlockEventData p_147485_1_) -@@ -1299,4 +1484,19 @@ +@@ -1299,4 +1497,19 @@ { } } diff --git a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch index 9524b136..a276e89d 100644 --- a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch +++ b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -1,9 +1,10 @@ --- ../src-base/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java +++ ../src-work/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java -@@ -1,14 +1,14 @@ +@@ -1,14 +1,15 @@ package net.minecraft.world.chunk.storage; -import com.google.common.collect.Maps; ++import carpet.commands.lifetime.spawning.LiteralSpawningReason; +import com.google.common.collect.Maps; //CM unused import import java.io.DataInputStream; import java.io.DataOutputStream; @@ -18,7 +19,7 @@ import javax.annotation.Nullable; import net.minecraft.block.Block; import net.minecraft.entity.Entity; -@@ -34,12 +34,62 @@ +@@ -34,12 +35,62 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -84,7 +85,7 @@ private final DataFixer field_193416_e; private boolean field_183014_e; -@@ -53,7 +103,8 @@ +@@ -53,7 +104,8 @@ public Chunk func_75815_a(World p_75815_1_, int p_75815_2_, int p_75815_3_) throws IOException { ChunkPos chunkpos = new ChunkPos(p_75815_2_, p_75815_3_); @@ -94,7 +95,7 @@ if (nbttagcompound == null) { -@@ -73,7 +124,10 @@ +@@ -73,7 +125,10 @@ public boolean func_191063_a(int p_191063_1_, int p_191063_2_) { ChunkPos chunkpos = new ChunkPos(p_191063_1_, p_191063_2_); @@ -106,7 +107,7 @@ return nbttagcompound != null ? true : RegionFileCache.func_191064_f(this.field_75825_d, p_191063_1_, p_191063_2_); } -@@ -132,37 +186,63 @@ +@@ -132,37 +187,63 @@ protected void func_75824_a(ChunkPos p_75824_1_, NBTTagCompound p_75824_2_) { @@ -182,7 +183,7 @@ try { this.func_183013_b(chunkpos, nbttagcompound); -@@ -171,17 +251,19 @@ +@@ -171,17 +252,19 @@ { field_151505_a.error("Failed to save chunk", (Throwable)exception); } @@ -211,7 +212,7 @@ } private void func_183013_b(ChunkPos p_183013_1_, NBTTagCompound p_183013_2_) throws IOException -@@ -249,7 +331,8 @@ +@@ -249,7 +332,8 @@ }); } @@ -221,7 +222,7 @@ { p_75820_3_.func_74768_a("xPos", p_75820_1_.field_76635_g); p_75820_3_.func_74768_a("zPos", p_75820_1_.field_76647_h); -@@ -295,6 +378,10 @@ +@@ -295,6 +379,10 @@ } p_75820_3_.func_74782_a("Sections", nbttaglist); @@ -232,7 +233,7 @@ p_75820_3_.func_74773_a("Biomes", p_75820_1_.func_76605_m()); p_75820_1_.func_177409_g(false); NBTTagList nbttaglist1 = new NBTTagList(); -@@ -383,6 +470,11 @@ +@@ -383,6 +471,11 @@ chunk.func_76602_a(aextendedblockstorage); @@ -244,3 +245,14 @@ if (p_75823_2_.func_150297_b("Biomes", 7)) { chunk.func_76616_a(p_75823_2_.func_74770_j("Biomes")); +@@ -521,6 +614,10 @@ + + public static void func_186052_a(Entity p_186052_0_, World p_186052_1_) + { ++ // CM lifetime tracker ++ // this method is only used in MobSpawnerBaseLogic ++ p_186052_0_.recordSpawning(LiteralSpawningReason.SPAWNER); ++ + if (p_186052_1_.func_72838_d(p_186052_0_) && p_186052_0_.func_184207_aI()) + { + for (Entity entity : p_186052_0_.func_184188_bt())