Mechanical Piston remastered

- Improved entity handling for moving constructs
- Fixed Pistons colliding inconsistently after world reload
This commit is contained in:
simibubi 2019-09-05 14:23:52 +02:00
parent a62dc492d5
commit 4d065bebc1
6 changed files with 195 additions and 126 deletions

View file

@ -3,6 +3,7 @@ package com.simibubi.create;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import com.simibubi.create.modules.contraptions.receivers.constructs.MovingConstructHandler;
import com.simibubi.create.modules.logistics.FrequencyHandler; import com.simibubi.create.modules.logistics.FrequencyHandler;
import com.simibubi.create.modules.logistics.InWorldItemProcessingHandler; import com.simibubi.create.modules.logistics.InWorldItemProcessingHandler;
import com.simibubi.create.modules.schematics.ServerSchematicLoader; import com.simibubi.create.modules.schematics.ServerSchematicLoader;
@ -31,12 +32,14 @@ public class Create {
public static ServerSchematicLoader schematicReceiver; public static ServerSchematicLoader schematicReceiver;
public static FrequencyHandler frequencyHandler; public static FrequencyHandler frequencyHandler;
public static InWorldItemProcessingHandler itemProcessingHandler; public static InWorldItemProcessingHandler itemProcessingHandler;
public static MovingConstructHandler constructHandler;
@SubscribeEvent @SubscribeEvent
public static void init(final FMLCommonSetupEvent event) { public static void init(final FMLCommonSetupEvent event) {
schematicReceiver = new ServerSchematicLoader(); schematicReceiver = new ServerSchematicLoader();
itemProcessingHandler = new InWorldItemProcessingHandler(); itemProcessingHandler = new InWorldItemProcessingHandler();
frequencyHandler = new FrequencyHandler(); frequencyHandler = new FrequencyHandler();
constructHandler = new MovingConstructHandler();
AllPackets.registerPackets(); AllPackets.registerPackets();
} }

View file

@ -29,6 +29,7 @@ public class Events {
IWorld world = event.getWorld(); IWorld world = event.getWorld();
Create.itemProcessingHandler.onLoadWorld(world); Create.itemProcessingHandler.onLoadWorld(world);
Create.frequencyHandler.onLoadWorld(world); Create.frequencyHandler.onLoadWorld(world);
Create.constructHandler.onLoadWorld(world);
} }
@SubscribeEvent @SubscribeEvent
@ -36,7 +37,7 @@ public class Events {
IWorld world = event.getWorld(); IWorld world = event.getWorld();
Create.itemProcessingHandler.onUnloadWorld(world); Create.itemProcessingHandler.onUnloadWorld(world);
Create.frequencyHandler.onUnloadWorld(world); Create.frequencyHandler.onUnloadWorld(world);
Create.constructHandler.onUnloadWorld(world);
} }
} }

View file

