Command work, and Added TileEntityType as a Forge Registry.

This commit is contained in:
LexManos 2018-09-15 00:59:19 -07:00
parent 3f743887e7
commit b4c23aba8e
22 changed files with 396 additions and 2758 deletions

View File

@ -0,0 +1,14 @@
--- a/net/minecraft/tileentity/TileEntityType.java
+++ b/net/minecraft/tileentity/TileEntityType.java
@@ -12,9 +12,9 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-public class TileEntityType<T extends TileEntity> {
+public class TileEntityType<T extends TileEntity> extends net.minecraftforge.registries.ForgeRegistryEntry<TileEntityType<T>> {
private static final Logger field_206866_A = LogManager.getLogger();
- public static final RegistryNamespaced<ResourceLocation, TileEntityType<?>> field_200970_a = new RegistryNamespaced<ResourceLocation, TileEntityType<?>>();
+ public static final RegistryNamespaced<ResourceLocation, TileEntityType<?>> field_200970_a = net.minecraftforge.registries.GameData.getWrapper(TileEntityType.class);
public static final TileEntityType<TileEntityFurnace> field_200971_b = func_200966_a("furnace", TileEntityType.Builder.func_200963_a(TileEntityFurnace::new));
public static final TileEntityType<TileEntityChest> field_200972_c = func_200966_a("chest", TileEntityType.Builder.func_200963_a(TileEntityChest::new));
public static final TileEntityType<TileEntityTrappedChest> field_200973_d = func_200966_a("trapped_chest", TileEntityType.Builder.func_200963_a(TileEntityTrappedChest::new));

View File

@ -1,27 +1,23 @@
--- a/net/minecraft/world/chunk/storage/AnvilChunkLoader.java
+++ b/net/minecraft/world/chunk/storage/AnvilChunkLoader.java
@@ -445,7 +445,20 @@
@@ -445,6 +445,16 @@
compound.setTag("Heightmaps", nbttagcompound2);
compound.setTag("Structures", this.func_202160_a(chunkIn.x, chunkIn.z, chunkIn.func_201609_c(), chunkIn.func_201604_d()));
+
+
+ try
+ {
+ final NBTTagCompound capTag = chunkIn.writeCapsToNBT();
+ if (capTag != null)
+ {
+ compound.setTag("ForgeCaps", capTag);
}
+ if (capTag != null) compound.setTag("ForgeCaps", capTag);
+ }
+ catch (Exception exception)
+ {
+ org.apache.logging.log4j.LogManager.getLogger().error("A capability provider has thrown an exception trying to write state. It will not persist. Report this to the mod author", exception);
+ }
+ }
}
private Chunk readChunkFromNBT(IWorld worldIn, NBTTagCompound compound) {
int i = compound.getInteger("xPos");
@@ -511,6 +524,10 @@
@@ -511,6 +521,10 @@
chunk.setModified(true);
}
@ -32,3 +28,12 @@
return chunk;
}
@@ -813,4 +827,8 @@
return flag;
}
+
+ public int getPendingSaveCount() {
+ return this.chunksToSave.size();
+ }
}

View File

