Merge branch 'basic_systems'

# Conflicts:
#	src/main/java/carpet/commands/SpawnCommand.java
#	src/main/java/carpet/helpers/CtrlQCrafting.javax
#	src/main/java/carpet/utils/HUDController.java
#	src/main/java/carpet/utils/SpawnReporter.java
#	src/main/resources/carpet.mixins.json
This commit is contained in:
gnembon committed 2019-05-13 01:51:36 -03:00
commit 5109263fd8
48 files changed
+6071 -54

No files matched your search

@@ -0,0 +1,262 @@
package carpet.helpers;
import carpet.CarpetSettings;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.TrapdoorBlock;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.init.Blocks;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.state.properties.ComparatorMode;
import net.minecraft.state.properties.Half;
import net.minecraft.state.properties.SlabType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class BlockRotator
{
public static boolean flipBlockWithCactus(World worldIn, BlockPos pos, BlockState state, PlayerEntity playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if (!playerIn.abilities.allowModifyWorld || !CarpetSettings.getBool("flippinCactus") || !player_holds_cactus_mainhand(playerIn))
{
return false;
}
return flip_block(worldIn, pos, state, playerIn, hand, facing, hitX, hitY, hitZ);
}
public static BlockState alternativeBlockPlacement(Block block, BlockItemUseContext context)//World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
{
//actual alternative block placement code
//
EnumFacing facing;
float hitX = context.getHitX();
PlayerEntity placer = context.getPlayer();
BlockPos pos = context.getPos();
World world = context.getWorld();
if (block instanceof BlockGlazedTerracotta)
{
facing = EnumFacing.byIndex((int)hitX - 2);
if(facing == EnumFacing.UP || facing == EnumFacing.DOWN)
{
facing = placer.getHorizontalFacing().getOpposite();
}
return block.getDefaultState().with(BlockHorizontal.HORIZONTAL_FACING, facing);
}
else if (block instanceof BlockObserver)
{
return block.getDefaultState()
.with(BlockDirectional.FACING, EnumFacing.byIndex((int)hitX - 2))
.with(BlockObserver.POWERED, true);
}
else if (block instanceof BlockRedstoneRepeater)
{
facing = EnumFacing.byIndex((((int)hitX) % 10) - 2);
if(facing == EnumFacing.UP || facing == EnumFacing.DOWN)
{
facing = placer.getHorizontalFacing().getOpposite();
}
return block.getDefaultState()
.with(BlockHorizontal.HORIZONTAL_FACING, facing)
.with(BlockRedstoneRepeater.DELAY, MathHelper.clamp((((int) hitX) / 10) + 1, 1, 4))
.with(BlockRedstoneRepeater.LOCKED, Boolean.FALSE);
}
else if (block instanceof BlockTrapDoor)
{
return block.getDefaultState()
.with(TrapdoorBlock.HORIZONTAL_FACING, EnumFacing.byIndex((((int)hitX) % 10) - 2))
.with(TrapdoorBlock.OPEN, Boolean.FALSE)
.with(TrapdoorBlock.HALF, (hitX > 10) ? Half.TOP : Half.BOTTOM)
.with(TrapdoorBlock.OPEN, world.isBlockPowered(pos));
}
else if (block instanceof BlockRedstoneComparator)
{
facing = EnumFacing.byIndex((((int)hitX) % 10) - 2);
if((facing == EnumFacing.UP) || (facing == EnumFacing.DOWN))
{
facing = placer.getHorizontalFacing().getOpposite();
}
ComparatorMode m = (hitX > 10)?ComparatorMode.SUBTRACT: ComparatorMode.COMPARE;
return block.getDefaultState()
.with(BlockHorizontal.HORIZONTAL_FACING, facing)
.with(BlockRedstoneComparator.POWERED, Boolean.FALSE)
.with(BlockRedstoneComparator.MODE, m);
}
else if (block instanceof BlockDispenser)
{
return block.getDefaultState()
.with(BlockDispenser.FACING, EnumFacing.byIndex((int)hitX - 2))
.with(BlockDispenser.TRIGGERED, Boolean.FALSE);
}
else if (block instanceof BlockPistonBase)
{
return block.getDefaultState()
.with(BlockDirectional.FACING,EnumFacing.byIndex((int)hitX - 2) )
.with(BlockPistonBase.EXTENDED, Boolean.FALSE);
}
else if (block instanceof BlockStairs)
{
return block.getStateForPlacement(context)//worldIn, pos, facing, hitX, hitY, hitZ, meta, placer)
.with(BlockStairs.FACING, EnumFacing.byIndex((((int)hitX) % 10) - 2))
.with(BlockStairs.HALF, ( hitX > 10)?Half.TOP : Half.BOTTOM);
}
return null;
}
public static ItemStack dispenserRotate(IBlockSource source, ItemStack stack)
{
EnumFacing sourceFace = source.getBlockState().get(BlockDispenser.FACING);
World world = source.getWorld();
BlockPos blockpos = source.getBlockPos().offset(sourceFace);
BlockState iblockstate = world.getBlockState(blockpos);
Block block = iblockstate.getBlock();
// Block rotation for blocks that can be placed in all 6 rotations.
if(block instanceof BlockDirectional || block instanceof BlockDispenser)
{
EnumFacing face = iblockstate.get(BlockDirectional.FACING);
face = face.rotateAround(sourceFace.getAxis());
if(sourceFace.getIndex() % 2 == 0)
{ // Rotate twice more to make blocks always rotate clockwise relative to the dispenser
// when index is equal to zero. when index is equal to zero the dispenser is in the opposite direction.
face = face.rotateAround(sourceFace.getAxis());
face = face.rotateAround(sourceFace.getAxis());
}
world.setBlockState(blockpos, iblockstate.with(BlockDirectional.FACING, face), 3);
}
else if(block instanceof BlockHorizontal) // Block rotation for blocks that can be placed in only 4 horizontal rotations.
{
EnumFacing face = iblockstate.get(BlockHorizontal.HORIZONTAL_FACING);
face = face.rotateAround(sourceFace.getAxis());
if(sourceFace.getIndex() % 2 == 0)
{ // same as above.
face = face.rotateAround(sourceFace.getAxis());
face = face.rotateAround(sourceFace.getAxis());
}
if(sourceFace.getIndex() <= 1)
{ // Make sure to suppress rotation when index is lower then 2 as that will result in a faulty rotation for
// blocks that only can be placed horizontaly.
world.setBlockState(blockpos, iblockstate.with(BlockHorizontal.HORIZONTAL_FACING, face), 3);
}
}
// Send block update to the block that just have been rotated.
world.neighborChanged(blockpos, block, source.getBlockPos());
return stack;
}
public static boolean flip_block(World worldIn, BlockPos pos, BlockState state, PlayerEntity playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
Block block = state.getBlock();
if ( (block instanceof BlockGlazedTerracotta) || (block instanceof BlockRedstoneDiode) || (block instanceof BlockRailBase) ||
(block instanceof BlockTrapDoor) || (block instanceof BlockLever) || (block instanceof BlockFenceGate))
{
worldIn.setBlockState(pos, block.rotate(state, Rotation.CLOCKWISE_90), 2 | 1024);
}
else if ((block instanceof BlockObserver) || (block instanceof BlockEndRod))
{
worldIn.setBlockState(pos, state.with(BlockDirectional.FACING, (EnumFacing)state.get(BlockDirectional.FACING).getOpposite()), 2 | 1024);
}
else if (block instanceof BlockDispenser)
{
worldIn.setBlockState(pos, state.with(BlockDispenser.FACING, state.get(BlockDispenser.FACING).getOpposite()), 2 | 1024);
}
else if (block instanceof BlockPistonBase)
{
if (!(state.get(BlockPistonBase.EXTENDED)))
worldIn.setBlockState(pos, state.with(BlockDirectional.FACING, state.get(BlockDirectional.FACING).getOpposite()), 2 | 1024);
}
else if (block instanceof BlockSlab)
{
if (!((BlockSlab) block).isFullCube(state))
{
worldIn.setBlockState(pos, state.with(BlockSlab.TYPE, state.get(BlockSlab.TYPE) == SlabType.TOP ? SlabType.BOTTOM : SlabType.TOP), 2 | 1024);
}
}
else if (block instanceof BlockHopper)
{
if ((EnumFacing)state.get(BlockHopper.FACING) != EnumFacing.DOWN)
{
worldIn.setBlockState(pos, state.with(BlockHopper.FACING, state.get(BlockHopper.FACING).rotateY()), 2 | 1024);
}
}
else if (block instanceof BlockStairs)
{
//LOG.error(String.format("hit with facing: %s, at side %.1fX, X %.1fY, Y %.1fZ",facing, hitX, hitY, hitZ));
if ((facing == EnumFacing.UP && hitY == 1.0f) || (facing == EnumFacing.DOWN && hitY == 0.0f))
{
worldIn.setBlockState(pos, state.with(BlockStairs.HALF, state.get(BlockStairs.HALF) == Half.TOP ? Half.BOTTOM : Half.TOP ), 2 | 1024);
}
else
{
boolean turn_right;
if (facing == EnumFacing.NORTH)
{
turn_right = (hitX <= 0.5);
}
else if (facing == EnumFacing.SOUTH)
{
turn_right = !(hitX <= 0.5);
}
else if (facing == EnumFacing.EAST)
{
turn_right = (hitZ <= 0.5);
}
else if (facing == EnumFacing.WEST)
{
turn_right = !(hitZ <= 0.5);
}
else
{
return false;
}
if (turn_right)
{
worldIn.setBlockState(pos, block.rotate(state, Rotation.COUNTERCLOCKWISE_90), 2 | 1024);
}
else
{
worldIn.setBlockState(pos, block.rotate(state, Rotation.CLOCKWISE_90), 2 | 1024);
}
}
}
else
{
return false;
}
worldIn.markBlockRangeForRenderUpdate(pos, pos);
return true;
}
private static boolean player_holds_cactus_mainhand(PlayerEntity playerIn)
{
return (!playerIn.getHeldItemMainhand().isEmpty()
&& playerIn.getHeldItemMainhand().getItem() instanceof ItemBlock &&
((ItemBlock) (playerIn.getHeldItemMainhand().getItem())).getBlock() == Blocks.CACTUS);
}
public static boolean flippinEligibility(Entity entity)
{
if (CarpetSettings.getBool("flippinCactus")
&& (entity instanceof PlayerEntity))
{
PlayerEntity player = (PlayerEntity)entity;
return (!player.getHeldItemOffhand().isEmpty()
&& player.getHeldItemOffhand().getItem() instanceof ItemBlock &&
((ItemBlock) (player.getHeldItemOffhand().getItem())).getBlock() == Blocks.CACTUS);
}
return false;
}
}
@@ -0,0 +1,22 @@
package carpet.helpers;
import net.minecraft.block.Material;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
public class BlockSaplingHelper
{
// Added code for checking water for dead shrub rule
public static boolean hasWater(IWorld worldIn, BlockPos pos)
{
for (BlockPos blockpos$mutableblockpos : BlockPos.iterate(pos.add(-4, -4, -4), pos.add(4, 1, 4)))
{
if (worldIn.getBlockState(blockpos$mutableblockpos).getMaterial() == Material.WATER)
{
return true;
}
}
return false;
}
}
@@ -0,0 +1,858 @@
package carpet.helpers;
import carpet.CarpetSettings;
import carpet.mixins.PlayerActionC2SPacketMixin;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.packet.PlayerActionC2SPacket;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import java.util.List;
public class EntityPlayerActionPack
{
private ServerPlayerEntity player;
private boolean doesAttack;
private int attackInterval;
private int attackCooldown;
private boolean doesUse;
private int useInterval;
private int useCooldown;
private boolean doesJump;
private int jumpInterval;
private int jumpCooldown;
private BlockPos currentBlock = new BlockPos(-1,-1,-1);
private int blockHitDelay;
private boolean isHittingBlock;
private float curBlockDamageMP;
private boolean sneaking;
private boolean sprinting;
private float forward;
private float strafing;
public EntityPlayerActionPack(ServerPlayerEntity playerIn)
{
player = playerIn;
stop();
}
public void copyFrom(EntityPlayerActionPack other)
{
doesAttack = other.doesAttack;
attackInterval = other.attackInterval;
attackCooldown = other.attackCooldown;
doesUse = other.doesUse;
useInterval = other.useInterval;
useCooldown = other.useCooldown;
doesJump = other.doesJump;
jumpInterval = other.jumpInterval;
jumpCooldown = other.jumpCooldown;
currentBlock = other.currentBlock;
blockHitDelay = other.blockHitDelay;
isHittingBlock = other.isHittingBlock;
curBlockDamageMP = other.curBlockDamageMP;
sneaking = other.sneaking;
sprinting = other.sprinting;
forward = other.forward;
strafing = other.strafing;
}
public EntityPlayerActionPack setAttack(int interval, int offset)
{
if (interval < 1)
{
CarpetSettings.LOG.error("attack interval needs to be positive");
return this;
}
this.doesAttack = true;
this.attackInterval = interval;
this.attackCooldown = interval+offset;
return this;
}
public EntityPlayerActionPack setUse(int interval, int offset)
{
if (interval < 1)
{
CarpetSettings.LOG.error("use interval needs to be positive");
return this;
}
this.doesUse = true;
this.useInterval = interval;
this.useCooldown = interval+offset;
return this;
}
public EntityPlayerActionPack setUseForever()
{
this.doesUse = true;
this.useInterval = 1;
this.useCooldown = 1;
return this;
}
public EntityPlayerActionPack setAttackForever()
{
this.doesAttack = true;
this.attackInterval = 1;
this.attackCooldown = 1;
return this;
}
public EntityPlayerActionPack setJump(int interval, int offset)
{
if (interval < 1)
{
CarpetSettings.LOG.error("jump interval needs to be positive");
return this;
}
this.doesJump = true;
this.jumpInterval = interval;
this.jumpCooldown = interval+offset;
return this;
}
public EntityPlayerActionPack setJumpForever()
{
this.doesJump = true;
this.jumpInterval = 1;
this.jumpCooldown = 1;
return this;
}
public EntityPlayerActionPack setSneaking(boolean doSneak)
{
sneaking = doSneak;
player.setSneaking(doSneak);
if (sprinting && sneaking)
setSprinting(false);
return this;
}
public EntityPlayerActionPack setSprinting(boolean doSprint)
{
sprinting = doSprint;
player.setSprinting(doSprint);
if (sneaking && sprinting)
setSneaking(false);
return this;
}
public EntityPlayerActionPack setForward(float value)
{
forward = value;
return this;
}
public EntityPlayerActionPack setStrafing(float value)
{
strafing = value;
return this;
}
public boolean look(String where)
{
switch (where)
{
case "north":
look(180.0f,0.0F); return true;
case "south":
look (0.0F, 0.0F); return true;
case "east":
look(-90.0F, 0.0F); return true;
case "west":
look(90.0F, 0.0F); return true;
case "up":
look(player.yaw, -90.0F); return true;
case "down":
look(player.yaw, 90.0F); return true;
case "left":
case "right":
return turn(where);
}
return false;
}
public EntityPlayerActionPack look(float yaw, float pitch)
{
player.setPositionAndAngles(player.x, player.y, player.z, yaw, MathHelper.clamp(pitch,-90.0F, 90.0F));
return this;
}
public boolean turn(String where)
{
switch (where)
{
case "left":
turn(-90.0F,0.0F); return true;
case "right":
turn (90.0F, 0.0F); return true;
case "back":
turn (180.0F, 0.0F); return true;
case "up":
turn(0.0F, -5.0F); return true;
case "down":
turn(0.0F, 5.0F); return true;
}
return false;
}
public EntityPlayerActionPack turn(float yaw, float pitch)
{
player.setPositionAndAngles(player.x, player.y, player.z, player.yaw+yaw,MathHelper.clamp(player.pitch+pitch,-90.0F, 90.0F));
return this;
}
public EntityPlayerActionPack stop()
{
this.doesUse = false;
this.doesAttack = false;
this.doesJump = false;
//resetBlockRemoving(false);
setSneaking(false);
setSprinting(false);
forward = 0.0F;
strafing = 0.0F;
player.setJumping(false);
return this;
}
public void swapHands()
{
PlayerActionC2SPacket packet = new PlayerActionC2SPacket();
((PlayerActionC2SPacketMixin)packet).setAction(PlayerActionC2SPacket.Action.SWAP_HELD_ITEMS);
player.networkHandler.onPlayerAction(packet);
}
public void dropItem()
{
PlayerActionC2SPacket packet = new PlayerActionC2SPacket();
((PlayerActionC2SPacketMixin)packet).setAction(PlayerActionC2SPacket.Action.DROP_ITEM);
player.networkHandler.onPlayerAction(packet);
}
public void mount()
{
List<Entity> entities = player.world.getEntities(player,player.getBoundingBox().expand(3.0D, 1.0D, 3.0D));
if (entities.size()==0)
{
return;
}
Entity closest = entities.get(0);
double distance = player.squaredDistanceTo(closest);
for (Entity e: entities)
{
double dd = player.squaredDistanceTo(e);
if (dd<distance)
{
distance = dd;
closest = e;
}
}
player.startRiding(closest,true);
}
public void dismount()
{
player.stopRiding();
}
public void onUpdate()
{
if (doesJump)
{
if (--jumpCooldown==0)
{
jumpCooldown = jumpInterval;
//jumpOnce();
player.setJumping(true);
}
else
{
player.setJumping(false);
}
}
boolean used = false;
if (doesUse && (--useCooldown)==0)
{
useCooldown = useInterval;
used = useOnce();
}
if (doesAttack)
{
if ((--attackCooldown) == 0)
{
attackCooldown = attackInterval;
if (!(used)) attackOnce();
}
}
if (forward != 0.0F)
{
player.forwardSpeed = forward*(sneaking?0.3F:1.0F);
}
if (strafing != 0.0F)
{
player.sidewaysSpeed = strafing*(sneaking?0.3F:1.0F);
}
}
public void jumpOnce()
{
if (player.onGround)
{
player.jump();
}
}
public void attackOnce()
{
/* needs rework due to 1.14 changes
HitResult raytraceresult = mouseOver();
if(raytraceresult == null) return;
switch (raytraceresult.getType())
{
case ENTITY:
player.attackTargetEntityWithCurrentItem(raytraceresult.entity);
this.player.swingArm(EnumHand.MAIN_HAND);
this.player.resetCooldown();
break;
case MISS:
break;
case BLOCK:
BlockPos blockpos = raytraceresult.getBlockPos();
if (player.getEntityWorld().getBlockState(blockpos).getMaterial() != Material.AIR)
{
onPlayerDamageBlock(blockpos,raytraceresult.sideHit.getOpposite());
this.player.swingArm(EnumHand.MAIN_HAND);
if (attackInterval > 1)
{
resetBlockRemoving(true);
}
break;
}
}
*/
}
public boolean useOnce()
{
/*needs rework due to 1.14 changes
BlockHitResult raytraceresult = mouseOver();
for (EnumHand enumhand : EnumHand.values())
{
ItemStack itemstack = this.player.getHeldItem(enumhand);
if (raytraceresult != null)
{
switch (raytraceresult.type)
{
case ENTITY:
Entity target = raytraceresult.entity;
Vec3d vec3d = new Vec3d(raytraceresult.hitVec.x - target.posX, raytraceresult.hitVec.y - target.posY, raytraceresult.hitVec.z - target.posZ);
boolean flag = player.canEntityBeSeen(target);
double d0 = 36.0D;
if (!flag)
{
d0 = 9.0D;
}
if (player.getDistanceSq(target) < d0)
{
EnumActionResult res = player.interactOn(target,enumhand);
if (res == EnumActionResult.SUCCESS)
{
return true;
}
res = target.applyPlayerInteraction(player, vec3d, enumhand);
if (res == EnumActionResult.SUCCESS)
{
return true;
}
}
break;
case MISS:
break;
case BLOCK:
BlockPos blockpos = raytraceresult.getBlockPos();
if (player.getEntityWorld().getBlockState(blockpos).getMaterial() != Material.AIR)
{
float x = (float) raytraceresult.hitVec.x;
float y = (float) raytraceresult.hitVec.y;
float z = (float) raytraceresult.hitVec.z;
EnumActionResult res = player.interactionManager.processRightClickBlock(player, player.getEntityWorld(), itemstack, enumhand, blockpos, raytraceresult.sideHit, x, y, z);
if (res == EnumActionResult.SUCCESS)
{
this.player.swingArm(enumhand);
return true;
}
}
}
}
EnumActionResult res = player.interactionManager.processRightClick(player,player.getEntityWorld(),itemstack,enumhand);
if (res == EnumActionResult.SUCCESS)
{
return true;
}
}
*/
return false;
}
/*needs rework due to 1.14 changes
private BlockHitResult rayTraceBlocks(double blockReachDistance)
{
Vec3d eyeVec = player.getEyePosition(1.0F);
Vec3d lookVec = player.getLook(1.0F);
Vec3d pointVec = eyeVec.add(lookVec.x * blockReachDistance, lookVec.y * blockReachDistance, lookVec.z * blockReachDistance);
return player.getEntityWorld().rayTraceBlocks(eyeVec, pointVec, RayTraceFluidMode.NEVER, false, true);
}
private BlockHitResult mouseOver()
{
World world = player.getEntityWorld();
BlockHitResult result = null;
Entity pointedEntity = null;
double reach = player.isCreative() ? 5.0D : 4.5D;
result = rayTraceBlocks(reach);
Vec3d eyeVec = player.getEyePosition(1.0F);
boolean flag = !player.isCreative();
if (player.isCreative()) reach = 6.0D;
double extendedReach = reach;
if (result != null)
{
extendedReach = result.hitVec.distanceTo(eyeVec);
if (world.getBlockState(result.getBlockPos()).getMaterial() == Material.AIR)
result = null;
}
Vec3d lookVec = player.getLook(1.0F);
Vec3d pointVec = eyeVec.add(lookVec.x * reach, lookVec.y * reach, lookVec.z * reach);
Vec3d hitVec = null;
List<Entity> list = world.getEntitiesInAABBexcluding(
player,
player.getBoundingBox().expand(lookVec.x * reach, lookVec.y * reach, lookVec.z * reach).grow(1.0D, 1.0D, 1.0D),
EntitySelectors.NOT_SPECTATING.and((Predicate<Entity>) e -> e != null && e.canBeCollidedWith())
);
double d2 = extendedReach;
for (int j = 0; j < list.size(); ++j)
{
Entity entity1 = list.get(j);
BoundingBox axisalignedbb = entity1.getBoundingBox().grow((double) entity1.getCollisionBorderSize());
BlockHitResult raytraceresult = axisalignedbb.calculateIntercept(eyeVec, pointVec);
if (axisalignedbb.contains(eyeVec))
{
if (d2 >= 0.0D)
{
pointedEntity = entity1;
hitVec = raytraceresult == null ? eyeVec : raytraceresult.hitVec;
d2 = 0.0D;
}
}
else if (raytraceresult != null)
{
double d3 = eyeVec.distanceTo(raytraceresult.hitVec);
if (d3 < d2 || d2 == 0.0D)
{
if (entity1.getLowestRidingEntity() == player.getLowestRidingEntity())
{
if (d2 == 0.0D)
{
pointedEntity = entity1;
hitVec = raytraceresult.hitVec;
}
}
else
{
pointedEntity = entity1;
hitVec = raytraceresult.hitVec;
d2 = d3;
}
}
}
}
if (pointedEntity != null && flag && eyeVec.distanceTo(hitVec) > 3.0D)
{
pointedEntity = null;
result = new BlockHitResult(BlockHitResult.Type.MISS, hitVec, (Direction) null, new BlockPos(hitVec));
}
if (pointedEntity != null && (d2 < extendedReach || result == null))
{
result = new BlockHitResult(pointedEntity, hitVec);
}
return result;
}
public boolean clickBlock(BlockPos loc, Direction face) // don't call this one
{
World world = player.getEntityWorld();
if (player.interactionManager.getGameType()!=GameMode.ADVENTURE)
{
if (player.interactionManager.getGameType() == GameMode.SPECTATOR)
{
return false;
}
if (!player.abilities.allowEdit)
{
ItemStack itemstack = player.getHeldItemMainhand();
if (itemstack.isEmpty())
{
return false;
}
BlockWorldState blockworldstate = new BlockWorldState(world, loc, false);
if (!itemstack.canDestroy(world.getTags(), blockworldstate))
{
return false;
}
}
}
if (!world.getWorldBorder().contains(loc))
{
return false;
}
else
{
if (player.interactionManager.getGameType()==GameMode.CREATIVE)
{
player.connection.processPlayerDigging(new CPacketPlayerDigging(loc, face, CPacketPlayerDigging.Action.START_DESTROY_BLOCK));
clickBlockCreative(world, loc, face);
this.blockHitDelay = 5;
}
else if (!this.isHittingBlock || !(currentBlock.equals(loc)))
{
if (this.isHittingBlock)
{
player.connection.processPlayerDigging(new CPacketPlayerDigging(this.currentBlock, face, CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK));
}
BlockState iblockstate = world.getBlockState(loc);
player.connection.processPlayerDigging(new CPacketPlayerDigging(loc, face, CPacketPlayerDigging.Action.START_DESTROY_BLOCK));
boolean flag = iblockstate.getMaterial() != Material.AIR;
if (flag && this.curBlockDamageMP == 0.0F)
{
iblockstate.getBlock().onBlockClicked(iblockstate, world, loc, player);
}
if (flag && iblockstate.getPlayerRelativeBlockHardness(player, world, loc) >= 1.0F)
{
this.onPlayerDestroyBlock(loc);
}
else
{
this.isHittingBlock = true;
this.currentBlock = loc;
this.curBlockDamageMP = 0.0F;
world.sendBlockBreakProgress(player.getEntityId(), this.currentBlock, (int)(this.curBlockDamageMP * 10.0F) - 1);
}
}
return true;
}
}
private void clickBlockCreative(World world, BlockPos pos, Direction facing)
{
if (!world.extinguishFire(player, pos, facing))
{
onPlayerDestroyBlock(pos);
}
}
public boolean onPlayerDamageBlock(BlockPos posBlock, Direction directionFacing) //continue clicking - one to call
{
if (this.blockHitDelay > 0)
{
--this.blockHitDelay;
return true;
}
World world = player.getEntityWorld();
if (player.interactionManager.getGameType()==GameMode.CREATIVE && world.getWorldBorder().contains(posBlock))
{
this.blockHitDelay = 5;
player.connection.processPlayerDigging(new CPacketPlayerDigging(posBlock, directionFacing, CPacketPlayerDigging.Action.START_DESTROY_BLOCK));
clickBlockCreative(world, posBlock, directionFacing);
return true;
}
else if (posBlock.equals(currentBlock))
{
BlockState iblockstate = world.getBlockState(posBlock);
if (iblockstate.getMaterial() == Material.AIR)
{
this.isHittingBlock = false;
return false;
}
else
{
this.curBlockDamageMP += iblockstate.getPlayerRelativeBlockHardness(player, world, posBlock);
if (this.curBlockDamageMP >= 1.0F)
{
this.isHittingBlock = false;
player.connection.processPlayerDigging(new CPacketPlayerDigging(posBlock, directionFacing, CPacketPlayerDigging.Action.STOP_DESTROY_BLOCK));
this.onPlayerDestroyBlock(posBlock);
this.curBlockDamageMP = 0.0F;
this.blockHitDelay = 5;
}
//player.getEntityId()
//send to all, even the breaker
world.sendBlockBreakProgress(-1, this.currentBlock, (int)(this.curBlockDamageMP * 10.0F) - 1);
return true;
}
}
else
{
return this.clickBlock(posBlock, directionFacing);
}
}
private boolean onPlayerDestroyBlock(BlockPos pos)
{
World world = player.getEntityWorld();
if (player.interactionManager.getGameType()!=GameMode.ADVENTURE)
{
if (player.interactionManager.getGameType() == GameMode.SPECTATOR)
{
return false;
}
if (player.abilities.allowEdit)
{
ItemStack itemstack = player.getHeldItemMainhand();
if (itemstack.isEmpty())
{
return false;
}
BlockWorldState blockworldstate = new BlockWorldState(world, pos, false);
if (!itemstack.canDestroy(world.getTags(), blockworldstate))
{
return false;
}
}
}
if (player.interactionManager.getGameType()==GameMode.CREATIVE && !player.getHeldItemMainhand().isEmpty() && player.getHeldItemMainhand().getItem() instanceof ItemSword)
{
return false;
}
else
{
BlockState iblockstate = world.getBlockState(pos);
Block block = iblockstate.getBlock();
if ((block instanceof BlockCommandBlock || block instanceof BlockStructure) && !player.canUseCommandBlock())
{
return false;
}
else if (iblockstate.getMaterial() == Material.AIR)
{
return false;
}
else
{
world.playEvent(2001, pos, Block.getStateId(iblockstate));
block.onBlockHarvested(world, pos, iblockstate, player);
FluidState ifluidstate = world.getFluidState(pos);
boolean flag = world.setBlockState(pos, ifluidstate.getBlockState(), 11);
if (flag)
{
block.onPlayerDestroy(world, pos, iblockstate);
}
this.currentBlock = new BlockPos(this.currentBlock.getX(), -1, this.currentBlock.getZ());
if (!(player.interactionManager.getGameType()==GameMode.CREATIVE))
{
ItemStack itemstack1 = player.getHeldItemMainhand();
if (!itemstack1.isEmpty())
{
itemstack1.onBlockDestroyed(world, iblockstate, pos, player);
if (itemstack1.isEmpty())
{
player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
}
}
}
return flag;
}
}
}
public void resetBlockRemoving(boolean force)
{
if (this.isHittingBlock || force)
{
player.connection.processPlayerDigging(new CPacketPlayerDigging(this.currentBlock, Direction.DOWN, CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK));
this.isHittingBlock = false;
this.curBlockDamageMP = 0.0F;
player.getEntityWorld().sendBlockBreakProgress(player.getEntityId(), this.currentBlock, -1);
player.resetCooldown();
blockHitDelay = 0;
this.currentBlock = new BlockPos(-1,-1,-1);
}
}
/*
public EnumActionResult processRightClickBlock(EntityPlayerSP player, WorldClient worldIn, BlockPos stack, Direction pos, Vec3d facing, EnumHand vec)
{
this.syncCurrentPlayItem();
ItemStack itemstack = player.getHeldItem(vec);
float f = (float)(facing.xCoord - (double)stack.getX());
float f1 = (float)(facing.yCoord - (double)stack.getY());
float f2 = (float)(facing.zCoord - (double)stack.getZ());
boolean flag = false;
if (!this.mc.world.getWorldBorder().contains(stack))
{
return EnumActionResult.FAIL;
}
else
{
if (this.currentGameType != GameMode.SPECTATOR)
{
BlockState iblockstate = worldIn.getBlockState(stack);
if ((!player.isSneaking() || player.getHeldItemMainhand().func_190926_b() && player.getHeldItemOffhand().func_190926_b()) && iblockstate.getBlock().onBlockActivated(worldIn, stack, iblockstate, player, vec, pos, f, f1, f2))
{
flag = true;
}
if (!flag && itemstack.getItem() instanceof ItemBlock)
{
ItemBlock itemblock = (ItemBlock)itemstack.getItem();
if (!itemblock.canPlaceBlockOnSide(worldIn, stack, pos, player, itemstack))
{
return EnumActionResult.FAIL;
}
}
}
this.connection.sendPacket(new CPacketPlayerTryUseItemOnBlock(stack, pos, vec, f, f1, f2));
if (!flag && this.currentGameType != GameMode.SPECTATOR)
{
if (itemstack.func_190926_b())
{
return EnumActionResult.PASS;
}
else if (player.getCooldownTracker().hasCooldown(itemstack.getItem()))
{
return EnumActionResult.PASS;
}
else
{
if (itemstack.getItem() instanceof ItemBlock && !player.canUseCommandBlock())
{
Block block = ((ItemBlock)itemstack.getItem()).getBlock();
if (block instanceof BlockCommandBlock || block instanceof BlockStructure)
{
return EnumActionResult.FAIL;
}
}
if (this.currentGameType.isCreative())
{
int i = itemstack.getMetadata();
int j = itemstack.func_190916_E();
EnumActionResult enumactionresult = itemstack.onItemUse(player, worldIn, stack, vec, pos, f, f1, f2);
itemstack.setItemDamage(i);
itemstack.func_190920_e(j);
return enumactionresult;
}
else
{
return itemstack.onItemUse(player, worldIn, stack, vec, pos, f, f1, f2);
}
}
}
else
{
return EnumActionResult.SUCCESS;
}
}
}
public EnumActionResult processRightClick(EntityPlayer player, World worldIn, EnumHand stack)
{
if (this.currentGameType == GameMode.SPECTATOR)
{
return EnumActionResult.PASS;
}
else
{
this.syncCurrentPlayItem();
this.connection.sendPacket(new CPacketPlayerTryUseItem(stack));
ItemStack itemstack = player.getHeldItem(stack);
if (player.getCooldownTracker().hasCooldown(itemstack.getItem()))
{
return EnumActionResult.PASS;
}
else
{
int i = itemstack.func_190916_E();
ActionResult<ItemStack> actionresult = itemstack.useItemRightClick(worldIn, player, stack);
ItemStack itemstack1 = actionresult.getResult();
if (itemstack1 != itemstack || itemstack1.func_190916_E() != i)
{
player.setHeldItem(stack, itemstack1);
}
return actionresult.getType();
}
}
}
public EnumActionResult interactWithEntity(EntityPlayer player, Entity target, EnumHand heldItem)
{
this.syncCurrentPlayItem();
this.connection.sendPacket(new CPacketUseEntity(target, heldItem));
return this.currentGameType == GameMode.SPECTATOR ? EnumActionResult.PASS : player.func_190775_a(target, heldItem);
}
*/
/*
* Handles right clicking an entity from the entities side, sends a packet to the server.
*
public EnumActionResult interactWithEntity(EntityPlayer player, Entity target, BlockHitResult raytrace, EnumHand heldItem)
{
this.syncCurrentPlayItem();
Vec3d vec3d = new Vec3d(raytrace.hitVec.xCoord - target.posX, raytrace.hitVec.yCoord - target.posY, raytrace.hitVec.zCoord - target.posZ);
this.connection.sendPacket(new CPacketUseEntity(target, heldItem, vec3d));
return this.currentGameType == GameMode.SPECTATOR ? EnumActionResult.PASS : target.applyPlayerInteraction(player, vec3d, heldItem);
}
*/
}
@@ -0,0 +1,203 @@
package carpet.helpers;
import carpet.CarpetSettings;
import net.minecraft.init.Biomes;
import net.minecraft.init.Blocks;
import net.minecraft.structure.Structure;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.provider.BiomeProvider;
import net.minecraft.world.biome.provider.OverworldBiomeProvider;
import net.minecraft.world.biome.provider.OverworldBiomeProviderSettings;
import net.minecraft.world.gen.ChunkGeneratorOverworld;
import net.minecraft.world.gen.IChunkGenerator;
import net.minecraft.world.gen.OverworldGenSettings;
import net.minecraft.world.gen.feature.BlockBlobConfig;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.FeatureConfig;
import net.minecraft.world.gen.feature.IcebergConfig;
import net.minecraft.world.gen.feature.LakesConfig;
import net.minecraft.world.gen.feature.OceanRuinFeature;
import net.minecraft.world.gen.feature.SeaGrassConfig;
import net.minecraft.world.gen.feature.ShrubFeature;
import net.minecraft.world.gen.feature.TallGrassConfig;
import net.minecraft.world.gen.feature.TallGrassFeature;
import net.minecraft.world.gen.feature.structure.MineshaftConfig;
import net.minecraft.world.gen.feature.structure.MineshaftStructure;
import net.minecraft.world.gen.feature.structure.OceanRuinConfig;
import net.minecraft.structure.OceanRuinGenerator;
import net.minecraft.world.gen.feature.structure.ShipwreckConfig;
import net.minecraft.structure.Structure;
import net.minecraft.world.gen.feature.structure.VillageConfig;
import net.minecraft.world.gen.feature.structure.VillagePieces;
import net.minecraft.world.gen.placement.CountConfig;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class FeatureGenerator
{
@FunctionalInterface
private interface Thing
{
Boolean plop(World world, BlockPos pos);
}
private static Thing simplePlop(Feature<DefaultFeatureConfig> feature)
{
return simplePlop(feature, FeatureConfig.DEFAULT);
}
private static Thing simplePlop(Feature feature, FeatureConfig config)
{
return (w, p) -> {
CarpetSettings.skipGenerationChecks=true;
boolean res = feature.generate(w, w.getChunkManager().getChunkGenerator(), w.random, p, config);
CarpetSettings.skipGenerationChecks=false;
return res;
};
}
private static Thing spawnCustomStructure(Structure structure, FeatureConfig config)
{
return (w, p) -> {
IChunkGenerator chunkgen = new ChunkGeneratorOverworld(w, null, new OverworldGenSettings())
{
@Nullable
@Override
public FeatureConfig getStructureConfig(Biome biomeIn, Structure<? extends FeatureConfig> structureIn)
{
return config;
}
@Override
public BiomeProvider getBiomeProvider()
{
return new OverworldBiomeProvider(new OverworldBiomeProviderSettings().setWorldInfo(w.getWorldInfo()))
{
@Nullable
@Override
public Biome getBiome(BlockPos pos, @Nullable Biome defaultBiome)
{
return Biomes.PLAINS;
}
};
}
};
return structure.plopAnywhere(w, p, chunkgen);
};
}
public static Boolean spawn(String name, World world, BlockPos pos)
{
if (featureMap.containsKey(name))
return featureMap.get(name).plop(world, pos);
return null;
}
private static Map<String, Thing> featureMap = new HashMap<String, Thing>() {{
put("oak", simplePlop(Feature.TREE));
put("oak_large", simplePlop(Feature.BIG_TREE));
put("birch", simplePlop(Feature.BIRCH_TREE));
put("birch_large", simplePlop(Feature.TALL_BIRCH_TREE));
put("shrub", simplePlop(Feature.SHRUB));
put("shrub_acacia", simplePlop(new ShrubFeature(Blocks.ACACIA_WOOD.getDefaultState(), Blocks.ACACIA_LEAVES.getDefaultState())));
put("shrub_birch", simplePlop(new ShrubFeature(Blocks.BIRCH_WOOD.getDefaultState(), Blocks.BIRCH_LEAVES.getDefaultState())));
put("shrub_snowy", simplePlop(new ShrubFeature(Blocks.BONE_BLOCK.getDefaultState(), Blocks.COBWEB.getDefaultState())));
put("jungle", simplePlop(Feature.JUNGLE_TREE));
put("spruce_matchstick", simplePlop(Feature.POINTY_TAIGA_TREE));
put("dark_oak", simplePlop(Feature.CANOPY_TREE));
put("acacia", simplePlop(Feature.SAVANNA_TREE));
put("spruce", simplePlop(Feature.TALL_TAIGA_TREE));
put("oak_swamp", simplePlop(Feature.SWAMP_TREE));
put("jungle_large", simplePlop(Feature.MESA_JUNGLE));
put("spruce_matchstick_large", simplePlop(Feature.MEGA_PINE_TREE_1));
put("spruce_large", simplePlop(Feature.MEGA_PINE_TREE_2));
put("well", simplePlop(Feature.DESERT_WELLS));
put("grass_jungle", simplePlop(Feature.JUNGLE_GRASS));
put("fern", simplePlop(Feature.TAIGA_GRASS));
put("grass", simplePlop(new TallGrassFeature(), new TallGrassConfig(Blocks.GRASS.getDefaultState())));
//put("", simplePlop(Feature.));
//put("", simplePlop(Feature.));
//put("", simplePlop(Feature.));
//put("", simplePlop(Feature.));
put("cactus", simplePlop(Feature.CACTUS));
put("dead_bush", simplePlop(Feature.DEAD_BUSH));
put("fossils", simplePlop(Feature.FOSSILS)); // spawn above, spawn invisible
put("mushroom_brown", simplePlop(Feature.BIG_BROWN_MUSHROOM));
put("mushroom_red", simplePlop(Feature.BIG_RED_MUSHROOM));
put("ice_spike", simplePlop(Feature.ICE_SPIKE));
put("glowstone", simplePlop(Feature.GLOWSTONE));
put("melon", simplePlop(Feature.MELON));
put("pumpkin", simplePlop(Feature.PUMPKIN));
put("sugarcane", simplePlop(Feature.REED));
put("lilypad", simplePlop(Feature.WATERLILY));
put("dungeon", simplePlop(Feature.DUNGEONS));
put("iceberg", simplePlop(Feature.ICEBERG, new IcebergConfig(Blocks.PACKED_ICE.getDefaultState())));
put("iceberg_blue", simplePlop(Feature.ICEBERG, new IcebergConfig(Blocks.BLUE_ICE.getDefaultState())));
put("lake", simplePlop(Feature.LAKES, new LakesConfig(Blocks.WATER)));
put("lake_lava", simplePlop(Feature.LAKES, new LakesConfig(Blocks.LAVA)));
//put("end tower", simplePlop(Feature.END_CRYSTAL_TOWER)); // requires more complex setup
put("end_island", simplePlop(Feature.END_ISLAND));
put("chorus", simplePlop(Feature.CHORUS_PLANT));
put("sea_grass", simplePlop(Feature.SEA_GRASS, new SeaGrassConfig(80, 0.8D)));
put("sea_grass_river", simplePlop(Feature.SEA_GRASS, new SeaGrassConfig(48, 0.4D)));
put("kelp", simplePlop(Feature.KELP));
put("coral_tree", simplePlop(Feature.CORAL_TREE));
put("coral_mushroom", simplePlop(Feature.CORAL_MUSHROOM));
put("coral_claw", simplePlop(Feature.CORAL_CLAW));
put("coral", (w, p) ->
w.rand.nextInt(3)!=0?
(w.rand.nextInt(2)!=0?
simplePlop(Feature.CORAL_MUSHROOM).plop(w, p):
simplePlop(Feature.CORAL_TREE).plop(w, p))
:simplePlop(Feature.CORAL_CLAW).plop(w, p));
put("sea_pickle", simplePlop(Feature.SEA_PICKLE, new CountConfig(20)));
put("boulder", simplePlop(Feature.BLOCK_BLOB, new BlockBlobConfig(Blocks.MOSSY_COBBLESTONE, 0)));
//structures
put("monument", Feature.OCEAN_MONUMENT::plopAnywhere);
put("fortress", Feature.FORTRESS::plopAnywhere);
put("mansion", Feature.WOODLAND_MANSION::plopAnywhere);
put("jungle_temple", Feature.JUNGLE_PYRAMID::plopAnywhere);
put("desert_temple", Feature.DESERT_PYRAMID::plopAnywhere);
put("end_city", Feature.END_CITY::plopAnywhere);
put("igloo", Feature.IGLOO::plopAnywhere);
put("shipwreck", spawnCustomStructure(Feature.SHIPWRECK, new ShipwreckConfig(true)));
put("shipwreck2", spawnCustomStructure(Feature.SHIPWRECK, new ShipwreckConfig(false)));
put("witchhut", Feature.SWAMP_HUT::plopAnywhere);
put("stronghold", Feature.STRONGHOLD::plopAnywhere);
put("oceanruin_small", spawnCustomStructure(Feature.OCEAN_RUIN,
new OceanRuinConfig(OceanRuinGenerator.Type.COLD, 0.0F, 0.5F)));
put("oceanruin_warm_small", spawnCustomStructure(Feature.OCEAN_RUIN,
new OceanRuinConfig(OceanRuinGenerator.Type.WARM, 0.0F, 0.5F)));
put("oceanruin_tall", spawnCustomStructure(Feature.OCEAN_RUIN,
new OceanRuinConfig(OceanRuinGenerator.Type.COLD, 1.0F, 0.0F)));
put("oceanruin_warm_tall", spawnCustomStructure(Feature.OCEAN_RUIN,
new OceanRuinConfig(OceanRuinGenerator.Type.WARM, 1.0F, 0.0F)));
put("oceanruin", spawnCustomStructure(Feature.OCEAN_RUIN,
new OceanRuinConfig(OceanRuinGenerator.Type.COLD, 1.0F, 1.0F)));
put("oceanruin_warm", spawnCustomStructure(Feature.OCEAN_RUIN,
new OceanRuinConfig(OceanRuinGenerator.Type.WARM, 1.0F, 1.0F)));
put("treasure", Feature.BURIED_TREASURE::plopAnywhere);
put("mineshaft", spawnCustomStructure(Feature.MINESHAFT, new MineshaftConfig(0.0, MineshaftStructure.Type.NORMAL)));
put("mineshaft_mesa", spawnCustomStructure(Feature.MINESHAFT, new MineshaftConfig(0.0, MineshaftStructure.Type.MESA)));
put("village", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.OAK)));
put("village_desert", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.SANDSTONE)));
put("village_savanna", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.ACACIA)));
put("village_taiga", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.SPRUCE)));
}};
}
@@ -0,0 +1,156 @@
package carpet.helpers;
import carpet.utils.Messenger;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.objects.Object2LongLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import net.minecraft.util.DyeColor;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.network.chat.BaseComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.MinecraftServer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
public class HopperCounter
{
public static final Map<DyeColor, HopperCounter> COUNTERS;
static
{
EnumMap<DyeColor, HopperCounter> counterMap = new EnumMap<>(DyeColor.class);
for (DyeColor color : DyeColor.values())
{
counterMap.put(color, new HopperCounter(color));
}
COUNTERS = Maps.immutableEnumMap(counterMap);
}
public final DyeColor color;
private final Object2LongMap<Item> counter = new Object2LongLinkedOpenHashMap<>();
private long startTick;
private long startMillis;
// private PubSubInfoProvider<Long> pubSubProvider;
private HopperCounter(DyeColor color)
{
this.color = color;
// pubSubProvider = new PubSubInfoProvider<>(QuickCarpet.PUBSUB, "carpet.counter." + color.getName(), 0, this::getTotalItems);
}
public void add(MinecraftServer server, ItemStack stack)
{
if (startTick == 0)
{
startTick = server.getTicks();
startMillis = System.currentTimeMillis();
}
Item item = stack.getItem();
counter.put(item, counter.getLong(item) + stack.getAmount());
// pubSubProvider.publish();
}
public void reset(MinecraftServer server)
{
counter.clear();
startTick = server.getTicks();
startMillis = System.currentTimeMillis();
// pubSubProvider.publish();
}
public static void resetAll(MinecraftServer server)
{
for (HopperCounter counter : COUNTERS.values())
{
counter.reset(server);
}
}
public static List<BaseComponent> formatAll(MinecraftServer server, boolean realtime)
{
List<BaseComponent> text = new ArrayList<>();
for (HopperCounter counter : COUNTERS.values())
{
List<BaseComponent> temp = counter.format(server, realtime, false);
if (temp.size() > 1)
{
if (!text.isEmpty()) text.add(Messenger.s(""));
text.addAll(temp);
}
}
if (text.isEmpty())
{
text.add(Messenger.s("No items have been counted yet."));
}
return text;
}
public List<BaseComponent> format(MinecraftServer server, boolean realTime, boolean brief)
{
if (counter.isEmpty())
{
if (brief)
{
return Collections.singletonList(Messenger.c("g "+color+": -, -/h, - min "));
}
return Collections.singletonList(Messenger.s(String.format("No items for %s yet", color.getName())));
}
long total = getTotalItems();
long ticks = Math.max(realTime ? (System.currentTimeMillis() - startMillis) / 50 : server.getTicks() - startTick, 1);
if (total == 0)
{
if (brief)
{
return Collections.singletonList(Messenger.c(String.format("c %s: 0, 0/h, %.1f min ", color, ticks / (20.0 * 60.0))));
}
return Collections.singletonList(Messenger.c(String.format("w No items for %s yet (%.2f min.%s)",
color.getName(), ticks / (20.0 * 60.0), (realTime ? " - real time" : "")),
"nb [X]", "^g reset", "!/counter " + color.getName() +" reset"));
}
if (brief)
{
return Collections.singletonList(Messenger.c(String.format("c %s: %d, %d/h, %.1f min ",
color.getName(), total, total * (20 * 60 * 60) / ticks, ticks / (20.0 * 60.0))));
}
List<BaseComponent> items = new ArrayList<>();
items.add(Messenger.c(String.format("w Items for %s (%.2f min.%s), total: %d, (%.1f/h):",
color, ticks*1.0/(20*60), (realTime?" - real time":""), total, total*1.0*(20*60*60)/ticks),
"nb [X]", "^g reset", "!/counter "+color+" reset"
));
items.addAll(counter.object2LongEntrySet().stream().map(e ->
{
BaseComponent itemName = new TranslatableComponent(e.getKey().getTranslationKey());
long count = e.getLongValue();
return Messenger.c("w - ", itemName, String.format("w : %d, %.1f/h",
count,
count * (20.0 * 60.0 * 60.0) / ticks));
}).collect(Collectors.toList()));
return items;
}
public static HopperCounter getCounter(String color)
{
try
{
DyeColor colorEnum = DyeColor.valueOf(color.toUpperCase(Locale.ROOT));
return COUNTERS.get(colorEnum);
}
catch (IllegalArgumentException e)
{
return null;
}
}
public long getTotalItems()
{
return counter.values().stream().mapToLong(Long::longValue).sum();
}
}
@@ -0,0 +1,19 @@
package carpet.helpers;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItem;
public class PortalHelper
{
public static boolean player_holds_obsidian(PlayerEntity playerIn)
{
if ( (!playerIn.getMainHandStack().isEmpty()
&& playerIn.getMainHandStack().getItem() instanceof BlockItem &&
((BlockItem)(playerIn.getMainHandStack().getItem())).getBlock() == Blocks.OBSIDIAN ))
{
return true;
}
return false;
}
}
@@ -0,0 +1,922 @@
package carpet.helpers;
//Author: theosib
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ThreadLocalRandom;
import net.minecraft.block.Block;
import net.minecraft.block.RedstoneWireBlock;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class RedstoneWireTurbo
{
/*
* This is Helper class for RedstoneWireBlock. It implements a minimially-invasive
* bolt-on accelerator that performs a breadth-first search through redstone wire blocks
* in order to more efficiently and deterministicly compute new redstone wire power levels
* and determine the order in which other blocks should be updated.
*
* Features:
* - Changes to RedstoneWireBlock are very limited, no other classes are affected, and the
* choice between old and new redstone wire update algorithms is switchable on-line.
* - The vanilla implementation relied on World.notifyNeighborsOfStateChange for redstone
* wire blocks to communicate power level changes to each other, generating 36 block
* updates per call. This improved implementation propagates power level changes directly
* between redstone wire blocks. Redstone wire power levels are therefore computed more quickly,
* and block updates are sent only to non-redstone blocks, many of which may perform an
* action when informed of a change in redstone power level. (Note: Block updates are not
* the same as state changes to redstone wire. Wire block states are updated as soon
* as they are computed.)
* - Of the 36 block updates generated by a call to World.notifyNeighborsOfStateChange,
* 12 of them are obviously redundant (e.g. the west neighbor of the east neighbor).
* These are eliminated.
* - Updates to redstone wire and other connected blocks are propagated in a breath-first
* manner, radiating out from the initial trigger (a block update to a redstone wire
* from something other than redstone wire).
* - Updates are scheduled both deterministicly and in an intuitive order, addressing bug
* MC-11193.
* - All redstone behavior that used to be locational now works the same in all locations.
* - All behaviors of redstone wire that used to be orientational now work the same in all
* orientations, as long as orientation can be determined; random otherwise. Some other
* redstone components still update directionally (e.g. switches), and this code can't
* compensate for that.
* - Information that is otherwise computed over and over again or which is expensive to
* to compute is cached for faster lookup. This includes coordinates of block position
* neighbors and block states that won't change behind our backs during the execution of
* this search algorithm.
* - Redundant block updates (both to redstone wire and to other blocks) are heavily
* consolidated. For worst-case scenarios (depowering of redstone wire) this results
* in a reduction of block updates by as much as 95% (factor of 1/21). Due to overheads,
* empirical testing shows a speedup better than 10x. This addresses bug MC-81098.
*
* Extensive testing has been performed to ensure that existing redstone contraptions still
* behave as expected. Results of early testing that identified undesirable behavior changes
* were addressed. Additionally, real-time performance testing revealed compute inefficiencies
* With earlier implementations of this accelerator. Some compatibility adjustments and
* performance optimizations resulted in harmless increases in block updates above the
* theoretical minimum.
*
* Only a single redstone machine was found to break: An instant dropper line hack that
* relies on powered rails and quasiconnectivity but doesn't work in all directions. The
* replacement is to lay redstone wire directly on top of the dropper line, which now works
* reliably in any direction.
*
* There are numerous other optimization that can be made, but those will be provided later in
* separate updates. This version is designed to be minimalistic.
*
* Many thanks to the following individuals for their help in testing this functionality:
* - pokechu22, _MethodZz_, WARBEN, NarcolepticFrog, CommandHelper (nessie), ilmango,
* OreoLamp, Xcom6000, tryashtar, RedCMD, Smokey95Dog, EDDxample, Rays Works,
* Nodnam, BlockyPlays, Grumm, NeunEinser, HelVince.
*/
/* Reference to RedstoneWireBlock object, which uses this accelerator */
private final RedstoneWireBlock wire;
/*
* Implementation:
*
* RedstoneWire Blocks are updated in concentric rings or "layers" radiating out from the
* initial block update that came from a call to RedstoneWireBlock.neighborChanged().
* All nodes put in Layer N are those with Manattan distance N from the trigger
* position, reachable through connected redstone wire blocks.
*
* Layer 0 represents the trigger block position that was input to neighborChanged.
* Layer 1 contains the immediate neighbors of that position.
* Layer N contains the neighbors of blocks in layer N-1, not including
* those in previous layers.
*
* Layers enforce an update order that is a function of Manhattan distance
* from the initial coordinates input to neighborChanged. The same
* coordinates may appear in multiple layers, but redundant updates are minimized.
* Block updates are sent layer-by-layer. If multiple of a block's neighbors experience
* redstone wire changes before its layer is processed, then those updates will be merged.
* If a block's update has been sent, but its neighboring redstone changes
* after that, then another update will be sent. This preserves compatibility with
* machines that rely on zero-tick behavior, except that the new functionality is non-
* locational.
*
* Within each layer, updates are ordered left-to-right relative to the direction of
* information flow. This makes the implementation non-orientational. Only when
* this direction is ambiguous is randomness applied (intentionally).
*/
//private final List<List<UpdateNode>> updateLayers = new ArrayList<>();
private List<UpdateNode> updateQueue0 = new ArrayList<>();
private List<UpdateNode> updateQueue1 = new ArrayList<>();
private List<UpdateNode> updateQueue2 = new ArrayList<>();
public RedstoneWireTurbo(RedstoneWireBlock wire) {
this.wire = wire;
}
/*
* Compute neighbors of a block. When a redstone wire value changes, previously it called
* World.notifyNeighborsOfStateChange. That lists immediately neighboring blocks in
* west, east, down, up, north, south order. For each of those neighbors, their own
* neighbors are updated in the same order. This generates 36 updates, but 12 of them are
* redundant; for instance the west neighbor of a block's east neighbor.
*
* Note that this ordering is only used to create the initial list of neighbors. Once
* the direction of signal flow is identified, the ordering of updates is completely
* reorganized.
*/
public static BlockPos[] computeAllNeighbors(final BlockPos pos) {
final int x = pos.getX();
final int y = pos.getY();
final int z = pos.getZ();
final BlockPos[] n = new BlockPos[24];
// Immediate neighbors, in the same order as
// World.notifyNeighborsOfStateChange, etc.:
// west, east, down, up, north, south
n[ 0] = new BlockPos(x-1, y , z );
n[ 1] = new BlockPos(x+1, y , z );
n[ 2] = new BlockPos(x , y-1, z );
n[ 3] = new BlockPos(x , y+1, z );
n[ 4] = new BlockPos(x , y , z-1);
n[ 5] = new BlockPos(x , y , z+1);
// Neighbors of neighbors, in the same order,
// except that duplicates are not included
n[ 6] = new BlockPos(x-2, y , z );
n[ 7] = new BlockPos(x-1, y-1, z );
n[ 8] = new BlockPos(x-1, y+1, z );
n[ 9] = new BlockPos(x-1, y , z-1);
n[10] = new BlockPos(x-1, y , z+1);
n[11] = new BlockPos(x+2, y , z );
n[12] = new BlockPos(x+1, y-1, z );
n[13] = new BlockPos(x+1, y+1, z );
n[14] = new BlockPos(x+1, y , z-1);
n[15] = new BlockPos(x+1, y , z+1);
n[16] = new BlockPos(x , y-2, z );
n[17] = new BlockPos(x , y-1, z-1);
n[18] = new BlockPos(x , y-1, z+1);
n[19] = new BlockPos(x , y+2, z );
n[20] = new BlockPos(x , y+1, z-1);
n[21] = new BlockPos(x , y+1, z+1);
n[22] = new BlockPos(x , y , z-2);
n[23] = new BlockPos(x , y , z+2);
return n;
}
/*
* We only want redstone wires to update redstone wires that are
* immediately adjacent. Some more distant updates can result
* in cross-talk that (a) wastes time and (b) can make the update
* order unintuitive. Therefore (relative to the neighbor order
* computed by computeAllNeighbors), updates are not scheduled
* for redstone wire in those non-connecting positions. On the
* other hand, updates will always be sent to *other* types of blocks
* in any of the 24 neighboring positions.
*/
private static final boolean[] update_redstone = {
true, true, false, false, true, true, // 0 to 5
false, true, true, false, false, false, // 6 to 11
true, true, false, false, false, true, // 12 to 17
true, false, true, true, false, false}; // 18 to 23
// Internal numbering for cardinal directions
private static final int North = 0;
private static final int East = 1;
private static final int South = 2;
private static final int West = 3;
// Names for debug print statements
private static final char dirname[] = {'N', 'E', 'S', 'W'};
/*
* These lookup tables completely remap neighbor positions into a left-to-right
* ordering, based on the cardinal direction that is determined to be forward.
* See below for more explanation.
*/
private static final int[] forward_is_north = {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15};
private static final int[] forward_is_east = {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10};
private static final int[] forward_is_south = {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9};
private static final int[] forward_is_west = {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14};
/* For any orientation, we end up with the update order defined below. This order is relative to any redstone wire block
* that is itself having an update computed, and this center position is marked with C.
* - The update position marked 0 is computed first, and the one marked 23 is last.
* - Forward is determined by the local direction of information flow into position C from prior updates.
* - The first updates are scheduled for the four positions below and above C.
* - Then updates are scheduled for the four horizontal neighbors of C, followed by the positions below and above those neighbors.
* - Finally, updates are scheduled for the remaining positions with Manhattan distance 2 from C (at the same Y coordinate).
* - For a given horizontal distance from C, updates are scheduled starting from directly left and stepping clockwise to directly
* right. The remaining positions behind C are scheduled counterclockwise so as to maintain the left-to-right ordering.
* - If C is in layer N of the update schedule, then all 24 positions may be scheduled for layer N+1. For redstone wire, no
* updates are scheduled for positions that cannot directly connect. Additionally, the four positions above and below C
* are ALSO scheduled for layer N+2.
* - This update order was selected after experimenting with a number of alternative schedules, based on its compatibility
* with existing redstone designs and behaviors that were considered to be intuitive by various testers. WARBEN in particular
* made some of the most challenging test cases, but the 3-tick clocks (made by RedCMD) were also challenging to fix,
* along with the rail-based instant dropper line built by ilmango. Numerous others made test cases as well, including
* NarcolepticFrog, nessie, and Pokechu22.
*
* - The forward direction ls determined locally. So when there are branches in the redstone wire, the left one will get updated
* before the right one. Each branch can have its own relative forward direction, resulting in the left side of a left branch
* having priority over the right branch of a left branch, which has priority over the left branch of a right branch, followed
* by the right branch of a right branch. And so forth. Since redstone power reduces to zero after a path distance of 15,
* that imposes a practical limit on the branching. Note that the branching is not tracked explicitly -- relative forward
* directions dictate relative sort order, which maintains the proper global ordering. This also makes it unnecessary to be
* concerned about branches meeting up with each other.
*
* ^
* |
* Forward
* <-- Left Right -->
*
* 18
* 10 17 5 19 11
* 2 8 0 12 16 4 C 6 20 9 1 13 3
* 14 21 7 23 15
* Further 22 Further
* Down Down Up Up
*
* Backward
* |
* V
*/
// This allows the above remapping tables to be looked up by cardial direction index
private static final int[][] reordering = {forward_is_north, forward_is_east, forward_is_south, forward_is_west};
/*
* Input: Array of UpdateNode objects in an order corresponding to the positions
* computed by computeAllNeighbors above.
* Output: Array of UpdateNode objects oriented using the above remapping tables
* corresponding to the identified heading (direction of information flow).
*/
private static void orientNeighbors(final UpdateNode[] src, final UpdateNode[] dst, final int heading) {
final int[] re = reordering[heading];
for (int i=0; i<24; i++) {
dst[i] = src[re[i]];
}
}
/*
* Structure to keep track of redstone wire blocks and
* neighbors that will receive updates.
*/
private static class UpdateNode {
public static enum Type {
UNKNOWN, REDSTONE, OTHER
}
BlockState currentState; // Keep track of redstone wire value
UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges)
BlockPos self; // UpdateNode's own position
BlockPos parent; // Which block pos spawned/updated this node
Type type = Type.UNKNOWN; // unknown, redstone wire, other type of block
int layer; // Highest layer this node is scheduled in
boolean visited; // To keep track of information flow direction, visited restone wire is marked
int xbias, zbias; // Remembers directionality of ancestor nodes; helps eliminate directional ambiguities.
}
/*
* Keep track of all block positions discovered during search and their current states.
* We want to remember one entry for each position.
*/
private final Map<BlockPos, UpdateNode> nodeCache = new HashMap<>();
/*
* For a newly created UpdateNode object, determine what type of block it is.
*/
private void identifyNode(final World worldIn, final UpdateNode upd1) {
final BlockPos pos = upd1.self;
final BlockState oldState = worldIn.getBlockState(pos);
upd1.currentState = oldState;
// Some neighbors of redstone wire are other kinds of blocks.
// These need to receive block updates to inform them that
// redstone wire values have changed.
final Block block = oldState.getBlock();
if (block != wire) {
// Mark this block as not redstone wire and therefore
// requiring updates
upd1.type = UpdateNode.Type.OTHER;
// Non-redstone blocks may propagate updates, but those updates
// are not handled by this accelerator. Therefore, we do not
// expand this position's neighbors.
return;
}
// One job of RedstoneWireBlock.neighborChanged is to convert
// redstone wires to items if the block beneath was removed.
// With this accelerator, RedstoneWireBlock.neighborChanged
// is only typically called for a single wire block, while
// others are processed internally by the breadth first search
// algorithm. To preserve this game behavior, this check must
// be replicated here.
if (!oldState.isValidPosition(worldIn, pos)) {
// Pop off the redstone dust
oldState.dropBlockAsItem(worldIn, pos, 0);
worldIn.removeBlock(pos);
// Mark this position as not being redstone wire
upd1.type = UpdateNode.Type.OTHER;
// Note: Sending updates to air blocks leads to an empty method.
// Testing shows this to be faster than explicitly avoiding updates to
// air blocks.
return;
}
// If the above conditions fail, then this is a redstone wire block.
upd1.type = UpdateNode.Type.REDSTONE;
}
/*
* Given which redstone wire blocks have been visited and not visited
* around the position currently being updated, compute the cardinal
* direction that is "forward."
*
* rx is the forward direction along the West/East axis
* rz is the forward direction along the North/South axis
*/
static private int computeHeading(final int rx, final int rz) {
// rx and rz can only take on values -1, 0, and 1, so we can
// compute a code number that allows us to use a single switch
// to determine the heading.
final int code = (rx + 1) + 3*(rz + 1);
switch (code) {
case 0: {
// Both rx and rz are -1 (northwest)
// Randomly choose one to be forward.
final int j = ThreadLocalRandom.current().nextInt(0, 1);
return (j==0) ? North : West;
}
case 1: {
// rx=0, rz=-1
// Definitively North
return North;
}
case 2: {
// rx=1, rz=-1 (northeast)
// Choose randomly between north and east
final int j = ThreadLocalRandom.current().nextInt(0, 1);
return (j==0) ? North : East;
}
case 3: {
// rx=-1, rz=0
// Definitively West
return West;
}
case 4: {
// rx=0, rz=0
// Heading is completely ambiguous. Choose
// randomly among the four cardinal directions.
return ThreadLocalRandom.current().nextInt(0, 4);
}
case 5: {
// rx=1, rz=0
// Definitively East
return East;
}
case 6: {
// rx=-1, rz=1 (southwest)
// Choose randomly between south and west
final int j = ThreadLocalRandom.current().nextInt(0, 1);
return (j==0) ? South : West;
}
case 7: {
// rx=0, rz=1
// Definitively South
return South;
}
case 8: {
// rx=1, rz=1 (southeast)
// Choose randomly between south and east
final int j = ThreadLocalRandom.current().nextInt(0, 1);
return (j==0) ? South : East;
}
}
// We should never get here
return ThreadLocalRandom.current().nextInt(0, 4);
}
// Select whether to use updateSurroundingRedstone from RedstoneWireBlock (old)
// or this helper class (new)
private static final boolean old_current_change = false;
/*
* Process a node whose neighboring redstone wire has experienced value changes.
*/
private void updateNode(final World worldIn, final UpdateNode upd1, final int layer) {
final BlockPos pos = upd1.self;
// Mark this redstone wire as having been visited so that it can be used
// to calculate direction of information flow.
upd1.visited = true;
// Look up the last known state.
// Due to the way other redstone components are updated, we do not
// have to worry about a state changing behind our backs. The rare
// exception is handled by scheduleReentrantNeighborChanged.
final BlockState oldState = upd1.currentState;
// Ask the wire block to compute its power level from its neighbors.
// This will also update the wire's power level and return a new
// state if it has changed. When a wire power level is changed,
// calculateCurrentChanges will immediately update the block state in the world
// and return the same value here to be cached in the corresponding
// UpdateNode object.
BlockState newState;
if (old_current_change) {
newState = wire.func_212568_b(worldIn, pos, oldState);
} else {
// Looking up block state is slow. This accelerator includes a version of
// calculateCurrentChanges that uses cahed wire values for a
// significant performance boost.
newState = this.calculateCurrentChanges(worldIn, upd1);
}
// Only inform neighors if the state has changed
if (newState != oldState) {
// Store the new state
upd1.currentState = newState;
// Inform neighbors of the change
propagateChanges(worldIn, upd1, layer);
}
}
/*
* This identifies the neighboring positions of a new UpdateNode object,
* determines their types, and links those to into the graph. Then based on
* what nodes in the redstone wire graph have been visited, the neighbors
* are reordered left-to-right relative to the direction of information flow.
*/
private void findNeighbors(final World worldIn, final UpdateNode upd1) {
final BlockPos pos = upd1.self;
// Get the list of neighbor coordinates
final BlockPos[] neighbors = computeAllNeighbors(pos);
// Temporary array of neighbors in cardinal ordering
final UpdateNode[] neighbor_nodes = new UpdateNode[24];
// Target array of neighbors sorted left-to-right
upd1.neighbor_nodes = new UpdateNode[24];
for (int i=0; i<24; i++) {
// Look up each neighbor in the node cache
final BlockPos pos2 = neighbors[i];
UpdateNode upd2 = nodeCache.get(pos2);
if (upd2 == null) {
// If this is a previously unreached position, create
// a new update node, add it to the cache, and identify what it is.
upd2 = new UpdateNode();
upd2.self = pos2;
upd2.parent = pos;
nodeCache.put(pos2, upd2);
identifyNode(worldIn, upd2);
}
// For non-redstone blocks, any of the 24 neighboring positions
// should receive a block update. However, some block coordinates
// may contain a redstone wire that does not directly connect to the
// one being expanded. To avoid redundant calculations and confusing
// cross-talk, those neighboring positions are not included.
if (update_redstone[i] || upd2.type != UpdateNode.Type.REDSTONE) {
neighbor_nodes[i] = upd2;
}
}
// Determine the directions from which the redstone signal may have come from. This
// checks for redstone wire at the same Y level and also Y+1 and Y-1, relative to the
// block being expanded.
final boolean fromWest = (neighbor_nodes[0].visited || neighbor_nodes[7].visited || neighbor_nodes[8].visited);
final boolean fromEast = (neighbor_nodes[1].visited || neighbor_nodes[12].visited || neighbor_nodes[13].visited);
final boolean fromNorth = (neighbor_nodes[4].visited || neighbor_nodes[17].visited || neighbor_nodes[20].visited);
final boolean fromSouth = (neighbor_nodes[5].visited || neighbor_nodes[18].visited || neighbor_nodes[21].visited);
int cx = 0, cz = 0;
if (fromWest) cx += 1;
if (fromEast) cx -= 1;
if (fromNorth) cz += 1;
if (fromSouth) cz -= 1;
int heading;
if (cx==0 && cz==0) {
// If there is no clear direction, try to inherit the heading from ancestor nodes.
heading = computeHeading(upd1.xbias, upd1.zbias);
// Propagate that heading to descendent nodes.
for (int i=0; i<24; i++) {
final UpdateNode nn = neighbor_nodes[i];
if (nn != null) {
nn.xbias = upd1.xbias;
nn.zbias = upd1.zbias;
}
}
} else {
if (cx != 0 && cz != 0) {
// If the heading is somewhat ambiguous, try to disambiguate based on
// ancestor nodes.
if (upd1.xbias != 0) cz = 0;
if (upd1.zbias != 0) cx = 0;
}
heading = computeHeading(cx, cz);
// Propagate that heading to descendent nodes.
for (int i=0; i<24; i++) {
final UpdateNode nn = neighbor_nodes[i];
if (nn != null) {
nn.xbias = cx;
nn.zbias = cz;
}
}
}
// Reorder neighboring UpdateNode objects according to the forward direction
// determined above.
orientNeighbors(neighbor_nodes, upd1.neighbor_nodes, heading);
}
/*
* For any redstone wire block in layer N, inform neighbors to recompute their states
* in layers N+1 and N+2;
*/
private void propagateChanges(final World worldIn, final UpdateNode upd1, final int layer) {
if (upd1.neighbor_nodes == null) {
// If this node has not been expanded yet, find its neigbors
findNeighbors(worldIn, upd1);
}
final BlockPos pos = upd1.self;
final int x = pos.getX();
final int y = pos.getY();
final int z = pos.getZ();
// Make sure there are enough layers in the list
//while (updateLayers.size() <= layer+2) updateLayers.add(new ArrayList<UpdateNode>());
// All neighbors may be scheduled for layer N+1
final int layer1 = layer + 1;
// If the node being updated (upd1) has already been expanded, then merely
// schedule updates to its neighbors.
for (int i=0; i<24; i++) {
final UpdateNode upd2 = upd1.neighbor_nodes[i];
// This test ensures that an UpdateNode is never scheduled to the same layer
// more than once. Also, skip non-connecting redstone wire blocks
if (upd2 != null && layer1 > upd2.layer) {
upd2.layer = layer1;
updateQueue1.add(upd2);
// Keep track of which block updated this neighbor
upd2.parent = pos;
}
}
// Nodes above and below are scheduled ALSO for layer N+2
final int layer2 = layer + 2;
// Repeat of the loop above, but only for the first four (above and below) neighbors
// and for layer N+2;
for (int i=0; i<4; i++) {
final UpdateNode upd2 = upd1.neighbor_nodes[i];
if (upd2 != null && layer2 > upd2.layer) {
upd2.layer = layer2;
updateQueue2.add(upd2);
upd2.parent = pos;
}
}
}
// The breadth-first search below will send block updates to blocks
// that are not redstone wire. If one of those updates results in
// a distant redstone wire getting an update, then this.neighborChanged
// will get called. This would be a reentrant call, and
// it is necessary to properly integrate those updates into the
// on-going search through redstone wire. Thus, we make the layer
// currently being processed visible at the object level.
// The current layer being processed by the breadth-first search
private int currentWalkLayer = 0;
private void shiftQueue() {
final List<UpdateNode> t = updateQueue0;
t.clear();
updateQueue0 = updateQueue1;
updateQueue1 = updateQueue2;
updateQueue2 = t;
}
/*
* Perform a breadth-first (layer by layer) traversal through redstone
* wire blocks, propagating value changes to neighbors in an order
* that is a function of distance from the initial call to
* this.neighborChanged.
*/
private void breadthFirstWalk(final World worldIn) {
shiftQueue();
currentWalkLayer = 1;
// Loop over all layers
while (updateQueue0.size()>0 || updateQueue1.size()>0) {
// Get the set of blocks in this layer
final List<UpdateNode> thisLayer = updateQueue0;
//if (thisLayer.size() < 1) {
//shiftQueue();
//currentWalkLayer++;
//continue;
//}
// Loop over all blocks in the layer. Recall that
// this is a List, preserving the insertion order of
// left-to-right based on direction of information flow.
for (UpdateNode upd : thisLayer) {
if (upd.type == UpdateNode.Type.REDSTONE) {
// If the node is is redstone wire,
// schedule updates to neighbors if its value
// has changed.
updateNode(worldIn, upd, currentWalkLayer);
} else {
// If this block is not redstone wire, send a block update.
// Redstone wire blocks get state updates, but they don't
// need block updates. Only non-redstone neighbors need updates.
// The following function is called "neighborChanged" in the latest
// deobfuscated names. World.func_190524_a is called from
// World.notifyNeighborsOfStateChange, and
// notifyNeighborsOfStateExcept. We don't use
// World.notifyNeighborsOfStateChange here, since we are
// already keeping track of all of the neighbor positions
// that need to be updated. All on its own, handling neighbors
// this way reduces block updates by 1/3 (24 instead of 36).
worldIn.neighborChanged(upd.self, wire, upd.parent);
}
}
// Move on to the next layer
shiftQueue();
currentWalkLayer++;
}
currentWalkLayer = 0;
}
/*
* Normally, when Minecraft is computing redstone wire power changes, and a wire power level
* change sends a block update to a neighboring functional component (e.g. piston, repeater, etc.),
* those updates are queued. Only once all redstone wire updates are complete will any component
* action generate any further block updates to redstone wire. Instant repeater lines, for instance,
* will process all wire updates for one redstone line, after which the pistons will zero-tick,
* after which the next redstone line performs all of its updates. Thus, each wire is processed in its
* own discrete wave.
*
* However, there are some corner cases where this pattern breaks, with a proof of concept discovered
* by Rays Works, which works the same in vanilla. The scenario is as follows:
* (1) A redstone wire is conducting a signal.
* (2) Partway through that wave of updates, a neighbor is updated that causes an update to a completely
* separate redstone wire.
* (3) This results in a call to RedstoneWireBlock.neighborChanged for that other wire, in the middle of
* an already on-going propagation through the first wire.
*
* The vanilla code, being depth-first, would end up fully processing the second wire before going back
* to finish processing the first one. (Although technically, vanilla has no special concept of "being
* in the middle" of processing updates to a wire.) For the breadth-first algorithm, we give this
* situation special handling, where the updates for the second wire are incorporated into the schedule
* for the first wire, and then the callstack is allowed to unwind back to the on-going search loop in
* order to continue processing both the first and second wire in the order of distance from the initial
* trigger.
*/
private BlockState scheduleReentrantNeighborChanged(final World worldIn, final BlockPos pos, final BlockState newState, final BlockPos source)
{
if (source != null) {
// If the cause of the redstone wire update is known, we can use that to help determine
// direction of information flow.
UpdateNode src = nodeCache.get(source);
if (src == null) {
src = new UpdateNode();
src.self = source;
src.parent = source;
src.visited = true;
identifyNode(worldIn, src);
nodeCache.put(source, src);
}
}
// Find or generate a node for the redstone block position receiving the update
UpdateNode upd = nodeCache.get(pos);
if (upd == null) {
upd = new UpdateNode();
upd.self = pos;
upd.parent = pos;
upd.visited = true;
identifyNode(worldIn, upd);
nodeCache.put(pos, upd);
}
upd.currentState = newState;
// Receiving this block update may mean something in the world changed.
// Therefore we clear the cached block info about all neighbors of
// the position receiving the update and then re-identify what they are.
if (upd.neighbor_nodes != null) {
for (int i=0; i<24; i++) {
final UpdateNode upd2 = upd.neighbor_nodes[i];
if (upd2 == null) continue;
upd2.type = UpdateNode.Type.UNKNOWN;
upd2.currentState = null;
identifyNode(worldIn, upd2);
}
}
// The block at 'pos' is a redstone wire and has been updated already by calling
// wire.calculateCurrentChanges, so we don't schedule that. However, we do need
// to schedule its neighbors. By passing the current value of 'currentWalkLayer' to
// propagateChanges, the neighbors of 'pos' are schedule for layers currentWalkLayer+1
// and currentWalkLayer+2.
propagateChanges(worldIn, upd, currentWalkLayer);
// Return here. The call stack will unwind back to the first call to
// updateSurroundingRedstone, whereupon the new updates just scheduled will
// be propagated. This also facilitates elimination of superfluous and
// redundant block updates.
return newState;
}
/*
* New version of pre-existing updateSurroundingRedstone, which is called from
* wire.updateSurroundingRedstone, which is called from wire.neighborChanged and a
* few other methods in RedstoneWireBlock. This sets off the breadth-first
* walk through all redstone dust connected to the initial position triggered.
*/
public BlockState updateSurroundingRedstone(final World worldIn, final BlockPos pos, final BlockState state, final BlockPos source)
{
// Check this block's neighbors and see if its power level needs to change
// Use the calculateCurrentChanges method in RedstoneWireBlock since we have no
// cached block states at this point.
final BlockState newState = wire.func_212568_b(worldIn, pos, state);
// If no change, exit
if (newState == state) {
return state;
}
// Check to see if this update was received during an on-going breadth first search
if (currentWalkLayer>0 || nodeCache.size()>0) {
// As breadthFirstWalk progresses, it sends block updates to neighbors. Some of those
// neighbors may affect the world so as to cause yet another redstone wire block to receive
// an update. If that happens, we need to integrate those redstone wire updates into the
// already on-going graph walk being performed by breadthFirstWalk.
return scheduleReentrantNeighborChanged(worldIn, pos, newState, source);
}
// If there are no on-going walks through redstone wire, then start a new walk.
// If the source of the block update to the redstone wire at 'pos' is known, we can use
// that to help determine the direction of information flow.
if (source != null) {
final UpdateNode src = new UpdateNode();
src.self = source;
src.parent = source;
src.visited = true;
nodeCache.put(source, src);
identifyNode(worldIn, src);
}
// Create a node representing the block at 'pos', and then propagate updates
// to its neighbors. As stated above, the call to wire.calculateCurrentChanges
// already performs the update to the block at 'pos', so it is not added to the schedule.
final UpdateNode upd = new UpdateNode();
upd.self = pos;
upd.parent = source!=null ? source : pos;
upd.currentState = newState;
upd.type = UpdateNode.Type.REDSTONE;
upd.visited = true;
nodeCache.put(pos, upd);
propagateChanges(worldIn, upd, 0);
// Perform the walk over all directly reachable redstone wire blocks, propagating wire value
// updates in a breadth first order out from the initial update received for the block at 'pos'.
breadthFirstWalk(worldIn);
// With the whole search completed, clear the list of all known blocks.
// We do not want to keep around state information that may be changed by other code.
// In theory, we could cache the neighbor block positions, but that is a separate
// optimization.
nodeCache.clear();
return newState;
}
// For any array of neighbors in an UpdateNode object, these are always
// the indices of the four immediate neighbors at the same Y coordinate.
private static final int[] rs_neighbors = {4, 5, 6, 7};
private static final int[] rs_neighbors_up = {9, 11, 13, 15};
private static final int[] rs_neighbors_dn = {8, 10, 12, 14};
/*
* Updated calculateCurrentChanges that is optimized for speed and uses
* the UpdateNode's neighbor array to find the redstone states of neighbors
* that might power it.
*/
private BlockState calculateCurrentChanges(final World worldIn, final UpdateNode upd)
{
BlockState state = upd.currentState;
final int i = state.get(RedstoneWireBlock.POWER);
int j = 0;
j = this.getMaxCurrentStrength(upd, j);
int l = 0;
wire.setCanProvidePower(false);
// Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
// and I'm not ready to try to replicate even more functionality from
// elsewhere in Minecraft into this accelerator. So sadly, we must
// suffer the performance hit of this very expensive call. If there
// is consistency to what this call returns, we may be able to cache it.
final int k = worldIn.getRedstonePowerFromNeighbors(upd.self);
wire.setCanProvidePower(true);
// The variable 'k' holds the maximum redstone power value of any adjacent blocks.
// If 'k' has the highest level of all neighbors, then the power level of this
// redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the
// following loop can affect the power level of the wire. Therefore, the loop is
// skipped if k is already 15.
if (k<15) {
if (upd.neighbor_nodes == null) {
// If this node's neighbors are not known, expand the node
findNeighbors(worldIn, upd);
}
// These remain constant, so pull them out of the loop.
// Regardless of which direction is forward, the UpdateNode for the
// position directly above the node being calculated is always
// at index 1.
UpdateNode center_up = upd.neighbor_nodes[1];
boolean center_up_is_cube = center_up.currentState.isNormalCube();
for (int m=0; m<4; m++) {
// Get the neighbor array index of each of the four cardinal
// neighbors.
int n = rs_neighbors[m];
// Get the max redstone power level of each of the cardinal
// neighbors
UpdateNode neighbor = upd.neighbor_nodes[n];
l = this.getMaxCurrentStrength(neighbor, l);
// Also check the positions above and below the cardinal
// neighbors
boolean neighbor_is_cube = neighbor.currentState.isNormalCube();
if (!neighbor_is_cube) {
UpdateNode neighbor_down = upd.neighbor_nodes[rs_neighbors_dn[m]];
l = this.getMaxCurrentStrength(neighbor_down, l);
} else
if (!center_up_is_cube) {
UpdateNode neighbor_up = upd.neighbor_nodes[rs_neighbors_up[m]];
l = this.getMaxCurrentStrength(neighbor_up, l);
}
}
}
// The new code sets this redstonewire block's power level to the highest neighbor
// minus 1. This usually results in wire power levels dropping by 2 at a time.
// This optimization alone has no impact on opdate order, only the number of updates.
j = l-1;
// If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will
// always be in the range of 0 to 15, the following if will correct that.
if (k>j) j=k;
if (i != j) {
// If the power level has changed from its previous value, compute a new state
// and set it in the world.
// Possible optimization: Don't commit state changes to the world until they
// need to be known by some nearby non-redstone-wire block.
state = state.with(RedstoneWireBlock.POWER, j);
worldIn.setBlockState(upd.self, state, 2);
}
return state;
}
/*
* Optimized function to compute a redstone wire's power level based on cached
* state.
*/
private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) {
if (upd.type != UpdateNode.Type.REDSTONE) return strength;
final int i = upd.currentState.get(RedstoneWireBlock.POWER);
return i > strength ? i : strength;
}
}
+169
View File
@@ -0,0 +1,169 @@
package carpet.helpers;
import carpet.CarpetServer;
import carpet.utils.Messenger;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.command.CommandManager;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.network.chat.BaseComponent;
public class TickSpeed
{
public static final int PLAYER_GRACE = 2;
public static float tickrate = 20.0f;
public static long mspt = 50l;
public static long time_bias = 0;
public static long time_warp_start_time = 0;
public static long time_warp_scheduled_ticks = 0;
public static PlayerEntity time_advancerer = null;
public static String tick_warp_callback = null;
public static ServerCommandSource tick_warp_sender = null;
public static int player_active_timeout = 0;
public static boolean process_entities = true;
public static boolean is_paused = false;
public static boolean is_superHot = false;
public static void reset_player_active_timeout()
{
if (player_active_timeout < PLAYER_GRACE)
{
player_active_timeout = PLAYER_GRACE;
}
}
public static void add_ticks_to_run_in_pause(int ticks)
{
player_active_timeout = PLAYER_GRACE+ticks;
}
public static void tickrate(float rate)
{
tickrate = rate;
mspt = (long)(1000.0/tickrate);
if (mspt <=0)
{
mspt = 1l;
tickrate = 1000.0f;
}
}
public static BaseComponent tickrate_advance(PlayerEntity player, int advance, String callback, ServerCommandSource source)
{
if (0 == advance)
{
tick_warp_callback = null;
tick_warp_sender = null;
finish_time_warp();
return Messenger.c("gi Warp interrupted");
}
if (time_bias > 0)
{
return Messenger.c("l Another player is already advancing time at the moment. Try later or talk to them");
}
time_advancerer = player;
time_warp_start_time = System.nanoTime();
time_warp_scheduled_ticks = advance;
time_bias = advance;
tick_warp_callback = callback;
tick_warp_sender = source;
return Messenger.c("gi Warp speed ....");
}
public static void finish_time_warp()
{
long completed_ticks = time_warp_scheduled_ticks - time_bias;
double milis_to_complete = System.nanoTime()-time_warp_start_time;
if (milis_to_complete == 0.0)
{
milis_to_complete = 1.0;
}
milis_to_complete /= 1000000.0;
int tps = (int) (1000.0D*completed_ticks/milis_to_complete);
double mspt = (1.0*milis_to_complete)/completed_ticks;
time_warp_scheduled_ticks = 0;
time_warp_start_time = 0;
if (tick_warp_callback != null)
{
CommandManager icommandmanager = tick_warp_sender.getMinecraftServer().getCommandManager();
try
{
int j = icommandmanager.execute(tick_warp_sender, tick_warp_callback);
if (j < 1)
{
if (time_advancerer != null)
{
Messenger.m(time_advancerer, "r Command Callback failed: ", "rb /"+tick_warp_callback,"/"+tick_warp_callback);
}
}
}
catch (Throwable var23)
{
if (time_advancerer != null)
{
Messenger.m(time_advancerer, "r Command Callback failed - unknown error: ", "rb /"+tick_warp_callback,"/"+tick_warp_callback);
}
}
tick_warp_callback = null;
tick_warp_sender = null;
}
if (time_advancerer != null)
{
Messenger.m(time_advancerer, String.format("gi ... Time warp completed with %d tps, or %.2f mspt",tps, mspt ));
time_advancerer = null;
}
else
{
Messenger.print_server_message(CarpetServer.minecraft_server, String.format("... Time warp completed with %d tps, or %.2f mspt",tps, mspt ));
}
time_bias = 0;
}
public static boolean continueWarp()
{
if (time_bias > 0)
{
if (time_bias == time_warp_scheduled_ticks) //first call after previous tick, adjust start time
{
time_warp_start_time = System.nanoTime();
}
time_bias -= 1;
return true;
}
else
{
finish_time_warp();
return false;
}
}
public static void tick(MinecraftServer server)
{
process_entities = true;
if (player_active_timeout > 0)
{
player_active_timeout--;
}
if (is_paused)
{
if (player_active_timeout < PLAYER_GRACE)
{
process_entities = false;
}
}
else if (is_superHot)
{
if (player_active_timeout <= 0)
{
process_entities = false;
}
}
}
}
+126
View File
@@ -0,0 +1,126 @@
package carpet.helpers;
import carpet.CarpetSettings;
import carpet.fakes.ExperienceOrbInterface;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ExperienceOrbEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
public class XPcombine
{
public static void searchForOtherXPNearbyCarpet(ExperienceOrbEntity first)
{
for (ExperienceOrbEntity entityxp : first.world.getEntities(ExperienceOrbEntity.class, first.getBoundingBox().expand(0.5D, 0.0D, 0.5D)))
{
combineItems(first, entityxp);
}
}
private static long tone = 0;
private static boolean combineItems(ExperienceOrbEntity first, ExperienceOrbEntity other)
{
if (
first == other || first.world.isClient
|| !first.isAlive() || !other.isAlive()
|| first.pickupDelay == 32767 || other.pickupDelay == 32767
|| first.age == -32768 || other.age == -32768
|| ((ExperienceOrbInterface)first).getCombineDelay() != 0 || ((ExperienceOrbInterface)other).getCombineDelay() != 0
)
{
return false;
}
int size = getTextureByXP(first.getExperienceAmount());
((ExperienceOrbInterface) first).setAmount(first.getExperienceAmount() + other.getExperienceAmount());
((ExperienceOrbInterface) first).setCombineDelay(Math.max(first.pickupDelay, other.pickupDelay));
first.age = Math.min(first.age, other.age);
other.remove();
ExperienceOrbEntity newOrb;
if (getTextureByXP(first.getExperienceAmount()) != size)
{
newOrb = new ExperienceOrbEntity(EntityType.EXPERIENCE_ORB, first.world);
newOrb.setPositionAndAngles(first.x, first.y, first.z, first.yaw, first.pitch);
((ExperienceOrbInterface)newOrb).setAmount(first.getExperienceAmount());
first.world.spawnEntity(newOrb);
first.remove();
}
else
{
((ExperienceOrbInterface) first).setCombineDelay(50);
newOrb = first;
}
newOrb.setVelocity(first.getVelocity().add(new Vec3d(
(first.world.random.nextDouble() * 1.0D - 0.5D),
(first.world.random.nextDouble() * 0.5D),
(first.world.random.nextDouble() * 1.0D - 0.5D)
)));
double pitch = 0.5+Math.log(1+(tone%12))/Math.log(12);
tone++;
CarpetSettings.LOG.error("Playing tone: "+pitch);
newOrb.world.playSoundFromEntity(
null,
newOrb,
SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP,
SoundCategory.BLOCKS, (float)pitch , (float)pitch); //newOrb.world.random.nextFloat()*0.5F+0.5F
/*newOrb.world.playSound(newOrb.x, newOrb.y, newOrb.z,
SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP,
SoundCategory.BLOCKS,
(float)pitch,
newOrb.world.random.nextFloat()*0.5F+0.5F,
true);*/
return true;
}
// COPY FROM CLIENT CODE
private static int getTextureByXP(int xpValue)
{
if (xpValue >= 2477)
{
return 10;
}
else if (xpValue >= 1237)
{
return 9;
}
else if (xpValue >= 617)
{
return 8;
}
else if (xpValue >= 307)
{
return 7;
}
else if (xpValue >= 149)
{
return 6;
}
else if (xpValue >= 73)
{
return 5;
}
else if (xpValue >= 37)
{
return 4;
}
else if (xpValue >= 17)
{
return 3;
}
else if (xpValue >= 7)
{
return 2;
}
else
{
return xpValue >= 3 ? 1 : 0;
}
}
}