@ -1,114 +0,0 @@
package com.simibubi.create.modules.contraptions.receivers.constructs;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import net.minecraft.block.material.PushReaction;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.ReuseableStream;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class ConstructEntityHelper {
static List<AxisAlignedBB> renderedBBs = new LinkedList<>();
public static void moveEntities(MechanicalPistonTileEntity te, float movementSpeed, Direction movementDirection,
float newOffset) {
World world = te.getWorld();
Vec3d movementVec = new Vec3d(te.getBlockState().get(BlockStateProperties.FACING).getDirectionVec());
Construct construct = te.movingConstruct;
if (world.isRemote) {
renderedBBs.clear();
if (construct.collisionBoxFront != null)
renderedBBs.add(construct.collisionBoxFront.offset(te.getConstructOffset(0)));
if (construct.collisionBoxBack != null)
renderedBBs.add(construct.collisionBoxBack.offset(te.getConstructOffset(0)));
}
if (construct.getCollisionBoxFront() != null) {
AxisAlignedBB constructBB = construct.getCollisionBoxFront().offset(te.getConstructOffset(0)).grow(.5f);
if (world.isRemote) {
renderedBBs.add(constructBB);
}
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, constructBB,
e -> e.getPushReaction() == PushReaction.NORMAL)) {
AxisAlignedBB entityBB = entity.getBoundingBox().offset(movementVec.scale(-1 * newOffset)).grow(.5f);
BlockPos min = new BlockPos(entityBB.minX, entityBB.minY, entityBB.minZ);// .add(-1, -1, -1);
BlockPos max = new BlockPos(entityBB.maxX, entityBB.maxY, entityBB.maxZ);// .add(1, 1, 1);
Stream<VoxelShape> hits = BlockPos.getAllInBox(min, max).filter(construct.blocks::containsKey)
.map(pos -> {
Vec3d vec = new Vec3d(pos).add(te.getConstructOffset(0));
return construct.blocks.get(pos).state.getShape(world, new BlockPos(vec)).withOffset(vec.x,
vec.y, vec.z);
});
ReuseableStream<VoxelShape> potentialHits = new ReuseableStream<>(hits);
// TODO: debug output
if (!world.isRemote) {
if (entity instanceof PlayerEntity)
((PlayerEntity) entity).sendStatusMessage(
new StringTextComponent("Potential Hits: " + potentialHits.createStream().count()),
true);
}
/////////////////
if (world.isRemote) {
for (Object shape : potentialHits.createStream().toArray())
renderedBBs.add(((VoxelShape) shape).getBoundingBox());
renderedBBs
.add(entity.getBoundingBox().offset(movementVec.scale(Math.signum(movementSpeed) * -.2f)));
}
Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(-movementSpeed)
.add(entity.getMotion());
Vec3d allowedMovement = Entity.getAllowedMovement(movement,
entity.getBoundingBox().offset(movementVec.scale(Math.signum(movementSpeed) * -.2f)), world,
ISelectionContext.forEntity(entity), potentialHits);
if (!allowedMovement.equals(movement)) {
entity.setMotion(allowedMovement.subtract(movement.subtract(entity.getMotion())));
}
}
}
}
@SubscribeEvent
public static void onRenderWorld(RenderWorldLastEvent event) {
// for (AxisAlignedBB bb : renderedBBs) {
// TessellatorHelper.prepareForDrawing();
// GlStateManager.disableTexture();
// GlStateManager.lineWidth(3);
// WorldRenderer.drawSelectionBoundingBox(bb.grow(1 / 256f), .5f, 1, .5f, 1);
// GlStateManager.lineWidth(1);
// GlStateManager.enableTexture();
// TessellatorHelper.cleanUpAfterDrawing();
// }
}
}

View file