@ -29,6 +29,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -207,6 +208,11 @@ public class DimensionManager
return worlds.keySet().toArray(new Integer[0]); // Only loaded dims, since usually used to cycle through loaded worlds
}
public static Stream<Integer> getIDStream()
{
return worlds.keySet().stream();
}
public static void setWorld(int id, @Nullable WorldServer world, MinecraftServer server)
{
if (world != null)

View File

@ -1,806 +0,0 @@
/*
* 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.fluids;
import java.util.Map;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLiquid;
import net.minecraft.block.BlockStairs;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.properties.PropertyInteger;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.property.ExtendedBlockState;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.common.property.PropertyFloat;
import net.minecraftforge.api.distmarker.Dist;
/**
* This is a base implementation for Fluid blocks.
*
* It is highly recommended that you extend this class or one of the Forge-provided child classes.
*
*/
public abstract class BlockFluidBase extends Block implements IFluidBlock
{
protected final static Map<Block, Boolean> defaultDisplacements = Maps.newHashMap();
static
{
defaultDisplacements.put(Blocks.OAK_DOOR, false);
defaultDisplacements.put(Blocks.SPRUCE_DOOR, false);
defaultDisplacements.put(Blocks.BIRCH_DOOR, false);
defaultDisplacements.put(Blocks.JUNGLE_DOOR, false);
defaultDisplacements.put(Blocks.ACACIA_DOOR, false);
defaultDisplacements.put(Blocks.DARK_OAK_DOOR, false);
defaultDisplacements.put(Blocks.TRAPDOOR, false);
defaultDisplacements.put(Blocks.IRON_TRAPDOOR, false);
defaultDisplacements.put(Blocks.OAK_FENCE, false);
defaultDisplacements.put(Blocks.SPRUCE_FENCE, false);
defaultDisplacements.put(Blocks.BIRCH_FENCE, false);
defaultDisplacements.put(Blocks.JUNGLE_FENCE, false);
defaultDisplacements.put(Blocks.DARK_OAK_FENCE, false);
defaultDisplacements.put(Blocks.ACACIA_FENCE, false);
defaultDisplacements.put(Blocks.NETHER_BRICK_FENCE, false);
defaultDisplacements.put(Blocks.OAK_FENCE_GATE, false);
defaultDisplacements.put(Blocks.SPRUCE_FENCE_GATE, false);
defaultDisplacements.put(Blocks.BIRCH_FENCE_GATE, false);
defaultDisplacements.put(Blocks.JUNGLE_FENCE_GATE, false);
defaultDisplacements.put(Blocks.DARK_OAK_FENCE_GATE, false);
defaultDisplacements.put(Blocks.ACACIA_FENCE_GATE, false);
defaultDisplacements.put(Blocks.WOODEN_PRESSURE_PLATE, false);
defaultDisplacements.put(Blocks.STONE_PRESSURE_PLATE, false);
defaultDisplacements.put(Blocks.LIGHT_WEIGHTED_PRESSURE_PLATE, false);
defaultDisplacements.put(Blocks.HEAVY_WEIGHTED_PRESSURE_PLATE, false);
defaultDisplacements.put(Blocks.LADDER, false);
defaultDisplacements.put(Blocks.IRON_BARS, false);
defaultDisplacements.put(Blocks.GLASS_PANE, false);
defaultDisplacements.put(Blocks.STAINED_GLASS_PANE, false);
defaultDisplacements.put(Blocks.PORTAL, false);
defaultDisplacements.put(Blocks.END_PORTAL, false);
defaultDisplacements.put(Blocks.COBBLESTONE_WALL, false);
defaultDisplacements.put(Blocks.BARRIER, false);
defaultDisplacements.put(Blocks.STANDING_BANNER, false);
defaultDisplacements.put(Blocks.WALL_BANNER, false);
defaultDisplacements.put(Blocks.CAKE, false);
defaultDisplacements.put(Blocks.IRON_DOOR, false);
defaultDisplacements.put(Blocks.STANDING_SIGN, false);
defaultDisplacements.put(Blocks.WALL_SIGN, false);
defaultDisplacements.put(Blocks.REEDS, false);
}
protected Map<Block, Boolean> displacements = Maps.newHashMap();
private static final class UnlistedPropertyBool extends Properties.PropertyAdapter<Boolean>
{
public UnlistedPropertyBool(String name)
{
super(PropertyBool.create(name));
}
}
public static final PropertyInteger LEVEL = PropertyInteger.create("level", 0, 15);
public static final PropertyFloat[] LEVEL_CORNERS = new PropertyFloat[4];
public static final PropertyFloat FLOW_DIRECTION = new PropertyFloat("flow_direction", -1000f, 1000f);
public static final UnlistedPropertyBool[] SIDE_OVERLAYS = new UnlistedPropertyBool[4];
public static final ImmutableList<IUnlistedProperty<?>> FLUID_RENDER_PROPS;
static
{
ImmutableList.Builder<IUnlistedProperty<?>> builder = ImmutableList.builder();
builder.add(FLOW_DIRECTION);
for(int i = 0; i < 4; i++)
{
LEVEL_CORNERS[i] = new PropertyFloat("level_corner_" + i, 0f, 1f);
builder.add(LEVEL_CORNERS[i]);
SIDE_OVERLAYS[i] = new UnlistedPropertyBool("side_overlay_" + i);
builder.add(SIDE_OVERLAYS[i]);
}
FLUID_RENDER_PROPS = builder.build();
}
protected int quantaPerBlock = 8;
protected float quantaPerBlockFloat = 8F;
protected float quantaFraction = 8f / 9f;
protected int density = 1;
protected int densityDir = -1;
protected int temperature = 295;
protected int tickRate = 20;
protected BlockRenderLayer renderLayer = BlockRenderLayer.TRANSLUCENT;
protected int maxScaledLight = 0;
protected final String fluidName;
/**
* This is the fluid used in the constructor. Use this reference to configure things
* like icons for your block. It might not be active in the registry, so do
* NOT expose it.
*/
protected final Fluid definedFluid;
public BlockFluidBase(Fluid fluid, Material material)
{
super(material);
this.setTickRandomly(true);
this.disableStats();
this.fluidName = fluid.getName();
this.density = fluid.density;
this.temperature = fluid.temperature;
this.maxScaledLight = fluid.luminosity;
this.tickRate = fluid.viscosity / 200;
this.densityDir = fluid.density > 0 ? -1 : 1;
fluid.setBlock(this);
this.definedFluid = fluid;
displacements.putAll(defaultDisplacements);
this.setDefaultState(blockState.getBaseState().withProperty(LEVEL, getMaxRenderHeightMeta()));
}
@Override
@Nonnull
protected BlockStateContainer createBlockState()
{
return new BlockStateContainer.Builder(this)
.add(LEVEL)
.add(FLUID_RENDER_PROPS.toArray(new IUnlistedProperty<?>[0]))
.build();
}
@Override
public int getMetaFromState(@Nonnull IBlockState state)
{
return state.getValue(LEVEL);
}
@Override
@Deprecated
@Nonnull
public IBlockState getStateFromMeta(int meta)
{
return this.getDefaultState().withProperty(LEVEL, meta);
}
public BlockFluidBase setQuantaPerBlock(int quantaPerBlock)
{
if (quantaPerBlock > 16 || quantaPerBlock < 1) quantaPerBlock = 8;
this.quantaPerBlock = quantaPerBlock;
this.quantaPerBlockFloat = quantaPerBlock;
this.quantaFraction = quantaPerBlock / (quantaPerBlock + 1f);
return this;
}
public BlockFluidBase setDensity(int density)
{
if (density == 0) density = 1;
this.density = density;
this.densityDir = density > 0 ? -1 : 1;
return this;
}
public BlockFluidBase setTemperature(int temperature)
{
this.temperature = temperature;
return this;
}
public BlockFluidBase setTickRate(int tickRate)
{
if (tickRate <= 0) tickRate = 20;
this.tickRate = tickRate;
return this;
}
public BlockFluidBase setRenderLayer(BlockRenderLayer renderLayer)
{
this.renderLayer = renderLayer;
return this;
}
public BlockFluidBase setMaxScaledLight(int maxScaledLight)
{
this.maxScaledLight = maxScaledLight;
return this;
}
public final int getDensity()
{
return density;
}
public final int getTemperature()
{
return temperature;
}
/**
* Returns true if the block at (pos) is displaceable. Does not displace the block.
*/
public boolean canDisplace(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block.isAir(state, world, pos))
{
return true;
}
if (block == this)
{
return false;
}
if (displacements.containsKey(block))
{
return displacements.get(block);
}
Material material = state.getMaterial();
if (material.blocksMovement() || material == Material.PORTAL || material == Material.STRUCTURE_VOID)
{
return false;
}
int density = getDensity(world, pos);
if (density == Integer.MAX_VALUE)
{
return true;
}
return this.density > density;
}
/**
* Attempt to displace the block at (pos), return true if it was displaced.
*/
public boolean displaceIfPossible(World world, BlockPos pos)
{
boolean canDisplace = canDisplace(world, pos);
if (canDisplace)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (!block.isAir(state, world, pos) && !isFluid(state))
{
// Forge: Vanilla has a 'bug' where snowballs don't drop like every other block. So special case because ewww...
if (block != Blocks.SNOW_LAYER) block.dropBlockAsItem(world, pos, state, 0);
}
}
return canDisplace;
}
public abstract int getQuantaValue(IBlockAccess world, BlockPos pos);
@Override
public abstract boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit);
public abstract int getMaxRenderHeightMeta();
/* BLOCK FUNCTIONS */
@Override
public void onBlockAdded(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state)
{
world.scheduleUpdate(pos, this, tickRate);
}
@Override
public void neighborChanged(@Nonnull IBlockState state, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Block neighborBlock, @Nonnull BlockPos neighbourPos)
{
world.scheduleUpdate(pos, this, tickRate);
}
// Used to prevent updates on chunk generation
@Override
public boolean requiresUpdates()
{
return false;
}
@Override
public boolean isPassable(@Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
return true;
}
@Override
@Nonnull
public Item getItemDropped(@Nonnull IBlockState state, @Nonnull Random rand, int fortune)
{
return Items.AIR;
}
@Override
public int quantityDropped(@Nonnull Random par1Random)
{
return 0;
}
@Override
public int tickRate(@Nonnull World world)
{
return tickRate;
}
@Override
@Nonnull
public Vec3d modifyAcceleration(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Entity entity, @Nonnull Vec3d vec)
{
return densityDir > 0 ? vec : vec.add(getFlowVector(world, pos));
}
@Override
public int getLightValue(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
if (maxScaledLight == 0)
{
return super.getLightValue(state, world, pos);
}
return (int) (getQuantaPercentage(world, pos) * maxScaledLight);
}
@Override
public boolean isOpaqueCube(@Nonnull IBlockState state)
{
return false;
}
@Override
public boolean isFullCube(@Nonnull IBlockState state)
{
return false;
}
@Override
public int getPackedLightmapCoords(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
int lightThis = world.getCombinedLight(pos, 0);
int lightUp = world.getCombinedLight(pos.up(), 0);
int lightThisBase = lightThis & 255;
int lightUpBase = lightUp & 255;
int lightThisExt = lightThis >> 16 & 255;
int lightUpExt = lightUp >> 16 & 255;
return (lightThisBase > lightUpBase ? lightThisBase : lightUpBase) |
((lightThisExt > lightUpExt ? lightThisExt : lightUpExt) << 16);
}
@Override
@OnlyIn(Dist.CLIENT)
@Nonnull
public BlockRenderLayer getBlockLayer()
{
return this.renderLayer;
}
@Override
@Nonnull
public BlockFaceShape getBlockFaceShape(@Nonnull IBlockAccess worldIn, @Nonnull IBlockState state, @Nonnull BlockPos pos, @Nonnull EnumFacing face)
{
return BlockFaceShape.UNDEFINED;
}
@Override
public boolean shouldSideBeRendered(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side)
{
IBlockState neighbor = world.getBlockState(pos.offset(side));
if (neighbor.getMaterial() == state.getMaterial())
{
return false;
}
if (side == (densityDir < 0 ? EnumFacing.UP : EnumFacing.DOWN))
{
return true;
}
return super.shouldSideBeRendered(state, world, pos, side);
}
private static boolean isFluid(@Nonnull IBlockState blockstate)
{
return blockstate.getMaterial().isLiquid() || blockstate.getBlock() instanceof IFluidBlock;
}
@Override
@Nonnull
public IBlockState getExtendedState(@Nonnull IBlockState oldState, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
IExtendedBlockState state = (IExtendedBlockState)oldState;
state = state.withProperty(FLOW_DIRECTION, (float)getFlowDirection(world, pos));
IBlockState[][] upBlockState = new IBlockState[3][3];
float[][] height = new float[3][3];
float[][] corner = new float[2][2];
upBlockState[1][1] = world.getBlockState(pos.down(densityDir));
height[1][1] = getFluidHeightForRender(world, pos, upBlockState[1][1]);
if (height[1][1] == 1)
{
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
corner[i][j] = 1;
}
}
}
else
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (i != 1 || j != 1)
{
upBlockState[i][j] = world.getBlockState(pos.add(i - 1, 0, j - 1).down(densityDir));
height[i][j] = getFluidHeightForRender(world, pos.add(i - 1, 0, j - 1), upBlockState[i][j]);
}
}
}
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
corner[i][j] = getFluidHeightAverage(height[i][j], height[i][j + 1], height[i + 1][j], height[i + 1][j + 1]);
}
}
//check for downflow above corners
boolean n = isFluid(upBlockState[0][1]);
boolean s = isFluid(upBlockState[2][1]);
boolean w = isFluid(upBlockState[1][0]);
boolean e = isFluid(upBlockState[1][2]);
boolean nw = isFluid(upBlockState[0][0]);
boolean ne = isFluid(upBlockState[0][2]);
boolean sw = isFluid(upBlockState[2][0]);
boolean se = isFluid(upBlockState[2][2]);
if (nw || n || w)
{
corner[0][0] = 1;
}
if (ne || n || e)
{
corner[0][1] = 1;
}
if (sw || s || w)
{
corner[1][0] = 1;
}
if (se || s || e)
{
corner[1][1] = 1;
}
}
for (int i = 0; i < 4; i++)
{
EnumFacing side = EnumFacing.getHorizontal(i);
BlockPos offset = pos.offset(side);
boolean useOverlay = world.getBlockState(offset).getBlockFaceShape(world, offset, side.getOpposite()) == BlockFaceShape.SOLID;
state = state.withProperty(SIDE_OVERLAYS[i], useOverlay);
}
state = state.withProperty(LEVEL_CORNERS[0], corner[0][0]);
state = state.withProperty(LEVEL_CORNERS[1], corner[0][1]);
state = state.withProperty(LEVEL_CORNERS[2], corner[1][1]);
state = state.withProperty(LEVEL_CORNERS[3], corner[1][0]);
return state;
}
/* FLUID FUNCTIONS */
public static int getDensity(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block instanceof BlockFluidBase)
{
return ((BlockFluidBase)block).getDensity();
}
Fluid fluid = getFluid(state);
if (fluid != null)
{
return fluid.getDensity();
}
return Integer.MAX_VALUE;
}
public static int getTemperature(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block instanceof BlockFluidBase)
{
return ((BlockFluidBase)block).getTemperature();
}
Fluid fluid = getFluid(state);
if (fluid != null)
{
return fluid.getTemperature();
}
return Integer.MAX_VALUE;
}
@Nullable
private static Fluid getFluid(IBlockState state)
{
Block block = state.getBlock();
if (block instanceof IFluidBlock)
{
return ((IFluidBlock)block).getFluid();
}
if (block instanceof BlockLiquid)
{
if (state.getMaterial() == Material.WATER)
{
return FluidRegistry.WATER;
}
if (state.getMaterial() == Material.LAVA)
{
return FluidRegistry.LAVA;
}
}
return null;
}
public static double getFlowDirection(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
if (!state.getMaterial().isLiquid())
{
return -1000.0;
}
Vec3d vec = ((BlockFluidBase)state.getBlock()).getFlowVector(world, pos);
return vec.x == 0.0D && vec.z == 0.0D ? -1000.0D : MathHelper.atan2(vec.z, vec.x) - Math.PI / 2D;
}
public final int getQuantaValueBelow(IBlockAccess world, BlockPos pos, int belowThis)
{
int quantaRemaining = getQuantaValue(world, pos);
if (quantaRemaining >= belowThis)
{
return -1;
}
return quantaRemaining;
}
public final int getQuantaValueAbove(IBlockAccess world, BlockPos pos, int aboveThis)
{
int quantaRemaining = getQuantaValue(world, pos);
if (quantaRemaining <= aboveThis)
{
return -1;
}
return quantaRemaining;
}
public final float getQuantaPercentage(IBlockAccess world, BlockPos pos)
{
int quantaRemaining = getQuantaValue(world, pos);
return quantaRemaining / quantaPerBlockFloat;
}
public float getFluidHeightAverage(float... flow)
{
float total = 0;
int count = 0;
for (int i = 0; i < flow.length; i++)
{
if (flow[i] >= quantaFraction)
{
total += flow[i] * 10;
count += 10;
}
if (flow[i] >= 0)
{
total += flow[i];
count++;
}
}
return total / count;
}
public float getFluidHeightForRender(IBlockAccess world, BlockPos pos, @Nonnull IBlockState up)
{
IBlockState here = world.getBlockState(pos);
if (here.getBlock() == this)
{
if (isFluid(up))
{
return 1;
}
if (getMetaFromState(here) == getMaxRenderHeightMeta())
{
return quantaFraction;
}
}
if (here.getBlock() instanceof BlockLiquid)
{
return Math.min(1 - BlockLiquid.getLiquidHeightPercent(here.getValue(BlockLiquid.LEVEL)), quantaFraction);
}
return !here.getMaterial().isSolid() && up.getBlock() == this ? 1 : this.getQuantaPercentage(world, pos) * quantaFraction;
}
public Vec3d getFlowVector(IBlockAccess world, BlockPos pos)
{
Vec3d vec = new Vec3d(0.0D, 0.0D, 0.0D);
int decay = getFlowDecay(world, pos);
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
BlockPos offset = pos.offset(side);
int otherDecay = getFlowDecay(world, offset);
if (otherDecay >= quantaPerBlock)
{
if (!world.getBlockState(offset).getMaterial().blocksMovement())
{
otherDecay = getFlowDecay(world, offset.up(densityDir));
if (otherDecay < quantaPerBlock)
{
int power = otherDecay - (decay - quantaPerBlock);
vec = vec.addVector(side.getFrontOffsetX() * power, 0, side.getFrontOffsetZ() * power);
}
}
}
else
{
int power = otherDecay - decay;
vec = vec.addVector(side.getFrontOffsetX() * power, 0, side.getFrontOffsetZ() * power);
}
}
if (hasVerticalFlow(world, pos))
{
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
BlockPos offset = pos.offset(side);
if (causesDownwardCurrent(world, offset, side) || causesDownwardCurrent(world, offset.down(densityDir), side))
{
vec = vec.normalize().addVector(0.0, 6.0 * densityDir, 0.0);
break;
}
}
}
return vec.normalize();
}
private int getFlowDecay(IBlockAccess world, BlockPos pos)
{
int quantaValue = getQuantaValue(world, pos);
return quantaValue > 0 && hasVerticalFlow(world, pos) ? 0 : quantaPerBlock - quantaValue;
}
private boolean hasVerticalFlow(IBlockAccess world, BlockPos pos)
{
return world.getBlockState(pos.down(densityDir)).getBlock() == this;
}
protected boolean causesDownwardCurrent(IBlockAccess world, BlockPos pos, EnumFacing face)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block == this) return false;
if (face == (densityDir < 0 ? EnumFacing.UP : EnumFacing.DOWN)) return true;
if (state.getMaterial() == Material.ICE) return false;
boolean flag = isExceptBlockForAttachWithPiston(block) || block instanceof BlockStairs;
return !flag && state.getBlockFaceShape(world, pos, face) == BlockFaceShape.SOLID;
}
/* IFluidBlock */
@Override
public Fluid getFluid()
{
return FluidRegistry.getFluid(fluidName);
}
@Override
public float getFilledPercentage(World world, BlockPos pos)
{
return getFilledPercentage((IBlockAccess) world, pos);
}
public float getFilledPercentage(IBlockAccess world, BlockPos pos)
{
int quantaRemaining = getQuantaValue(world, pos);
float remaining = (quantaRemaining + 1f) / (quantaPerBlockFloat + 1f);
return remaining * (density > 0 ? 1 : -1);
}
@Override
public AxisAlignedBB getCollisionBoundingBox(@Nonnull IBlockState blockState, @Nonnull IBlockAccess worldIn, @Nonnull BlockPos pos)
{
return NULL_AABB;
}
@Override
@OnlyIn(Dist.CLIENT)
public Vec3d getFogColor(World world, BlockPos pos, IBlockState state, Entity entity, Vec3d originalColor, float partialTicks)
{
if (!isWithinFluid(world, pos, ActiveRenderInfo.projectViewFromEntity(entity, partialTicks)))
{
BlockPos otherPos = pos.down(densityDir);
IBlockState otherState = world.getBlockState(otherPos);
return otherState.getBlock().getFogColor(world, otherPos, otherState, entity, originalColor, partialTicks);
}
if (getFluid() != null)
{
int color = getFluid().getColor();
float red = (color >> 16 & 0xFF) / 255.0F;
float green = (color >> 8 & 0xFF) / 255.0F;
float blue = (color & 0xFF) / 255.0F;
return new Vec3d(red, green, blue);
}
return super.getFogColor(world, pos, state, entity, originalColor, partialTicks);
}
@Override
public IBlockState getStateAtViewpoint(IBlockState state, IBlockAccess world, BlockPos pos, Vec3d viewpoint)
{
if (!isWithinFluid(world, pos, viewpoint))
{
return world.getBlockState(pos.down(densityDir));
}
return super.getStateAtViewpoint(state, world, pos, viewpoint);
}
private boolean isWithinFluid(IBlockAccess world, BlockPos pos, Vec3d vec)
{
float filled = getFilledPercentage(world, pos);
return filled < 0 ? vec.y > pos.getY() + filled + 1
: vec.y < pos.getY() + filled;
}
}

