Clean up Forge commands. Add HelpCommand. Fix #4444 (#4451)

This commit is contained in:
mezz authored and GitHub committed 2017-10-27 20:28:00 -07:00
1 parent 5733cd364e
commit 0960b60d9c
11 files changed
+749 -282

No files matched your search

@@ -24,11 +24,11 @@ import java.util.Queue;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentBase;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.WorldWorkerManager.IWorker;
@@ -80,11 +80,17 @@ public class ChunkGenWorker implements IWorker
return ret;
}
@Deprecated // TODO remove in 1.13
public TextComponentTranslation getStartMessage()
{
return new TextComponentTranslation("commands.forge.gen.start", total, start.getX(), start.getZ(), dim);
}
public TextComponentBase getStartMessage(ICommandSender sender)
{
return TextComponentHelper.createComponentTranslation(sender, "commands.forge.gen.start", total, start.getX(), start.getZ(), dim);
}
@Override
public boolean hasWork()
{
@@ -105,7 +111,7 @@ public class ChunkGenWorker implements IWorker
world = DimensionManager.getWorld(dim);
if (world == null)
{
listener.sendMessage(new TextComponentTranslation("commands.forge.gen.dim_fail", dim));
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.dim_fail", dim));
queue.clear();
return;
}
@@ -119,7 +125,7 @@ public class ChunkGenWorker implements IWorker
if (++lastNotification >= notificationFrequency)
{
listener.sendMessage(new TextComponentTranslation("commands.forge.gen.progress", total - queue.size(), total));
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.progress", total - queue.size(), total));
lastNotification = 0;
}
@@ -153,7 +159,7 @@ public class ChunkGenWorker implements IWorker
if (queue.size() == 0)
{
listener.sendMessage(new TextComponentTranslation("commands.forge.gen.complete", genned, total, dim));
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.complete", genned, total, dim));
ForgeModContainer.dimensionUnloadQueueDelay = oldUnloadDelay;
}
}
@@ -0,0 +1,192 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.server.command;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
class CommandEntity extends CommandTreeBase
{
public CommandEntity()
{
addSubcommand(new EntityListCommand());
addSubcommand(new CommandTreeHelp(this));
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.entity.usage";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public String getName()
{
return "entity";
}
private static class EntityListCommand extends CommandBase
{
@Override
public String getName()
{
return "list";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.entity.list.usage";
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
String filter = "*";
if (args.length > 0)
{
filter = args[0];
}
final String cleanFilter = filter.replace("?", ".?").replace("*", ".*?");
Set<ResourceLocation> names = EntityList.getEntityNameList().stream().filter(n -> n.toString().matches(cleanFilter)).collect(Collectors.toSet());
if (names.isEmpty())
throw new WrongUsageException("commands.forge.entity.list.invalid");
int dim = args.length > 1 ? parseInt(args[1]) : sender.getEntityWorld().provider.getDimension();
WorldServer world = DimensionManager.getWorld(dim);
if (world == null)
throw new WrongUsageException("commands.forge.entity.list.invalidworld", dim);
Map<ResourceLocation, MutablePair<Integer, Map<ChunkPos, Integer>>> list = Maps.newHashMap();
List<Entity> entities = world.loadedEntityList;
entities.forEach(e -> {
ResourceLocation key = EntityList.getKey(e);
MutablePair<Integer, Map<ChunkPos, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));
ChunkPos chunk = new ChunkPos(e.getPosition());
info.left++;
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
});
if (names.size() == 1)
{
ResourceLocation name = names.iterator().next();
Pair<Integer, Map<ChunkPos, Integer>> info = list.get(name);
if (info == null)
throw new WrongUsageException("commands.forge.entity.list.none");
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.entity.list.single.header", name, info.getLeft()));
List<Map.Entry<ChunkPos, Integer>> toSort = new ArrayList<>();
toSort.addAll(info.getRight().entrySet());
toSort.sort((a, b) -> {
if (Objects.equals(a.getValue(), b.getValue()))
{
return a.getKey().toString().compareTo(b.getKey().toString());
}
else
{
return b.getValue() - a.getValue();
}
});
long limit = 10;
for (Map.Entry<ChunkPos, Integer> e : toSort)
{
if (limit-- == 0) break;
sender.sendMessage(new TextComponentString(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z));
}
}
else
{
List<Pair<ResourceLocation, Integer>> info = new ArrayList<>();
list.forEach((key, value) -> {
if (names.contains(key))
{
Pair<ResourceLocation, Integer> of = Pair.of(key, value.left);
info.add(of);
}
});
info.sort((a, b) -> {
if (Objects.equals(a.getRight(), b.getRight()))
{
return a.getKey().toString().compareTo(b.getKey().toString());
}
else
{
return b.getRight() - a.getRight();
}
});
if (info.size() == 0)
throw new WrongUsageException("commands.forge.entity.list.none");
int count = info.stream().mapToInt(Pair::getRight).sum();
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.entity.list.multiple.header", count));
info.forEach(e -> sender.sendMessage(new TextComponentString(" " + e.getValue() + ": " + e.getKey())));
}
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
{
if (args.length == 1)
{
String[] entityNames = EntityList.getEntityNameList().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new);
return getListOfStringsMatchingLastWord(args, entityNames);
}
return Collections.emptyList();
}
}
}
@@ -0,0 +1,90 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.server.command;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import net.minecraft.command.CommandBase;
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 net.minecraftforge.common.WorldWorkerManager;
class CommandGenerate extends CommandBase
{
@Override
public String getName()
{
return "generate";
}
@Override
public List<String> getAliases()
{
return Collections.singletonList("gen");
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.gen.usage";
}
@Override
public int getRequiredPermissionLevel()
{
return 4;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
// x y z chunkCount [dim] [interval]
if (args.length < 4)
{
throw new WrongUsageException("commands.forge.gen.usage");
}
BlockPos blockpos = parseBlockPos(sender, args, 1, false);
int count = parseInt(args[3], 10);
int dim = args.length >= 5 ? parseInt(args[4]) : sender.getEntityWorld().provider.getDimension();
int interval = args.length >= 6 ? parseInt(args[5]) : -1;
BlockPos chunkpos = new BlockPos(blockpos.getX() >> 4, 0, blockpos.getZ() >> 4);
ChunkGenWorker worker = new ChunkGenWorker(sender, chunkpos, count, dim, interval);
sender.sendMessage(worker.getStartMessage(sender));
WorldWorkerManager.addWorker(worker);
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
{
if (args.length < 4)
{
return getTabCompletionCoordinate(args, 0, targetPos);
}
// Chunk Count? No completion
// Dimension, Add support for names? Get list of ids? Meh
return Collections.emptyList();
}
}
@@ -0,0 +1,111 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.server.command;
import java.text.DecimalFormat;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.DimensionType;
import net.minecraftforge.common.DimensionManager;
class CommandTps extends CommandBase
{
private static final DecimalFormat TIME_FORMATTER = new DecimalFormat("########0.000");
@Override
public String getName()
{
return "tps";
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.tps.usage";
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
{
return true;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
int dim = 0;
boolean summary = true;
if (args.length > 0)
{
dim = parseInt(args[0]);
summary = false;
}
if (summary)
{
for (Integer dimId : DimensionManager.getIDs())
{
double worldTickTime = mean(server.worldTickTimes.get(dimId)) * 1.0E-6D;
double worldTPS = Math.min(1000.0/worldTickTime, 20);
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tps.summary", getDimensionPrefix(dimId), TIME_FORMATTER.format(worldTickTime), TIME_FORMATTER.format(worldTPS)));
}
double meanTickTime = mean(server.tickTimeArray) * 1.0E-6D;
double meanTPS = Math.min(1000.0/meanTickTime, 20);
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tps.summary","Overall", TIME_FORMATTER.format(meanTickTime), TIME_FORMATTER.format(meanTPS)));
}
else
{
double worldTickTime = mean(server.worldTickTimes.get(dim)) * 1.0E-6D;
double worldTPS = Math.min(1000.0/worldTickTime, 20);
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tps.summary", getDimensionPrefix(dim), TIME_FORMATTER.format(worldTickTime), TIME_FORMATTER.format(worldTPS)));
}
}
private static String getDimensionPrefix(int dimId)
{
DimensionType providerType = DimensionManager.getProviderType(dimId);
if (providerType == null)
{
return String.format("Dim %2d", dimId);
}
else
{
return String.format("Dim %2d (%s)", dimId, providerType.getName());
}
}
private static long mean(long[] values)
{
long sum = 0L;
for (long v : values)
{
sum += v;
}
return sum / values.length;
}
}
@@ -0,0 +1,84 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.server.command;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import net.minecraft.command.CommandBase;
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 net.minecraftforge.server.ForgeTimeTracker;
class CommandTrack extends CommandBase
{
@Override
public String getName()
{
return "track";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.usage.tracking";
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
if (args.length != 2)
{
throw new WrongUsageException("commands.forge.usage.tracking");
}
String type = args[0];
int duration = parseInt(args[1], 1, 60);
if ("te".equals(type))
{
ForgeTimeTracker.tileEntityTrackingDuration = duration;
ForgeTimeTracker.tileEntityTracking = true;
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.te.enabled", duration));
}
else
{
throw new WrongUsageException("commands.forge.usage.tracking");
}
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
{
if (args.length == 1)
{
return Collections.singletonList("te");
}
return Collections.emptyList();
}
}
@@ -25,9 +25,7 @@ import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentString;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
@@ -44,16 +42,32 @@ import java.util.Map;
*/
public abstract class CommandTreeBase extends CommandBase
{
private final Map<String, ICommand> commandMap = new HashMap<String, ICommand>();
private final Map<String, ICommand> commandMap = new HashMap<>();
private final Map<String, ICommand> commandAliasMap = new HashMap<>();
public void addSubcommand(ICommand c)
public void addSubcommand(ICommand command)
{
commandMap.put(c.getName(), c);
commandMap.put(command.getName(), command);
for (String alias : command.getAliases())
{
commandAliasMap.put(alias, command);
}
}
public Collection<ICommand> getSubCommands()
{
return getCommandMap().values();
return commandMap.values();
}
@Nullable
public ICommand getSubCommand(String command)
{
ICommand cmd = commandMap.get(command);
if (cmd != null)
{
return cmd;
}
return commandAliasMap.get(command);
}
public Map<String, ICommand> getCommandMap()
@@ -63,12 +77,12 @@ public abstract class CommandTreeBase extends CommandBase
public List<ICommand> getSortedCommandList()
{
List<ICommand> list = new ArrayList<ICommand>(getSubCommands());
List<ICommand> list = new ArrayList<>(getSubCommands());
Collections.sort(list);
return list;
}
private static String[] shiftArgs(String[] s)
private static String[] shiftArgs(@Nullable String[] s)
{
if(s == null || s.length == 0)
{
@@ -81,14 +95,13 @@ public abstract class CommandTreeBase extends CommandBase
}
@Override
@Nonnull
public List<String> getTabCompletions(@Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args, @Nullable BlockPos pos)
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos pos)
{
if(args.length == 1)
{
List<String> keys = new ArrayList<String>();
List<String> keys = new ArrayList<>();
for(ICommand c : getCommandMap().values())
for (ICommand c : getSubCommands())
{
if(c.checkPermission(server, sender))
{
@@ -96,11 +109,11 @@ public abstract class CommandTreeBase extends CommandBase
}
}
Collections.sort(keys, null);
keys.sort(null);
return getListOfStringsMatchingLastWord(args, keys);
}
ICommand cmd = getCommandMap().get(args[0]);
ICommand cmd = getSubCommand(args[0]);
if(cmd != null)
{
@@ -111,12 +124,12 @@ public abstract class CommandTreeBase extends CommandBase
}
@Override
public boolean isUsernameIndex(@Nonnull String[] args, int index)
public boolean isUsernameIndex(String[] args, int index)
{
if(index > 0 && args.length > 1)
if (index > 0 && args.length > 1)
{
ICommand cmd = getCommandMap().get(args[0]);
if(cmd != null)
ICommand cmd = getSubCommand(args[0]);
if (cmd != null)
{
return cmd.isUsernameIndex(shiftArgs(args), index - 1);
}
@@ -126,19 +139,21 @@ public abstract class CommandTreeBase extends CommandBase
}
@Override
public void execute(@Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args) throws CommandException
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
if(args.length < 1)
if (args.length < 1)
{
sender.sendMessage(new TextComponentString(CommandBase.joinNiceStringFromCollection(commandMap.keySet())));
String subCommandsString = getAvailableSubCommandsString(server, sender);
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.tree_base.available_subcommands", subCommandsString));
}
else
{
ICommand cmd = getCommandMap().get(args[0]);
ICommand cmd = getSubCommand(args[0]);
if(cmd == null)
{
throw new CommandException("commands.tree_base.invalid_cmd", args[0]);
String subCommandsString = getAvailableSubCommandsString(server, sender);
throw new CommandException("commands.tree_base.invalid_cmd.list_subcommands", args[0], subCommandsString);
}
else if(!cmd.checkPermission(server, sender))
{
@@ -150,4 +165,17 @@ public abstract class CommandTreeBase extends CommandBase
}
}
}
private String getAvailableSubCommandsString(MinecraftServer server, ICommandSender sender)
{
Collection<String> availableCommands = new ArrayList<>();
for (ICommand command : getSubCommands())
{
if (command.checkPermission(server, sender))
{
availableCommands.add(command.getName());
}
}
return CommandBase.joinNiceStringFromCollection(availableCommands);
}
}
@@ -0,0 +1,121 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.server.command;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
/**
* Add help for parent and all its children.
* Must be added to parent after all other commands.
*/
public class CommandTreeHelp extends CommandTreeBase
{
private final ICommand parent;
public CommandTreeHelp(CommandTreeBase parent)
{
this.parent = parent;
for (ICommand command : parent.getSubCommands())
{
addSubcommand(new HelpSubCommand(this, command));
}
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public String getName()
{
return "help";
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.usage.help";
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
if (args.length == 0)
{
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, parent.getUsage(sender)));
for (ICommand subCommand : getSubCommands())
{
if (subCommand instanceof HelpSubCommand && subCommand.checkPermission(server, sender))
{
subCommand.execute(server, sender, args);
}
}
return;
}
super.execute(server, sender, args);
}
public static class HelpSubCommand extends CommandBase
{
private final CommandTreeHelp parent;
private final ICommand command;
public HelpSubCommand(CommandTreeHelp parent, ICommand command)
{
this.parent = parent;
this.command = command;
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public String getName()
{
return command.getName();
}
@Override
public String getUsage(ICommandSender sender)
{
return command.getUsage(sender);
}
@Override
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
{
return command.checkPermission(server, sender);
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, command.getUsage(sender)));
}
}
}
@@ -19,41 +19,21 @@
package net.minecraftforge.server.command;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import net.minecraft.command.ICommand;
import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Maps;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.WorldWorkerManager;
import net.minecraftforge.server.ForgeTimeTracker;
public class ForgeCommand extends CommandBase {
private static final DecimalFormat timeFormatter = new DecimalFormat("########0.000");
public class ForgeCommand extends CommandTreeBase
{
public ForgeCommand()
{
super.addSubcommand(new CommandTps());
super.addSubcommand(new CommandTrack());
super.addSubcommand(new CommandGenerate());
super.addSubcommand(new CommandEntity());
super.addSubcommand(new CommandTreeHelp(this));
}
@Override
public String getName()
@@ -62,239 +42,26 @@ public class ForgeCommand extends CommandBase {
}
@Override
public String getUsage(ICommandSender icommandsender)
public void addSubcommand(ICommand command)
{
return "commands.forge.usage";
throw new UnsupportedOperationException("Don't add sub-commands to /forge, create your own command.");
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
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(server, sender, args);
}
/*else if ("tpslog".equals(args[0]))
{
doTPSLog(server, sender, args);
}
*/
else if ("track".equals(args[0]))
{
handleTracking(server, sender, args);
}
else if ("gen".equals(args[0]))
{
handleGen(server, sender, args);
}
else if ("entity".equals(args[0]))
{
handleEntity(server, sender, args);
}
else
{
throw new WrongUsageException("commands.forge.usage");
}
return 0;
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
{
if (args.length <= 1)
return getListOfStringsMatchingLastWord(args, "help", "tps", "track", "gen", "entity");
switch (args[0].toLowerCase(Locale.ENGLISH))
{
case "gen":
if (args.length > 1 && args.length <= 4)
return getTabCompletionCoordinate(args, 1, targetPos);
if (args.length == 5) //Chunk Count? No completion
return Collections.emptyList();
if (args.length == 6) // Dimension, Add support for names? Get list of ids? Meh
return Collections.emptyList();
break;
case "entity":
if (args.length == 2)
return getListOfStringsMatchingLastWord(args, "help", "list");
if (args.length == 3)
return getListOfStringsMatchingLastWord(args,EntityList.getEntityNameList().stream().map(e -> e.toString()).sorted().toArray(String[]::new));
break;
}
return Collections.emptyList();
return true;
}
private void handleTracking(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
@Override
public String getUsage(ICommandSender icommandsender)
{
if (args.length != 3)
{
throw new WrongUsageException("commands.forge.usage.tracking");
}
String type = args[1];
int duration = parseInt(args[2], 1, 60);
if ("te".equals(type))
{
doTurnOnTileEntityTracking(server, sender, duration);
}
else
{
throw new WrongUsageException("commands.forge.usage.tracking");
}
}
private void doTurnOnTileEntityTracking(MinecraftServer server, ICommandSender sender, int duration)
{
ForgeTimeTracker.tileEntityTrackingDuration = duration;
ForgeTimeTracker.tileEntityTracking = true;
sender.sendMessage(new TextComponentTranslation("commands.forge.tracking.te.enabled", duration));
}
private void doTPSLog(MinecraftServer server, ICommandSender sender, String[] args)
{
}
private void displayTPS(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
int dim = 0;
boolean summary = true;
if (args.length > 1)
{
dim = parseInt(args[1]);
summary = false;
}
if (summary)
{
for (Integer dimId : DimensionManager.getIDs())
{
double worldTickTime = ForgeCommand.mean(server.worldTickTimes.get(dimId)) * 1.0E-6D;
double worldTPS = Math.min(1000.0/worldTickTime, 20);
sender.sendMessage(new TextComponentTranslation("commands.forge.tps.summary",String.format("Dim %d", dimId), timeFormatter.format(worldTickTime), timeFormatter.format(worldTPS)));
}
double meanTickTime = ForgeCommand.mean(server.tickTimeArray) * 1.0E-6D;
double meanTPS = Math.min(1000.0/meanTickTime, 20);
sender.sendMessage(new TextComponentTranslation("commands.forge.tps.summary","Overall", timeFormatter.format(meanTickTime), timeFormatter.format(meanTPS)));
}
else
{
double worldTickTime = ForgeCommand.mean(server.worldTickTimes.get(dim)) * 1.0E-6D;
double worldTPS = Math.min(1000.0/worldTickTime, 20);
sender.sendMessage(new TextComponentTranslation("commands.forge.tps.summary",String.format("Dim %d", dim), timeFormatter.format(worldTickTime), timeFormatter.format(worldTPS)));
}
}
private void handleGen(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
// gen x y z chunkCount [dim] [shape?]
if (args.length < 5)
throw new WrongUsageException("commands.forge.gen.usage");
BlockPos blockpos = parseBlockPos(sender, args, 1, false);
int count = parseInt(args[4], 10);
int dim = args.length >= 6 ? parseInt(args[5]) : sender.getEntityWorld().provider.getDimension();
int interval = args.length >= 7 ? parseInt(args[6]) : -1;
BlockPos chunkpos = new BlockPos(blockpos.getX() >> 4, 0, blockpos.getZ() >> 4);
ChunkGenWorker worker = new ChunkGenWorker(sender, chunkpos, count, dim, interval);
sender.sendMessage(worker.getStartMessage());
WorldWorkerManager.addWorker(worker);
}
private static long mean(long[] values)
{
long sum = 0l;
for (long v : values)
{
sum+=v;
}
return sum / values.length;
}
private void handleEntity(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help"))
throw new WrongUsageException("commands.forge.entity.usage");
switch (args[1].toLowerCase(Locale.ENGLISH))
{
case "list":
String filter = "*";
if (args.length > 2)
{
if (args[2].toLowerCase(Locale.ENGLISH).equals("help"))
throw new WrongUsageException("commands.forge.entity.list.usage");
filter = args[2];
}
final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
Set<ResourceLocation> names = EntityList.getEntityNameList().stream().filter(n -> n.toString().matches(cleanfilter)).collect(Collectors.toSet());
if (names.isEmpty())
throw new WrongUsageException("commands.forge.entity.list.invalid");
int dim = args.length > 3 ? parseInt(args[3]) : sender.getEntityWorld().provider.getDimension();
Map<ResourceLocation, MutablePair<Integer, Map<ChunkPos, Integer>>> list = Maps.newHashMap();
WorldServer world = DimensionManager.getWorld(dim);
if (world == null)
throw new WrongUsageException("commands.forge.entity.list.invalidworld", dim);
List<Entity> entities = world.getLoadedEntityList();
entities.forEach(e -> {
ResourceLocation key = EntityList.getKey(e);
MutablePair<Integer, Map<ChunkPos, Integer>> info = list.get(key);
if (info == null)
{
info = MutablePair.of(0, Maps.newHashMap());
list.put(key, info);
}
ChunkPos chunk = new ChunkPos(e.getPosition());
info.left++;
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
});
if (names.size() == 1)
{
ResourceLocation name = names.iterator().next();
Pair<Integer, Map<ChunkPos, Integer>> info = list.get(name);
if (info == null)
throw new WrongUsageException("commands.forge.entity.list.none");
sender.sendMessage(new TextComponentTranslation("commands.forge.entity.list.single.header", name, info.getLeft()));
info.getRight().entrySet().stream()
.sorted((a,b) -> a.getValue() != b.getValue() ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString()))
.limit(10).forEach(e -> sender.sendMessage(new TextComponentString(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z)));
}
else
{
List<Pair<ResourceLocation, Integer>> info = list.entrySet().stream()
.filter(e -> names.contains(e.getKey()))
.map(e -> Pair.of(e.getKey(), e.getValue().left))
.sorted((a, b) -> a.getRight() != b.getRight() ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString()))
.collect(Collectors.toList());
if (info == null || info.size() == 0)
throw new WrongUsageException("commands.forge.entity.list.none");
int count = info.stream().mapToInt(a -> a.getRight()).sum();
sender.sendMessage(new TextComponentTranslation("commands.forge.entity.list.multiple.header", count));
info.forEach(e -> sender.sendMessage(new TextComponentString(" " + e.getValue() + ": " + e.getKey())));
}
break;
}
return "commands.forge.usage";
}
}
@@ -0,0 +1,57 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.server.command;
import io.netty.channel.Channel;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.text.TextComponentBase;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.translation.I18n;
import net.minecraftforge.fml.common.network.NetworkRegistry;
public class TextComponentHelper
{
private TextComponentHelper() {}
/**
* Detects when sending to a vanilla client and falls back to sending english,
* since they don't have the lang data necessary to translate on the client.
*/
public static TextComponentBase createComponentTranslation(ICommandSender sender, final String translation, final Object... args)
{
if (isVanillaClient(sender))
{
return new TextComponentString(I18n.translateToLocalFormatted(translation, args));
}
return new TextComponentTranslation(translation, args);
}
private static boolean isVanillaClient(ICommandSender sender)
{
if (sender instanceof EntityPlayerMP)
{
EntityPlayerMP playerMP = (EntityPlayerMP) sender;
Channel channel = playerMP.connection.netManager.channel();
return !channel.attr(NetworkRegistry.FML_MARKER).get();
}
return false;
}
}
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.server.command;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;
@@ -1,12 +1,14 @@
commands.forge.usage=Use /forge <subcommand>. Subcommands are tps, track, gen
commands.forge.usage.tracking=Use /forge track <type> <duration>. Valid types are te (Tile Entities). Duration is < 60.
commands.forge.usage=Use /forge <subcommand>
commands.forge.usage.tracking=Use /forge track <type> <duration>. Valid types are "te" (Tile Entities). Duration in seconds must be <= 60.
commands.forge.usage.help=Use help [command] to view usages for that command.
commands.forge.tps.summary=%s : Mean tick time: %d ms. Mean TPS: %d
commands.forge.tps.usage=Use /forge tps [dimension]
commands.forge.gen.usage=Use /forge gen <x> <y> <z> <chunkCount> [dimension] [interval]
commands.forge.gen.dim_fail=Failed to load world for dimension %d, Task terminated.
commands.forge.gen.progress=Generation Progress: %d/%d
commands.forge.gen.complete=Finished generating %d new chunks (out of %d) for dimension %d.
commands.forge.gen.start=Starting to generate %d chunks in a spiral around %d, %d in dimension %d.
commands.forge.entity.usage=Use /forge entity [list] help for more infomration on a specific command.
commands.forge.entity.usage=Use /forge entity help for more information on entity subcommands.
commands.forge.entity.list.usage=Use /forge entity list [filter] [dim] to get entity info that matches the optional filter.
commands.forge.entity.list.invalid=Invalid filter, does not match any entities. Use /forge entity list for a proper list
commands.forge.entity.list.invalidworld=Could not load world for dimension %d. Please select a valid dimension.
@@ -16,6 +18,8 @@ commands.forge.entity.list.multiple.header=Total: %d
commands.forge.tracking.te.enabled=Tile Entity tracking enabled for %d seconds.
commands.tree_base.invalid_cmd=Invalid subcommand '%s'!
commands.tree_base.invalid_cmd.list_subcommands=Invalid subcommand '%s'! Available subcommands: %s
commands.tree_base.available_subcommands=Available SubCommands: %s
forge.texture.preload.warning=Warning: Texture %s not preloaded, will cause render glitches!
forge.client.shutdown.internal=Shutting down internal server...
forge.update.newversion=New Forge version available: %s