diff --git a/common/assets/forge/lang/en_US.lang b/common/assets/forge/lang/en_US.lang new file mode 100644 index 0000000000..2f9368d417 --- /dev/null +++ b/common/assets/forge/lang/en_US.lang @@ -0,0 +1,5 @@ +commands.forge.usage=Use /forge . Subcommands are tps, track +commands.forge.usage.tracking=Use /forge track . Valid types are te (Tile Entities). Duration is < 60. +commands.forge.tps.summary=%s : Mean tick time: %d ms. Mean TPS: %d + +commands.forge.tracking.te.enabled=Tile Entity tracking enabled for %d seconds. \ No newline at end of file diff --git a/common/net/minecraftforge/classloading/FMLForgePlugin.java b/common/net/minecraftforge/classloading/FMLForgePlugin.java index f5bfae55ca..c6a4a84e70 100644 --- a/common/net/minecraftforge/classloading/FMLForgePlugin.java +++ b/common/net/minecraftforge/classloading/FMLForgePlugin.java @@ -1,5 +1,6 @@ package net.minecraftforge.classloading; +import java.io.File; import java.util.Map; import cpw.mods.fml.relauncher.IFMLLoadingPlugin; @@ -7,6 +8,7 @@ import cpw.mods.fml.relauncher.IFMLLoadingPlugin; public class FMLForgePlugin implements IFMLLoadingPlugin { public static boolean RUNTIME_DEOBF = false; + public static File forgeLocation; @Override public String[] getLibraryRequestClass() @@ -39,5 +41,6 @@ public class FMLForgePlugin implements IFMLLoadingPlugin public void injectData(Map data) { RUNTIME_DEOBF = (Boolean)data.get("runtimeDeobfuscationEnabled"); + forgeLocation = (File)data.get("coremodLocation"); } } diff --git a/common/net/minecraftforge/common/ForgeChunkManager.java b/common/net/minecraftforge/common/ForgeChunkManager.java index d5741d8a3a..5b96f96f8e 100644 --- a/common/net/minecraftforge/common/ForgeChunkManager.java +++ b/common/net/minecraftforge/common/ForgeChunkManager.java @@ -22,8 +22,10 @@ import com.google.common.collect.BiMap; import com.google.common.collect.ForwardingSet; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; +import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.LinkedHashMultimap; @@ -34,6 +36,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Multiset; +import com.google.common.collect.Multisets; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import com.google.common.collect.TreeMultiset; diff --git a/common/net/minecraftforge/common/ForgeDummyContainer.java b/common/net/minecraftforge/common/ForgeDummyContainer.java index d3b82ae05a..93deec9b55 100644 --- a/common/net/minecraftforge/common/ForgeDummyContainer.java +++ b/common/net/minecraftforge/common/ForgeDummyContainer.java @@ -10,14 +10,18 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.management.PlayerInstance; import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.WorldInfo; +import net.minecraftforge.classloading.FMLForgePlugin; import net.minecraftforge.common.network.ForgeConnectionHandler; import net.minecraftforge.common.network.ForgeNetworkHandler; import net.minecraftforge.common.network.ForgePacketHandler; import net.minecraftforge.common.network.ForgeTinyPacketHandler; +import net.minecraftforge.server.command.ForgeCommand; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; +import cpw.mods.fml.client.FMLFileResourcePack; +import cpw.mods.fml.client.FMLFolderResourcePack; import cpw.mods.fml.common.DummyModContainer; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.LoadController; @@ -27,6 +31,7 @@ import cpw.mods.fml.common.WorldAccessContainer; import cpw.mods.fml.common.event.FMLConstructionEvent; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.network.FMLNetworkHandler; import cpw.mods.fml.common.network.NetworkMod; @@ -174,6 +179,11 @@ public class ForgeDummyContainer extends DummyModContainer implements WorldAcces ForgeChunkManager.loadConfiguration(); } + @Subscribe + public void serverStarting(FMLServerStartingEvent evt) + { + evt.registerServerCommand(new ForgeCommand(evt.getServer())); + } @Override public NBTTagCompound getDataForWriting(SaveHandler handler, WorldInfo info) { @@ -191,4 +201,22 @@ public class ForgeDummyContainer extends DummyModContainer implements WorldAcces DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null); } } + + @Override + public File getSource() + { + return FMLForgePlugin.forgeLocation; + } + @Override + public Class getCustomResourcePackClass() + { + if (getSource().isDirectory()) + { + return FMLFolderResourcePack.class; + } + else + { + return FMLFileResourcePack.class; + } + } } diff --git a/common/net/minecraftforge/server/ForgeTimeTracker.java b/common/net/minecraftforge/server/ForgeTimeTracker.java new file mode 100644 index 0000000000..e6357b0a7d --- /dev/null +++ b/common/net/minecraftforge/server/ForgeTimeTracker.java @@ -0,0 +1,113 @@ +package net.minecraftforge.server; + +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.MapMaker; + +import net.minecraft.entity.Entity; +import net.minecraft.tileentity.TileEntity; + +public class ForgeTimeTracker { + public static boolean tileEntityTracking; + public static int tileEntityTrackingDuration; + public static long tileEntityTrackingTime; + + private Map tileEntityTimings; + private Map entityTimings; + + private static final ForgeTimeTracker INSTANCE = new ForgeTimeTracker(); + + private WeakReference tile; + private WeakReference entity; + + private long timing; + + private ForgeTimeTracker() + { + MapMaker mm = new MapMaker(); + mm.weakKeys(); + tileEntityTimings = mm.makeMap(); + entityTimings = mm.makeMap(); + } + + + private void trackTileStart(TileEntity tileEntity, long nanoTime) + { + if (tileEntityTrackingTime == 0) + { + tileEntityTrackingTime = nanoTime; + } + else if (tileEntityTrackingTime + tileEntityTrackingDuration < nanoTime) + { + tileEntityTracking = false; + tileEntityTrackingTime = 0; + + return; + } + tile = new WeakReference(tileEntity); + timing = nanoTime; + } + + + private void trackTileEnd(TileEntity tileEntity, long nanoTime) + { + if (tile == null || tile.get() != tileEntity) + { + tile = null; + // race, exit + return; + } + int[] timings = tileEntityTimings.get(tileEntity); + if (timings == null) + { + timings = new int[101]; + tileEntityTimings.put(tileEntity, timings); + } + int idx = timings[100] = (timings[100] + 1) % 100; + timings[idx] = (int) (nanoTime - timing); + } + + public static ImmutableMap getTileTimings() + { + return INSTANCE.buildImmutableTileEntityTimingMap(); + } + + private ImmutableMap buildImmutableTileEntityTimingMap() + { + Builder builder = ImmutableMap.builder(); + for (Entry entry : tileEntityTimings.entrySet()) + { + builder.put(entry.getKey(), Arrays.copyOfRange(entry.getValue(), 0, 100)); + } + return builder.build(); + } + + + public static void trackStart(TileEntity tileEntity) + { + if (!tileEntityTracking) return; + INSTANCE.trackTileStart(tileEntity, System.nanoTime()); + } + + public static void trackEnd(TileEntity tileEntity) + { + if (!tileEntityTracking) return; + INSTANCE.trackTileEnd(tileEntity, System.nanoTime()); + } + + public static void trackStart(Entity par1Entity) + { + + } + + public static void trackEnd(Entity par1Entity) + { + + } + +} diff --git a/common/net/minecraftforge/server/command/ForgeCommand.java b/common/net/minecraftforge/server/command/ForgeCommand.java new file mode 100644 index 0000000000..23e7cd63a0 --- /dev/null +++ b/common/net/minecraftforge/server/command/ForgeCommand.java @@ -0,0 +1,147 @@ +package net.minecraftforge.server.command; + +import java.text.DecimalFormat; +import java.util.List; + +import com.google.common.collect.Multiset; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommand; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatMessageComponent; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.ForgeChunkManager; +import net.minecraftforge.server.ForgeTimeTracker; + +public class ForgeCommand extends CommandBase { + + private MinecraftServer server; + + public ForgeCommand(MinecraftServer server) + { + this.server = server; + } + private static final DecimalFormat timeFormatter = new DecimalFormat("########0.000"); + + @Override + public String getCommandName() + { + return "forge"; + } + + @Override + public String getCommandUsage(ICommandSender icommandsender) + { + return "commands.forge.usage"; + } + + @Override + public int getRequiredPermissionLevel() + { + return 2; + } + @Override + public void processCommand(ICommandSender sender, String[] args) + { + if (args.length == 0) + { + throw new WrongUsageException("commands.forge.usage"); + } + else if ("help".equals(args[0])) + { + throw new WrongUsageException("commands.forge.usage"); + } + else if ("tps".equals(args[0])) + { + displayTPS(sender,args); + } + else if ("tpslog".equals(args[0])) + { + doTPSLog(sender,args); + } + else if ("track".equals(args[0])) + { + handleTracking(sender, args); + } + else + { + throw new WrongUsageException("commands.forge.usage"); + } + } + + private void handleTracking(ICommandSender sender, String[] args) + { + if (args.length != 3) + { + throw new WrongUsageException("commands.forge.usage.tracking"); + } + String type = args[1]; + int duration = parseIntBounded(sender, args[2], 1, 60); + + if ("te".equals(type)) + { + doTurnOnTileEntityTracking(sender, duration); + } + else + { + throw new WrongUsageException("commands.forge.usage.tracking"); + } + } + + private void doTurnOnTileEntityTracking(ICommandSender sender, int duration) + { + ForgeTimeTracker.tileEntityTrackingDuration = duration; + ForgeTimeTracker.tileEntityTracking = true; + sender.sendChatToPlayer(ChatMessageComponent.func_111082_b("commands.forge.tracking.te.enabled", duration)); + } + + private void doTPSLog(ICommandSender sender, String[] args) + { + + } + + private void displayTPS(ICommandSender sender, String[] args) + { + int dim = 0; + boolean summary = true; + if (args.length > 1) + { + dim = parseInt(sender, args[1]); + summary = false; + } + if (summary) + { + for (Integer dimId : DimensionManager.getIDs()) + { + double worldTickTime = ForgeCommand.mean(this.server.worldTickTimes.get(dimId)) * 1.0E-6D; + double worldTPS = Math.min(1000.0/worldTickTime, 20); + sender.sendChatToPlayer(ChatMessageComponent.func_111082_b("commands.forge.tps.summary",String.format("Dim %d", dimId), timeFormatter.format(worldTickTime), timeFormatter.format(worldTPS))); + } + double meanTickTime = ForgeCommand.mean(this.server.tickTimeArray) * 1.0E-6D; + double meanTPS = Math.min(1000.0/meanTickTime, 20); + sender.sendChatToPlayer(ChatMessageComponent.func_111082_b("commands.forge.tps.summary","Overall", timeFormatter.format(meanTickTime), timeFormatter.format(meanTPS))); + } + else + { + double worldTickTime = ForgeCommand.mean(this.server.worldTickTimes.get(dim)) * 1.0E-6D; + double worldTPS = Math.min(1000.0/worldTickTime, 20); + sender.sendChatToPlayer(ChatMessageComponent.func_111082_b("commands.forge.tps.summary",String.format("Dim %d", dim), timeFormatter.format(worldTickTime), timeFormatter.format(worldTPS))); + } + } + + private static long mean(long[] values) + { + long sum = 0l; + for (long v : values) + { + sum+=v; + } + + return sum / values.length; + } +}