From c70f3dcc5f3963f40540859f24d14169ad28fbf7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 4 Oct 2024 18:31:33 +0100 Subject: [PATCH] Update respawn logic --- .../unicopia/entity/player/Pony.java | 33 ++-- .../unicopia/entity/player/SpawnLocator.java | 156 +++++++++--------- .../mixin/server/MixinPlayerManager.java | 14 -- .../mixin/server/MixinServerPlayerEntity.java | 21 ++- .../mixin/server/MixinServerWorld.java | 9 +- .../network/MsgRequestSpeciesChange.java | 2 +- 6 files changed, 117 insertions(+), 118 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 33fa82c4..e3c18a5c 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -365,24 +365,33 @@ public class Pony extends Living implements Copyable, Update return Optional.of(asEntity().getUuid()); } + public void forceRespawnOnRaceChange() { + if (isSpawnInvalid(getOrigin())) { + BlockPos respawnPos = entity.getWorldSpawnPos((ServerWorld)asWorld(), getOrigin()); + if (!isSpawnInvalid(respawnPos)) { + Vec3d pos = respawnPos.toBottomCenterPos(); + entity.updatePosition(pos.x, pos.y, pos.z); + } + } + onSpawn(); + } + public void onSpawn() { - if (entity.getWorld() instanceof ServerWorld sw && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE) { - boolean mustAvoidSun = getObservedSpecies() == Race.BAT && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin()); - boolean mustAvoidAir = getCompositeRace().includes(Race.SEAPONY) && !sw.getFluidState(getOrigin()).isIn(FluidTags.WATER); - if (mustAvoidSun || mustAvoidAir) { - SpawnLocator.selectSpawnPosition(sw, entity, mustAvoidAir, mustAvoidSun); - if ((mustAvoidAir && !sw.getFluidState(getOrigin()).isIn(FluidTags.WATER)) - || (mustAvoidSun && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin()))) { - Race suppressedRace = getSuppressedRace(); - if (suppressedRace != Race.UNSET) { - setSpecies(suppressedRace); - } - } + if (isSpawnInvalid(getOrigin())) { + Race suppressedRace = getSuppressedRace(); + if (suppressedRace != Race.UNSET) { + setSpecies(suppressedRace); } } ticksSunImmunity = INITIAL_SUN_IMMUNITY; } + public boolean isSpawnInvalid(BlockPos pos) { + return (entity.getWorld() instanceof ServerWorld sw && sw.getDimension().hasSkyLight() && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE) + && ((getCompositeRace().includes(Race.BAT) && MeteorlogicalUtil.isPositionExposedToSun(asWorld(), pos)) + || (getCompositeRace().includes(Race.SEAPONY) && !asWorld().getFluidState(pos).isIn(FluidTags.WATER))); + } + @Override public boolean beforeUpdate() { if (compositeRace.includes(Race.UNSET) || entity.age % 2 == 0) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java index 13d30bf3..f6047185 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java @@ -1,25 +1,23 @@ package com.minelittlepony.unicopia.entity.player; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.util.MeteorlogicalUtil; +import net.fabricmc.fabric.api.util.TriState; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.fluid.FluidState; import net.minecraft.registry.tag.FluidTags; -import net.minecraft.server.network.SpawnLocating; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.*; -import net.minecraft.util.math.random.Random; import net.minecraft.world.Heightmap; import net.minecraft.world.chunk.WorldChunk; -public class SpawnLocator extends SpawnLocating { - private static BlockPos findSafeSpawnLocation(ServerWorld world, int x, int z, boolean avoidAir) { - if (!avoidAir) { - return findOverworldSpawn(world, x, z); - } - +public class SpawnLocator { + // Modified from SpawnLocating.findOverworldSpawn + private static BlockPos findOverworldSpawn(ServerWorld world, int x, int z, boolean avoidAir) { boolean hasCeiling = world.getDimension().hasCeiling(); WorldChunk chunk = world.getChunk(ChunkSectionPos.getSectionCoord(x), ChunkSectionPos.getSectionCoord(z)); int startHeight = hasCeiling @@ -38,93 +36,89 @@ public class SpawnLocator extends SpawnLocating { for (int y = startHeight + 1; y >= world.getBottomY(); --y) { mutable.set(x, y, z); BlockState state = world.getBlockState(mutable); - FluidState fluid = state.getFluidState(); - if (fluid.isEmpty()) { - continue; - } - if (!fluid.isIn(FluidTags.WATER)) { - break; - } - if (!Block.isFaceFullSquare(state.getCollisionShape(world, mutable), Direction.UP)) { + var pass = checkAtmosphere(state.getFluidState(), avoidAir); + if (pass == TriState.TRUE) { continue; + } else if (pass == TriState.FALSE) { + return null; } - return mutable.up().toImmutable(); + if (Block.isFaceFullSquare(state.getCollisionShape(world, mutable), Direction.UP)) { + return mutable.up().toImmutable(); + } } return null; } + private static BlockPos findAdjustedOverworldSpawn(ServerWorld world, PlayerEntity entity, Box box, BlockPos basePos, + int x, int z, + int spawnRadius, boolean avoidAir, boolean avoidSun) { + BlockPos spawnPos = SpawnLocator.findOverworldSpawn(world, x, z, avoidAir); + + if (spawnPos == null) { + return null; + } + + if (avoidSun && MeteorlogicalUtil.isPositionExposedToSun(world, spawnPos)) { + spawnPos = findUndergroundSpaceBelow(world, entity, basePos, spawnRadius, box, spawnPos); + + if (MeteorlogicalUtil.isPositionExposedToSun(world, spawnPos)) { + return null; + } + } + + if (!checkAtmosphere(world, spawnPos, avoidAir)) { + return null; + } + + return spawnPos; + } + + public static BlockPos findAdjustedOverworldSpawn(ServerWorld world, PlayerEntity entity, Box box, BlockPos basePos, int x, int z, Operation operation) { + boolean avoidSun = Pony.of(entity).getCompositeRace().includes(Race.BAT); + boolean avoidAir = Pony.of(entity).getCompositeRace().includes(Race.SEAPONY); + if (!(avoidSun || avoidAir)) { + return operation.call(world, x, z); + } + int spawnRadius = Math.max(16, world.getServer().getSpawnRadius(world)); + return findAdjustedOverworldSpawn(world, entity, box, basePos, x, z, spawnRadius, avoidAir, avoidSun); + } + + private static TriState checkAtmosphere(FluidState state, boolean avoidAir) { + if (avoidAir) { + if (state.isEmpty()) { + return TriState.TRUE; + } + if (!state.isIn(FluidTags.WATER)) { + return TriState.FALSE; + } + } + + return state.isEmpty() ? TriState.DEFAULT : TriState.FALSE; + } + private static boolean checkAtmosphere(ServerWorld world, BlockPos pos, boolean avoidAir) { if (avoidAir) { return world.getFluidState(pos).isIn(FluidTags.WATER); - } return world.getFluidState(pos).isEmpty(); } - public static void selectSpawnPosition(ServerWorld world, PlayerEntity entity, boolean avoidAir, boolean avoidSun) { - BlockPos spawnPos = world.getSpawnPos(); - int spawnRadius = Math.min( - MathHelper.floor(world.getWorldBorder().getDistanceInsideBorder(spawnPos.getX(), spawnPos.getZ())), - Math.max(0, world.getServer().getSpawnRadius(world)) - ); - - long l = spawnRadius * 2 + 1; - long m = l * l; - int spawnArea = m > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)m; - int offsetMultiplier = spawnArea <= 16 ? spawnArea - 1 : 17; - int rng = Random.create().nextInt(spawnArea); - - BlockPos.Mutable mutable = new BlockPos.Mutable(); - - for (int attempt = 0; attempt < spawnArea; attempt++) { - int tile = (rng + offsetMultiplier * attempt) % spawnArea; - int x = tile % (spawnRadius * 2 + 1); - int z = tile / (spawnRadius * 2 + 1); - - BlockPos candidatePos = findSafeSpawnLocation(world, - spawnPos.getX() + x - spawnRadius, - spawnPos.getZ() + z - spawnRadius, - avoidAir - ); - - if (candidatePos == null) { - continue; - } - - mutable.set(candidatePos); - mutable.move(0, -1, 0); - - while (!world.isAir(mutable) && mutable.getY() >= spawnPos.getY() - spawnRadius * 2 && !world.isOutOfHeightLimit(mutable)) { - mutable.move(0, -1, 0); - } - while (world.isAir(mutable) && mutable.getY() >= spawnPos.getY() - spawnRadius * 2 && !world.isOutOfHeightLimit(mutable)) { - mutable.move(0, -1, 0); - } - - if (!checkAtmosphere(world, mutable, avoidAir)) { - continue; - } - - if (!world.isAir(mutable)) { - mutable.move(0, 1, 0); - } - - if (!checkAtmosphere(world, mutable, avoidAir)) { - continue; - } - - entity.refreshPositionAndAngles(mutable, 0, 0); - - if (!world.isSpaceEmpty(entity)) { - continue; - } - - if (avoidSun && MeteorlogicalUtil.isPositionExposedToSun(world, mutable)) { - continue; - } - - break; + private static BlockPos findUndergroundSpaceBelow(ServerWorld world, PlayerEntity entity, BlockPos basePos, int spawnRadius, Box box, BlockPos pos) { + BlockPos.Mutable mutable = pos.mutableCopy(); + mutable.move(0, -1, 0); + // move to ground + while (!world.isSpaceEmpty(entity, box.offset(mutable.toBottomCenterPos())) + && mutable.getY() >= basePos.getY() - spawnRadius * 2 + && mutable.getY() > world.getBottomY() + 1) { + mutable.move(Direction.DOWN); } + // move down until we find a place we can stand + while (world.isSpaceEmpty(entity, box.offset(mutable.down().toBottomCenterPos())) + && mutable.getY() >= basePos.getY() - spawnRadius * 2 + && mutable.getY() > world.getBottomY() + 1) { + mutable.move(Direction.DOWN); + } + return mutable.toImmutable(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinPlayerManager.java b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinPlayerManager.java index 271bf40a..74fcabb5 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinPlayerManager.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinPlayerManager.java @@ -5,10 +5,6 @@ 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; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import com.minelittlepony.unicopia.EquineContext; -import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.entity.Entity; @@ -36,14 +32,4 @@ abstract class MixinPlayerManager { } } } - - @Inject(method = "respawnPlayer", at = @At("HEAD")) - private void beforeRespawnPlayer(ServerPlayerEntity player, boolean alive, CallbackInfoReturnable info) { - InteractionManager.getInstance().setEquineContext(EquineContext.of(player)); - } - - @Inject(method = "respawnPlayer", at = @At("RETURN")) - private void afterRespawnPlayer(ServerPlayerEntity player, boolean alive, CallbackInfoReturnable info) { - InteractionManager.getInstance().clearEquineContext(); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayerEntity.java index 30a69af2..b662df7b 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayerEntity.java @@ -7,9 +7,13 @@ 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.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.duck.ServerPlayerEntityDuck; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.entity.player.SpawnLocator; import com.minelittlepony.unicopia.server.world.UGameRules; import com.mojang.datafixers.util.Either; @@ -18,9 +22,11 @@ import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.screen.ScreenHandlerListener; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.util.Unit; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; @Mixin(ServerPlayerEntity.class) abstract class MixinServerPlayerEntity extends PlayerEntity implements ScreenHandlerListener, Equine.Container, ServerPlayerEntityDuck { @@ -36,9 +42,10 @@ abstract class MixinServerPlayerEntity extends PlayerEntity implements ScreenHan get().copyFrom(((Equine.Container)oldPlayer).get(), alive); } - @Inject(method = "trySleep(Lnet/minecraft/util/math/BlockPos;)Lcom/mojang/datafixers/util/Either;", - at = @At(value = "FIELD", target = "net/minecraft/entity/player/PlayerEntity$SleepFailureReason.NOT_POSSIBLE_NOW:Lnet/minecraft/entity/player/PlayerEntity$SleepFailureReason;"), - cancellable = true) + @Inject(method = "trySleep(Lnet/minecraft/util/math/BlockPos;)Lcom/mojang/datafixers/util/Either;", at = @At( + value = "FIELD", + target = "net/minecraft/entity/player/PlayerEntity$SleepFailureReason.NOT_POSSIBLE_NOW:Lnet/minecraft/entity/player/PlayerEntity$SleepFailureReason;" + ), cancellable = true) private void onTrySleep(BlockPos pos, CallbackInfoReturnable> info) { if (get().getSpecies().isNocturnal() && get().asWorld().getGameRules().getBoolean(UGameRules.DO_NOCTURNAL_BAT_PONIES)) { ((PlayerEntity)this).sendMessage(Text.translatable("block.unicopia.bed.no_sleep.nocturnal"), true); @@ -57,4 +64,12 @@ abstract class MixinServerPlayerEntity extends PlayerEntity implements ScreenHan private void onStartRiding(Entity entity, boolean force, CallbackInfoReturnable info) { get().getPhysics().cancelFlight(true); } + + @WrapOperation(method = "getWorldSpawnPos", at = @At( + value = "INVOKE", + target = "net/minecraft/server/network/SpawnLocating.findOverworldSpawn(Lnet/minecraft/server/world/ServerWorld;II)Lnet/minecraft/util/math/BlockPos;" + )) + private BlockPos adjustSpawnPosition(ServerWorld world, int x, int z, Operation operation, ServerWorld unused, BlockPos basePos, @Local Box box) { + return SpawnLocator.findAdjustedOverworldSpawn(world, this, box, basePos, x, z, operation); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerWorld.java b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerWorld.java index b24c24ae..7782d27a 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerWorld.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerWorld.java @@ -16,23 +16,18 @@ import net.minecraft.block.BlockState; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.world.StructureWorldAccess; -import net.minecraft.world.World; @Mixin(ServerWorld.class) -abstract class MixinServerWorld extends World implements StructureWorldAccess, NocturnalSleepManager.Source { +abstract class MixinServerWorld implements StructureWorldAccess, NocturnalSleepManager.Source { private NocturnalSleepManager nocturnalSleepManager; - MixinServerWorld() { super(null, null, null, null, null, false, false, 0, 0); } - @Inject(method = "onBlockChanged", at = @At("HEAD")) private void onOnBlockChanged(BlockPos pos, BlockState oldState, BlockState newState, CallbackInfo info) { ((BlockDestructionManager.Source)this).getDestructionManager().onBlockChanged(pos, oldState, newState); } - @ModifyConstant(method = "sendSleepingStatus()V", constant = @Constant( - stringValue = "sleep.skipping_night" - )) + @ModifyConstant(method = "sendSleepingStatus()V", constant = @Constant(stringValue = "sleep.skipping_night")) private String modifySleepingMessage(String initial) { return getNocturnalSleepManager().getTimeSkippingMessage(initial); } diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java index 93a95b14..06c48936 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java @@ -48,7 +48,7 @@ public record MsgRequestSpeciesChange ( }); } - player.onSpawn(); + player.forceRespawnOnRaceChange(); } }