@ -1,12 +1,11 @@
package com.simibubi.create.modules.contraptions.receivers.constructs; package com.simibubi.create.modules.contraptions.receivers.constructs;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.Create;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalPistonBlock.PistonState; import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalPistonBlock.PistonState;
@ -25,8 +24,6 @@ import net.minecraft.world.gen.feature.template.Template.BlockInfo;
public class MechanicalPistonTileEntity extends KineticTileEntity implements ITickableTileEntity { public class MechanicalPistonTileEntity extends KineticTileEntity implements ITickableTileEntity {
protected static List<MechanicalPistonTileEntity> movingPistons = new ArrayList<>();
protected Construct movingConstruct; protected Construct movingConstruct;
protected float offset; protected float offset;
protected boolean running; protected boolean running;
@ -107,7 +104,8 @@ public class MechanicalPistonTileEntity extends KineticTileEntity implements ITi
// Run // Run
running = true; running = true;
offset = movingConstruct.initialExtensionProgress; offset = movingConstruct.initialExtensionProgress;
movingPistons.add(this); if (!world.isRemote)
Create.constructHandler.add(this);
sendData(); sendData();
getWorld().setBlockState(pos, getBlockState().with(MechanicalPistonBlock.STATE, PistonState.MOVING), 66); getWorld().setBlockState(pos, getBlockState().with(MechanicalPistonBlock.STATE, PistonState.MOVING), 66);
@ -146,7 +144,8 @@ public class MechanicalPistonTileEntity extends KineticTileEntity implements ITi
} }
running = false; running = false;
movingPistons.remove(this); if (!world.isRemote)
Create.constructHandler.remove(this);
movingConstruct = null; movingConstruct = null;
sendData(); sendData();
} }
@ -171,7 +170,7 @@ public class MechanicalPistonTileEntity extends KineticTileEntity implements ITi
Direction movementDirection = getBlockState().get(BlockStateProperties.FACING); Direction movementDirection = getBlockState().get(BlockStateProperties.FACING);
float newOffset = offset + movementSpeed; float newOffset = offset + movementSpeed;
ConstructEntityHelper.moveEntities(this, movementSpeed, movementDirection, newOffset); MovingConstructHandler.moveEntities(this, movementSpeed, movementDirection, newOffset);
if (world.isRemote) { if (world.isRemote) {
offset = newOffset; offset = newOffset;
@ -208,7 +207,8 @@ public class MechanicalPistonTileEntity extends KineticTileEntity implements ITi
// Other moving Pistons // Other moving Pistons
int maxPossibleRange = Construct.MAX_EXTENSIONS + Construct.MAX_CHAINED_BLOCKS + Construct.MAX_CHAINED_CHASSIS; int maxPossibleRange = Construct.MAX_EXTENSIONS + Construct.MAX_CHAINED_BLOCKS + Construct.MAX_CHAINED_CHASSIS;
Iterator<MechanicalPistonTileEntity> iterator = movingPistons.iterator(); Iterator<MechanicalPistonTileEntity> iterator = Create.constructHandler.getOtherMovingPistonsInWorld(this)
.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
MechanicalPistonTileEntity otherPiston = iterator.next(); MechanicalPistonTileEntity otherPiston = iterator.next();

View file

@ -0,0 +1,179 @@
package com.simibubi.create.modules.contraptions.receivers.constructs;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import com.simibubi.create.Create;
import net.minecraft.block.material.PushReaction;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.ReuseableStream;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class MovingConstructHandler {
static List<AxisAlignedBB> renderedBBs = new LinkedList<>();
static Map<IWorld, List<MechanicalPistonTileEntity>> movingPistons = new HashMap<>();
public void onLoadWorld(IWorld world) {
movingPistons.put(world, new ArrayList<>());
Create.logger.info("Prepared Construct List for " + world.getDimension().getType().getRegistryName());
}
public void onUnloadWorld(IWorld world) {
movingPistons.remove(world);
Create.logger.info("Removed Construct List for " + world.getDimension().getType().getRegistryName());
}
public static void moveEntities(MechanicalPistonTileEntity te, float movementSpeed, Direction movementDirection,
float newOffset) {
World world = te.getWorld();
Vec3d movementVec = new Vec3d(te.getBlockState().get(BlockStateProperties.FACING).getDirectionVec());
Construct construct = te.movingConstruct;
if (world.isRemote) {
renderedBBs.clear();
// if (construct.collisionBoxFront != null)
// renderedBBs.add(construct.collisionBoxFront.offset(te.getConstructOffset(0)));
// if (construct.collisionBoxBack != null)
// renderedBBs.add(construct.collisionBoxBack.offset(te.getConstructOffset(0)));
}
if (construct.getCollisionBoxFront() != null) {
AxisAlignedBB constructBB = construct.getCollisionBoxFront().offset(te.getConstructOffset(0)).grow(.5f);
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, constructBB,
e -> e.getPushReaction() == PushReaction.NORMAL)) {
AxisAlignedBB entityScanBB = entity.getBoundingBox().offset(movementVec.scale(-1 * newOffset))
.grow(.5f);
BlockPos min = new BlockPos(entityScanBB.minX, entityScanBB.minY, entityScanBB.minZ);
BlockPos max = new BlockPos(entityScanBB.maxX, entityScanBB.maxY, entityScanBB.maxZ);
Stream<VoxelShape> hits = BlockPos.getAllInBox(min, max).filter(construct.blocks::containsKey)
.map(pos -> {
Vec3d vec = new Vec3d(pos).add(te.getConstructOffset(te.getMovementSpeed() > 0 ? 1 : 0));
return construct.blocks.get(pos).state.getShape(world, new BlockPos(vec)).withOffset(vec.x,
vec.y, vec.z);
});
ReuseableStream<VoxelShape> potentialHits = new ReuseableStream<>(hits);
AxisAlignedBB entityBB = entity.getBoundingBox();
Vec3d motion = entity.getMotion();
Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(-movementSpeed).add(motion);
Vec3d allowedMovement = Entity.getAllowedMovement(movement, entityBB, world,
ISelectionContext.forEntity(entity), potentialHits);
for (Object shape : potentialHits.createStream().toArray()) {
VoxelShape voxelShape = (VoxelShape) shape;
if (!entityBB.intersects(voxelShape.getBoundingBox()))
continue;
Direction bestSide = Direction.DOWN;
double bestOffset = 100;
double finalOffset = 0;
for (Direction face : Direction.values()) {
Axis axis = face.getAxis();
double d = axis == Axis.X ? entityBB.getXSize()
: axis == Axis.Y ? entityBB.getYSize() : entityBB.getZSize();
d = d + 1.5f;
Vec3d nudge = new Vec3d(face.getDirectionVec()).scale(d);
AxisAlignedBB nudgedBB = entityBB.offset(nudge.getX(), nudge.getY(), nudge.getZ());
double nudgeDistance = face.getAxisDirection() == AxisDirection.POSITIVE ? -d : d;
double offset = voxelShape.getAllowedOffset(face.getAxis(), nudgedBB, nudgeDistance);
double abs = Math.abs(nudgeDistance - offset);
if (abs < Math.abs(bestOffset) && abs != 0) {
bestOffset = abs;
finalOffset = abs;
bestSide = face;
}
}
if (bestOffset != 0) {
entity.move(MoverType.SELF, new Vec3d(bestSide.getDirectionVec()).scale(finalOffset));
switch (bestSide.getAxis()) {
case X:
entity.setMotion(0, motion.y, motion.z);
break;
case Y:
entity.setMotion(motion.x, bestSide == Direction.UP ? movementSpeed + 1 / 8f : 0, motion.z);
entity.fall(entity.fallDistance, 1);
entity.fallDistance = 0;
entity.onGround = true;
break;
case Z:
entity.setMotion(motion.x, motion.y, 0);
break;
}
break;
}
}
if (entity instanceof PlayerEntity && !world.isRemote)
return;
if (!allowedMovement.equals(movement)) {
if (allowedMovement.y != movement.y) {
entity.fall(entity.fallDistance, 1);
entity.fallDistance = 0;
entity.onGround = true;
}
entity.setMotion(allowedMovement.subtract(movement.subtract(motion)));
}
}
}
}
public void add(MechanicalPistonTileEntity mechanicalPistonTileEntity) {
movingPistons.get(mechanicalPistonTileEntity.getWorld()).add(mechanicalPistonTileEntity);
}
public void remove(MechanicalPistonTileEntity mechanicalPistonTileEntity) {
movingPistons.get(mechanicalPistonTileEntity.getWorld()).remove(mechanicalPistonTileEntity);
}
public List<MechanicalPistonTileEntity> getOtherMovingPistonsInWorld(
MechanicalPistonTileEntity mechanicalPistonTileEntity) {
return movingPistons.get(mechanicalPistonTileEntity.getWorld());
}
// @SubscribeEvent
// public static void onRenderWorld(RenderWorldLastEvent event) {
// for (AxisAlignedBB bb : renderedBBs) {
// TessellatorHelper.prepareForDrawing();
// GlStateManager.disableTexture();
// GlStateManager.lineWidth(3);
// int color = ColorHelper.rainbowColor(renderedBBs.indexOf(bb) * 170);
// WorldRenderer.drawSelectionBoundingBox(bb.grow(1 / 256f), (color >> 16 & 0xFF) / 256f,
// (color >> 8 & 0xFF) / 256f, (color & 0xFF) / 256f, 1);
// GlStateManager.lineWidth(1);
// GlStateManager.enableTexture();
// TessellatorHelper.cleanUpAfterDrawing();
// }
// }
}

View file

@ -51,12 +51,12 @@ public class FrequencyHandler {
public void onLoadWorld(IWorld world) { public void onLoadWorld(IWorld world) {
connections.put(world, new HashMap<>()); connections.put(world, new HashMap<>());
Create.logger.info("Prepared network space for " + world.getDimension().getType().getRegistryName()); Create.logger.info("Prepared Network Space for " + world.getDimension().getType().getRegistryName());
} }
public void onUnloadWorld(IWorld world) { public void onUnloadWorld(IWorld world) {
connections.remove(world); connections.remove(world);
Create.logger.info("Removed network space for " + world.getDimension().getType().getRegistryName()); Create.logger.info("Removed Network Space for " + world.getDimension().getType().getRegistryName());
} }
private static Pair<Frequency, Frequency> getNetworkKey(IHaveWireless actor) { private static Pair<Frequency, Frequency> getNetworkKey(IHaveWireless actor) {