base mod created
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraftforge.server.timings.TimeTracker;
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see net.minecraftforge.server.timings.TimeTracker
|
||||
*/
|
||||
@Deprecated
|
||||
public class ForgeTimeTracker {
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see net.minecraftforge.server.timings.TimeTracker
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean tileEntityTracking;
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see net.minecraftforge.server.timings.TimeTracker
|
||||
*/
|
||||
@Deprecated
|
||||
public static int tileEntityTrackingDuration;
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see net.minecraftforge.server.timings.TimeTracker
|
||||
*/
|
||||
@Deprecated
|
||||
public static long tileEntityTrackingTime;
|
||||
|
||||
private Map<TileEntity,int[]> tileEntityTimings;
|
||||
private WeakReference<TileEntity> tile;
|
||||
|
||||
private static final ForgeTimeTracker INSTANCE = new ForgeTimeTracker();
|
||||
|
||||
/* not implemented
|
||||
private WeakReference<Entity> entity;
|
||||
private Map<Entity,int[]> entityTimings;
|
||||
*/
|
||||
|
||||
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>(tileEntity);
|
||||
timing = nanoTime;
|
||||
}
|
||||
|
||||
|
||||
private void trackTileEnd(TileEntity tileEntity, long nanoTime)
|
||||
{
|
||||
if (tile == null || tile.get() != tileEntity)
|
||||
{
|
||||
tile = null;
|
||||
// race, exit
|
||||
return;
|
||||
}
|
||||
int[] timings = tileEntityTimings.computeIfAbsent(tileEntity, k -> new int[101]);
|
||||
int idx = timings[100] = (timings[100] + 1) % 100;
|
||||
timings[idx] = (int) (nanoTime - timing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see TimeTracker#getTimingData()
|
||||
*/
|
||||
@Deprecated
|
||||
public static ImmutableMap<TileEntity,int[]> getTileTimings()
|
||||
{
|
||||
return INSTANCE.buildImmutableTileEntityTimingMap();
|
||||
}
|
||||
|
||||
private ImmutableMap<TileEntity, int[]> buildImmutableTileEntityTimingMap()
|
||||
{
|
||||
ImmutableMap.Builder<TileEntity, int[]> builder = new ImmutableMap.Builder<>();
|
||||
TimeTracker.TILE_ENTITY_UPDATE.getTimingData().stream()
|
||||
.filter(t -> t.getObject().get() != null).forEach(e -> builder.put(e.getObject().get(), e.getRawTimingData()));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see TimeTracker#trackStart(Object)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void trackStart(TileEntity tileEntity)
|
||||
{
|
||||
TimeTracker.TILE_ENTITY_UPDATE.trackStart(tileEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see TimeTracker#trackEnd(Object)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void trackEnd(TileEntity tileEntity)
|
||||
{
|
||||
TimeTracker.TILE_ENTITY_UPDATE.trackEnd(tileEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see TimeTracker#trackStart(Object)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void trackStart(Entity par1Entity)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in 1.13 - Implementation has been moved
|
||||
* @see TimeTracker#trackEnd(Object)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void trackEnd(Entity par1Entity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
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.MinecraftException;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
public class ChunkGenWorker implements IWorker
|
||||
{
|
||||
private final ICommandSender listener;
|
||||
protected final BlockPos start;
|
||||
protected final int total;
|
||||
private final int dim;
|
||||
private final Queue<BlockPos> queue;
|
||||
private final int notificationFrequency;
|
||||
private int lastNotification = 0;
|
||||
private long lastNotifcationTime = 0;
|
||||
private int genned = 0;
|
||||
private Boolean keepingLoaded;
|
||||
|
||||
public ChunkGenWorker(ICommandSender listener, BlockPos start, int total, int dim, int interval)
|
||||
{
|
||||
this.listener = listener;
|
||||
this.start = start;
|
||||
this.total = total;
|
||||
this.dim = dim;
|
||||
this.queue = buildQueue();
|
||||
this.notificationFrequency = interval != -1 ? interval : Math.max(total / 20, 100); //Every 5% or every 100, whichever is more.
|
||||
this.lastNotifcationTime = System.currentTimeMillis(); //We also notify at least once every 60 seconds, to show we haven't froze.
|
||||
}
|
||||
|
||||
protected Queue<BlockPos> buildQueue()
|
||||
{
|
||||
Queue<BlockPos> ret = new ArrayDeque<BlockPos>();
|
||||
ret.add(start);
|
||||
|
||||
//This *should* spiral outwards, starting on right side, down, left, up, right, but hey we'll see!
|
||||
int radius = 1;
|
||||
while (ret.size() < total)
|
||||
{
|
||||
for (int q = -radius + 1; q <= radius && ret.size() < total; q++)
|
||||
ret.add(start.add(radius, 0, q));
|
||||
|
||||
for (int q = radius - 1; q >= -radius && ret.size() < total; q--)
|
||||
ret.add(start.add(q, 0, radius));
|
||||
|
||||
for (int q = radius - 1; q >= -radius && ret.size() < total; q--)
|
||||
ret.add(start.add(-radius, 0, q));
|
||||
|
||||
for (int q = -radius + 1; q <= radius && ret.size() < total; q++)
|
||||
ret.add(start.add(q, 0, -radius));
|
||||
|
||||
radius++;
|
||||
}
|
||||
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()
|
||||
{
|
||||
return queue.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
WorldServer world = DimensionManager.getWorld(dim);
|
||||
if (world == null)
|
||||
{
|
||||
DimensionManager.initDimension(dim);
|
||||
world = DimensionManager.getWorld(dim);
|
||||
if (world == null)
|
||||
{
|
||||
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.dim_fail", dim));
|
||||
queue.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AnvilChunkLoader loader = world.getChunkProvider().chunkLoader instanceof AnvilChunkLoader ? (AnvilChunkLoader)world.getChunkProvider().chunkLoader : null;
|
||||
if (loader != null && loader.getPendingSaveCount() > 100)
|
||||
{
|
||||
|
||||
if (lastNotifcationTime < System.currentTimeMillis() - 10*1000)
|
||||
{
|
||||
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.progress", total - queue.size(), total));
|
||||
lastNotifcationTime = System.currentTimeMillis();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockPos next = queue.poll();
|
||||
|
||||
if (next != null)
|
||||
{
|
||||
// While we work we don't want to cause world load spam so pause unloading the world.
|
||||
if (keepingLoaded == null)
|
||||
{
|
||||
keepingLoaded = DimensionManager.keepDimensionLoaded(dim, true);
|
||||
}
|
||||
|
||||
if (++lastNotification >= notificationFrequency || lastNotifcationTime < System.currentTimeMillis() - 60*1000)
|
||||
{
|
||||
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.progress", total - queue.size(), total));
|
||||
lastNotification = 0;
|
||||
lastNotifcationTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
int x = next.getX();
|
||||
int z = next.getZ();
|
||||
|
||||
Chunk target = world.getChunkFromChunkCoords(x, z);
|
||||
Chunk[] chunks = { target };
|
||||
|
||||
if (!target.isTerrainPopulated())
|
||||
{
|
||||
// In order for a chunk to populate, The chunks around its bottom right corner need to be loaded.
|
||||
// So lets load those chunks, but this needs to be done in a certain order to make this trigger.
|
||||
// So this does load more chunks then it should, and is a hack, but lets go!.
|
||||
chunks = new Chunk[] {
|
||||
target,
|
||||
world.getChunkFromChunkCoords(x + 1, z),
|
||||
world.getChunkFromChunkCoords(x + 1, z + 1),
|
||||
world.getChunkFromChunkCoords(x, z + 1),
|
||||
};
|
||||
try
|
||||
{
|
||||
world.getChunkProvider().chunkLoader.saveChunk(world, target);
|
||||
}
|
||||
catch (IOException | MinecraftException e)
|
||||
{
|
||||
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.saveerror", e.getMessage()));
|
||||
}
|
||||
genned++;
|
||||
}
|
||||
|
||||
for (Chunk chunk : chunks) //Now lets unload them. Note: Saving is done off thread so there may be cache hits, but this should still unload everything.
|
||||
{
|
||||
PlayerChunkMapEntry watchers = world.getPlayerChunkMap().getEntry(chunk.x, chunk.z);
|
||||
if (watchers == null) //If there are no players watching this, this will be null, so we can unload.
|
||||
world.getChunkProvider().queueUnload(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.size() == 0)
|
||||
{
|
||||
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.complete", genned, total, dim));
|
||||
if (keepingLoaded != null && keepingLoaded)
|
||||
{
|
||||
DimensionManager.keepDimensionLoaded(dim, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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 it.unimi.dsi.fastutil.ints.IntSortedSet;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandDimensions extends CommandBase
|
||||
{
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "dimensions";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.dimensions.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given ICommandSender has permission to execute this command
|
||||
*/
|
||||
@Override
|
||||
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.dimensions.list"));
|
||||
for (Map.Entry<DimensionType, IntSortedSet> entry : DimensionManager.getRegisteredDimensions().entrySet())
|
||||
{
|
||||
sender.sendMessage(new TextComponentString(entry.getKey().getName() + ": " + entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.entity.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "entity";
|
||||
}
|
||||
|
||||
private static class EntityListCommand extends CommandBase
|
||||
{
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "list";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.entity.list.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@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())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of options for when the user presses the TAB key
|
||||
*/
|
||||
@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,109 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "generate";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of aliases for this command. <b>Never return null!</b>
|
||||
*/
|
||||
@Override
|
||||
public List<String> getAliases()
|
||||
{
|
||||
return Collections.singletonList("gen");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.gen.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@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, 0, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of options for when the user presses the TAB key
|
||||
*/
|
||||
@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,138 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.ICommandSender;
|
||||
import net.minecraft.command.WrongUsageException;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.common.util.ITeleporter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandSetDimension extends CommandBase
|
||||
{
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "setdimension";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of aliases for this command. <b>Never return null!</b>
|
||||
*/
|
||||
@Override
|
||||
public List<String> getAliases()
|
||||
{
|
||||
return Collections.singletonList("setdim");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.setdim.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of options for when the user presses the TAB key
|
||||
*/
|
||||
@Override
|
||||
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
|
||||
{
|
||||
if (args.length > 2 && args.length <= 5)
|
||||
{
|
||||
return getTabCompletionCoordinate(args, 2, targetPos);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
// args: <entity> <dim> [<x> <y> <z>]
|
||||
if (args.length != 2 && args.length != 5)
|
||||
{
|
||||
throw new WrongUsageException("commands.forge.setdim.usage");
|
||||
}
|
||||
Entity entity = getEntity(server, sender, args[0]);
|
||||
if (!checkEntity(entity))
|
||||
{
|
||||
throw new CommandException("commands.forge.setdim.invalid.entity", entity.getName());
|
||||
}
|
||||
int dimension = parseInt(args[1]);
|
||||
if (!DimensionManager.isDimensionRegistered(dimension))
|
||||
{
|
||||
throw new CommandException("commands.forge.setdim.invalid.dim", dimension);
|
||||
}
|
||||
if (dimension == entity.dimension)
|
||||
{
|
||||
throw new CommandException("commands.forge.setdim.invalid.nochange", entity.getName(), dimension);
|
||||
}
|
||||
BlockPos pos = args.length == 5 ? parseBlockPos(sender, args, 2, false) : sender.getPosition();
|
||||
entity.changeDimension(dimension, new CommandTeleporter(pos));
|
||||
}
|
||||
|
||||
private static boolean checkEntity(Entity entity)
|
||||
{
|
||||
// use vanilla portal logic, try to avoid doing anything too silly
|
||||
return !entity.isRiding() && !entity.isBeingRidden() && entity.isNonBoss();
|
||||
}
|
||||
|
||||
private static class CommandTeleporter implements ITeleporter
|
||||
{
|
||||
private final BlockPos targetPos;
|
||||
|
||||
private CommandTeleporter(BlockPos targetPos)
|
||||
{
|
||||
this.targetPos = targetPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeEntity(World world, Entity entity, float yaw)
|
||||
{
|
||||
entity.moveToBlockPosAndAngles(targetPos, yaw, entity.rotationPitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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");
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "tps";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.tps.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given ICommandSender has permission to execute this command
|
||||
*/
|
||||
@Override
|
||||
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@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,413 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
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.entity.Entity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.server.timings.ForgeTimings;
|
||||
import net.minecraftforge.server.timings.TimeTracker;
|
||||
|
||||
class CommandTrack extends CommandTreeBase
|
||||
{
|
||||
|
||||
private static final DecimalFormat TIME_FORMAT = new DecimalFormat("#####0.00");
|
||||
|
||||
public CommandTrack()
|
||||
{
|
||||
addSubcommand(new StartTrackingCommand());
|
||||
addSubcommand(new ResetTrackingCommand());
|
||||
addSubcommand(new TrackResultsTileEntity());
|
||||
addSubcommand(new TrackResultsEntity());
|
||||
addSubcommand(new CommandTreeHelp(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "track";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.tracking.usage";
|
||||
}
|
||||
|
||||
private static class StartTrackingCommand extends CommandBase
|
||||
{
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
if (args.length != 2)
|
||||
{
|
||||
throw new WrongUsageException(getUsage(sender));
|
||||
}
|
||||
String type = args[0];
|
||||
int duration = parseInt(args[1], 1, 60);
|
||||
if ("te".equals(type))
|
||||
{
|
||||
TimeTracker.TILE_ENTITY_UPDATE.reset();
|
||||
TimeTracker.TILE_ENTITY_UPDATE.enable(duration);
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.te.enabled", duration));
|
||||
}
|
||||
else if ("entity".equals(type))
|
||||
{
|
||||
TimeTracker.ENTITY_UPDATE.reset();
|
||||
TimeTracker.ENTITY_UPDATE.enable(duration);
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.entity.enabled", duration));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WrongUsageException(getUsage(sender));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of options for when the user presses the TAB key
|
||||
*/
|
||||
@Override
|
||||
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
|
||||
{
|
||||
return Arrays.asList("te", "entity");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "start";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.tracking.start.usage";
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResetTrackingCommand extends CommandBase
|
||||
{
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "reset";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.tracking.reset.usage";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
if (args.length != 1)
|
||||
{
|
||||
throw new WrongUsageException(getUsage(sender));
|
||||
}
|
||||
String type = args[0];
|
||||
if ("te".equals(type))
|
||||
{
|
||||
TimeTracker.TILE_ENTITY_UPDATE.reset();
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.reset"));
|
||||
}
|
||||
else if ("entity".equals(type))
|
||||
{
|
||||
TimeTracker.ENTITY_UPDATE.reset();
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.reset"));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WrongUsageException(getUsage(sender));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of options for when the user presses the TAB key
|
||||
*/
|
||||
@Override
|
||||
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
|
||||
{
|
||||
return Arrays.asList("te", "entity");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A base command for all the tracking results commands
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static abstract class TrackResultsBaseCommand<T> extends CommandBase
|
||||
{
|
||||
|
||||
private TimeTracker<T> tracker;
|
||||
|
||||
protected TrackResultsBaseCommand(TimeTracker<T> tracker)
|
||||
{
|
||||
this.tracker = tracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time objects recorded by the time tracker sorted by average time
|
||||
*
|
||||
* @return A list of time objects
|
||||
*/
|
||||
protected List<ForgeTimings<T>> getSortedTimings()
|
||||
{
|
||||
ArrayList<ForgeTimings<T>> list = new ArrayList<>();
|
||||
|
||||
list.addAll(tracker.getTimingData());
|
||||
list.sort(Comparator.comparingDouble(ForgeTimings::getAverageTimings));
|
||||
Collections.reverse(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
List<ForgeTimings<T>> timingsList = getSortedTimings();
|
||||
if (timingsList.isEmpty())
|
||||
{
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.noData"));
|
||||
}
|
||||
else
|
||||
{
|
||||
timingsList.stream()
|
||||
.filter(timings -> timings.getObject().get() != null)
|
||||
.limit(10)
|
||||
.forEach(timings -> sender.sendMessage(buildTrackString(sender, timings))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ITextComponent buildTrackString(ICommandSender sender, ForgeTimings<T> data);
|
||||
|
||||
/**
|
||||
* Gets the time suffix for the provided time in nanoseconds
|
||||
*
|
||||
* @param time The time in nanoseconds
|
||||
* @return The time suffix
|
||||
*/
|
||||
protected String getTimeSuffix(double time)
|
||||
{
|
||||
if (time < 1000)
|
||||
{
|
||||
return "µs";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "ms";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a world dimension ID into a name
|
||||
*
|
||||
* @param dimId The dimension ID
|
||||
* @return The name of the dimension
|
||||
*/
|
||||
protected String getWorldName(int dimId)
|
||||
{
|
||||
DimensionType type = DimensionManager.getProviderType(dimId);
|
||||
if (type == null)
|
||||
{
|
||||
return "Dim " + dimId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return type.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TrackResultsEntity extends TrackResultsBaseCommand<Entity>
|
||||
{
|
||||
public TrackResultsEntity()
|
||||
{
|
||||
super(TimeTracker.ENTITY_UPDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "entity";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.tracking.entity.usage";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ITextComponent buildTrackString(ICommandSender sender, ForgeTimings<Entity> data)
|
||||
{
|
||||
Entity entity = data.getObject().get();
|
||||
if (entity == null)
|
||||
return TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.invalid");
|
||||
|
||||
BlockPos currentPos = entity.getPosition();
|
||||
String world = getWorldName(entity.world.provider.getDimension());
|
||||
double averageTimings = data.getAverageTimings();
|
||||
String tickTime = (averageTimings > 1000 ? TIME_FORMAT.format(averageTimings / 1000) : TIME_FORMAT.format(averageTimings)) + getTimeSuffix(
|
||||
averageTimings);
|
||||
|
||||
return TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.timingEntry", entity.getName(),
|
||||
world, currentPos.getX(), currentPos.getY(), currentPos.getZ(), tickTime);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TrackResultsTileEntity extends TrackResultsBaseCommand<TileEntity>
|
||||
{
|
||||
|
||||
public TrackResultsTileEntity()
|
||||
{
|
||||
super(TimeTracker.TILE_ENTITY_UPDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "te";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.tracking.te.usage";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ITextComponent buildTrackString(ICommandSender sender, ForgeTimings<TileEntity> data)
|
||||
{
|
||||
TileEntity te = data.getObject().get();
|
||||
if (te == null)
|
||||
return TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.invalid");
|
||||
|
||||
String name = getTileEntityName(te);
|
||||
BlockPos pos = te.getPos();
|
||||
|
||||
double averageTimings = data.getAverageTimings();
|
||||
String tickTime = (averageTimings > 1000 ? TIME_FORMAT.format(averageTimings / 1000) : TIME_FORMAT.format(averageTimings)) + getTimeSuffix(
|
||||
averageTimings);
|
||||
return TextComponentHelper
|
||||
.createComponentTranslation(sender, "commands.forge.tracking.timingEntry", name,
|
||||
getWorldName(te.getWorld().provider.getDimension()),
|
||||
pos.getX(), pos.getY(), pos.getZ(), tickTime);
|
||||
}
|
||||
|
||||
private String getTileEntityName(TileEntity tileEntity)
|
||||
{
|
||||
ResourceLocation registryId = TileEntity.getKey(tileEntity.getClass());
|
||||
if (registryId == null)
|
||||
return tileEntity.getClass().getSimpleName();
|
||||
else
|
||||
{
|
||||
return registryId.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base class for commands that has subcommands.
|
||||
* <p>
|
||||
* E.g. /team settings set [value]
|
||||
* settings is subcommand of team and set is subcommand of settings
|
||||
*/
|
||||
public abstract class CommandTreeBase extends CommandBase
|
||||
{
|
||||
private final Map<String, ICommand> commandMap = new HashMap<>();
|
||||
private final Map<String, ICommand> commandAliasMap = new HashMap<>();
|
||||
|
||||
public void addSubcommand(ICommand command)
|
||||
{
|
||||
commandMap.put(command.getName(), command);
|
||||
for (String alias : command.getAliases())
|
||||
{
|
||||
commandAliasMap.put(alias, command);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ICommand> getSubCommands()
|
||||
{
|
||||
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()
|
||||
{
|
||||
return Collections.unmodifiableMap(commandMap);
|
||||
}
|
||||
|
||||
public List<ICommand> getSortedCommandList()
|
||||
{
|
||||
List<ICommand> list = new ArrayList<>(getSubCommands());
|
||||
Collections.sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static String[] shiftArgs(@Nullable String[] s)
|
||||
{
|
||||
if(s == null || s.length == 0)
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
String[] s1 = new String[s.length - 1];
|
||||
System.arraycopy(s, 1, s1, 0, s1.length);
|
||||
return s1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of options for when the user presses the TAB key
|
||||
*/
|
||||
@Override
|
||||
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos pos)
|
||||
{
|
||||
if(args.length == 1)
|
||||
{
|
||||
List<String> keys = new ArrayList<>();
|
||||
|
||||
for (ICommand c : getSubCommands())
|
||||
{
|
||||
if(c.checkPermission(server, sender))
|
||||
{
|
||||
keys.add(c.getName());
|
||||
}
|
||||
}
|
||||
|
||||
keys.sort(null);
|
||||
return getListOfStringsMatchingLastWord(args, keys);
|
||||
}
|
||||
|
||||
ICommand cmd = getSubCommand(args[0]);
|
||||
|
||||
if(cmd != null)
|
||||
{
|
||||
return cmd.getTabCompletions(server, sender, shiftArgs(args), pos);
|
||||
}
|
||||
|
||||
return super.getTabCompletions(server, sender, args, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the specified command parameter index is a username parameter.
|
||||
*/
|
||||
@Override
|
||||
public boolean isUsernameIndex(String[] args, int index)
|
||||
{
|
||||
if (index > 0 && args.length > 1)
|
||||
{
|
||||
ICommand cmd = getSubCommand(args[0]);
|
||||
if (cmd != null)
|
||||
{
|
||||
return cmd.isUsernameIndex(shiftArgs(args), index - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
if (args.length < 1)
|
||||
{
|
||||
String subCommandsString = getAvailableSubCommandsString(server, sender);
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.tree_base.available_subcommands", subCommandsString));
|
||||
}
|
||||
else
|
||||
{
|
||||
ICommand cmd = getSubCommand(args[0]);
|
||||
|
||||
if(cmd == null)
|
||||
{
|
||||
String subCommandsString = getAvailableSubCommandsString(server, sender);
|
||||
throw new CommandException("commands.tree_base.invalid_cmd.list_subcommands", args[0], subCommandsString);
|
||||
}
|
||||
else if(!cmd.checkPermission(server, sender))
|
||||
{
|
||||
throw new CommandException("commands.generic.permission");
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.execute(server, sender, shiftArgs(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,149 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "help";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return "commands.forge.usage.help";
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return command.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender sender)
|
||||
{
|
||||
return command.getUsage(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given ICommandSender has permission to execute this command
|
||||
*/
|
||||
@Override
|
||||
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
|
||||
{
|
||||
return command.checkPermission(server, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the command is executed
|
||||
*/
|
||||
@Override
|
||||
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
|
||||
{
|
||||
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, command.getUsage(sender)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.ICommand;
|
||||
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
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 CommandSetDimension());
|
||||
super.addSubcommand(new CommandDimensions());
|
||||
super.addSubcommand(new CommandTreeHelp(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "forge";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSubcommand(ICommand command)
|
||||
{
|
||||
throw new UnsupportedOperationException("Don't add sub-commands to /forge, create your own command.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required permission level for this command.
|
||||
*/
|
||||
@Override
|
||||
public int getRequiredPermissionLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given ICommandSender has permission to execute this command
|
||||
*/
|
||||
@Override
|
||||
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage string for the command.
|
||||
*/
|
||||
@Override
|
||||
public String getUsage(ICommandSender icommandsender)
|
||||
{
|
||||
return "commands.forge.usage";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.network.NetHandlerPlayServer;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
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;
|
||||
NetHandlerPlayServer connection = playerMP.connection;
|
||||
if (connection != null)
|
||||
{
|
||||
NetworkManager netManager = connection.netManager;
|
||||
Channel channel = netManager.channel();
|
||||
return !channel.attr(NetworkRegistry.FML_MARKER).get();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraftforge.server.command;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import org.jline.reader.Candidate;
|
||||
import org.jline.reader.Completer;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.ParsedLine;
|
||||
|
||||
final class ConsoleCommandCompleter implements Completer
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private final DedicatedServer server;
|
||||
|
||||
public ConsoleCommandCompleter(DedicatedServer server)
|
||||
{
|
||||
this.server = checkNotNull(server, "server");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates)
|
||||
{
|
||||
String buffer = line.line();
|
||||
boolean prefix;
|
||||
if (buffer.isEmpty() || buffer.charAt(0) != '/')
|
||||
{
|
||||
buffer = '/' + buffer;
|
||||
prefix = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
prefix = true;
|
||||
}
|
||||
|
||||
final String input = buffer;
|
||||
Future<List<String>> tabComplete = this.server.callFromMainThread(() -> this.server.getTabCompletions(this.server, input, this.server.getPosition(), false));
|
||||
|
||||
try
|
||||
{
|
||||
for (String completion : tabComplete.get())
|
||||
{
|
||||
if (!completion.isEmpty())
|
||||
{
|
||||
boolean hasPrefix = prefix || completion.charAt(0) != '/';
|
||||
Candidate candidate = new Candidate(hasPrefix ? completion : completion.substring(1));
|
||||
candidates.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (ExecutionException e)
|
||||
{
|
||||
logger.error("Failed to tab complete", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.console;
|
||||
|
||||
import net.minecraftforge.server.terminalconsole.TerminalConsoleAppender;
|
||||
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import org.jline.reader.EndOfFileException;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
import org.jline.reader.UserInterruptException;
|
||||
import org.jline.terminal.Terminal;
|
||||
|
||||
public final class TerminalHandler
|
||||
{
|
||||
|
||||
private TerminalHandler()
|
||||
{
|
||||
}
|
||||
|
||||
public static boolean handleCommands(DedicatedServer server)
|
||||
{
|
||||
final Terminal terminal = TerminalConsoleAppender.getTerminal();
|
||||
if (terminal == null)
|
||||
return false;
|
||||
|
||||
LineReader reader = LineReaderBuilder.builder()
|
||||
.appName("Forge")
|
||||
.terminal(terminal)
|
||||
.completer(new ConsoleCommandCompleter(server))
|
||||
.build();
|
||||
reader.setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION);
|
||||
reader.unsetOpt(LineReader.Option.INSERT_TAB);
|
||||
|
||||
TerminalConsoleAppender.setReader(reader);
|
||||
|
||||
try
|
||||
{
|
||||
String line;
|
||||
while (!server.isServerStopped() && server.isServerRunning())
|
||||
{
|
||||
try
|
||||
{
|
||||
line = reader.readLine("> ");
|
||||
}
|
||||
catch (EndOfFileException ignored)
|
||||
{
|
||||
// Continue reading after EOT
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
line = line.trim();
|
||||
if (!line.isEmpty())
|
||||
{
|
||||
server.addPendingCommand(line, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (UserInterruptException e)
|
||||
{
|
||||
server.initiateShutdown();
|
||||
}
|
||||
finally
|
||||
{
|
||||
TerminalConsoleAppender.setReader(null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.server.permission.context.IContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Default implementation of PermissionAPI.
|
||||
* {@link #hasPermission(GameProfile, String, IContext)} is based on DefaultPermissionLevel
|
||||
*
|
||||
* @see IPermissionHandler
|
||||
*/
|
||||
public enum DefaultPermissionHandler implements IPermissionHandler
|
||||
{
|
||||
INSTANCE;
|
||||
private static final HashMap<String, DefaultPermissionLevel> PERMISSION_LEVEL_MAP = new HashMap<String, DefaultPermissionLevel>();
|
||||
private static final HashMap<String, String> DESCRIPTION_MAP = new HashMap<String, String>();
|
||||
|
||||
@Override
|
||||
public void registerNode(String node, DefaultPermissionLevel level, String desc)
|
||||
{
|
||||
PERMISSION_LEVEL_MAP.put(node, level);
|
||||
|
||||
if(!desc.isEmpty())
|
||||
{
|
||||
DESCRIPTION_MAP.put(node, desc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getRegisteredNodes()
|
||||
{
|
||||
return Collections.unmodifiableSet(PERMISSION_LEVEL_MAP.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GameProfile profile, String node, @Nullable IContext context)
|
||||
{
|
||||
DefaultPermissionLevel level = getDefaultPermissionLevel(node);
|
||||
|
||||
if(level == DefaultPermissionLevel.NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(level == DefaultPermissionLevel.ALL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
|
||||
return server != null && server.getPlayerList().canSendCommands(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNodeDescription(String node)
|
||||
{
|
||||
String desc = DESCRIPTION_MAP.get(node);
|
||||
return desc == null ? "" : desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The default permission level of a node. If the permission isn't registred, it will return NONE
|
||||
*/
|
||||
public DefaultPermissionLevel getDefaultPermissionLevel(String node)
|
||||
{
|
||||
DefaultPermissionLevel level = PERMISSION_LEVEL_MAP.get(node);
|
||||
return level == null ? DefaultPermissionLevel.NONE : level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission;
|
||||
|
||||
/**
|
||||
* <table><thead><tr><th>Level</th><th>Player</th><th>OP</th></tr>
|
||||
* </thead><tbody>
|
||||
* <tr><td>ALL</td><td>true</td><td>true</td></tr>
|
||||
* <tr><td>OP</td><td>false</td><td>true</td></tr>
|
||||
* <tr><td>NONE</td><td>false</td><td>false</td></tr>
|
||||
* </tbody></table>
|
||||
*/
|
||||
public enum DefaultPermissionLevel
|
||||
{
|
||||
ALL,
|
||||
OP,
|
||||
NONE
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraftforge.server.permission.context.IContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IPermissionHandler
|
||||
{
|
||||
/**
|
||||
* Use {@link PermissionAPI#registerNode(String, DefaultPermissionLevel, String)}
|
||||
*/
|
||||
void registerNode(String node, DefaultPermissionLevel level, String desc);
|
||||
|
||||
/**
|
||||
* @return Immutable collection of all registered nodes
|
||||
*/
|
||||
Collection<String> getRegisteredNodes();
|
||||
|
||||
/**
|
||||
* Use {@link PermissionAPI#hasPermission(GameProfile, String, IContext)}
|
||||
*/
|
||||
boolean hasPermission(GameProfile profile, String node, @Nullable IContext context);
|
||||
|
||||
/**
|
||||
* @param node Permission node
|
||||
* @return Description of the node. "" in case this node doesn't have a decription
|
||||
* @see #registerNode(String, DefaultPermissionLevel, String)
|
||||
*/
|
||||
String getNodeDescription(String node);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.LoaderState;
|
||||
import net.minecraftforge.server.permission.context.IContext;
|
||||
import net.minecraftforge.server.permission.context.PlayerContext;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PermissionAPI
|
||||
{
|
||||
private static IPermissionHandler permissionHandler = DefaultPermissionHandler.INSTANCE;
|
||||
|
||||
/**
|
||||
* <b>Only use this in PreInit state!</b>
|
||||
*/
|
||||
public static void setPermissionHandler(IPermissionHandler handler)
|
||||
{
|
||||
Preconditions.checkNotNull(handler, "Permission handler can't be null!");
|
||||
Preconditions.checkState(Loader.instance().getLoaderState().ordinal() <= LoaderState.PREINITIALIZATION.ordinal(), "Can't register after IPermissionHandler PreInit!");
|
||||
FMLLog.log.warn("Replacing {} with {}", permissionHandler.getClass().getName(), handler.getClass().getName());
|
||||
permissionHandler = handler;
|
||||
}
|
||||
|
||||
public static IPermissionHandler getPermissionHandler()
|
||||
{
|
||||
return permissionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Only use this after PreInit state!</b>
|
||||
*
|
||||
* @param node Permission node, best if it's lowercase and contains '.' (e.g. <code>"modid.subgroup.permission_id"</code>)
|
||||
* @param level Default permission level for this node. If not isn't registered, it's level is going to be 'NONE'
|
||||
* @param desc Optional description of the node
|
||||
*/
|
||||
public static String registerNode(String node, DefaultPermissionLevel level, String desc)
|
||||
{
|
||||
Preconditions.checkNotNull(node, "Permission node can't be null!");
|
||||
Preconditions.checkNotNull(level, "Permission level can't be null!");
|
||||
Preconditions.checkNotNull(desc, "Permission description can't be null!");
|
||||
Preconditions.checkArgument(!node.isEmpty(), "Permission node can't be empty!");
|
||||
Preconditions.checkState(Loader.instance().getLoaderState().ordinal() > LoaderState.PREINITIALIZATION.ordinal(), "Can't register permission nodes before Init!");
|
||||
permissionHandler.registerNode(node, level, desc);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param profile GameProfile of the player who is requesting permission. The player doesn't have to be online
|
||||
* @param node Permission node. See {@link #registerNode(String, DefaultPermissionLevel, String)}
|
||||
* @param context Context for this permission. Highly recommended to not be null. See {@link IContext}
|
||||
* @return true, if player has permission, false if he does not.
|
||||
* @see DefaultPermissionHandler
|
||||
*/
|
||||
public static boolean hasPermission(GameProfile profile, String node, @Nullable IContext context)
|
||||
{
|
||||
Preconditions.checkNotNull(profile, "GameProfile can't be null!");
|
||||
Preconditions.checkNotNull(node, "Permission node can't be null!");
|
||||
Preconditions.checkArgument(!node.isEmpty(), "Permission node can't be empty!");
|
||||
return permissionHandler.hasPermission(profile, node, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method using EntityPlayer and creating PlayerContext
|
||||
*
|
||||
* @see PermissionAPI#hasPermission(GameProfile, String, IContext)
|
||||
*/
|
||||
public static boolean hasPermission(EntityPlayer player, String node)
|
||||
{
|
||||
Preconditions.checkNotNull(player, "Player can't be null!");
|
||||
return hasPermission(player.getGameProfile(), node, new PlayerContext(player));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AreaContext extends PlayerContext
|
||||
{
|
||||
private final AxisAlignedBB area;
|
||||
|
||||
public AreaContext(EntityPlayer ep, AxisAlignedBB aabb)
|
||||
{
|
||||
super(ep);
|
||||
area = Preconditions.checkNotNull(aabb, "AxisAlignedBB can't be null in AreaContext!");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(ContextKey<T> key)
|
||||
{
|
||||
return key.equals(ContextKeys.AREA) ? (T) area : super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean covers(ContextKey<?> key)
|
||||
{
|
||||
return key.equals(ContextKeys.AREA);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockPosContext extends PlayerContext
|
||||
{
|
||||
private final BlockPos blockPos;
|
||||
private IBlockState blockState;
|
||||
private EnumFacing facing;
|
||||
|
||||
public BlockPosContext(EntityPlayer ep, BlockPos pos, @Nullable IBlockState state, @Nullable EnumFacing f)
|
||||
{
|
||||
super(ep);
|
||||
blockPos = Preconditions.checkNotNull(pos, "BlockPos can't be null in BlockPosContext!");
|
||||
blockState = state;
|
||||
facing = f;
|
||||
}
|
||||
|
||||
public BlockPosContext(EntityPlayer ep, ChunkPos pos)
|
||||
{
|
||||
this(ep, new BlockPos(pos.getXStart() + 8, 0, pos.getZStart() + 8), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(ContextKey<T> key)
|
||||
{
|
||||
if(key.equals(ContextKeys.POS))
|
||||
{
|
||||
return (T) blockPos;
|
||||
}
|
||||
else if(key.equals(ContextKeys.BLOCK_STATE))
|
||||
{
|
||||
if(blockState == null)
|
||||
{
|
||||
blockState = getWorld().getBlockState(blockPos);
|
||||
}
|
||||
|
||||
return (T) blockState;
|
||||
}
|
||||
else if(key.equals(ContextKeys.FACING))
|
||||
{
|
||||
return (T) facing;
|
||||
}
|
||||
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean covers(ContextKey<?> key)
|
||||
{
|
||||
return key.equals(ContextKeys.POS) || key.equals(ContextKeys.BLOCK_STATE) || (facing != null && key.equals(ContextKeys.FACING));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Context implements IContext
|
||||
{
|
||||
private Map<ContextKey<?>, Object> map;
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public World getWorld()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public EntityPlayer getPlayer()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(ContextKey<T> key)
|
||||
{
|
||||
return map == null || map.isEmpty() ? null : (T) map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(ContextKey<?> key)
|
||||
{
|
||||
return covers(key) || (map != null && !map.isEmpty() && map.containsKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Context object
|
||||
*
|
||||
* @param key Context key
|
||||
* @param obj Context object. Can be null
|
||||
* @return itself, for easy context chaining
|
||||
*/
|
||||
public <T> Context set(ContextKey<T> key, @Nullable T obj)
|
||||
{
|
||||
if(covers(key))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
if(map == null)
|
||||
{
|
||||
map = new HashMap<ContextKey<?>, Object>();
|
||||
}
|
||||
|
||||
map.put(key, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected boolean covers(ContextKey<?> key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public final class ContextKey<T>
|
||||
{
|
||||
private final String ID;
|
||||
private final Class<T> typeClass;
|
||||
|
||||
public static <E> ContextKey<E> create(String id, Class<E> c)
|
||||
{
|
||||
Preconditions.checkNotNull(id, "ContextKey's ID can't be null!");
|
||||
Preconditions.checkNotNull(c, "ContextKey's Type can't be null!");
|
||||
|
||||
if(id.isEmpty())
|
||||
{
|
||||
throw new IllegalArgumentException("ContextKey's ID can't be blank!");
|
||||
}
|
||||
|
||||
return new ContextKey<E>(id, c);
|
||||
}
|
||||
|
||||
private ContextKey(String id, Class<T> c)
|
||||
{
|
||||
ID = id;
|
||||
typeClass = c;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return ID;
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return ID.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return o == this || (o != null && o.toString().equals(ID));
|
||||
}
|
||||
|
||||
public Class<T> getTypeClass()
|
||||
{
|
||||
return typeClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
* Some default context keys, for easier compatibility
|
||||
*/
|
||||
public class ContextKeys
|
||||
{
|
||||
/**
|
||||
* BlockPos for interacting, breaking and other permissions
|
||||
*/
|
||||
public static final ContextKey<BlockPos> POS = ContextKey.create("pos", BlockPos.class);
|
||||
|
||||
/**
|
||||
* The entity can be anything that gets interacted with - a sheep when you try to dye it, skeleton that you attack, etc.
|
||||
*/
|
||||
public static final ContextKey<Entity> TARGET = ContextKey.create("target", Entity.class);
|
||||
|
||||
public static final ContextKey<EnumFacing> FACING = ContextKey.create("facing", EnumFacing.class);
|
||||
public static final ContextKey<AxisAlignedBB> AREA = ContextKey.create("area", AxisAlignedBB.class);
|
||||
public static final ContextKey<IBlockState> BLOCK_STATE = ContextKey.create("blockstate", IBlockState.class);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Use {@link BlockPosContext} or {@link PlayerContext} when possible
|
||||
*/
|
||||
public interface IContext
|
||||
{
|
||||
/**
|
||||
* World from where permission is requested. Can be null
|
||||
*/
|
||||
@Nullable
|
||||
World getWorld();
|
||||
|
||||
/**
|
||||
* @return Player requesting permission. Can be null
|
||||
*/
|
||||
@Nullable
|
||||
EntityPlayer getPlayer();
|
||||
|
||||
/**
|
||||
* @param key Context key
|
||||
* @return Context object
|
||||
*/
|
||||
@Nullable
|
||||
<T> T get(ContextKey<T> key);
|
||||
|
||||
/**
|
||||
* @param key Context key
|
||||
* @return true if context contains this key
|
||||
*/
|
||||
boolean has(ContextKey<?> key);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class PlayerContext extends Context
|
||||
{
|
||||
private final EntityPlayer player;
|
||||
|
||||
public PlayerContext(EntityPlayer ep)
|
||||
{
|
||||
player = Preconditions.checkNotNull(ep, "Player can't be null in PlayerContext!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld()
|
||||
{
|
||||
return player.getEntityWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPlayer getPlayer()
|
||||
{
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TargetContext extends PlayerContext
|
||||
{
|
||||
private final Entity target;
|
||||
|
||||
public TargetContext(EntityPlayer ep, @Nullable Entity entity)
|
||||
{
|
||||
super(ep);
|
||||
target = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(ContextKey<T> key)
|
||||
{
|
||||
return key.equals(ContextKeys.TARGET) ? (T) target : super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean covers(ContextKey<?> key)
|
||||
{
|
||||
return target != null && key.equals(ContextKeys.TARGET);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.permission.context;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class WorldContext extends Context
|
||||
{
|
||||
private final World world;
|
||||
|
||||
public WorldContext(World w)
|
||||
{
|
||||
world = Preconditions.checkNotNull(w, "World can't be null in WorldContext!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public EntityPlayer getPlayer()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
@javax.annotation.ParametersAreNonnullByDefault
|
||||
@mcp.MethodsReturnNonnullByDefault
|
||||
package net.minecraftforge.server.permission.context;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
@javax.annotation.ParametersAreNonnullByDefault
|
||||
@mcp.MethodsReturnNonnullByDefault
|
||||
package net.minecraftforge.server.permission;
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* TerminalConsoleAppender
|
||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.server.terminalconsole;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
||||
import org.apache.logging.log4j.core.pattern.HighlightConverter;
|
||||
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternConverter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternParser;
|
||||
import org.apache.logging.log4j.util.PerformanceSensitive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simplified version of {@link HighlightConverter} that uses
|
||||
* {@link TerminalConsoleAppender} to detect if Ansi escape codes can be used
|
||||
* to highlight errors and warnings in the console.
|
||||
*
|
||||
* <p>If configured, it will mark all logged errors with a red color and all
|
||||
* warnings with a yellow color. It can be only used together with
|
||||
* {@link TerminalConsoleAppender}.</p>
|
||||
*
|
||||
* <p>{@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} may be used
|
||||
* to force the use of ANSI colors even in unsupported environments.</p>
|
||||
*
|
||||
* <p><b>Example usage:</b> {@code %highlightError{%level: %message}}</p>
|
||||
*/
|
||||
@Plugin(name = "highlightError", category = PatternConverter.CATEGORY)
|
||||
@ConverterKeys({ "highlightError" })
|
||||
@PerformanceSensitive("allocation")
|
||||
public class HighlightErrorConverter extends LogEventPatternConverter
|
||||
{
|
||||
private static final String ANSI_RESET = "\u001B[39;0m";
|
||||
private static final String ANSI_ERROR = "\u001B[31;1m";
|
||||
private static final String ANSI_WARN = "\u001B[33;1m";
|
||||
|
||||
private final List<PatternFormatter> formatters;
|
||||
|
||||
/**
|
||||
* Construct the converter.
|
||||
*
|
||||
* @param formatters The pattern formatters to generate the text to highlight
|
||||
*/
|
||||
protected HighlightErrorConverter(List<PatternFormatter> formatters)
|
||||
{
|
||||
super("highlightError", null);
|
||||
this.formatters = formatters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void format(LogEvent event, StringBuilder toAppendTo)
|
||||
{
|
||||
if (TerminalConsoleAppender.isAnsiSupported())
|
||||
{
|
||||
Level level = event.getLevel();
|
||||
if (level.isMoreSpecificThan(Level.ERROR))
|
||||
{
|
||||
format(ANSI_ERROR, event, toAppendTo);
|
||||
return;
|
||||
}
|
||||
else if (level.isMoreSpecificThan(Level.WARN))
|
||||
{
|
||||
format(ANSI_WARN, event, toAppendTo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0, size = formatters.size(); i < size; i++)
|
||||
{
|
||||
formatters.get(i).format(event, toAppendTo);
|
||||
}
|
||||
}
|
||||
|
||||
private void format(String style, LogEvent event, StringBuilder toAppendTo)
|
||||
{
|
||||
int start = toAppendTo.length();
|
||||
toAppendTo.append(style);
|
||||
int end = toAppendTo.length();
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0, size = formatters.size(); i < size; i++)
|
||||
{
|
||||
formatters.get(i).format(event, toAppendTo);
|
||||
}
|
||||
|
||||
if (toAppendTo.length() == end)
|
||||
{
|
||||
// No content so we don't need to append the ANSI escape code
|
||||
toAppendTo.setLength(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append reset code after the line
|
||||
toAppendTo.append(ANSI_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesThrowable()
|
||||
{
|
||||
for (final PatternFormatter formatter : formatters)
|
||||
{
|
||||
if (formatter.handlesThrowable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new instance of the {@link HighlightErrorConverter} with the
|
||||
* specified options.
|
||||
*
|
||||
* @param config The current configuration
|
||||
* @param options The pattern options
|
||||
* @return The new instance
|
||||
*/
|
||||
@Nullable
|
||||
public static HighlightErrorConverter newInstance(Configuration config, String[] options)
|
||||
{
|
||||
if (options.length != 1)
|
||||
{
|
||||
LOGGER.error("Incorrect number of options on highlightError. Expected 1 received " + options.length);
|
||||
return null;
|
||||
}
|
||||
if (options[0] == null)
|
||||
{
|
||||
LOGGER.error("No pattern supplied on highlightError");
|
||||
return null;
|
||||
}
|
||||
|
||||
PatternParser parser = PatternLayout.createPatternParser(config);
|
||||
List<PatternFormatter> formatters = parser.parse(options[0]);
|
||||
return new HighlightErrorConverter(formatters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* TerminalConsoleAppender
|
||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.server.terminalconsole;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
||||
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternConverter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternParser;
|
||||
import org.apache.logging.log4j.util.PerformanceSensitive;
|
||||
import org.apache.logging.log4j.util.PropertiesUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Replaces Minecraft formatting codes in the result of a pattern with
|
||||
* appropriate ANSI escape codes. The implementation will only replace valid
|
||||
* color codes using the section sign (§).
|
||||
*
|
||||
* <p>The {@link MinecraftFormattingConverter} can be only used together with
|
||||
* {@link TerminalConsoleAppender} to detect if the current console supports
|
||||
* color output. When running in an unsupported environment, it will
|
||||
* automatically strip all formatting codes instead.</p>
|
||||
*
|
||||
* <p>{@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} may be used
|
||||
* to force the use of ANSI colors even in unsupported environments. As an
|
||||
* alternative, {@link #KEEP_FORMATTING_PROPERTY} may be used to keep the
|
||||
* raw Minecraft formatting codes.</p>
|
||||
*
|
||||
* <p><b>Example usage:</b> {@code %minecraftFormatting{%message}}<br>
|
||||
* It can be configured to always strip formatting codes from the message:
|
||||
* {@code %minecraftFormatting{%message}{strip}}</p>
|
||||
*
|
||||
* @see <a href="http://minecraft.gamepedia.com/Formatting_codes">
|
||||
* Formatting Codes</a>
|
||||
*/
|
||||
@Plugin(name = "minecraftFormatting", category = PatternConverter.CATEGORY)
|
||||
@ConverterKeys({ "minecraftFormatting" })
|
||||
@PerformanceSensitive("allocation")
|
||||
public class MinecraftFormattingConverter extends LogEventPatternConverter
|
||||
{
|
||||
/**
|
||||
* System property that allows disabling the replacement of Minecraft
|
||||
* formatting codes entirely, keeping them in the console output. For
|
||||
* some applications they might be easier and more accurate for parsing
|
||||
* in applications like certain control panels.
|
||||
*
|
||||
* <p>If this system property is not set, or set to any value except
|
||||
* {@code true}, all Minecraft formatting codes will be replaced
|
||||
* or stripped from the console output.</p>
|
||||
*/
|
||||
public static final String KEEP_FORMATTING_PROPERTY = TerminalConsoleAppender.PROPERTY_PREFIX + ".keepMinecraftFormatting";
|
||||
|
||||
private static final boolean KEEP_FORMATTING = PropertiesUtil.getProperties().getBooleanProperty(KEEP_FORMATTING_PROPERTY);
|
||||
|
||||
private static final String ANSI_RESET = "\u001B[39;0m";
|
||||
|
||||
private static final char COLOR_CHAR = '\u00A7'; // §
|
||||
private static final String LOOKUP = "0123456789abcdefklmnor";
|
||||
|
||||
private static final String[] ansiCodes = new String[] {
|
||||
"\u001B[0;30;22m", // Black §0
|
||||
"\u001B[0;34;22m", // Dark Blue §1
|
||||
"\u001B[0;32;22m", // Dark Green §2
|
||||
"\u001B[0;36;22m", // Dark Aqua §3
|
||||
"\u001B[0;31;22m", // Dark Red §4
|
||||
"\u001B[0;35;22m", // Dark Purple §5
|
||||
"\u001B[0;33;22m", // Gold §6
|
||||
"\u001B[0;37;22m", // Gray §7
|
||||
"\u001B[0;30;1m", // Dark Gray §8
|
||||
"\u001B[0;34;1m", // Blue §9
|
||||
"\u001B[0;32;1m", // Green §a
|
||||
"\u001B[0;36;1m", // Aqua §b
|
||||
"\u001B[0;31;1m", // Red §c
|
||||
"\u001B[0;35;1m", // Light Purple §d
|
||||
"\u001B[0;33;1m", // Yellow §e
|
||||
"\u001B[0;37;1m", // White §f
|
||||
"\u001B[5m", // Obfuscated §k
|
||||
"\u001B[21m", // Bold §l
|
||||
"\u001B[9m", // Strikethrough §m
|
||||
"\u001B[4m", // Underline §n
|
||||
"\u001B[3m", // Italic §o
|
||||
ANSI_RESET, // Reset §r
|
||||
};
|
||||
|
||||
private final boolean ansi;
|
||||
private final List<PatternFormatter> formatters;
|
||||
|
||||
/**
|
||||
* Construct the converter.
|
||||
*
|
||||
* @param formatters The pattern formatters to generate the text to manipulate
|
||||
* @param strip If true, the converter will strip all formatting codes
|
||||
*/
|
||||
protected MinecraftFormattingConverter(List<PatternFormatter> formatters, boolean strip)
|
||||
{
|
||||
super("minecraftFormatting", null);
|
||||
this.formatters = formatters;
|
||||
this.ansi = !strip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void format(LogEvent event, StringBuilder toAppendTo)
|
||||
{
|
||||
int start = toAppendTo.length();
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0, size = formatters.size(); i < size; i++)
|
||||
{
|
||||
formatters.get(i).format(event, toAppendTo);
|
||||
}
|
||||
|
||||
if (KEEP_FORMATTING || toAppendTo.length() == start)
|
||||
{
|
||||
// Skip replacement if disabled or if the content is empty
|
||||
return;
|
||||
}
|
||||
|
||||
String content = toAppendTo.substring(start);
|
||||
format(content, toAppendTo, start, ansi && TerminalConsoleAppender.isAnsiSupported());
|
||||
}
|
||||
|
||||
private static void format(String s, StringBuilder result, int start, boolean ansi)
|
||||
{
|
||||
int next = s.indexOf(COLOR_CHAR);
|
||||
int last = s.length() - 1;
|
||||
if (next == -1 || next == last)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
result.setLength(start + next);
|
||||
|
||||
int pos = next;
|
||||
int format;
|
||||
do {
|
||||
if (pos != next)
|
||||
{
|
||||
result.append(s, pos, next);
|
||||
}
|
||||
|
||||
format = LOOKUP.indexOf(s.charAt(next + 1));
|
||||
if (format != -1)
|
||||
{
|
||||
if (ansi)
|
||||
{
|
||||
result.append(ansiCodes[format]);
|
||||
}
|
||||
pos = next += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
next++;
|
||||
}
|
||||
|
||||
next = s.indexOf(COLOR_CHAR, next);
|
||||
} while (next != -1 && next < last);
|
||||
|
||||
result.append(s, pos, s.length());
|
||||
if (ansi)
|
||||
{
|
||||
result.append(ANSI_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new instance of the {@link MinecraftFormattingConverter} with the
|
||||
* specified options.
|
||||
*
|
||||
* @param config The current configuration
|
||||
* @param options The pattern options
|
||||
* @return The new instance
|
||||
*
|
||||
* @see MinecraftFormattingConverter
|
||||
*/
|
||||
@Nullable
|
||||
public static MinecraftFormattingConverter newInstance(Configuration config, String[] options)
|
||||
{
|
||||
if (options.length < 1 || options.length > 2)
|
||||
{
|
||||
LOGGER.error("Incorrect number of options on minecraftFormatting. Expected at least 1, max 2 received " + options.length);
|
||||
return null;
|
||||
}
|
||||
if (options[0] == null)
|
||||
{
|
||||
LOGGER.error("No pattern supplied on minecraftFormatting");
|
||||
return null;
|
||||
}
|
||||
|
||||
PatternParser parser = PatternLayout.createPatternParser(config);
|
||||
List<PatternFormatter> formatters = parser.parse(options[0]);
|
||||
boolean strip = options.length > 1 && "strip".equals(options[1]);
|
||||
return new MinecraftFormattingConverter(formatters, strip);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* TerminalConsoleAppender
|
||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.server.terminalconsole;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Core;
|
||||
import org.apache.logging.log4j.core.Filter;
|
||||
import org.apache.logging.log4j.core.Layout;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
import org.apache.logging.log4j.util.PropertiesUtil;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.TerminalBuilder;
|
||||
|
||||
/**
|
||||
* An {@link Appender} that uses the JLine 3.x {@link Terminal} to print messages
|
||||
* to the console.
|
||||
*
|
||||
* <p>The JLine {@link Terminal} extends the regular console output with support
|
||||
* for Ansi escape codes on Windows. Additionally, it's {@link LineReader}
|
||||
* interface can be used to implement enhanced console input, with an
|
||||
* persistent input line, as well as command history and command completion.</p>
|
||||
*
|
||||
* <p>The {@code TerminalConsole} appender replaces the default {@code Console}
|
||||
* appender in your log4j configuration. By default, log4j will automatically
|
||||
* close the standard output when the original {@code Console} appender is
|
||||
* removed. Consequently, it is necessary to keep an unused {@code Console}
|
||||
* appender.</p>
|
||||
*
|
||||
* <p><b>Example usage:</b></p>
|
||||
* <pre>{@code <TerminalConsole>
|
||||
* <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
||||
* </TerminalConsole>
|
||||
*
|
||||
* <Console name="SysOut" target="SYSTEM_OUT"/>}</pre>
|
||||
*
|
||||
* <p>To use the enhanced console input it is necessary to set the
|
||||
* {@link LineReader} using {@link #setReader(LineReader)}. The appender will
|
||||
* then automatically redraw the current prompt. When creating the
|
||||
* {@link LineReader} it's important to use the {@link Terminal}
|
||||
* returned by {@link #getTerminal()}. Additionally, the reader should
|
||||
* be removed from the appender as soon as it's no longer accepting
|
||||
* input (for example when the user interrupted input using CTRL + C.</p>
|
||||
*
|
||||
* <p>By default, the JLine {@link Terminal} is enabled when the application
|
||||
* is started with an attached terminal session. Usually, this is only the
|
||||
* case if the application is started from the command line, not if it gets
|
||||
* started by another application.</p>
|
||||
*
|
||||
* <p>In some cases, it might be possible to support a subset of the features
|
||||
* in these unsupported environments (e.g. only ANSI color codes). In these
|
||||
* cases, the system properties may be used to override the default behaviour:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link TerminalConsoleAppender#JLINE_OVERRIDE_PROPERTY} - To enable the extended JLine
|
||||
* input. By default this will also enable the ANSI escape codes.</li>
|
||||
* <li>{@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} - To enable the output of ANSI
|
||||
* escape codes. May be used to force the use of ANSI escape codes
|
||||
* if JLine is disabled or to disable them if it is enabled.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Plugin(name = TerminalConsoleAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
|
||||
public class TerminalConsoleAppender extends AbstractAppender
|
||||
{
|
||||
public static final String PLUGIN_NAME = "TerminalConsole";
|
||||
/**
|
||||
* The prefix used for all system properties in TerminalConsoleAppender.
|
||||
*/
|
||||
public static final String PROPERTY_PREFIX = "terminal";
|
||||
/**
|
||||
* System property that allows overriding the default detection of the
|
||||
* console to force enable or force disable the use of JLine. In some
|
||||
* environments the automatic detection might not work properly.
|
||||
* <p>
|
||||
* <p>If this system property is not set, or set to an invalid value
|
||||
* (neither {@code true} nor {@code false}) then we will attempt
|
||||
* to detect the best option automatically.</p>
|
||||
*/
|
||||
public static final String JLINE_OVERRIDE_PROPERTY = PROPERTY_PREFIX + ".jline";
|
||||
/**
|
||||
* System property that allows overriding the use of ANSI escape codes
|
||||
* for console formatting even though running in an unsupported
|
||||
* environment. By default, ANSI color codes are only enabled if JLine
|
||||
* is enabled. Some systems might be able to handle ANSI escape codes
|
||||
* but are not capable of JLine's extended input mechanism.
|
||||
* <p>
|
||||
* <p>If this system property is not set, or set to an invalid value
|
||||
* (neither {@code true} nor {@code false}) then we will attempt
|
||||
* to detect the best option automatically.</p>
|
||||
*/
|
||||
public static final String ANSI_OVERRIDE_PROPERTY = PROPERTY_PREFIX + ".ansi";
|
||||
public static final Boolean ANSI_OVERRIDE = getOptionalBooleanProperty(ANSI_OVERRIDE_PROPERTY);
|
||||
|
||||
/**
|
||||
* We grab the standard output {@link PrintStream} early, otherwise we
|
||||
* might cause infinite loops later if the application redirects
|
||||
* {@link System#out} to Log4J.
|
||||
*/
|
||||
private static final PrintStream stdout = System.out;
|
||||
|
||||
private static boolean initialized;
|
||||
@Nullable
|
||||
private static Terminal terminal;
|
||||
@Nullable
|
||||
private static LineReader reader;
|
||||
|
||||
/**
|
||||
* Returns the {@link Terminal} that is used to print messages to the
|
||||
* console. Returns {@code null} in unsupported environments, unless
|
||||
* overridden using the {@link TerminalConsoleAppender#JLINE_OVERRIDE_PROPERTY} system
|
||||
* property.
|
||||
*
|
||||
* @return The terminal, or null if not supported
|
||||
* @see TerminalConsoleAppender
|
||||
*/
|
||||
@Nullable
|
||||
public static Terminal getTerminal()
|
||||
{
|
||||
return terminal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently configured {@link LineReader} that is used to
|
||||
* read input from the console. May be null if no {@link LineReader}
|
||||
* was configured by the environment.
|
||||
*
|
||||
* @return The current line reader, or null if none
|
||||
*/
|
||||
@Nullable
|
||||
public static LineReader getReader()
|
||||
{
|
||||
return reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link LineReader} that is used to read input from the console.
|
||||
* Setting the {@link LineReader} will allow the appender to automatically
|
||||
* redraw the input line when a new log message is added.
|
||||
*
|
||||
* <p><b>Note:</b> The specified {@link LineReader} must be created with
|
||||
* the terminal returned by {@link #getTerminal()}.</p>
|
||||
*
|
||||
* @param newReader The new line reader
|
||||
*/
|
||||
public static void setReader(@Nullable LineReader newReader)
|
||||
{
|
||||
if (newReader != null && newReader.getTerminal() != terminal)
|
||||
{
|
||||
throw new IllegalArgumentException("Reader was not created with TerminalConsoleAppender.getTerminal()");
|
||||
}
|
||||
|
||||
reader = newReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether ANSI escapes codes should be written to the console
|
||||
* output.
|
||||
*
|
||||
* <p>The return value is {@code true} by default if the JLine terminal
|
||||
* is enabled and {@code false} otherwise. It may be overridden using
|
||||
* the {@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} system property.</p>
|
||||
*
|
||||
* @return true if ANSI escapes codes should be written to the console
|
||||
*/
|
||||
public static boolean isAnsiSupported()
|
||||
{
|
||||
return ANSI_OVERRIDE != null ? ANSI_OVERRIDE : terminal != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link TerminalConsoleAppender}.
|
||||
*
|
||||
* @param name The name of the appender
|
||||
* @param filter The filter, can be {@code null}
|
||||
* @param layout The layout to use
|
||||
* @param ignoreExceptions If {@code true} exceptions encountered when
|
||||
* appending events are logged, otherwise they are propagated to the
|
||||
* caller
|
||||
*/
|
||||
protected TerminalConsoleAppender(String name, @Nullable Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions)
|
||||
{
|
||||
super(name, filter, layout, ignoreExceptions);
|
||||
initializeTerminal();
|
||||
}
|
||||
|
||||
private static void initializeTerminal()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
// A system property can be used to override our automatic detection
|
||||
Boolean jlineOverride = getOptionalBooleanProperty(JLINE_OVERRIDE_PROPERTY);
|
||||
|
||||
// By default, we disable JLine if there is no terminal attached
|
||||
// (e.g. if the program output is redirected to a file or if it's
|
||||
// started by some kind of control panel)
|
||||
|
||||
// The same applies to IDEs, they usually provide only a very basic
|
||||
// console implementation without support for ANSI escape codes
|
||||
// (used for colors) or characters like \r.
|
||||
|
||||
// There are two exceptions:
|
||||
// 1. IntelliJ IDEA supports colors and control characters
|
||||
// (We try to detect it using an additional JAR it adds to the classpath)
|
||||
// 2. The system property forces the use of JLine.
|
||||
boolean dumb = jlineOverride == Boolean.TRUE || System.getProperty("java.class.path").contains("idea_rt.jar");
|
||||
|
||||
if (jlineOverride != Boolean.FALSE)
|
||||
{
|
||||
try
|
||||
{
|
||||
terminal = TerminalBuilder.builder().dumb(dumb).build();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// Unless disabled using one of the exceptions above,
|
||||
// JLine throws an exception before creating a dumb terminal
|
||||
// Dumb terminals are used if there is no real terminal attached
|
||||
// to the application.
|
||||
|
||||
if (LOGGER.isDebugEnabled())
|
||||
{
|
||||
// Log with stacktrace
|
||||
LOGGER.warn("Disabling terminal, you're running in an unsupported environment.", e);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.warn("Disabling terminal, you're running in an unsupported environment.");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("Failed to initialize terminal. Falling back to standard output", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Boolean getOptionalBooleanProperty(String name)
|
||||
{
|
||||
String value = PropertiesUtil.getProperties().getStringProperty(name);
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.equalsIgnoreCase("true"))
|
||||
{
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
else if (value.equalsIgnoreCase("false"))
|
||||
{
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.warn("Invalid value for boolean input property '{}': {}", name, value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event)
|
||||
{
|
||||
if (terminal != null)
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
// Draw the prompt line again if a reader is available
|
||||
reader.callWidget(LineReader.CLEAR);
|
||||
terminal.writer().print(getLayout().toSerializable(event));
|
||||
reader.callWidget(LineReader.REDRAW_LINE);
|
||||
reader.callWidget(LineReader.REDISPLAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
terminal.writer().print(getLayout().toSerializable(event));
|
||||
}
|
||||
|
||||
terminal.writer().flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
stdout.print(getLayout().toSerializable(event));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the JLine {@link Terminal} (if available) and restores the original
|
||||
* terminal settings.
|
||||
*
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public static void close() throws IOException
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
initialized = false;
|
||||
if (terminal != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
terminal.close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
terminal = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link TerminalConsoleAppender}.
|
||||
*
|
||||
* @param name The name of the appender
|
||||
* @param filter The filter, can be {@code null}
|
||||
* @param layout The layout, can be {@code null}
|
||||
* @param ignoreExceptions If {@code true} exceptions encountered when
|
||||
* appending events are logged, otherwise they are propagated to the
|
||||
* caller
|
||||
* @return The new appender
|
||||
*/
|
||||
@PluginFactory
|
||||
public static TerminalConsoleAppender createAppender(
|
||||
@Required(message = "No name provided for TerminalConsoleAppender") @PluginAttribute("name") String name,
|
||||
@PluginElement("Filter") @Nullable Filter filter,
|
||||
@PluginElement("Layout") @Nullable Layout<? extends Serializable> layout,
|
||||
@PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) boolean ignoreExceptions)
|
||||
{
|
||||
if (layout == null)
|
||||
{
|
||||
layout = PatternLayout.createDefaultLayout();
|
||||
}
|
||||
|
||||
return new TerminalConsoleAppender(name, filter, layout, ignoreExceptions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraftforge.server.terminalconsole;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* TerminalConsoleAppender
|
||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.server.terminalconsole.util;
|
||||
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.Node;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
import org.apache.logging.log4j.core.layout.PatternMatch;
|
||||
import org.apache.logging.log4j.core.layout.PatternSelector;
|
||||
import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
||||
import org.apache.logging.log4j.core.pattern.PatternParser;
|
||||
import org.apache.logging.log4j.util.PerformanceSensitive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link PatternSelector} that selects patterns based on the logger name.
|
||||
* Can be used to log messages from different loggers using different patterns.
|
||||
*
|
||||
* <p>Multiple logger names may be separated using comma in the
|
||||
* {@link PatternMatch#getKey() PatternMatch "key"}. The pattern will be applied
|
||||
* if the logger name matches at least one of them.</p>
|
||||
*
|
||||
* <p><b>Example usage:</b></p>
|
||||
* <pre>{@code <PatternLayout>
|
||||
* <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss} %level] [%logger]: %msg%n">
|
||||
* <!-- Log root (empty logger name), "Main", and net.minecrell.* without logger prefix -->
|
||||
* <PatternMatch key=",Main,net.minecrell." pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
||||
* <PatternMatch key="com.example.Logger" pattern="EXAMPLE: %msg%n"/>
|
||||
* </LoggerNamePatternSelector>
|
||||
* </PatternLayout>}</pre>
|
||||
*/
|
||||
@Plugin(name = "LoggerNamePatternSelector", category = Node.CATEGORY, elementType = PatternSelector.ELEMENT_TYPE)
|
||||
@PerformanceSensitive("allocation")
|
||||
public class LoggerNamePatternSelector implements PatternSelector
|
||||
{
|
||||
private static class LoggerNameSelector
|
||||
{
|
||||
private final String name;
|
||||
private final boolean isPackage;
|
||||
private final PatternFormatter[] formatters;
|
||||
|
||||
LoggerNameSelector(String name, PatternFormatter[] formatters)
|
||||
{
|
||||
this.name = name;
|
||||
this.isPackage = name.endsWith(".");
|
||||
this.formatters = formatters;
|
||||
}
|
||||
|
||||
PatternFormatter[] get()
|
||||
{
|
||||
return this.formatters;
|
||||
}
|
||||
|
||||
boolean test(String s)
|
||||
{
|
||||
return this.isPackage ? s.startsWith(this.name) : s.equals(this.name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final PatternFormatter[] defaultFormatters;
|
||||
private final List<LoggerNameSelector> formatters = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs a new {@link LoggerNamePatternSelector}.
|
||||
*
|
||||
* @param defaultPattern The default pattern to use if no logger name matches
|
||||
* @param properties The pattern match rules to use
|
||||
* @param alwaysWriteExceptions Write exceptions even if pattern does not
|
||||
* include exception conversion
|
||||
* @param disableAnsi If true, disable all ANSI escape codes
|
||||
* @param noConsoleNoAnsi If true and {@link System#console()} is null,
|
||||
* disable ANSI escape codes
|
||||
* @param config The configuration
|
||||
*/
|
||||
protected LoggerNamePatternSelector(String defaultPattern, PatternMatch[] properties,
|
||||
boolean alwaysWriteExceptions, boolean disableAnsi, boolean noConsoleNoAnsi, Configuration config)
|
||||
{
|
||||
PatternParser parser = PatternLayout.createPatternParser(config);
|
||||
this.defaultFormatters = toArray(parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi));
|
||||
for (PatternMatch property : properties)
|
||||
{
|
||||
PatternFormatter[] formatters = toArray(parser.parse(property.getPattern(), alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi));
|
||||
for (String name : property.getKey().split(","))
|
||||
{
|
||||
this.formatters.add(new LoggerNameSelector(name, formatters));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PatternFormatter[] toArray(List<PatternFormatter> formatters)
|
||||
{
|
||||
return formatters.toArray(new PatternFormatter[formatters.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatternFormatter[] getFormatters(LogEvent event)
|
||||
{
|
||||
final String loggerName = event.getLoggerName();
|
||||
if (loggerName != null)
|
||||
{
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < this.formatters.size(); i++)
|
||||
{
|
||||
LoggerNameSelector selector = this.formatters.get(i);
|
||||
if (selector.test(loggerName))
|
||||
{
|
||||
return selector.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.defaultFormatters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LoggerNamePatternSelector}.
|
||||
*
|
||||
* @param defaultPattern The default pattern to use if no logger name matches
|
||||
* @param properties The pattern match rules to use
|
||||
* @param alwaysWriteExceptions Write exceptions even if pattern does not
|
||||
* include exception conversion
|
||||
* @param disableAnsi If true, disable all ANSI escape codes
|
||||
* @param noConsoleNoAnsi If true and {@link System#console()} is null,
|
||||
* disable ANSI escape codes
|
||||
* @param config The configuration
|
||||
* @return The new pattern selector
|
||||
*/
|
||||
@PluginFactory
|
||||
public static LoggerNamePatternSelector createSelector(
|
||||
@Required(message = "Default pattern is required") @PluginAttribute(value = "defaultPattern") String defaultPattern,
|
||||
@PluginElement("PatternMatch") PatternMatch[] properties,
|
||||
@PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) boolean alwaysWriteExceptions,
|
||||
@PluginAttribute("disableAnsi") boolean disableAnsi,
|
||||
@PluginAttribute("noConsoleNoAnsi") boolean noConsoleNoAnsi,
|
||||
@PluginConfiguration Configuration config)
|
||||
{
|
||||
return new LoggerNamePatternSelector(defaultPattern, properties, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi, config);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraftforge.server.terminalconsole.util;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.timings;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* ForgeTimings aggregates timings data collected by {@link TimeTracker} for an Object
|
||||
* and performs operations for interpretation of the data.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class ForgeTimings<T>
|
||||
{
|
||||
|
||||
private WeakReference<T> object;
|
||||
|
||||
private int[] rawTimingData;
|
||||
|
||||
public ForgeTimings(T object, int[] rawTimingData)
|
||||
{
|
||||
this.object = new WeakReference<T>(object);
|
||||
this.rawTimingData = rawTimingData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object that the timings are for
|
||||
*
|
||||
* @return The object
|
||||
*/
|
||||
public WeakReference<T> getObject()
|
||||
{
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Averages the raw timings data collected
|
||||
*
|
||||
* @return An average of the raw timing data
|
||||
*/
|
||||
public double getAverageTimings()
|
||||
{
|
||||
double sum = 0.0;
|
||||
|
||||
for (int data : rawTimingData)
|
||||
{
|
||||
sum += data;
|
||||
}
|
||||
|
||||
return sum / rawTimingData.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the raw timings data collected by the tracker
|
||||
* @return The raw timing data
|
||||
* @deprecated Added for compatibility, remove in 1.13
|
||||
*/
|
||||
public int[] getRawTimingData(){
|
||||
return Arrays.copyOfRange(rawTimingData, 0, rawTimingData.length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.timings;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
/**
|
||||
* A class to assist in the collection of data to measure the update times of ticking objects {currently Tile Entities and Entities}
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class TimeTracker<T>
|
||||
{
|
||||
|
||||
/**
|
||||
* A tracker for timing tile entity update
|
||||
*/
|
||||
public static final TimeTracker<TileEntity> TILE_ENTITY_UPDATE = new TimeTracker<>();
|
||||
/**
|
||||
* A tracker for timing entity updates
|
||||
*/
|
||||
public static final TimeTracker<Entity> ENTITY_UPDATE = new TimeTracker<>();
|
||||
|
||||
private boolean enabled;
|
||||
private int trackingDuration;
|
||||
private Map<T, int[]> timings = new MapMaker().weakKeys().makeMap();
|
||||
private WeakReference<T> currentlyTracking;
|
||||
private long trackTime;
|
||||
private long timing;
|
||||
|
||||
/**
|
||||
* Returns the timings data recorded by the tracker
|
||||
*
|
||||
* @return An immutable list of timings data collected by this tracker
|
||||
*/
|
||||
public ImmutableList<ForgeTimings<T>> getTimingData()
|
||||
{
|
||||
ImmutableList.Builder<ForgeTimings<T>> builder = ImmutableList.builder();
|
||||
|
||||
for (Map.Entry<T, int[]> entry : timings.entrySet())
|
||||
{
|
||||
builder.add(new ForgeTimings<>(entry.getKey(), Arrays.copyOfRange(entry.getValue(), 0, 99)));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the tracker (clears timings and stops any in-progress timings)
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
enabled = false;
|
||||
trackTime = 0;
|
||||
timings.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the timing of the currently tracking object
|
||||
*
|
||||
* @param tracking The object to stop timing
|
||||
*/
|
||||
public void trackEnd(T tracking)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
this.trackEnd(tracking, System.nanoTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recording tracking data for the given duration in seconds
|
||||
*
|
||||
* @param duration The duration for the time to track
|
||||
*/
|
||||
public void enable(int duration)
|
||||
{
|
||||
this.trackingDuration = duration;
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts timing of the provided object
|
||||
*
|
||||
* @param toTrack The object to start timing
|
||||
*/
|
||||
public void trackStart(T toTrack)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
this.trackStart(toTrack, System.nanoTime());
|
||||
}
|
||||
|
||||
private void trackEnd(T object, long nanoTime)
|
||||
{
|
||||
if (currentlyTracking == null || currentlyTracking.get() != object)
|
||||
{
|
||||
currentlyTracking = null;
|
||||
return;
|
||||
}
|
||||
int[] timings = this.timings.computeIfAbsent(object, k -> new int[101]);
|
||||
int idx = timings[100] = (timings[100] + 1) % 100;
|
||||
timings[idx] = (int) (nanoTime - timing);
|
||||
}
|
||||
|
||||
private void trackStart(T toTrack, long nanoTime)
|
||||
{
|
||||
if (trackTime == 0)
|
||||
{
|
||||
trackTime = nanoTime;
|
||||
}
|
||||
else if (trackTime + TimeUnit.NANOSECONDS.convert(trackingDuration, TimeUnit.SECONDS) < nanoTime)
|
||||
{
|
||||
enabled = false;
|
||||
trackTime = 0;
|
||||
}
|
||||
|
||||
currentlyTracking = new WeakReference<>(toTrack);
|
||||
timing = nanoTime;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user