View File

@ -1,338 +0,0 @@
/*
* 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.fluids;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import com.google.common.primitives.Ints;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.event.ForgeEventFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* This is a fluid block implementation which emulates vanilla Minecraft fluid behavior.
*
* It is highly recommended that you use/extend this class for "classic" fluid blocks.
*
*/
public class BlockFluidClassic extends BlockFluidBase
{
protected static final List<EnumFacing> SIDES = Collections.unmodifiableList(Arrays.asList(
EnumFacing.WEST, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.SOUTH));
protected boolean[] isOptimalFlowDirection = new boolean[4];
protected int[] flowCost = new int[4];
protected boolean canCreateSources = false;
protected FluidStack stack;
public BlockFluidClassic(Fluid fluid, Material material)
{
super(fluid, material);
stack = new FluidStack(fluid, Fluid.BUCKET_VOLUME);
}
public BlockFluidClassic setFluidStack(FluidStack stack)
{
this.stack = stack;
return this;
}
public BlockFluidClassic setFluidStackAmount(int amount)
{
this.stack.amount = amount;
return this;
}
@Override
public int getQuantaValue(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
if (state.getBlock().isAir(state, world, pos))
{
return 0;
}
if (state.getBlock() != this)
{
return -1;
}
return quantaPerBlock - state.getValue(LEVEL);
}
@Override
public boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit)
{
return fullHit && state.getValue(LEVEL) == 0;
}
@Override
public int getMaxRenderHeightMeta()
{
return 0;
}
@Override
public void updateTick(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Random rand)
{
int quantaRemaining = quantaPerBlock - state.getValue(LEVEL);
int expQuanta = -101;
// check adjacent block levels if non-source
if (quantaRemaining < quantaPerBlock)
{
int adjacentSourceBlocks = 0;
if (ForgeEventFactory.canCreateFluidSource(world, pos, state, canCreateSources))
{
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
if (isSourceBlock(world, pos.offset(side))) adjacentSourceBlocks++;
}
}
// new source block
if (adjacentSourceBlocks >= 2 && (world.getBlockState(pos.up(densityDir)).getMaterial().isSolid() || isSourceBlock(world, pos.up(densityDir))))
{
expQuanta = quantaPerBlock;
}
// unobstructed flow from 'above'
else if (world.getBlockState(pos.down(densityDir)).getBlock() == this
|| hasDownhillFlow(world, pos, EnumFacing.EAST)
|| hasDownhillFlow(world, pos, EnumFacing.WEST)
|| hasDownhillFlow(world, pos, EnumFacing.NORTH)
|| hasDownhillFlow(world, pos, EnumFacing.SOUTH))
{
expQuanta = quantaPerBlock - 1;
}
else
{
int maxQuanta = -100;
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
maxQuanta = getLargerQuanta(world, pos.offset(side), maxQuanta);
}
expQuanta = maxQuanta - 1;
}
// decay calculation
if (expQuanta != quantaRemaining)
{
quantaRemaining = expQuanta;
if (expQuanta <= 0)
{
world.setBlockToAir(pos);
}
else
{
world.setBlockState(pos, state.withProperty(LEVEL, quantaPerBlock - expQuanta), 2);
world.scheduleUpdate(pos, this, tickRate);
world.notifyNeighborsOfStateChange(pos, this, false);
}
}
}
// Flow vertically if possible
if (canDisplace(world, pos.up(densityDir)))
{
flowIntoBlock(world, pos.up(densityDir), 1);
return;
}
// Flow outward if possible
int flowMeta = quantaPerBlock - quantaRemaining + 1;
if (flowMeta >= quantaPerBlock)
{
return;
}
if (isSourceBlock(world, pos) || !isFlowingVertically(world, pos))
{
if (world.getBlockState(pos.down(densityDir)).getBlock() == this)
{
flowMeta = 1;
}
boolean flowTo[] = getOptimalFlowDirections(world, pos);
for (int i = 0; i < 4; i++)
{
if (flowTo[i]) flowIntoBlock(world, pos.offset(SIDES.get(i)), flowMeta);
}
}
}
protected final boolean hasDownhillFlow(IBlockAccess world, BlockPos pos, EnumFacing direction)
{
return world.getBlockState(pos.offset(direction).down(densityDir)).getBlock() == this
&& (canFlowInto(world, pos.offset(direction))
|| canFlowInto(world, pos.down(densityDir)));
}
public boolean isFlowingVertically(IBlockAccess world, BlockPos pos)
{
return world.getBlockState(pos.up(densityDir)).getBlock() == this ||
(world.getBlockState(pos).getBlock() == this && canFlowInto(world, pos.up(densityDir)));
}
public boolean isSourceBlock(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
return state.getBlock() == this && state.getValue(LEVEL) == 0;
}
protected boolean[] getOptimalFlowDirections(World world, BlockPos pos)
{
for (int side = 0; side < 4; side++)
{
flowCost[side] = 1000;
BlockPos pos2 = pos.offset(SIDES.get(side));
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
{
continue;
}
if (canFlowInto(world, pos2.up(densityDir)))
{
flowCost[side] = 0;
}
else
{
flowCost[side] = calculateFlowCost(world, pos2, 1, side);
}
}
int min = Ints.min(flowCost);
for (int side = 0; side < 4; side++)
{
isOptimalFlowDirection[side] = flowCost[side] == min;
}
return isOptimalFlowDirection;
}
protected int calculateFlowCost(World world, BlockPos pos, int recurseDepth, int side)
{
int cost = 1000;
for (int adjSide = 0; adjSide < 4; adjSide++)
{
if (SIDES.get(adjSide) == SIDES.get(side).getOpposite())
{
continue;
}
BlockPos pos2 = pos.offset(SIDES.get(adjSide));
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
{
continue;
}
if (canFlowInto(world, pos2.up(densityDir)))
{
return recurseDepth;
}
if (recurseDepth >= quantaPerBlock / 2)
{
continue;
}
cost = Math.min(cost, calculateFlowCost(world, pos2, recurseDepth + 1, adjSide));
}
return cost;
}
protected void flowIntoBlock(World world, BlockPos pos, int meta)
{
if (meta < 0) return;
if (displaceIfPossible(world, pos))
{
world.setBlockState(pos, this.getDefaultState().withProperty(LEVEL, meta), 3);
}
}
protected boolean canFlowInto(IBlockAccess world, BlockPos pos)
{
return world.getBlockState(pos).getBlock() == this || canDisplace(world, pos);
}
protected int getLargerQuanta(IBlockAccess world, BlockPos pos, int compare)
{
int quantaRemaining = getQuantaValue(world, pos);
if (quantaRemaining <= 0)
{
return compare;
}
return quantaRemaining >= compare ? quantaRemaining : compare;
}
/* IFluidBlock */
@Override
public int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace)
{
if (fluidStack.amount < Fluid.BUCKET_VOLUME)
{
return 0;
}
if (doPlace)
{
FluidUtil.destroyBlockOnFluidPlacement(world, pos);
world.setBlockState(pos, this.getDefaultState(), 11);
}
return Fluid.BUCKET_VOLUME;
}
@Override
@Nullable
public FluidStack drain(World world, BlockPos pos, boolean doDrain)
{
if (!isSourceBlock(world, pos))
{
return null;
}
if (doDrain)
{
world.setBlockToAir(pos);
}
return stack.copy();
}
@Override
public boolean canDrain(World world, BlockPos pos)
{
return isSourceBlock(world, pos);
}
}

