Portals fixes

This commit is contained in:
Aleksey 2021-04-03 20:30:29 +03:00
parent afc1cd888a
commit 86a57a1bd8
2 changed files with 222 additions and 121 deletions

View file

@ -27,10 +27,8 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.Direction.AxisDirection;
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.chunk.Chunk;
import net.minecraft.world.dimension.DimensionType;
import ru.betterend.client.render.ERenderLayer;
import ru.betterend.interfaces.IColorProvider;
@ -38,6 +36,7 @@ import ru.betterend.interfaces.IRenderTypeable;
import ru.betterend.interfaces.TeleportingEntity;
import ru.betterend.registry.EndParticles;
import ru.betterend.registry.EndPortals;
import ru.betterend.rituals.EternalRitual;
public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable, IColorProvider {
public static final IntProperty PORTAL = BlockProperties.PORTAL;
@ -82,8 +81,7 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable
@Override
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
if (world instanceof ServerWorld && !entity.hasVehicle() && !entity.hasPassengers() && entity.canUsePortals()) {
if (entity.hasNetherPortalCooldown()) return;
if (world.isClient || !validate(entity)) return;
entity.resetNetherPortalCooldown();
ServerWorld currentWorld = (ServerWorld) world;
MinecraftServer server = currentWorld.getServer();
@ -94,7 +92,7 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable
if (exitPos == null) return;
if (entity instanceof ServerPlayerEntity) {
ServerPlayerEntity player = (ServerPlayerEntity) entity;
this.teleportPlayer(player, destination, exitPos);
teleportPlayer(player, destination, exitPos);
} else {
TeleportingEntity teleEntity = (TeleportingEntity) entity;
teleEntity.beSetExitPos(exitPos);
@ -104,6 +102,10 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable
}
}
}
private boolean validate(Entity entity) {
return !entity.hasVehicle() && !entity.hasPassengers() &&
entity.canUsePortals() && !entity.hasNetherPortalCooldown();
}
private void teleportPlayer(ServerPlayerEntity player, ServerWorld destination, BlockPos exitPos) {
@ -114,7 +116,6 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable
teleEntity.beSetExitPos(exitPos);
player.moveToWorld(destination);
}
player.resetNetherPortalCooldown();
}
@Override
@ -131,47 +132,31 @@ public class EndPortalBlock extends NetherPortalBlock implements IRenderTypeable
double currentMultiplier = Objects.requireNonNull(registry.get(currentWorldId)).getCoordinateScale();
double multiplier = targetMultiplier > currentMultiplier ? 1.0 / targetMultiplier : currentMultiplier;
BlockPos.Mutable basePos = currentPos.mutableCopy().set(currentPos.getX() * multiplier, currentPos.getY(), currentPos.getZ() * multiplier);
Direction direction = Direction.EAST;
BlockPos.Mutable checkPos = basePos.mutableCopy();
for (int step = 1; step <= 128; step++) {
for (int i = 0; i < (step >> 1); i++) {
Chunk chunk = targetWorld.getChunk(checkPos);
if (chunk != null) {
int surfaceY = chunk.sampleHeightmap(Heightmap.Type.WORLD_SURFACE, checkPos.getX() & 15, checkPos.getZ() & 15);
int motionY = chunk.sampleHeightmap(Heightmap.Type.MOTION_BLOCKING, checkPos.getX() & 15, checkPos.getZ() & 15);
int ceil = Math.max(surfaceY, motionY) + 1;
if (ceil < 5) continue;
checkPos.setY(ceil);
while (checkPos.getY() >= 5) {
BlockState state = targetWorld.getBlockState(checkPos);
if (state.isOf(this)) {
Axis axis = state.get(AXIS);
BlockState currentState = currentWorld.getBlockState(currentPos);
int radius = (EternalRitual.SEARCH_RADIUS >> 4) + 1;
checkPos = EternalRitual.findBlockPos(targetWorld, checkPos, radius, this, state -> state.isOf(this) &&
state.get(PORTAL).equals(currentState.get(PORTAL)));
if (checkPos != null) {
BlockState checkState = targetWorld.getBlockState(checkPos);
Axis axis = checkState.get(AXIS);
checkPos = findCenter(targetWorld, checkPos, axis);
Direction frontDir = Direction.from(axis, AxisDirection.POSITIVE).rotateYClockwise();
Direction entityDir = entity.getMovementDirection();
if (entityDir.getAxis().isVertical()) {
entityDir = frontDir;
}
if (frontDir != entityDir && frontDir.getOpposite() != entityDir) {
entity.applyRotation(BlockRotation.CLOCKWISE_90);
entityDir = entityDir.rotateYClockwise();
}
return checkPos.offset(entityDir);
}
checkPos.move(Direction.DOWN);
}
}
checkPos.move(direction);
}
direction = direction.rotateYClockwise();
}
return null;
}
private BlockPos.Mutable findCenter(World world, BlockPos.Mutable pos, Direction.Axis axis) {
return this.findCenter(world, pos, axis, 1);
return findCenter(world, pos, axis, 1);
}
private BlockPos.Mutable findCenter(World world, BlockPos.Mutable pos, Direction.Axis axis, int step) {

View file

@ -1,10 +1,16 @@
package ru.betterend.rituals;
import java.awt.Point;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.function.Predicate;
import com.google.common.collect.Lists;
import net.minecraft.util.BlockRotation;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.WorldChunk;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Sets;
@ -64,6 +70,8 @@ public class EternalRitual {
private final static Block PORTAL = EndBlocks.END_PORTAL_BLOCK;
private final static BooleanProperty ACTIVE = BlockProperties.ACTIVE;
public final static int SEARCH_RADIUS = calculateSearchSteps(48);
private World world;
private Direction.Axis axis;
private Identifier targetWorldId;
@ -152,11 +160,14 @@ public class EternalRitual {
int portalId = EndPortals.getPortalIdByItem(itemId);
activatePortal(world, center, portalId);
doEffects((ServerWorld) world, center);
if (exit == null) {
exit = findPortalPos(portalId);
} else {
World targetWorld = getTargetWorld(portalId);
if (!checkFrame(targetWorld, exit)) {
Identifier worldId = targetWorld.getRegistryKey().getValue();
if (exit == null) {
initPortal(worldId, portalId);
} else {
if (!worldId.equals(targetWorldId)) {
initPortal(worldId, portalId);
} else if (!checkFrame(targetWorld, exit.down())) {
Direction.Axis portalAxis = (Direction.Axis.X == axis) ? Direction.Axis.Z : Direction.Axis.X;
generatePortal(targetWorld, exit, portalAxis, portalId);
}
@ -165,6 +176,11 @@ public class EternalRitual {
this.active = true;
}
private void initPortal(Identifier worldId, int portalId) {
targetWorldId = worldId;
exit = findPortalPos(portalId);
}
private void doEffects(ServerWorld serverWorld, BlockPos center) {
Direction moveX, moveY;
if (Direction.Axis.X == axis) {
@ -255,22 +271,38 @@ public class EternalRitual {
this.active = false;
}
@Nullable
private BlockPos findFrame(World world, BlockPos.Mutable startPos) {
List<BlockPos.Mutable> foundPos = findAllBlockPos(world, startPos, (SEARCH_RADIUS >> 4) + 1, FRAME,
blockState -> blockState.isOf(FRAME) && !blockState.get(ACTIVE));
for(BlockPos.Mutable testPos : foundPos) {
if (checkFrame(world, testPos)) {
return testPos;
}
}
return null;
}
private BlockPos findPortalPos(int portalId) {
targetWorldId = EndPortals.getWorldId(portalId);
MinecraftServer server = world.getServer();
ServerWorld targetWorld = (ServerWorld) getTargetWorld(portalId);
Registry<DimensionType> registry = Objects.requireNonNull(server).getRegistryManager().getDimensionTypes();
double multiplier = Objects.requireNonNull(registry.get(targetWorldId)).getCoordinateScale();
BlockPos.Mutable basePos = center.mutableCopy().set(center.getX() / multiplier, center.getY(), center.getZ() / multiplier);
BlockPos framePos = findFrame(targetWorld, basePos.mutableCopy());
if (framePos != null) {
return framePos.up();
}
Direction.Axis portalAxis = (Direction.Axis.X == axis) ? Direction.Axis.Z : Direction.Axis.X;
int worldCeil = targetWorld.getDimensionHeight() - 1;
if (checkIsAreaValid(targetWorld, basePos, portalAxis)) {
EternalRitual.generatePortal(targetWorld, basePos, portalAxis, portalId);
generatePortal(targetWorld, basePos, portalAxis, portalId);
return basePos.toImmutable();
} else {
Direction direction = Direction.EAST;
BlockPos.Mutable checkPos = basePos.mutableCopy();
for (int step = 1; step <= 96; step++) {
int radius = (int) ((SEARCH_RADIUS / multiplier) + 1);
for (int step = 1; step < radius; step++) {
for (int i = 0; i < (step >> 1); i++) {
Chunk chunk = targetWorld.getChunk(checkPos);
if (chunk != null) {
@ -340,62 +372,6 @@ public class EternalRitual {
return state.isSolidBlock(world, pos) && state.isFullCube(world, pos);
}
public static void generatePortal(World world, BlockPos center, Direction.Axis axis, int portalId) {
BlockPos framePos = center.down();
Direction moveDir = Direction.Axis.X == axis ? Direction.EAST : Direction.NORTH;
BlockState frame = FRAME.getDefaultState().with(ACTIVE, true);
FRAME_MAP.forEach(point -> {
BlockPos pos = framePos.mutableCopy().move(moveDir, point.x).move(Direction.UP, point.y);
world.setBlockState(pos, frame);
pos = framePos.mutableCopy().move(moveDir, -point.x).move(Direction.UP, point.y);
world.setBlockState(pos, frame);
});
BlockState portal = PORTAL.getDefaultState().with(EndPortalBlock.AXIS, axis).with(EndPortalBlock.PORTAL, portalId);
PORTAL_MAP.forEach(point -> {
BlockPos pos = center.mutableCopy().move(moveDir, point.x).move(Direction.UP, point.y);
world.setBlockState(pos, portal);
pos = center.mutableCopy().move(moveDir, -point.x).move(Direction.UP, point.y);
world.setBlockState(pos, portal);
});
generateBase(world, framePos, moveDir);
}
private static void generateBase(World world, BlockPos center, Direction moveX) {
BlockState base = BASE.getDefaultState();
Direction moveY = moveX.rotateYClockwise();
BASE_MAP.forEach(point -> {
BlockPos pos = center.mutableCopy().move(moveX, point.x).move(moveY, point.y);
world.setBlockState(pos, base);
pos = center.mutableCopy().move(moveX, -point.x).move(moveY, point.y);
world.setBlockState(pos, base);
pos = center.mutableCopy().move(moveX, point.x).move(moveY, -point.y);
world.setBlockState(pos, base);
pos = center.mutableCopy().move(moveX, -point.x).move(moveY, -point.y);
world.setBlockState(pos, base);
});
}
public static boolean checkArea(World world, BlockPos center, Direction.Axis axis) {
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
for (BlockPos checkPos : BlockPos.iterate(center.offset(moveDir.rotateYClockwise()), center.offset(moveDir.rotateYCounterclockwise()))) {
for (Point point : PORTAL_MAP) {
BlockPos pos = checkPos.mutableCopy().move(moveDir, point.x).move(Direction.UP, point.y);
BlockState state = world.getBlockState(pos);
if (isStateInvalid(state)) return false;
pos = checkPos.mutableCopy().move(moveDir, -point.x).move(Direction.UP, point.y);
state = world.getBlockState(pos);
if (isStateInvalid(state)) return false;
}
}
return true;
}
private static boolean isStateInvalid(BlockState state) {
if (!state.getFluidState().isEmpty()) return true;
Material material = state.getMaterial();
return !material.isReplaceable() && !material.equals(Material.PLANT);
}
public void configure(BlockPos initial) {
BlockPos checkPos = initial.east(12);
if (this.hasPedestal(checkPos)) {
@ -523,4 +499,144 @@ public class EternalRitual {
Objects.equals(center, ritual.center) &&
Objects.equals(exit, ritual.exit);
}
public static void generatePortal(World world, BlockPos center, Direction.Axis axis, int portalId) {
BlockPos framePos = center.down();
Direction moveDir = Direction.Axis.X == axis ? Direction.EAST : Direction.NORTH;
BlockState frame = FRAME.getDefaultState().with(ACTIVE, true);
FRAME_MAP.forEach(point -> {
BlockPos pos = framePos.mutableCopy().move(moveDir, point.x).move(Direction.UP, point.y);
world.setBlockState(pos, frame);
pos = framePos.mutableCopy().move(moveDir, -point.x).move(Direction.UP, point.y);
world.setBlockState(pos, frame);
});
BlockState portal = PORTAL.getDefaultState().with(EndPortalBlock.AXIS, axis).with(EndPortalBlock.PORTAL, portalId);
PORTAL_MAP.forEach(point -> {
BlockPos pos = center.mutableCopy().move(moveDir, point.x).move(Direction.UP, point.y);
world.setBlockState(pos, portal);
pos = center.mutableCopy().move(moveDir, -point.x).move(Direction.UP, point.y);
world.setBlockState(pos, portal);
});
generateBase(world, framePos, moveDir);
}
private static void generateBase(World world, BlockPos center, Direction moveX) {
BlockState base = BASE.getDefaultState();
Direction moveY = moveX.rotateYClockwise();
BASE_MAP.forEach(point -> {
BlockPos pos = center.mutableCopy().move(moveX, point.x).move(moveY, point.y);
world.setBlockState(pos, base);
pos = center.mutableCopy().move(moveX, -point.x).move(moveY, point.y);
world.setBlockState(pos, base);
pos = center.mutableCopy().move(moveX, point.x).move(moveY, -point.y);
world.setBlockState(pos, base);
pos = center.mutableCopy().move(moveX, -point.x).move(moveY, -point.y);
world.setBlockState(pos, base);
});
}
public static boolean checkArea(World world, BlockPos center, Direction.Axis axis) {
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
for (BlockPos checkPos : BlockPos.iterate(center.offset(moveDir.rotateYClockwise()), center.offset(moveDir.rotateYCounterclockwise()))) {
for (Point point : PORTAL_MAP) {
BlockPos pos = checkPos.mutableCopy().move(moveDir, point.x).move(Direction.UP, point.y);
BlockState state = world.getBlockState(pos);
if (isStateInvalid(state)) return false;
pos = checkPos.mutableCopy().move(moveDir, -point.x).move(Direction.UP, point.y);
state = world.getBlockState(pos);
if (isStateInvalid(state)) return false;
}
}
return true;
}
private static boolean isStateInvalid(BlockState state) {
if (!state.getFluidState().isEmpty()) return true;
Material material = state.getMaterial();
return !material.isReplaceable() && !material.equals(Material.PLANT);
}
/**
* @param world World for search
* @param checkPos Start search position
* @param radius Search radius
* @param searchBlock Target block
* @param condition Predicate for test block states in the chunk section
*
* @return Position of the first found block or null.
*/
@Nullable
public static BlockPos.Mutable findBlockPos(World world, BlockPos.Mutable checkPos, int radius, Block searchBlock, Predicate<BlockState> condition) {
Direction moveDirection = Direction.EAST;
for (int step = 1; step < radius; step++) {
for (int i = 0; i < (step >> 1); i++) {
Chunk chunk = world.getChunk(checkPos);
if (!(chunk instanceof WorldChunk) || ((WorldChunk) chunk).isEmpty()) continue;
for (ChunkSection section : chunk.getSectionArray()) {
if (section == null || !section.getContainer().hasAny(condition)) continue;
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for(int z = 0; z < 16; z++) {
BlockState checkState = section.getBlockState(x, y, z);
if (checkState.isOf(searchBlock)) {
int worldX = (chunk.getPos().x << 4) + x;
int worldY = section.getYOffset() + y;
int worldZ = (chunk.getPos().z << 4) + z;
checkPos.set(worldX, worldY, worldZ);
return checkPos;
}
}
}
}
}
checkPos.move(moveDirection, 16);
}
moveDirection = moveDirection.rotateYClockwise();
}
return null;
}
/**
* @param world World for search
* @param checkPos Start search position
* @param radius Search radius
* @param searchBlock Target block
* @param condition Predicate for test block states in the chunk section
*
* @return List of positions of the all found blocks or empty list.
*/
public static List<BlockPos.Mutable> findAllBlockPos(World world, BlockPos.Mutable checkPos, int radius, Block searchBlock, Predicate<BlockState> condition) {
List<BlockPos.Mutable> posFound = Lists.newArrayList();
Direction moveDirection = Direction.EAST;
for (int step = 1; step < radius; step++) {
for (int i = 0; i < (step >> 1); i++) {
Chunk chunk = world.getChunk(checkPos);
if (!(chunk instanceof WorldChunk) || ((WorldChunk) chunk).isEmpty()) continue;
for (ChunkSection section : chunk.getSectionArray()) {
if (section == null || !section.getContainer().hasAny(condition)) continue;
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for(int z = 0; z < 16; z++) {
BlockState checkState = section.getBlockState(x, y, z);
if (checkState.isOf(searchBlock)) {
int worldX = (chunk.getPos().x << 4) + x;
int worldY = section.getYOffset() + y;
int worldZ = (chunk.getPos().z << 4) + z;
checkPos.set(worldX, worldY, worldZ);
posFound.add(checkPos.mutableCopy());
}
}
}
}
}
checkPos.move(moveDirection, 16);
}
moveDirection = moveDirection.rotateYClockwise();
}
return posFound;
}
public static int calculateSearchSteps(int radius) {
return radius * 4 - 1;
}
}