diff --git a/src/main/java/ru/betterend/blocks/EndPortalBlock.java b/src/main/java/ru/betterend/blocks/EndPortalBlock.java index b5cf09ae..ce43282b 100644 --- a/src/main/java/ru/betterend/blocks/EndPortalBlock.java +++ b/src/main/java/ru/betterend/blocks/EndPortalBlock.java @@ -11,23 +11,25 @@ import net.minecraft.block.Blocks; import net.minecraft.block.NetherPortalBlock; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.registry.Registry; -import net.minecraft.world.Heightmap; import net.minecraft.world.World; import net.minecraft.world.WorldAccess; import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.gen.feature.ConfiguredFeatures; + import ru.betterend.client.ERenderLayer; import ru.betterend.client.IRenderTypeable; import ru.betterend.interfaces.TeleportingEntity; import ru.betterend.registry.BlockTagRegistry; -import ru.betterend.registry.FeatureRegistry; import ru.betterend.registry.ParticleRegistry; +import ru.betterend.util.PortalFrameHelper; +import ru.betterend.world.features.DefaultFeature; public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable { public EndPortalBlock() { @@ -69,13 +71,16 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable if (world instanceof ServerWorld && entity instanceof LivingEntity && !entity.hasVehicle() && !entity.hasPassengers() && entity.canUsePortals()) { TeleportingEntity teleEntity = TeleportingEntity.class.cast(entity); if (teleEntity.hasCooldown()) return; - teleEntity.beSetCooldown(500); + teleEntity.beSetCooldown(300); boolean isOverworld = world.getRegistryKey().equals(World.OVERWORLD); ServerWorld destination = ((ServerWorld) world).getServer().getWorld(isOverworld ? World.END : World.OVERWORLD); BlockPos exitPos = this.findExitPos(destination, pos, entity); - teleEntity.beSetExitPos(exitPos); - entity.moveToWorld(destination); - teleEntity.beSetExitPos(null); + if (entity instanceof ServerPlayerEntity) { + ((ServerPlayerEntity) entity).teleport(destination, exitPos.getX(), exitPos.getY(), exitPos.getZ(), entity.yaw, entity.pitch); + } else { + teleEntity.beSetExitPos(exitPos); + entity.moveToWorld(destination); + } } } @@ -91,7 +96,7 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable BlockPos.Mutable basePos; if (world.getRegistryKey().equals(World.OVERWORLD)) { basePos = pos.mutableCopy().set(pos.getX() / mult, pos.getY(), pos.getZ() / mult); - topY = world.getTopY(Heightmap.Type.WORLD_SURFACE, basePos.getX(), basePos.getZ()); + topY = DefaultFeature.getPosOnSurface(world, basePos).getY(); } else { basePos = pos.mutableCopy().set(pos.getX() * mult, pos.getY(), pos.getZ() * mult); topY = world.getHeight(); @@ -102,20 +107,20 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable BlockState state = world.getBlockState(position); if(state.isOf(this)) { if (state.get(AXIS).equals(Direction.Axis.X)) { - return position.add(1, 0, 0); - } else { return position.add(0, 0, 1); + } else { + return position.add(1, 0, 0); } } } bottom.setY(basePos.getY()); Direction.Axis axis = entity.getMovementDirection().getAxis(); - if (checkIsAreaValid(world, bottom)) { - generatePortalFrame(world, bottom, axis, true); + if (checkIsAreaValid(world, bottom, axis)) { + PortalFrameHelper.generatePortalFrame(world, bottom, axis, true); if (axis.equals(Direction.Axis.X)) { - return bottom.add(1, 1, 0); - } else { return bottom.add(0, 1, 1); + } else { + return bottom.add(1, 1, 0); } } else { if (bottom.getY() > top.getY()) { @@ -124,12 +129,12 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable top = buff; } for(BlockPos position : BlockPos.iterate(bottom, top)) { - if (checkIsAreaValid(world, position)) { - generatePortalFrame(world, position, axis, true); + if (checkIsAreaValid(world, position, axis)) { + PortalFrameHelper.generatePortalFrame(world, position, axis, true); if (axis.equals(Direction.Axis.X)) { - return position.add(1, 1, 0); - } else { return position.add(0, 1, 1); + } else { + return position.add(1, 1, 0); } } } @@ -139,17 +144,24 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable } else { basePos.setY(topY); } - generatePortalFrame(world, basePos, axis, true); + PortalFrameHelper.generatePortalFrame(world, basePos, axis, true); if (axis.equals(Direction.Axis.X)) { - return basePos.add(1, 1, 0); - } else { return basePos.add(0, 1, 1); + } else { + return basePos.add(1, 1, 0); } } - private boolean checkIsAreaValid(World world, BlockPos pos) { - BlockPos bottomCorner = pos.add(-1, 0, -1); - BlockPos topCorner = bottomCorner.add(4, 4, 1); + private boolean checkIsAreaValid(World world, BlockPos pos, Direction.Axis axis) { + BlockPos topCorner, bottomCorner; + if (axis.equals(Direction.Axis.X)) { + bottomCorner = pos.add(0, 0, -1); + topCorner = bottomCorner.add(0, 4, 4); + } else { + bottomCorner = pos.add(-1, 0, 0); + topCorner = bottomCorner.add(4, 4, 0); + } + if (!isBaseSolid(world, bottomCorner, axis)) return false; int airBlocks = 0; boolean free = true; for (BlockPos position : BlockPos.iterate(bottomCorner, topCorner)) { @@ -162,7 +174,23 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable free &= this.validBlock(state, surfaceBlock.getBlock()); } } - return free && airBlocks == 48; + return free && airBlocks >= 48; + } + + private boolean isBaseSolid(World world, BlockPos pos, Direction.Axis axis) { + boolean solid = true; + if (axis.equals(Direction.Axis.X)) { + for (int i = 0; i < 4; i++) { + BlockPos checkPos = pos.down().add(0, 0, i); + solid &= world.getBlockState(checkPos).isSolidBlock(world, checkPos); + } + } else { + for (int i = 0; i < 4; i++) { + BlockPos checkPos = pos.down().add(i, 0, 0); + solid &= world.getBlockState(checkPos).isSolidBlock(world, checkPos); + } + } + return solid; } private boolean validBlock(BlockState state, Block surfaceBlock) { @@ -172,8 +200,4 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable state.isOf(Blocks.SAND) || state.isOf(Blocks.GRAVEL); } - - public static void generatePortalFrame(ServerWorld world, BlockPos pos, Direction.Axis axis, boolean active) { - FeatureRegistry.END_PORTAL.configure(axis, active).getFeatureConfigured().generate(world, world.getChunkManager().getChunkGenerator(), new Random(), pos); - } } diff --git a/src/main/java/ru/betterend/mixin/common/ServerPlayerEntityMixin.java b/src/main/java/ru/betterend/mixin/common/ServerPlayerEntityMixin.java index 628dd1d3..20dd3b05 100644 --- a/src/main/java/ru/betterend/mixin/common/ServerPlayerEntityMixin.java +++ b/src/main/java/ru/betterend/mixin/common/ServerPlayerEntityMixin.java @@ -1,118 +1,20 @@ package ru.betterend.mixin.common; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import com.mojang.authlib.GameProfile; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.network.packet.s2c.play.DifficultyS2CPacket; -import net.minecraft.network.packet.s2c.play.EntityStatusEffectS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerAbilitiesS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; -import net.minecraft.network.packet.s2c.play.WorldEventS2CPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.PlayerManager; -import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.network.ServerPlayerInteractionManager; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.registry.RegistryKey; -import net.minecraft.world.TeleportTarget; -import net.minecraft.world.World; -import net.minecraft.world.WorldProperties; -import net.minecraft.world.biome.source.BiomeAccess; + import ru.betterend.interfaces.TeleportingEntity; @Mixin(ServerPlayerEntity.class) -public abstract class ServerPlayerEntityMixin extends PlayerEntity implements TeleportingEntity { +public abstract class ServerPlayerEntityMixin implements TeleportingEntity { - private BlockPos beExitPos; private long beCooldown; - @Shadow - private float syncedHealth; - @Shadow - private int syncedExperience; - @Shadow - private int syncedFoodLevel; - @Shadow - private boolean inTeleportationState; - @Shadow - public ServerPlayNetworkHandler networkHandler; - @Final - @Shadow - public MinecraftServer server; - @Final - @Shadow - public ServerPlayerInteractionManager interactionManager; - - public ServerPlayerEntityMixin(World world, BlockPos pos, float yaw, GameProfile profile) { - super(world, pos, yaw, profile); - } - - @Shadow - public abstract ServerWorld getServerWorld(); - - @Shadow - protected abstract void worldChanged(ServerWorld origin); - - @Inject(method = "moveToWorld", at = @At("HEAD"), cancellable = true) - public void moveToWorld(ServerWorld destination, CallbackInfoReturnable info) { - ServerWorld serverWorld = this.getServerWorld(); - RegistryKey registryKey = serverWorld.getRegistryKey(); - if (beExitPos != null && registryKey == World.END && destination.getRegistryKey() == World.OVERWORLD) { - this.inTeleportationState = true; - ServerPlayerEntity player = ServerPlayerEntity.class.cast(this); - WorldProperties worldProperties = destination.getLevelProperties(); - this.networkHandler.sendPacket(new PlayerRespawnS2CPacket(destination.getDimension(), destination.getRegistryKey(), BiomeAccess.hashSeed(destination.getSeed()), this.interactionManager.getGameMode(), this.interactionManager.getPreviousGameMode(), destination.isDebugWorld(), destination.isFlat(), true)); - this.networkHandler.sendPacket(new DifficultyS2CPacket(worldProperties.getDifficulty(), worldProperties.isDifficultyLocked())); - PlayerManager playerManager = this.server.getPlayerManager(); - playerManager.sendCommandTree(player); - serverWorld.removePlayer(player); - this.removed = false; - TeleportTarget teleportTarget = this.getTeleportTarget(destination); - if (teleportTarget != null) { - serverWorld.getProfiler().push("placing"); - this.setWorld(destination); - destination.onPlayerChangeDimension(player); - this.setRotation(teleportTarget.yaw, teleportTarget.pitch); - this.refreshPositionAfterTeleport(teleportTarget.position.x, teleportTarget.position.y, teleportTarget.position.z); - serverWorld.getProfiler().pop(); - this.worldChanged(serverWorld); - this.interactionManager.setWorld(destination); - this.networkHandler.sendPacket(new PlayerAbilitiesS2CPacket(this.abilities)); - playerManager.sendWorldInfo(player, destination); - playerManager.sendPlayerStatus(player); - this.getStatusEffects().forEach(statusEffectInstance -> { - this.networkHandler.sendPacket(new EntityStatusEffectS2CPacket(getEntityId(), statusEffectInstance)); - }); - this.networkHandler.sendPacket(new WorldEventS2CPacket(1032, BlockPos.ORIGIN, 0, false)); - this.syncedExperience = -1; - this.syncedHealth = -1.0F; - this.syncedFoodLevel = -1; - info.setReturnValue(player); - info.cancel(); - } - } - } - - @Inject(method = "getTeleportTarget", at = @At("HEAD"), cancellable = true) - protected void getTeleportTarget(ServerWorld destination, CallbackInfoReturnable info) { - if (beExitPos != null) { - info.setReturnValue(new TeleportTarget(new Vec3d(beExitPos.getX() + 0.5D, beExitPos.getY(), beExitPos.getZ() + 0.5D), getVelocity(), yaw, pitch)); - info.cancel(); - } - } - @Inject(method = "tick", at = @At("TAIL")) public void baseTick(CallbackInfo info) { if (hasCooldown()) { @@ -131,12 +33,10 @@ public abstract class ServerPlayerEntityMixin extends PlayerEntity implements Te } @Override - public void beSetExitPos(BlockPos pos) { - this.beExitPos = pos; - } + public void beSetExitPos(BlockPos pos) {} @Override public BlockPos beGetExitPos() { - return this.beExitPos; + return null; } } diff --git a/src/main/java/ru/betterend/util/PortalFrameHelper.java b/src/main/java/ru/betterend/util/PortalFrameHelper.java new file mode 100644 index 00000000..a42a0f98 --- /dev/null +++ b/src/main/java/ru/betterend/util/PortalFrameHelper.java @@ -0,0 +1,138 @@ +package ru.betterend.util; + +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; + +import ru.betterend.registry.FeatureRegistry; + +public class PortalFrameHelper { + + public static boolean checkPortalFrame(World world, BlockPos pos, Block frameBlock) { + if (world == null || pos == null) return false; + if (!world.getBlockState(pos).isOf(frameBlock)) return false; + BlockPos bottomCorner = findBottomCorner(world, pos, frameBlock); + if (bottomCorner == null) return false; + boolean valid = true; + BlockPos checkPos = bottomCorner.up(); + Direction moveDir = Direction.UP; + while(!checkPos.equals(bottomCorner)) { + while(valid && !checkPos.equals(bottomCorner)) { + valid = world.getBlockState(checkPos).isOf(frameBlock); + if (!valid) { + switch(moveDir) { + case UP: { + if (world.getBlockState(checkPos.east()).isOf(frameBlock)) { + checkPos = checkPos.east(); + moveDir = Direction.EAST; + valid = true; + } else if (world.getBlockState(checkPos.north()).isOf(frameBlock)) { + checkPos = checkPos.north(); + moveDir = Direction.NORTH; + valid = true; + } + break; + } + case DOWN: { + if (world.getBlockState(checkPos.west()).isOf(frameBlock)) { + checkPos = checkPos.west(); + moveDir = Direction.WEST; + valid = true; + } else if (world.getBlockState(checkPos.south()).isOf(frameBlock)) { + checkPos = checkPos.south(); + moveDir = Direction.SOUTH; + valid = true; + } + break; + } + case NORTH: + case EAST: { + if (world.getBlockState(checkPos.down()).isOf(frameBlock)) { + checkPos = checkPos.down(); + moveDir = Direction.DOWN; + valid = true; + } + break; + } + default: + return false; + } + if (!valid) return false; + } else { + switch(moveDir) { + case UP: { + checkPos = checkPos.up(); + break; + } + case DOWN: { + checkPos = checkPos.down(); + break; + } + case NORTH: { + checkPos = checkPos.north(); + break; + } + case SOUTH: { + checkPos = checkPos.south(); + break; + } + case EAST: { + checkPos = checkPos.east(); + break; + } + case WEST: { + checkPos = checkPos.west(); + break; + } + } + } + } + } + return valid; + } + + private static BlockPos findBottomCorner(World world, BlockPos pos, Block frameBlock) { + BlockState up = world.getBlockState(pos.up()); + BlockState down = world.getBlockState(pos.down()); + BlockState north = world.getBlockState(pos.north()); + BlockState south = world.getBlockState(pos.south()); + BlockState west = world.getBlockState(pos.west()); + BlockState east = world.getBlockState(pos.east()); + if (up.isOf(frameBlock) && !down.isOf(frameBlock)) { + if (north.isOf(frameBlock) || east.isOf(frameBlock)) { + return pos; + } else if (west.isOf(frameBlock)) { + return findBottomCorner(world, pos.west(), frameBlock); + } else if (south.isOf(frameBlock)){ + return findBottomCorner(world, pos.south(), frameBlock); + } + return null; + } else if (down.isOf(frameBlock)) { + if (west.isOf(frameBlock)) { + return findBottomCorner(world, pos.west(), frameBlock); + } else if (south.isOf(frameBlock)) { + return findBottomCorner(world, pos.south(), frameBlock); + } else { + return findBottomCorner(world, pos.down(), frameBlock); + } + } else if (west.isOf(frameBlock)) { + return findBottomCorner(world, pos.west(), frameBlock); + } else if (south.isOf(frameBlock)) { + return findBottomCorner(world, pos.south(), frameBlock); + } + return null; + } + + public static void generatePortalFrame(ServerWorld world, BlockPos pos, Direction.Axis axis, boolean active) { + FeatureRegistry.END_PORTAL.configure(axis, active).getFeatureConfigured().generate(world, world.getChunkManager().getChunkGenerator(), new Random(), pos); + } + + public static void generateEternalPortalFrame(ServerWorld world, BlockPos pos, Direction.Axis axis, boolean active) { + FeatureRegistry.END_PORTAL_ETERNAL.configure(axis, active).getFeatureConfigured().generate(world, world.getChunkManager().getChunkGenerator(), new Random(), pos); + } +} diff --git a/src/main/java/ru/betterend/world/features/DefaultFeature.java b/src/main/java/ru/betterend/world/features/DefaultFeature.java index ed56799c..239e6c4f 100644 --- a/src/main/java/ru/betterend/world/features/DefaultFeature.java +++ b/src/main/java/ru/betterend/world/features/DefaultFeature.java @@ -17,19 +17,19 @@ public abstract class DefaultFeature extends Feature { super(DefaultFeatureConfig.CODEC); } - protected BlockPos getPosOnSurface(StructureWorldAccess world, BlockPos pos) { + public static BlockPos getPosOnSurface(StructureWorldAccess world, BlockPos pos) { return world.getTopPosition(Type.WORLD_SURFACE, pos); } - protected BlockPos getPosOnSurfaceWG(StructureWorldAccess world, BlockPos pos) { + public static BlockPos getPosOnSurfaceWG(StructureWorldAccess world, BlockPos pos) { return world.getTopPosition(Type.WORLD_SURFACE_WG, pos); } - protected BlockPos getPosOnSurfaceRaycast(StructureWorldAccess world, BlockPos pos) { - return this.getPosOnSurfaceRaycast(world, pos, 256); + public static BlockPos getPosOnSurfaceRaycast(StructureWorldAccess world, BlockPos pos) { + return getPosOnSurfaceRaycast(world, pos, 256); } - protected BlockPos getPosOnSurfaceRaycast(StructureWorldAccess world, BlockPos pos, int dist) { + public static BlockPos getPosOnSurfaceRaycast(StructureWorldAccess world, BlockPos pos, int dist) { int h = BlocksHelper.downRay(world, pos, dist); return pos.down(h); }