View File

@ -1,300 +0,0 @@
/*
* 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.fluids;
import java.util.Random;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
/**
* This is a cellular-automata based finite fluid block implementation.
*
* It is highly recommended that you use/extend this class for finite fluid blocks.
*
*/
public class BlockFluidFinite extends BlockFluidBase
{
public BlockFluidFinite(Fluid fluid, Material material)
{
super(fluid, material);
}
@Override
public int getQuantaValue(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
if (state.getBlock().isAir(state, world, pos))
{
return 0;
}
if (state.getBlock() != this)
{
return -1;
}
return state.getValue(LEVEL) + 1;
}
@Override
public boolean canCollideCheck(@Nonnull IBlockState state, boolean fullHit)
{
return fullHit;
}
@Override
public int getMaxRenderHeightMeta()
{
return quantaPerBlock - 1;
}
@Override
public void updateTick(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Random rand)
{
boolean changed = false;
int quantaRemaining = state.getValue(LEVEL) + 1;
// Flow vertically if possible
int prevRemaining = quantaRemaining;
quantaRemaining = tryToFlowVerticallyInto(world, pos, quantaRemaining);
if (quantaRemaining < 1)
{
return;
}
else if (quantaRemaining != prevRemaining)
{
changed = true;
if (quantaRemaining == 1)
{
world.setBlockState(pos, state.withProperty(LEVEL, quantaRemaining - 1), 2);
return;
}
}
else if (quantaRemaining == 1)
{
return;
}
// Flow out if possible
int lowerThan = quantaRemaining - 1;
int total = quantaRemaining;
int count = 1;
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
BlockPos off = pos.offset(side);
if (displaceIfPossible(world, off))
world.setBlockToAir(off);
int quanta = getQuantaValueBelow(world, off, lowerThan);
if (quanta >= 0)
{
count++;
total += quanta;
}
}
if (count == 1)
{
if (changed)
{
world.setBlockState(pos, state.withProperty(LEVEL, quantaRemaining - 1), 2);
}
return;
}
int each = total / count;
int rem = total % count;
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
BlockPos off = pos.offset(side);
int quanta = getQuantaValueBelow(world, off, lowerThan);
if (quanta >= 0)
{
int newQuanta = each;
if (rem == count || rem > 1 && rand.nextInt(count - rem) != 0)
{
++newQuanta;
--rem;
}
if (newQuanta != quanta)
{
if (newQuanta == 0)
{
world.setBlockToAir(off);
}
else
{
world.setBlockState(off, getDefaultState().withProperty(LEVEL, newQuanta - 1), 2);
}
world.scheduleUpdate(off, this, tickRate);
}
--count;
}
}
if (rem > 0)
{
++each;
}
world.setBlockState(pos, state.withProperty(LEVEL, each - 1), 2);
}
public int tryToFlowVerticallyInto(World world, BlockPos pos, int amtToInput)
{
IBlockState myState = world.getBlockState(pos);
BlockPos other = pos.add(0, densityDir, 0);
if (other.getY() < 0 || other.getY() >= world.getHeight())
{
world.setBlockToAir(pos);
return 0;
}
int amt = getQuantaValueBelow(world, other, quantaPerBlock);
if (amt >= 0)
{
amt += amtToInput;
if (amt > quantaPerBlock)
{
world.setBlockState(other, myState.withProperty(LEVEL, quantaPerBlock - 1), 3);
world.scheduleUpdate(other, this, tickRate);
return amt - quantaPerBlock;
}
else if (amt > 0)
{
world.setBlockState(other, myState.withProperty(LEVEL, amt - 1), 3);
world.scheduleUpdate(other, this, tickRate);
world.setBlockToAir(pos);
return 0;
}
return amtToInput;
}
else
{
int density_other = getDensity(world, other);
if (density_other == Integer.MAX_VALUE)
{
if (displaceIfPossible(world, other))
{
world.setBlockState(other, myState.withProperty(LEVEL, amtToInput - 1), 3);
world.scheduleUpdate(other, this, tickRate);
world.setBlockToAir(pos);
return 0;
}
else
{
return amtToInput;
}
}
if (densityDir < 0)
{
if (density_other < density) // then swap
{
IBlockState state = world.getBlockState(other);
world.setBlockState(other, myState.withProperty(LEVEL, amtToInput - 1), 3);
world.setBlockState(pos, state, 3);
world.scheduleUpdate(other, this, tickRate);
world.scheduleUpdate(pos, state.getBlock(), state.getBlock().tickRate(world));
return 0;
}
}
else
{
if (density_other > density)
{
IBlockState state = world.getBlockState(other);
world.setBlockState(other, myState.withProperty(LEVEL, amtToInput - 1), 3);
world.setBlockState(pos, state, 3);
world.scheduleUpdate(other, this, tickRate);
world.scheduleUpdate(pos, state.getBlock(), state.getBlock().tickRate(world));
return 0;
}
}
return amtToInput;
}
}
/* IFluidBlock */
@Override
public int place(World world, BlockPos pos, @Nonnull FluidStack fluidStack, boolean doPlace)
{
IBlockState existing = world.getBlockState(pos);
float quantaAmount = Fluid.BUCKET_VOLUME / quantaPerBlockFloat;
// If the stack contains more available fluid than the full source block,
// set a source block
int closest = Fluid.BUCKET_VOLUME;
int quanta = quantaPerBlock;
if (fluidStack.amount < closest)
{
// Figure out maximum level to match stack amount
closest = MathHelper.floor(quantaAmount * MathHelper.floor(fluidStack.amount / quantaAmount));
quanta = MathHelper.floor(closest / quantaAmount);
}
if (existing.getBlock() == this)
{
int existingQuanta = existing.getValue(LEVEL) + 1;
int missingQuanta = quantaPerBlock - existingQuanta;
closest = Math.min(closest, MathHelper.floor(missingQuanta * quantaAmount));
quanta = Math.min(quanta + existingQuanta, quantaPerBlock);
}
// If too little (or too much, technically impossible) fluid is to be placed, abort
if (quanta < 1 || quanta > 16)
return 0;
if (doPlace)
{
FluidUtil.destroyBlockOnFluidPlacement(world, pos);
world.setBlockState(pos, getDefaultState().withProperty(LEVEL, quanta - 1), 11);
}
return closest;
}
@Override
public FluidStack drain(World world, BlockPos pos, boolean doDrain)
{
final FluidStack fluidStack = new FluidStack(getFluid(), MathHelper.floor(getQuantaPercentage(world, pos) * Fluid.BUCKET_VOLUME));
if (doDrain)
{
world.setBlockToAir(pos);
}
return fluidStack;
}
@Override
public boolean canDrain(World world, BlockPos pos)
{
return true;
}
}

View File

