diff --git a/src/main/java/org/dimdev/dimdoors/ModConfig.java b/src/main/java/org/dimdev/dimdoors/ModConfig.java index c2fc4e57..c8cf5d97 100644 --- a/src/main/java/org/dimdev/dimdoors/ModConfig.java +++ b/src/main/java/org/dimdev/dimdoors/ModConfig.java @@ -88,7 +88,7 @@ public final class ModConfig implements ConfigData { } public static class General { - @Tooltip public double teleportOffset = 0.5; + @Tooltip public double teleportOffset = 0; @Tooltip public boolean riftBoundingBoxInCreative; @Tooltip public double riftCloseSpeed = 0.01; @Tooltip public double riftGrowthSpeed = 1; diff --git a/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java b/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java index ffaed7f4..6bc6d847 100644 --- a/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java +++ b/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java @@ -3,9 +3,11 @@ package org.dimdev.dimdoors.api.block; import net.minecraft.block.BlockState; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; public interface AfterMoveCollidableBlock { // only triggers on servers - void onAfterMovePlayerCollision(BlockState state, ServerWorld world, BlockPos pos, ServerPlayerEntity player); + ActionResult onAfterMovePlayerCollision(BlockState state, ServerWorld world, BlockPos pos, ServerPlayerEntity player, Vec3d positionChange); } diff --git a/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java b/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java new file mode 100644 index 00000000..61c093b8 --- /dev/null +++ b/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java @@ -0,0 +1,7 @@ +package org.dimdev.dimdoors.api.entity; + +import net.minecraft.util.math.Vec3d; + +public interface LastPositionProvider { + Vec3d getLastPos(); +} diff --git a/src/main/java/org/dimdev/dimdoors/block/DimensionalPortalBlock.java b/src/main/java/org/dimdev/dimdoors/block/DimensionalPortalBlock.java index 797f762a..3f05d36e 100644 --- a/src/main/java/org/dimdev/dimdoors/block/DimensionalPortalBlock.java +++ b/src/main/java/org/dimdev/dimdoors/block/DimensionalPortalBlock.java @@ -32,6 +32,7 @@ import net.minecraft.world.World; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +// TODO: copy over all the necessary bits from DimensionalDoorBlock public class DimensionalPortalBlock extends Block implements RiftProvider { public static DirectionProperty FACING = HorizontalFacingBlock.FACING; diff --git a/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java b/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java index 8e0e657d..4fc52dd4 100644 --- a/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java +++ b/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java @@ -12,6 +12,7 @@ import org.dimdev.dimdoors.DimensionalDoorsInitializer; import org.dimdev.dimdoors.api.block.AfterMoveCollidableBlock; import org.dimdev.dimdoors.api.block.CustomBreakBlock; import org.dimdev.dimdoors.api.block.ExplosionConvertibleBlock; +import org.dimdev.dimdoors.api.entity.LastPositionProvider; import org.dimdev.dimdoors.api.util.math.MathUtil; import org.dimdev.dimdoors.api.util.math.TransformationMatrix3d; import org.dimdev.dimdoors.block.CoordinateTransformerBlock; @@ -51,38 +52,78 @@ public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements Rift @Override @SuppressWarnings("deprecation") - // TODO: change from onEntityCollision to some method for checking if player crossed portal plane public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) { if (world.isClient || entity instanceof ServerPlayerEntity) { return; } - onCollision(state, world, pos, entity); + onCollision(state, world, pos, entity, entity.getPos().subtract(((LastPositionProvider) entity).getLastPos())); + super.onEntityCollision(state, world, pos, entity); } @Override - public void onAfterMovePlayerCollision(BlockState state, ServerWorld world, BlockPos pos, ServerPlayerEntity player) { - onCollision(state, world, pos, player); + public ActionResult onAfterMovePlayerCollision(BlockState state, ServerWorld world, BlockPos pos, ServerPlayerEntity player, Vec3d positionChange) { + return onCollision(state, world, pos, player, positionChange); } - private void onCollision(BlockState state, World world, BlockPos pos, Entity entity) { - // TODO: replace with dimdoor cooldown? - if (entity.hasNetherPortalCooldown()) { - entity.resetNetherPortalCooldown(); - return; - } - entity.resetNetherPortalCooldown(); - + private ActionResult onCollision(BlockState state, World world, BlockPos pos, Entity entity, Vec3d positionChange) { BlockPos top = state.get(HALF) == DoubleBlockHalf.UPPER ? pos : pos.up(); BlockPos bottom = top.down(); BlockState doorState = world.getBlockState(bottom); - if (doorState.getBlock() == this && doorState.get(DoorBlock.OPEN)) { // '== this' to check if not half-broken - this.getRift(world, pos, state).teleport(entity); - if (DimensionalDoorsInitializer.getConfig().getDoorsConfig().closeDoorBehind) { - world.setBlockState(top, world.getBlockState(top).with(DoorBlock.OPEN, false)); - world.setBlockState(bottom, world.getBlockState(bottom).with(DoorBlock.OPEN, false)); - } + // TODO: decide whether door should need to be open for teleportation + if (doorState.getBlock() != this || !doorState.get(DoorBlock.OPEN)) { // '== this' to check if not half-broken + return ActionResult.PASS; } + Vec3d currentPos = entity.getPos(); + Vec3d previousPos = currentPos.subtract(positionChange); + + // TODO: rewrite this to be usable more universally + // check whether portal plane was traversed + double portalHalfWidth = 0.5; + double portalHeight = 2; + // check in DefaultTransformation for the correct offset of the portal planes + double portalOffsetFromCenter = 0.31; + Vec3d portalNormal = Vec3d.of(state.get(FACING).getOpposite().getVector()); + Vec3d origin = Vec3d.ofBottomCenter(bottom); + Vec3d bottomMiddlePortalPoint = origin.add(portalNormal.multiply(portalOffsetFromCenter)); + + double dotCurrent = portalNormal.dotProduct(currentPos.subtract(bottomMiddlePortalPoint)); + double dotPrevious = portalNormal.dotProduct(previousPos.subtract(bottomMiddlePortalPoint)); + if (!(dotCurrent <= 0 && dotPrevious >= 0) && !(dotCurrent >= 0 && dotPrevious <= 0) || (dotCurrent == 0 && dotPrevious == 0)) { + // start and end point of movement are on same side of the portal plane or both inside the plane + return ActionResult.PASS; + } + + Vec3d yVec = new Vec3d(0, 1, 0); + Vec3d xzVec = portalNormal.crossProduct(yVec); + + Vec3d vecFromPreviousPosToPortalPlane = bottomMiddlePortalPoint.subtract(previousPos); + Vec3d normalizedPositionChange = positionChange.normalize(); + Vec3d pointOfIntersection = previousPos.add(normalizedPositionChange.multiply(vecFromPreviousPosToPortalPlane.dotProduct(normalizedPositionChange) / normalizedPositionChange.dotProduct(normalizedPositionChange))); + + // figure out whether the point of Intersection is actually inside the portal plane; + Vec3d intersectionRelativeToPortalPlane = pointOfIntersection.subtract(bottomMiddlePortalPoint); + double relativeIntersectionHeight = intersectionRelativeToPortalPlane.dotProduct(yVec); + double relativeIntersectionWidth = intersectionRelativeToPortalPlane.dotProduct(xzVec); + if (relativeIntersectionHeight < 0 || relativeIntersectionHeight > portalHeight || Math.abs(relativeIntersectionWidth) > portalHalfWidth) { + // intersection is outside of plane width/ height + return ActionResult.PASS; + } + + // TODO: replace with dimdoor cooldown? + if (entity.hasNetherPortalCooldown()) { + entity.resetNetherPortalCooldown(); + return ActionResult.PASS; + } + entity.resetNetherPortalCooldown(); + + + this.getRift(world, pos, state).teleport(entity); + if (DimensionalDoorsInitializer.getConfig().getDoorsConfig().closeDoorBehind) { + world.setBlockState(top, world.getBlockState(top).with(DoorBlock.OPEN, false)); + world.setBlockState(bottom, world.getBlockState(bottom).with(DoorBlock.OPEN, false)); + } + return ActionResult.SUCCESS; } @Override @@ -213,7 +254,7 @@ public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements Rift @Override public TransformationMatrix3d.TransformationMatrix3dBuilder transformationBuilder(BlockState state, BlockPos pos) { return TransformationMatrix3d.builder() - .inverseTranslate(Vec3d.ofCenter(pos).add(Vec3d.of(state.get(DoorBlock.FACING).getVector()).multiply(-0.5))) + .inverseTranslate(Vec3d.ofCenter(pos).add(Vec3d.of(state.get(DoorBlock.FACING).getVector()).multiply(-0.31))) .inverseRotate(MathUtil.directionEulerAngle(state.get(DoorBlock.FACING).getOpposite())); } diff --git a/src/main/java/org/dimdev/dimdoors/block/entity/EntranceRiftBlockEntity.java b/src/main/java/org/dimdev/dimdoors/block/entity/EntranceRiftBlockEntity.java index 74aa3be1..3199c4dc 100644 --- a/src/main/java/org/dimdev/dimdoors/block/entity/EntranceRiftBlockEntity.java +++ b/src/main/java/org/dimdev/dimdoors/block/entity/EntranceRiftBlockEntity.java @@ -2,19 +2,14 @@ package org.dimdev.dimdoors.block.entity; import java.util.Optional; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.world.ServerWorld; -import net.minecraft.world.World; -import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dimdev.dimdoors.DimensionalDoorsInitializer; -import org.dimdev.dimdoors.api.util.Location; import org.dimdev.dimdoors.block.CoordinateTransformerBlock; import org.dimdev.dimdoors.block.RiftProvider; import org.dimdev.dimdoors.api.client.DefaultTransformation; import org.dimdev.dimdoors.api.client.Transformer; -import org.dimdev.dimdoors.item.DimensionalDoorItem; import org.dimdev.dimdoors.item.RiftKeyItem; import org.dimdev.dimdoors.pockets.DefaultDungeonDestinations; import org.dimdev.dimdoors.rift.registry.Rift; @@ -31,8 +26,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket; -import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.TranslatableText; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -41,7 +34,6 @@ import net.minecraft.util.math.Vec3d; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; public class EntranceRiftBlockEntity extends RiftBlockEntity { private static final EscapeTarget ESCAPE_TARGET = new EscapeTarget(true); @@ -99,7 +91,7 @@ public class EntranceRiftBlockEntity extends RiftBlockEntity { public boolean receiveEntity(Entity entity, Vec3d relativePos, EulerAngle relativeAngle, Vec3d relativeVelocity) { BlockState state = this.getWorld().getBlockState(this.getPos()); Block block = state.getBlock(); - Vec3d targetPos = Vec3d.ofCenter(this.pos).add(Vec3d.of(this.getOrientation().getOpposite().getVector()).multiply(DimensionalDoorsInitializer.getConfig().getGeneralConfig().teleportOffset + 0.5)); + Vec3d targetPos = Vec3d.ofCenter(this.pos).add(Vec3d.of(this.getOrientation().getOpposite().getVector()).multiply(DimensionalDoorsInitializer.getConfig().getGeneralConfig().teleportOffset + 0.01/* slight offset to prevent issues due to mathematical inaccuracies*/)); /* Unused code that needs to be edited if there are other ways to get to limbo But if it is only dimteleport and going through rifts then this code isn't nessecary @@ -118,8 +110,6 @@ public class EntranceRiftBlockEntity extends RiftBlockEntity { relativeVelocity = flipper.transform(relativeVelocity); } - relativePos = relativePos.add(new Vec3d(0, 0, 1).multiply(0.6)); // TODO: skip this for Immersive Portals - TransformationMatrix3d.TransformationMatrix3dBuilder transformationBuilder = transformer.transformationBuilder(state, this.getPos()); TransformationMatrix3d.TransformationMatrix3dBuilder rotatorBuilder = transformer.rotatorBuilder(state, this.getPos()); targetPos = transformer.transformOut(transformationBuilder, relativePos); @@ -127,6 +117,8 @@ public class EntranceRiftBlockEntity extends RiftBlockEntity { relativeVelocity = transformer.rotateOut(rotatorBuilder, relativeVelocity); } + // TODO: open door + TeleportUtil.teleport(entity, this.world, targetPos, relativeAngle, relativeVelocity); return true; diff --git a/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java b/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java new file mode 100644 index 00000000..0b604063 --- /dev/null +++ b/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java @@ -0,0 +1,24 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.util.math.Vec3d; +import org.dimdev.dimdoors.api.entity.LastPositionProvider; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + + +@Mixin(Entity.class) +public abstract class EntityMixin implements LastPositionProvider { + private Vec3d lastPos; + + @Inject(method = "checkBlockCollision()V", at = @At("TAIL")) + public void checkBlockCollisionSaveLastPos(CallbackInfo ci) { + lastPos = ((Entity) (Object) this).getPos(); + } + + public Vec3d getLastPos() { + return lastPos == null ? ((Entity) (Object) this).getPos() : lastPos; + } +} diff --git a/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java b/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java index 8a09973a..b7339763 100644 --- a/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java +++ b/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java @@ -18,6 +18,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class ServerPlayNetworkHandlerMixin { @Shadow public ServerPlayerEntity player; + @Shadow + private double lastTickX; + @Shadow + private double lastTickY; + @Shadow + private double lastTickZ; @Inject(method = "onPlayerMove", at = @At("TAIL")) protected void checkBlockCollision(PlayerMoveC2SPacket packet, CallbackInfo ci) { @@ -28,16 +34,26 @@ public class ServerPlayNetworkHandlerMixin { if (player.world.isRegionLoaded(blockPos, blockPos2)) { BlockPos.Mutable mutable = new BlockPos.Mutable(); + boolean done = false; for(int i = blockPos.getX(); i <= blockPos2.getX(); ++i) { for(int j = blockPos.getY(); j <= blockPos2.getY(); ++j) { for(int k = blockPos.getZ(); k <= blockPos2.getZ(); ++k) { mutable.set(i, j, k); BlockState blockState = player.world.getBlockState(mutable); Block block = blockState.getBlock(); - if (block instanceof AfterMoveCollidableBlock) { - ((AfterMoveCollidableBlock) block).onAfterMovePlayerCollision(blockState, player.getServerWorld(), mutable, player); + if (block instanceof AfterMoveCollidableBlock && ((AfterMoveCollidableBlock) block).onAfterMovePlayerCollision(blockState, player.getServerWorld(), mutable, player, player.getPos().subtract(lastTickX, lastTickY, lastTickZ)).isAccepted()) { + done = true; + } + if (done) { + break; } } + if (done) { + break; + } + } + if (done) { + break; } } } diff --git a/src/main/resources/dimdoors.mixins.json b/src/main/resources/dimdoors.mixins.json index 199fa842..c82f23b5 100644 --- a/src/main/resources/dimdoors.mixins.json +++ b/src/main/resources/dimdoors.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_16", "mixins": [ "BlockMixin", + "EntityMixin", "ExplosionMixin", "ExtendedServerPlayNetworkhandlerMixin", "ItemMixin",