@ -30,11 +30,9 @@ import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.translation.I18n;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.item.EnumRarity;
@ -131,7 +129,7 @@ public class Fluid
* The default value of null should remain for any Fluid without a Block implementation.
*/
protected Block block = null;
/**
* Color used by universal bucket and the ModelFluid baked model.
* Note that this int includes the alpha so converting this to RGB with alpha would be
@ -245,13 +243,13 @@ public class Fluid
this.emptySound = emptySound;
return this;
}
public Fluid setColor(Color color)
{
this.color = color.getRGB();
return this;
}
public Fluid setColor(int color)
{
this.color = color;
@ -275,15 +273,10 @@ public class Fluid
public final boolean isLighterThanAir()
{
int density = this.density;
if (block instanceof BlockFluidBase)
{
density = ((BlockFluidBase) block).getDensity();
}
return density <= 0;
return this.density <= 0;
}
/**
/**
* Determines if this fluid should vaporize in dimensions where water vaporizes when placed.
* To preserve the intentions of vanilla, fluids that can turn lava into obsidian should vaporize.
* This prevents players from making the nether safe with a single bucket.
@ -299,7 +292,7 @@ public class Fluid
return block.getDefaultState().getMaterial() == Material.WATER;
}
/**
/**
* Called instead of placing the fluid block if {@link WorldProvider#doesWaterVaporize()} and {@link #doesVaporize(FluidStack)} are true.
* Override this to make your explosive liquid blow up instead of the default smoke, etc.
* Based on {@link net.minecraft.item.ItemBucket#tryPlaceContainedLiquid(EntityPlayer, World, BlockPos)}

View File

@ -19,39 +19,11 @@
package net.minecraftforge.fluids;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.minecraftforge.fml.ModThreadContext;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.registries.IRegistryDelegate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import javax.annotation.Nullable;
/**
* Handles Fluid registrations. Fluids MUST be registered in order to function.
*/
@ -59,196 +31,8 @@ public abstract class FluidRegistry
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker FLUIDS = MarkerManager.getMarker("FLUIDS");
static int maxID = 0;
static BiMap<String, Fluid> fluids = HashBiMap.create();
static BiMap<Fluid, Integer> fluidIDs = HashBiMap.create();
static BiMap<Integer, String> fluidNames = HashBiMap.create(); //Caching this just makes some other calls faster
static BiMap<Block, Fluid> fluidBlocks;
// the globally unique fluid map - only used to associate non-defaults during world/server loading
static BiMap<String,Fluid> masterFluidReference = HashBiMap.create();
static BiMap<String,String> defaultFluidName = HashBiMap.create();
static Map<Fluid,FluidDelegate> delegates = Maps.newHashMap();
static boolean universalBucketEnabled = false;
static Set<Fluid> bucketFluids = Sets.newHashSet();
public static final Fluid WATER = new Fluid("water", new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"), new ResourceLocation("blocks/water_overlay")) {
@Override
public String getLocalizedName(FluidStack fs) {
return I18n.translateToLocal("tile.water.name");
}
}.setBlock(Blocks.WATER).setUnlocalizedName(Blocks.WATER.getUnlocalizedName());
public static final Fluid LAVA = new Fluid("lava", new ResourceLocation("blocks/lava_still"), new ResourceLocation("blocks/lava_flow")) {
@Override
public String getLocalizedName(FluidStack fs) {
return I18n.translateToLocal("tile.lava.name");
}
}.setBlock(Blocks.LAVA).setLuminosity(15).setDensity(3000).setViscosity(6000).setTemperature(1300).setUnlocalizedName(Blocks.LAVA.getUnlocalizedName());
static
{
registerFluid(WATER);
registerFluid(LAVA);
}
private FluidRegistry(){}
/**
* Called by Forge to prepare the ID map for server -> client sync.
* Modders, DO NOT call this.
*/
public static void initFluidIDs(BiMap<Fluid, Integer> newfluidIDs, Set<String> defaultNames)
{
maxID = newfluidIDs.size();
loadFluidDefaults(newfluidIDs, defaultNames);
}
/**
* Called by forge to load default fluid IDs from the world or from server -> client for syncing
* DO NOT call this and expect useful behaviour.
* @param localFluidIDs
* @param defaultNames
*/
private static void loadFluidDefaults(BiMap<Fluid, Integer> localFluidIDs, Set<String> defaultNames)
{
// If there's an empty set of default names, use the defaults as defined locally
if (defaultNames.isEmpty()) {
defaultNames.addAll(defaultFluidName.values());
}
BiMap<String, Fluid> localFluids = HashBiMap.create(fluids);
for (String defaultName : defaultNames)
{
Fluid fluid = masterFluidReference.get(defaultName);
if (fluid == null) {
String derivedName = defaultName.split(":",2)[1];
String localDefault = defaultFluidName.get(derivedName);
if (localDefault == null) {
LOGGER.error(FLUIDS, "The fluid {} (specified as {}) is missing from this instance - it will be removed",derivedName,defaultName);
continue;
}
fluid = masterFluidReference.get(localDefault);
LOGGER.error(FLUIDS, "The fluid {} specified as default is not present - it will be reverted to default {}",defaultName,localDefault);
}
LOGGER.debug(FLUIDS, "The fluid {} has been selected as the default fluid for {}",defaultName,fluid.getName());
Fluid oldFluid = localFluids.put(fluid.getName(), fluid);
Integer id = localFluidIDs.remove(oldFluid);
localFluidIDs.put(fluid, id);
}
BiMap<Integer, String> localFluidNames = HashBiMap.create();
for (Entry<Fluid, Integer> e : localFluidIDs.entrySet()) {
localFluidNames.put(e.getValue(), e.getKey().getName());
}
fluidIDs = localFluidIDs;
fluids = localFluids;
fluidNames = localFluidNames;
fluidBlocks = null;
for (FluidDelegate fd : delegates.values())
{
fd.rebind();
}
}
/**
* Register a new Fluid. If a fluid with the same name already exists, registration the alternative fluid is tracked
* in case it is the default in another place
*
* @param fluid
* The fluid to register.
* @return True if the fluid was registered as the current default fluid, false if it was only registered as an alternative
*/
public static boolean registerFluid(Fluid fluid)
{
masterFluidReference.put(uniqueName(fluid), fluid);
delegates.put(fluid, new FluidDelegate(fluid, fluid.getName()));
if (fluids.containsKey(fluid.getName()))
{
return false;
}
fluids.put(fluid.getName(), fluid);
maxID++;
fluidIDs.put(fluid, maxID);
fluidNames.put(maxID, fluid.getName());
defaultFluidName.put(fluid.getName(), uniqueName(fluid));
MinecraftForge.EVENT_BUS.post(new FluidRegisterEvent(fluid.getName(), maxID));
return true;
}
private static String uniqueName(Fluid fluid)
{
return ModThreadContext.get().getActiveContainer().getPrefix() +":"+fluid.getName();
}
/**
* Is the supplied fluid the current default fluid for it's name
* @param fluid the fluid we're testing
* @return if the fluid is default
*/
public static boolean isFluidDefault(Fluid fluid)
{
return fluids.containsValue(fluid);
}
/**
* Does the supplied fluid have an entry for it's name (whether or not the fluid itself is default)
* @param fluid the fluid we're testing
* @return if the fluid's name has a registration entry
*/
public static boolean isFluidRegistered(Fluid fluid)
{
return fluid != null && fluids.containsKey(fluid.getName());
}
public static boolean isFluidRegistered(String fluidName)
{
return fluids.containsKey(fluidName);
}
public static Fluid getFluid(String fluidName)
{
return fluids.get(fluidName);
}
public static String getFluidName(Fluid fluid)
{
return fluids.inverse().get(fluid);
}
public static String getFluidName(FluidStack stack)
{
return getFluidName(stack.getFluid());
}
@Nullable
public static FluidStack getFluidStack(String fluidName, int amount)
{
if (!fluids.containsKey(fluidName))
{
return null;
}
return new FluidStack(getFluid(fluidName), amount);
}
/**
* Returns a read-only map containing Fluid Names and their associated Fluids.
*/
public static Map<String, Fluid> getRegisteredFluids()
{
return ImmutableMap.copyOf(fluids);
}
/**
* Returns a read-only map containing Fluid Names and their associated IDs.
* Modders should never actually use this, use the String names.
*/
@Deprecated
public static Map<Fluid, Integer> getRegisteredFluidIDs()
{
return ImmutableMap.copyOf(fluidIDs);
}
/**
* Enables the universal bucket in forge.
@ -264,205 +48,4 @@ public abstract class FluidRegistry
{
return universalBucketEnabled;
}
/**
* Registers a fluid with the universal bucket.
* This only has an effect if the universal bucket is enabled.
* @param fluid The fluid that the bucket shall be able to hold
* @return True if the fluid was added successfully, false if it already was registered or couldn't be registered with the bucket.
*/
public static boolean addBucketForFluid(Fluid fluid)
{
if(fluid == null) {
return false;
}
// register unregistered fluids
if (!isFluidRegistered(fluid))
{
registerFluid(fluid);
}
return bucketFluids.add(fluid);
}
/**
* All fluids registered with the universal bucket
* @return An immutable set containing the fluids
*/
public static Set<Fluid> getBucketFluids()
{
return Collections.unmodifiableSet(bucketFluids);
}
public static Fluid lookupFluidForBlock(Block block)
{
if (fluidBlocks == null)
{
BiMap<Block, Fluid> tmp = HashBiMap.create();
for (Fluid fluid : fluids.values())
{
if (fluid.canBePlacedInWorld() && fluid.getBlock() != null)
{
tmp.put(fluid.getBlock(), fluid);
}
}
fluidBlocks = tmp;
}
if (block == Blocks.FLOWING_WATER)
{
block = Blocks.WATER;
}
else if (block == Blocks.FLOWING_LAVA)
{
block = Blocks.LAVA;
}
return fluidBlocks.get(block);
}
public static class FluidRegisterEvent extends Event
{
private final String fluidName;
private final int fluidID;
public FluidRegisterEvent(String fluidName, int fluidID)
{
this.fluidName = fluidName;
this.fluidID = fluidID;
}
public String getFluidName()
{
return fluidName;
}
public int getFluidID()
{
return fluidID;
}
}
public static int getMaxID()
{
return maxID;
}
public static String getDefaultFluidName(Fluid key)
{
String name = masterFluidReference.inverse().get(key);
if (Strings.isNullOrEmpty(name)) {
LOGGER.error(FLUIDS, "The fluid registry is corrupted. A fluid {} {} is not properly registered. The mod that registered this is broken",key.getClass().getName(),key.getName());
throw new IllegalStateException("The fluid registry is corrupted");
}
return name;
}
@Nullable
public static String getModId(@Nullable FluidStack fluidStack)
{
if (fluidStack != null)
{
String defaultFluidName = getDefaultFluidName(fluidStack.getFluid());
if (defaultFluidName != null)
{
ResourceLocation fluidResourceName = new ResourceLocation(defaultFluidName);
return fluidResourceName.getResourceDomain();
}
}
return null;
}
public static void loadFluidDefaults(NBTTagCompound tag)
{
Set<String> defaults = Sets.newHashSet();
if (tag.hasKey("DefaultFluidList",9))
{
LOGGER.debug(FLUIDS, "Loading persistent fluid defaults from world");
NBTTagList tl = tag.getTagList("DefaultFluidList", 8);
for (int i = 0; i < tl.tagCount(); i++)
{
defaults.add(tl.getStringTagAt(i));
}
}
else
{
LOGGER.debug(FLUIDS,"World is missing persistent fluid defaults - using local defaults");
}
loadFluidDefaults(HashBiMap.create(fluidIDs), defaults);
}
public static void writeDefaultFluidList(NBTTagCompound forgeData)
{
NBTTagList tagList = new NBTTagList();
for (Entry<String, Fluid> def : fluids.entrySet())
{
tagList.appendTag(new NBTTagString(getDefaultFluidName(def.getValue())));
}
forgeData.setTag("DefaultFluidList", tagList);
}
public static void validateFluidRegistry()
{
Set<Fluid> illegalFluids = Sets.newHashSet();
for (Fluid f : fluids.values())
{
if (!masterFluidReference.containsValue(f))
{
illegalFluids.add(f);
}
}
if (!illegalFluids.isEmpty())
{
LOGGER.fatal(FLUIDS, "The fluid registry is corrupted. Something has inserted a fluid without registering it");
LOGGER.fatal(FLUIDS, "There is {} unregistered fluids",illegalFluids.size());
for (Fluid f: illegalFluids)
{
LOGGER.fatal(FLUIDS, " Fluid name : {}, type: {}",f.getName(),f.getClass().getName());
}
LOGGER.fatal(FLUIDS, "The mods that own these fluids need to register them properly");
throw new IllegalStateException("The fluid map contains fluids unknown to the master fluid registry");
}
}
static IRegistryDelegate<Fluid> makeDelegate(Fluid fl)
{
return delegates.get(fl);
}
private static class FluidDelegate implements IRegistryDelegate<Fluid>
{
private String name;
private Fluid fluid;
FluidDelegate(Fluid fluid, String name)
{
this.fluid = fluid;
this.name = name;
}
@Override
public Fluid get()
{
return fluid;
}
@Override
public ResourceLocation name() {
return new ResourceLocation(name);
}
@Override
public Class<Fluid> type()
{
return Fluid.class;
}
void rebind()
{
fluid = fluids.get(name);
}
}
}

View File

@ -43,7 +43,6 @@ import net.minecraftforge.common.capabilities.OptionalCapabilityInstance;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.fluids.capability.wrappers.BlockLiquidWrapper;
import net.minecraftforge.fluids.capability.wrappers.BlockWrapper;
import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper;
import net.minecraftforge.items.CapabilityItemHandler;

View File

@ -21,13 +21,13 @@ package net.minecraftforge.registries;
import net.minecraft.block.Block;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.init.Bootstrap;
import net.minecraft.item.Item;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionType;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.SoundEvent;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.fml.common.registry.GameRegistry;
@ -39,6 +39,7 @@ import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfessio
* Created to have a central place to access the registries directly if modders need.
* It is still advised that if you are registering things to go through {@link GameRegistry} register methods, but queries and iterations can use this.
*/
@SuppressWarnings("unchecked")
public class ForgeRegistries
{
static { init(); } // This must be above the fields so we guarantee it's run before findRegistry is called. Yay static inializers
@ -52,6 +53,7 @@ public class ForgeRegistries
public static final IForgeRegistry<Enchantment> ENCHANTMENTS = RegistryManager.ACTIVE.getRegistry(Enchantment.class);
public static final IForgeRegistry<VillagerProfession> VILLAGER_PROFESSIONS = RegistryManager.ACTIVE.getRegistry(VillagerProfession.class);
public static final IForgeRegistry<EntityType<?>> ENTITIES = RegistryManager.ACTIVE.getRegistry(EntityType.class);
public static final IForgeRegistry<TileEntityType<?>> TILE_ENTITIES = RegistryManager.ACTIVE.getRegistry(TileEntityType.class);
public static final IForgeRegistry<IRecipe> RECIPES = RegistryManager.ACTIVE.getRegistry(IRecipe.class);
/**

View File

@ -33,6 +33,7 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionType;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ObjectIntIdentityMap;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvent;
@ -87,6 +88,7 @@ public class GameData
public static final ResourceLocation POTIONTYPES = new ResourceLocation("minecraft:potiontypes");
public static final ResourceLocation ENCHANTMENTS = new ResourceLocation("minecraft:enchantments");
public static final ResourceLocation ENTITIES = new ResourceLocation("minecraft:entities");
public static final ResourceLocation TILEENTITIES = new ResourceLocation("minecraft:tileentities");
public static final ResourceLocation RECIPES = new ResourceLocation("minecraft:recipes");
public static final ResourceLocation PROFESSIONS = new ResourceLocation("minecraft:villagerprofessions");
private static final int MAX_BLOCK_ID = 4095;
@ -98,6 +100,7 @@ public class GameData
private static final int MAX_POTIONTYPE_ID = Integer.MAX_VALUE >> 5; // Int (SPacketEffect)
private static final int MAX_ENCHANTMENT_ID = Short.MAX_VALUE - 1; // Short - serialized as a short in ItemStack NBTs.
private static final int MAX_ENTITY_ID = Integer.MAX_VALUE >> 5; // Varint (SPacketSpawnMob)
private static final int MAX_TILE_ENTITY_ID = Integer.MAX_VALUE; //Doesnt seem to be serialized anywhere, so no max.
private static final int MAX_RECIPE_ID = Integer.MAX_VALUE >> 5; // Varint CPacketRecipeInfo/SPacketRecipeBook
private static final int MAX_PROFESSION_ID = 1024; //TODO: Is this serialized anywhere anymore?
@ -111,6 +114,7 @@ public class GameData
init();
}
@SuppressWarnings("unchecked")
public static void init()
{
if ( DISABLE_VANILLA_REGISTRIES)
@ -132,6 +136,7 @@ public class GameData
makeRegistry(PROFESSIONS, VillagerProfession.class, MAX_PROFESSION_ID).create();
// TODO do we need the callback and the static field anymore?
makeRegistry(ENTITIES, EntityType.class, MAX_ENTITY_ID).create();
makeRegistry(TILEENTITIES, TileEntityType.class, MAX_TILE_ENTITY_ID).disableSaving().create();
}
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int min, int max)

View File

@ -23,21 +23,21 @@ import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Queue;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.CommandSource;
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.minecraft.world.storage.SessionLockException;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.WorldWorkerManager.IWorker;
public class ChunkGenWorker implements IWorker
{
private final ICommandSender listener;
private final CommandSource listener;
protected final BlockPos start;
protected final int total;
private final int dim;
@ -48,7 +48,7 @@ public class ChunkGenWorker implements IWorker
private int genned = 0;
private Boolean keepingLoaded;
public ChunkGenWorker(ICommandSender listener, BlockPos start, int total, int dim, int interval)
public ChunkGenWorker(CommandSource listener, BlockPos start, int total, int dim, int interval)
{
this.listener = listener;
this.start = start;
@ -85,17 +85,11 @@ public class ChunkGenWorker implements IWorker
return ret;
}
@Deprecated // TODO remove in 1.13
public TextComponentTranslation getStartMessage()
public TextComponentBase getStartMessage(CommandSource sender)
{
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()
{
@ -112,7 +106,7 @@ public class ChunkGenWorker implements IWorker
world = DimensionManager.getWorld(dim);
if (world == null)
{
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.dim_fail", dim));
listener.func_197030_a(new TextComponentTranslation("commands.forge.gen.dim_fail", dim), true);
queue.clear();
return false;
}
@ -124,7 +118,7 @@ public class ChunkGenWorker implements IWorker
if (lastNotifcationTime < System.currentTimeMillis() - 10*1000)
{
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.progress", total - queue.size(), total));
listener.func_197030_a(new TextComponentTranslation("commands.forge.gen.progress", total - queue.size(), total), true);
lastNotifcationTime = System.currentTimeMillis();
}
return false;
@ -142,7 +136,7 @@ public class ChunkGenWorker implements IWorker
if (++lastNotification >= notificationFrequency || lastNotifcationTime < System.currentTimeMillis() - 60*1000)
{
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.progress", total - queue.size(), total));
listener.func_197030_a(new TextComponentTranslation("commands.forge.gen.progress", total - queue.size(), total), true);
lastNotification = 0;
lastNotifcationTime = System.currentTimeMillis();
}
@ -150,27 +144,27 @@ public class ChunkGenWorker implements IWorker
int x = next.getX();
int z = next.getZ();
Chunk target = world.getChunkFromChunkCoords(x, z);
Chunk target = world.getChunk(x, z);
Chunk[] chunks = { target };
if (!target.isTerrainPopulated())
if (!target.isPopulated())
{
// 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),
world.getChunk(x + 1, z),
world.getChunk(x + 1, z + 1),
world.getChunk(x, z + 1),
};
try
{
world.getChunkProvider().chunkLoader.saveChunk(world, target);
}
catch (IOException | MinecraftException e)
catch (IOException | SessionLockException e)
{
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.saveerror", e.getMessage()));
listener.func_197030_a(new TextComponentTranslation("commands.forge.gen.saveerror", e.getMessage()), true);
}
genned++;
}
@ -185,7 +179,7 @@ public class ChunkGenWorker implements IWorker
if (queue.size() == 0)
{
listener.sendMessage(TextComponentHelper.createComponentTranslation(listener, "commands.forge.gen.complete", genned, total, dim));
listener.func_197030_a(new TextComponentTranslation("commands.forge.gen.complete", genned, total, dim), true);
if (keepingLoaded != null && keepingLoaded)
{
DimensionManager.keepDimensionLoaded(dim, false);

View File

@ -20,49 +20,30 @@
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.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.DimensionType;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.DimensionManager;
import java.util.Map;
public class CommandDimensions extends CommandBase
import com.mojang.brigadier.builder.ArgumentBuilder;
public class CommandDimensions
{
@Override
public String getName()
static ArgumentBuilder<CommandSource, ?> register()
{
return "dimensions";
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.dimensions.usage";
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
{
return true;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
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()));
}
return Commands.func_197057_a("dimensions")
.requires(cs->cs.func_197034_c(0)) //permission
.executes(ctx -> {
ctx.getSource().func_197030_a(new TextComponentTranslation("commands.forge.dimensions.list"), true);
for (Map.Entry<DimensionType, IntSortedSet> entry : DimensionManager.getRegisteredDimensions().entrySet())
{
ctx.getSource().func_197030_a(new TextComponentString(entry.getKey().getName() + ": " + entry.getValue()), true);
}
return 0;
});
}
}

View File

@ -29,94 +29,77 @@ import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import net.minecraft.command.CommandBase;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.command.ISuggestionProvider;
import net.minecraft.command.arguments.BlockPosArgument;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityType;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
class CommandEntity extends CommandTreeBase
class CommandEntity
{
public CommandEntity()
static ArgumentBuilder<CommandSource, ?> register()
{
addSubcommand(new EntityListCommand());
addSubcommand(new CommandTreeHelp(this));
return Commands.func_197057_a("entity")
.then(EntityListCommand.register()); //TODO: //Kill, spawn, etc..
}
@Override
public String getUsage(ICommandSender sender)
private static class EntityListCommand
{
return "commands.forge.entity.usage";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public String getName()
{
return "entity";
}
private static class EntityListCommand extends CommandBase
{
@Override
public String getName()
private static final SimpleCommandExceptionType INVALID_FILTER = new SimpleCommandExceptionType(new TextComponentTranslation("commands.forge.entity.list.invalid"));
private static final DynamicCommandExceptionType INVALID_DIMENSION = new DynamicCommandExceptionType(dim -> new TextComponentTranslation("commands.forge.entity.list.invalidworld", dim));
private static final SimpleCommandExceptionType NO_ENTITIES = new SimpleCommandExceptionType(new TextComponentTranslation("commands.forge.entity.list.none"));
static ArgumentBuilder<CommandSource, ?> register()
{
return "list";
return Commands.func_197057_a("list")
.requires(cs->cs.func_197034_c(2)) //permission
.then(Commands.func_197056_a("filter", StringArgumentType.string())
.suggests((ctx, builder) -> ISuggestionProvider.func_197013_a(ForgeRegistries.ENTITIES.getKeys().stream().map(id -> id.toString()), builder))
.then(Commands.func_197056_a("dim", IntegerArgumentType.integer())
.suggests((ctx, builder) -> ISuggestionProvider.func_197013_a(DimensionManager.getIDStream().sorted().map(id -> id.toString()), builder))
.executes(ctx -> execute(ctx.getSource(), StringArgumentType.getString(ctx, "filter"), IntegerArgumentType.getInteger(ctx, "dim")))
)
.executes(ctx -> execute(ctx.getSource(), StringArgumentType.getString(ctx, "filter"), ctx.getSource().func_197023_e().provider.getDimension()))
)
.executes(ctx -> execute(ctx.getSource(), "*", ctx.getSource().func_197023_e().provider.getDimension()));
}
@Override
public int getRequiredPermissionLevel()
private static int execute(CommandSource sender, String filter, int dim) throws CommandException
{
return 2;
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.entity.list.usage";
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
String filter = "*";
if (args.length > 0)
{
filter = args[0];
}
final String cleanFilter = filter.replace("?", ".?").replace("*", ".*?");
Set<ResourceLocation> names = EntityList.getEntityNameList().stream().filter(n -> n.toString().matches(cleanFilter)).collect(Collectors.toSet());
Set<ResourceLocation> names = ForgeRegistries.ENTITIES.getKeys().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();
throw INVALID_FILTER.create();
WorldServer world = DimensionManager.getWorld(dim);
if (world == null)
throw new WrongUsageException("commands.forge.entity.list.invalidworld", dim);
throw INVALID_DIMENSION.create(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()));
MutablePair<Integer, Map<ChunkPos, Integer>> info = list.computeIfAbsent(e.func_200600_R().getRegistryName(), k -> MutablePair.of(0, Maps.newHashMap()));
ChunkPos chunk = new ChunkPos(e.getPosition());
info.left++;
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
@ -127,25 +110,23 @@ class CommandEntity extends CommandTreeBase
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()));
throw NO_ENTITIES.create();
sender.func_197030_a(new TextComponentTranslation("commands.forge.entity.list.single.header", name, info.getLeft()), true);
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));
sender.func_197030_a(new TextComponentString(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z), true);
}
}
else
@ -161,33 +142,19 @@ class CommandEntity extends CommandTreeBase
});
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");
throw NO_ENTITIES.create();
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())));
sender.func_197030_a(new TextComponentTranslation("commands.forge.entity.list.multiple.header", count), true);
info.forEach(e -> sender.func_197030_a(new TextComponentString(" " + e.getValue() + ": " + e.getKey()), true));
}
}
@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();
}
}
}

View File

@ -20,72 +20,61 @@
package net.minecraftforge.server.command;
import javax.annotation.Nullable;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.command.ISuggestionProvider;
import net.minecraft.command.arguments.BlockPosArgument;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.WorldWorkerManager;
class CommandGenerate extends CommandBase
class CommandGenerate
{
@Override
public String getName()
static ArgumentBuilder<CommandSource, ?> register()
{
return "generate";
return Commands.func_197057_a("generate")
.requires(cs->cs.func_197034_c(4)) //permission
.then(Commands.func_197056_a("pos", BlockPosArgument.func_197276_a())
.then(Commands.func_197056_a("count", IntegerArgumentType.integer(1))
.then(Commands.func_197056_a("dim", IntegerArgumentType.integer())
.suggests((ctx, builder) -> ISuggestionProvider.func_197013_a(DimensionManager.getIDStream().sorted().map(id -> id.toString()), builder))
.then(Commands.func_197056_a("interval", IntegerArgumentType.integer())
.executes(ctx -> execute(ctx.getSource(), BlockPosArgument.func_197274_b(ctx, "pos"), getInt(ctx, "count"), getInt(ctx, "dim"), getInt(ctx, "interval")))
)
.executes(ctx -> execute(ctx.getSource(), BlockPosArgument.func_197274_b(ctx, "pos"), getInt(ctx, "count"), getInt(ctx, "dim"), -1))
)
.executes(ctx -> execute(ctx.getSource(), BlockPosArgument.func_197274_b(ctx, "pos"), getInt(ctx, "count"), ctx.getSource().func_197023_e().provider.getDimension(), -1))
)
);
}
@Override
public List<String> getAliases()
private static int getInt(CommandContext<CommandSource> ctx, String name)
{
return Collections.singletonList("gen");
return IntegerArgumentType.getInteger(ctx, name);
}
@Override
public String getUsage(ICommandSender sender)
private static int execute(CommandSource source, BlockPos pos, int count, int dim, int interval) throws CommandException
{
return "commands.forge.gen.usage";
}
BlockPos chunkpos = new BlockPos(pos.getX() >> 4, 0, pos.getZ() >> 4);
@Override
public int getRequiredPermissionLevel()
{
return 4;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
// x y z chunkCount [dim] [interval]
if (args.length < 4)
{
throw new WrongUsageException("commands.forge.gen.usage");
}
BlockPos blockpos = parseBlockPos(sender, args, 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));
ChunkGenWorker worker = new ChunkGenWorker(source, chunkpos, count, dim, interval);
source.func_197030_a(worker.getStartMessage(source), true);
WorldWorkerManager.addWorker(worker);
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
{
if (args.length < 4)
{
return getTabCompletionCoordinate(args, 0, targetPos);
}
// Chunk Count? No completion
// Dimension, Add support for names? Get list of ids? Meh
return Collections.emptyList();
}
}

View File

@ -21,85 +21,76 @@ package net.minecraftforge.server.command;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.ISuggestionProvider;
import net.minecraft.command.WrongUsageException;
import net.minecraft.command.arguments.BlockPosArgument;
import net.minecraft.command.arguments.EntityArgument;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.util.ITeleporter;
import javax.annotation.Nullable;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class CommandSetDimension extends CommandBase
public class CommandSetDimension
{
@Override
public String getName()
private static final SimpleCommandExceptionType NO_ENTITIES = new SimpleCommandExceptionType(new TextComponentTranslation("commands.forge.setdim.invalid.entity"));
private static final DynamicCommandExceptionType INVALID_DIMENSION = new DynamicCommandExceptionType(dim -> new TextComponentTranslation("commands.forge.setdim.invalid.dim", dim));
static ArgumentBuilder<CommandSource, ?> register()
{
return "setdimension";
return Commands.func_197057_a("setdimension")
.requires(cs->cs.func_197034_c(2)) //permission
.then(Commands.func_197056_a("targets", EntityArgument.func_197093_b())
.then(Commands.func_197056_a("dim", IntegerArgumentType.integer())
.suggests((ctx, builder) -> ISuggestionProvider.func_197013_a(DimensionManager.getIDStream().sorted().map(id -> id.toString()), builder))
.then(Commands.func_197056_a("pos", BlockPosArgument.func_197276_a())
.executes(ctx -> execute(ctx.getSource(), EntityArgument.func_197087_c(ctx, "targets"), IntegerArgumentType.getInteger(ctx, "dim"), BlockPosArgument.func_197274_b(ctx, "pos")))
)
.executes(ctx -> execute(ctx.getSource(), EntityArgument.func_197087_c(ctx, "targets"), IntegerArgumentType.getInteger(ctx, "dim"), new BlockPos(ctx.getSource().func_197036_d())))
)
);
}
@Override
public List<String> getAliases()
private static int execute(CommandSource sender, Collection<? extends Entity> entities, int dim, BlockPos pos) throws CommandException
{
return Collections.singletonList("setdim");
}
entities.removeIf(CommandSetDimension::checkEntity);
if (entities.isEmpty())
throw NO_ENTITIES.create();
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.setdim.usage";
}
if (!DimensionManager.isDimensionRegistered(dim))
throw INVALID_DIMENSION.create(dim);
@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();
}
final ITeleporter teleporter = new CommandTeleporter(pos);
entities.stream().filter(e -> e.dimension == dim).forEach(e -> sender.func_197030_a(new TextComponentTranslation("commands.forge.setdim.invalid.nochange", e.getDisplayName(), dim), true));
entities.stream().filter(e -> e.dimension != dim).forEach(e -> e.changeDimension(dim, teleporter));
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@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));
return 0;
}
private static boolean checkEntity(Entity entity)
{
// use vanilla portal logic, try to avoid doing anything too silly
return !entity.isRiding() && !entity.isBeingRidden() && entity.isNonBoss();
return entity.isRiding() || entity.isBeingRidden() || entity.isNonBoss();
}
private static class CommandTeleporter implements ITeleporter

View File

@ -20,93 +20,74 @@
package net.minecraftforge.server.command;
import java.text.DecimalFormat;
import java.util.stream.Collectors;
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 com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.command.ISuggestionProvider;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.DimensionManager;
class CommandTps extends CommandBase
class CommandTps
{
private static final DynamicCommandExceptionType INVALID_DIMENSION = new DynamicCommandExceptionType(dim -> new TextComponentTranslation("commands.forge.tps.invalid", dim, DimensionManager.getIDStream().sorted().map(id -> id.toString()).collect(Collectors.joining(", "))));
private static final DecimalFormat TIME_FORMATTER = new DecimalFormat("########0.000");
@Override
public String getName()
static ArgumentBuilder<CommandSource, ?> register()
{
return "tps";
}
return Commands.func_197057_a("tps")
.requires(cs->cs.func_197034_c(0)) //permission
.then(Commands.func_197056_a("dim", IntegerArgumentType.integer())
.suggests((ctx, builder) -> ISuggestionProvider.func_197013_a(DimensionManager.getIDStream().sorted().map(id -> id.toString()), builder))
.executes(ctx -> sendTime(ctx.getSource(), IntegerArgumentType.getInteger(ctx, "dim")))
)
.executes(ctx -> {
for (Integer dim : (Iterable<Integer>)DimensionManager.getIDStream().sorted()::iterator)
sendTime(ctx.getSource(), dim);
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.tps.usage";
}
double meanTickTime = mean(ctx.getSource().func_197028_i().tickTimeArray) * 1.0E-6D;
double meanTPS = Math.min(1000.0/meanTickTime, 20);
ctx.getSource().func_197030_a(new TextComponentTranslation("commands.forge.tps.summary.all", TIME_FORMATTER.format(meanTickTime), TIME_FORMATTER.format(meanTPS)), true);
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
{
return true;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
int dim = 0;
boolean summary = true;
if (args.length > 0)
{
dim = parseInt(args[0]);
summary = false;
}
if (summary)
{
for (Integer dimId : DimensionManager.getIDs())
{
double worldTickTime = mean(server.worldTickTimes.get(dimId)) * 1.0E-6D;
double worldTPS = Math.min(1000.0/worldTickTime, 20);
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tps.summary", getDimensionPrefix(dimId), TIME_FORMATTER.format(worldTickTime), TIME_FORMATTER.format(worldTPS)));
return 0;
}
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)
private static long[] getTimes(int dim)
{
DimensionType providerType = DimensionManager.getProviderType(dimId);
if (providerType == null)
{
return String.format("Dim %2d", dimId);
}
//TODO: Implement, needs patch.
return null;
}
private static int sendTime(CommandSource cs, int dim) throws CommandSyntaxException
{
long[] times = getTimes(dim);
if (times == null)
throw INVALID_DIMENSION.create(dim);
double worldTickTime = mean(times) * 1.0E-6D;
double worldTPS = Math.min(1000.0 / worldTickTime, 20);
DimensionType type = DimensionManager.getProviderType(dim);
if (type == null)
cs.func_197030_a(new TextComponentTranslation("commands.forge.tps.summary.basic", dim, TIME_FORMATTER.format(worldTickTime), TIME_FORMATTER.format(worldTPS)), true);
else
{
return String.format("Dim %2d (%s)", dimId, providerType.getName());
}
cs.func_197030_a(new TextComponentTranslation("commands.forge.tps.summary.named", dim, type.getName(), TIME_FORMATTER.format(worldTickTime), TIME_FORMATTER.format(worldTPS)), true);
return 1;
}
private static long mean(long[] values)
{
long sum = 0L;
for (long v : values)
{
sum += v;
}
return sum / values.length;
}
}

View File

@ -20,187 +20,114 @@
package net.minecraftforge.server.command;
import javax.annotation.Nullable;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
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 java.util.function.Function;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.command.ISuggestionProvider;
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.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.server.timings.ForgeTimings;
import net.minecraftforge.server.timings.TimeTracker;
class CommandTrack extends CommandTreeBase
class CommandTrack
{
private static final DecimalFormat TIME_FORMAT = new DecimalFormat("#####0.00");
public CommandTrack()
static ArgumentBuilder<CommandSource, ?> register()
{
addSubcommand(new StartTrackingCommand());
addSubcommand(new ResetTrackingCommand());
addSubcommand(new TrackResultsTileEntity());
addSubcommand(new TrackResultsEntity());
addSubcommand(new CommandTreeHelp(this));
return Commands.func_197057_a("track")
.then(StartTrackingCommand.register())
.then(ResetTrackingCommand.register())
.then(TrackResultsEntity.register())
.then(TrackResultsTileEntity.register())
.then(StartTrackingCommand.register());
}
@Override
public String getName()
private static class StartTrackingCommand
{
return "track";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.tracking.usage";
}
private static class StartTrackingCommand extends CommandBase
{
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
static ArgumentBuilder<CommandSource, ?> register()
{
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));
}
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
{
return Arrays.asList("te", "entity");
}
@Override
public String getName()
{
return "start";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.tracking.start.usage";
return Commands.func_197057_a("start")
.requires(cs->cs.func_197034_c(2)) //permission
.then(Commands.func_197057_a("te")
.then(Commands.func_197056_a("duration", IntegerArgumentType.integer(1))
.executes(ctx -> {
int duration = IntegerArgumentType.getInteger(ctx, "duration");
TimeTracker.TILE_ENTITY_UPDATE.reset();
TimeTracker.TILE_ENTITY_UPDATE.enable(duration);
ctx.getSource().func_197030_a(new TextComponentTranslation("commands.forge.tracking.te.enabled", duration), true);
return 0;
})
)
)
.then(Commands.func_197057_a("entity")
.then(Commands.func_197056_a("duration", IntegerArgumentType.integer(1))
.executes(ctx -> {
int duration = IntegerArgumentType.getInteger(ctx, "duration");
TimeTracker.ENTITY_UPDATE.reset();
TimeTracker.ENTITY_UPDATE.enable(duration);
ctx.getSource().func_197030_a(new TextComponentTranslation("commands.forge.tracking.entity.enabled", duration), true);
return 0;
})
)
);
}
}
private static class ResetTrackingCommand extends CommandBase
private static class ResetTrackingCommand
{
@Override
public String getName()
static ArgumentBuilder<CommandSource, ?> register()
{
return "reset";
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.tracking.reset.usage";
}
@Override
public int getRequiredPermissionLevel()
{
return 2;
}
@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));
}
}
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos)
{
return Arrays.asList("te", "entity");
return Commands.func_197057_a("reset")
.requires(cs->cs.func_197034_c(2)) //permission
.then(Commands.func_197057_a("te")
.executes(ctx -> {
TimeTracker.TILE_ENTITY_UPDATE.reset();
ctx.getSource().func_197030_a(new TextComponentTranslation("commands.forge.tracking.te.reset"), true);
return 0;
})
)
.then(Commands.func_197057_a("entity")
.executes(ctx -> {
TimeTracker.ENTITY_UPDATE.reset();
ctx.getSource().func_197030_a(new TextComponentTranslation("commands.forge.tracking.entity.reset"), true);
return 0;
})
);
}
}
/**
* A base command for all the tracking results commands
*
* @param <T>
*/
private static abstract class TrackResultsBaseCommand<T> extends CommandBase
private static class TrackResults
{
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()
private static <T> List<ForgeTimings<T>> getSortedTimings(TimeTracker<T> tracker)
{
ArrayList<ForgeTimings<T>> list = new ArrayList<>();
@ -211,42 +138,21 @@ class CommandTrack extends CommandTreeBase
return list;
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
private static <T> int execute(CommandSource source, TimeTracker<T> tracker, Function<ForgeTimings<T>, ITextComponent> toString) throws CommandException
{
List<ForgeTimings<T>> timingsList = getSortedTimings();
List<ForgeTimings<T>> timingsList = getSortedTimings(tracker);
if (timingsList.isEmpty())
{
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, "commands.forge.tracking.noData"));
source.func_197030_a(new TextComponentTranslation("commands.forge.tracking.no_data"), true);
}
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";
.forEach(timings -> source.func_197030_a(toString.apply(timings), true));
}
return 0;
}
/**
@ -255,105 +161,53 @@ class CommandTrack extends CommandTreeBase
* @param dimId The dimension ID
* @return The name of the dimension
*/
protected String getWorldName(int dimId)
protected static 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>
private static class TrackResultsEntity
{
public TrackResultsEntity()
static ArgumentBuilder<CommandSource, ?> register()
{
super(TimeTracker.ENTITY_UPDATE);
}
return Commands.func_197057_a("entity").executes(ctx -> TrackResults.execute(ctx.getSource(), TimeTracker.ENTITY_UPDATE, data ->
{
Entity entity = data.getObject().get();
if (entity == null)
return new TextComponentTranslation("commands.forge.tracking.invalid");
@Override
public String getName()
{
return "entity";
}
BlockPos pos = entity.getPosition();
double averageTimings = data.getAverageTimings();
String tickTime = (averageTimings > 1000 ? TIME_FORMAT.format(averageTimings / 1000) : TIME_FORMAT.format(averageTimings)) + (averageTimings < 1000 ? "µs" : "ms");
@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);
return new TextComponentTranslation("commands.forge.tracking.timing_entry", entity.func_200600_R().getRegistryName(), TrackResults.getWorldName(entity.world.provider.getDimension()), pos.getX(), pos.getY(), pos.getZ(), tickTime);
})
);
}
}
private static class TrackResultsTileEntity extends TrackResultsBaseCommand<TileEntity>
private static class TrackResultsTileEntity
{
public TrackResultsTileEntity()
static ArgumentBuilder<CommandSource, ?> register()
{
super(TimeTracker.TILE_ENTITY_UPDATE);
}
return Commands.func_197057_a("te").executes(ctx -> TrackResults.execute(ctx.getSource(), TimeTracker.TILE_ENTITY_UPDATE, data ->
{
TileEntity te = data.getObject().get();
if (te == null)
return new TextComponentTranslation("commands.forge.tracking.invalid");
@Override
public String getName()
{
return "te";
}
BlockPos pos = te.getPos();
@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();
}
double averageTimings = data.getAverageTimings();
String tickTime = (averageTimings > 1000 ? TIME_FORMAT.format(averageTimings / 1000) : TIME_FORMAT.format(averageTimings)) + (averageTimings < 1000 ? "µs" : "ms");
return new TextComponentTranslation("commands.forge.tracking.timing_entry", te.func_200662_C().getRegistryName(), TrackResults.getWorldName(te.getWorld().provider.getDimension()), pos.getX(), pos.getY(), pos.getZ(), tickTime);
})
);
}
}
}

View File

@ -1,181 +0,0 @@
/*
* 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;
}
@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);
}
@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;
}
@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);
}
}

View File

@ -1,122 +0,0 @@
/*
* 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));
}
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public String getName()
{
return "help";
}
@Override
public String getUsage(ICommandSender sender)
{
return "commands.forge.usage.help";
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
if (args.length == 0)
{
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, parent.getUsage(sender)));
for (ICommand subCommand : getSubCommands())
{
if (subCommand instanceof HelpSubCommand && subCommand.checkPermission(server, sender))
{
subCommand.execute(server, sender, args);
}
}
return;
}
super.execute(server, sender, args);
}
public static class HelpSubCommand extends CommandBase
{
private final CommandTreeHelp parent;
private final ICommand command;
public HelpSubCommand(CommandTreeHelp parent, ICommand command)
{
this.parent = parent;
this.command = command;
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
@Override
public String getName()
{
return command.getName();
}
@Override
public String getUsage(ICommandSender sender)
{
return command.getUsage(sender);
}
@Override
public boolean checkPermission(MinecraftServer server, ICommandSender sender)
{
return command.checkPermission(server, sender);
}
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException
{
sender.sendMessage(TextComponentHelper.createComponentTranslation(sender, command.getUsage(sender)));
}
}
}

View File

@ -22,24 +22,19 @@ package net.minecraftforge.server.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.command.CommandSource;
import net.minecraft.util.text.TextComponentString;
public class ForgeCommand
{
public ForgeCommand(CommandDispatcher<CommandSource> dispatcher)
{
dispatcher.register(LiteralArgumentBuilder.<CommandSource>literal("forge").
requires(cs->cs.func_197034_c(2)).
then(LiteralArgumentBuilder.literal("tps")).
executes(c->{c.getSource().func_197021_a(new TextComponentString("command.forge.tps.summary")); return 1;}));
/*
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));
*/
dispatcher.register(
LiteralArgumentBuilder.<CommandSource>literal("forge")
.then(CommandTps.register())
.then(CommandTrack.register())
.then(CommandEntity.register())
.then(CommandGenerate.register())
.then(CommandDimensions.register())
.then(CommandSetDimension.register())
);
}
}

View File

@ -6,5 +6,31 @@
"fml.menu.mods.z_to_a": "Z-A",
"fml.menu.mods.config": "Config",
"fml.menu.modoptions": "Mod Options...",
"fml.menu.loadingmods": "{0,choice,0#No mods|1#1 mod|1<{0} mods} loaded"
"fml.menu.loadingmods": "{0,choice,0#No mods|1#1 mod|1<{0} mods} loaded",
"commands.forge.dimensions.list": "Currently registered dimensions by type:",
"commands.forge.entity.list.invalid": "Invalid filter, does not match any entities. Use /forge entity list for a proper list",
"commands.forge.entity.list.invalidworld": "Could not load world for dimension {0}. Please select a valid dimension.",
"commands.forge.entity.list.none": "No entities found.",
"commands.forge.entity.list.single.header": "Entity: {0} Total: {1}",
"commands.forge.entity.list.multiple.header": "Total: {0}",
"commands.forge.gen.usage": "Use /forge gen <x> <y> <z> <chunkCount> [dimension] [interval]",
"commands.forge.gen.dim_fail": "Failed to load world for dimension {0}, Task terminated.",
"commands.forge.gen.progress": "Generation Progress: {0}/{1}",
"commands.forge.gen.complete": "Finished generating {0} new chunks (out of {2}) for dimension {3}.",
"commands.forge.gen.start": "Starting to generate {0} chunks in a spiral around {1}, {2} in dimension {3}.",
"commands.forge.setdim.invalid.entity": "The entity selected ({0}) is not valid.",
"commands.forge.setdim.invalid.dim": "The dimension ID specified ({0}) is not valid.",
"commands.forge.setdim.invalid.nochange": "The entity selected ({0}) is already in the dimension specified ({1}).",
"commands.forge.tps.invalid": "Invalid dimension {0} Possible values: {1}",
"commands.forge.tps.summary.all": "Overall: Mean tick time: {1} ms. Mean TPS: {2}",
"commands.forge.tps.summary.basic": "Dim {0}: Mean tick time: {1} ms. Mean TPS: {2}",
"commands.forge.tps.summary.named": "Dim {0} ({1}): Mean tick time: {2} ms. Mean TPS: {3}",
"commands.forge.tracking.entity.enabled": "Entity tracking enabled for %d seconds.",
"commands.forge.tracking.entity.reset": "Entity timings data has been cleared!",
"commands.forge.tracking.invalid": "Invalid tracking data.",
"commands.forge.tracking.te.enabled": "Tile Entity tracking enabled for %d seconds.",
"commands.forge.tracking.te.reset": "Tile entity timings data has been cleared!",
"commands.forge.tracking.timing_entry": "{0} - {1} [{2}, {3}, {4}]: {5}",
"commands.forge.tracking.no_data": "No data has been recorded yet."
}