Merge branch 'fix/crash-on-death-1.21.8' into 1.21.8
This commit is contained in:
commit
59ecfa81e5
|
|
@ -85,6 +85,14 @@ public class CarryOnCommon
|
|||
args
|
||||
);
|
||||
|
||||
Services.PLATFORM.registerClientboundPacket(
|
||||
tschipp.carryon.networking.clientbound.ClientboundSyncCarryDataPacket.TYPE,
|
||||
tschipp.carryon.networking.clientbound.ClientboundSyncCarryDataPacket.class,
|
||||
tschipp.carryon.networking.clientbound.ClientboundSyncCarryDataPacket.CODEC,
|
||||
tschipp.carryon.networking.clientbound.ClientboundSyncCarryDataPacket::handle,
|
||||
args
|
||||
);
|
||||
|
||||
Services.PLATFORM.registerClientboundPacket(
|
||||
ClientboundStartRidingOtherPlayerPacket.TYPE,
|
||||
ClientboundStartRidingOtherPlayerPacket.class,
|
||||
|
|
|
|||
|
|
@ -70,4 +70,4 @@ public class CarryOnCommonClient
|
|||
{
|
||||
return Minecraft.getInstance().player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -290,15 +290,46 @@ public class PlacementHandler
|
|||
public static void placeCarriedOnDeath(ServerPlayer oldPlayer, ServerPlayer newPlayer, boolean died)
|
||||
{
|
||||
CarryOnData carry = CarryOnDataManager.getCarryData(oldPlayer);
|
||||
if (((ServerLevel) oldPlayer.level()).getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || !died) {
|
||||
if (!carry.isCarrying(CarryType.PLAYER)) {
|
||||
if (died) {
|
||||
boolean keepInv = ((ServerLevel) oldPlayer.level()).getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY);
|
||||
if (keepInv) {
|
||||
// With keepInventory, the carried content should persist on the player after respawn
|
||||
if (!carry.isCarrying(CarryType.PLAYER)) {
|
||||
CarryOnDataManager.setCarryData(newPlayer, carry);
|
||||
newPlayer.getInventory().setSelectedSlot(oldPlayer.getInventory().getSelectedSlot());
|
||||
}
|
||||
} else {
|
||||
// Without keepInventory, place the carried content at the death location (not respawn)
|
||||
ServerLevel deathLevel = (ServerLevel) oldPlayer.level();
|
||||
BlockPos deathPos = oldPlayer.blockPosition();
|
||||
if (carry.isCarrying(CarryType.ENTITY)) {
|
||||
Entity entity = carry.getEntity(deathLevel);
|
||||
entity.setPos(Vec3.atBottomCenterOf(deathPos));
|
||||
deathLevel.addFreshEntity(entity);
|
||||
if (entity instanceof Mob mob)
|
||||
mob.playAmbientSound();
|
||||
} else if (carry.isCarrying(CarryType.BLOCK)) {
|
||||
BlockState state = carry.getBlock();
|
||||
BlockPlaceContext context = new BlockPlaceContext(oldPlayer, InteractionHand.MAIN_HAND, ItemStack.EMPTY, BlockHitResult.miss(Vec3.atCenterOf(deathPos), Direction.DOWN, deathPos));
|
||||
state = getPlacementState(state, oldPlayer, context, deathPos);
|
||||
BlockPos placePos = getDeathPlacementPosFrom(state, oldPlayer, deathPos);
|
||||
BlockEntity blockEntity = carry.getBlockEntity(placePos, deathLevel.registryAccess());
|
||||
deathLevel.setBlock(placePos, state, 3);
|
||||
if (blockEntity != null)
|
||||
deathLevel.setBlockEntity(blockEntity);
|
||||
}
|
||||
// Clear carry data on the new player only to avoid syncing an update for the despawned old player
|
||||
carry.clear();
|
||||
CarryOnDataManager.setCarryData(newPlayer, carry);
|
||||
newPlayer.getInventory().setSelectedSlot(oldPlayer.getInventory().getSelectedSlot());
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
placeCarried(oldPlayer);
|
||||
// Not a death (e.g., dimension change): transfer carry data to the new player instance
|
||||
if (!carry.isCarrying(CarryType.PLAYER)) {
|
||||
CarryOnDataManager.setCarryData(newPlayer, carry);
|
||||
newPlayer.getInventory().setSelectedSlot(oldPlayer.getInventory().getSelectedSlot());
|
||||
}
|
||||
}
|
||||
|
||||
public static void placeCarried(ServerPlayer player)
|
||||
|
|
@ -357,6 +388,39 @@ public class PlacementHandler
|
|||
return p;
|
||||
}
|
||||
|
||||
private static BlockPos getDeathPlacementPosFrom(BlockState state, ServerPlayer player, BlockPos origin)
|
||||
{
|
||||
BlockPos p = origin;
|
||||
|
||||
int DISTANCE = 15;
|
||||
|
||||
List<BlockPos> potentialPositions = new ArrayList<>();
|
||||
|
||||
for (int j = 0; j < DISTANCE * 2; j++) {
|
||||
for (int i = 0; i < DISTANCE * 2; i++) {
|
||||
for (int k = 0; k < DISTANCE * 2; k++) {
|
||||
int x = i % 2 == 0 ? i / 2 : -(i / 2);
|
||||
int y = j % 2 == 0 ? j / 2 : -(j / 2);
|
||||
int z = k % 2 == 0 ? k / 2 : -(k / 2);
|
||||
potentialPositions.add(new BlockPos(p.getX() + x, p.getY() + y, p.getZ() + z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
potentialPositions.sort(Comparator.comparingDouble(posA -> player.distanceToSqr(posA.getCenter())));
|
||||
|
||||
for(BlockPos potential : potentialPositions)
|
||||
{
|
||||
BlockPlaceContext context = new BlockPlaceContext(player, InteractionHand.MAIN_HAND, ItemStack.EMPTY, BlockHitResult.miss(Vec3.atCenterOf(potential), Direction.DOWN, potential));
|
||||
boolean canPlace = state.canSurvive(player.level(), potential) && player.level().getBlockState(potential).canBeReplaced(context) && player.level().isUnobstructed(state, potential, CollisionContext.of(player));
|
||||
|
||||
if (canPlace)
|
||||
return potential;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int getPassengerCount(Entity entity)
|
||||
{
|
||||
int passengers = 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* GNU Lesser General Public License v3
|
||||
* Copyright (C) 2024 Tschipp
|
||||
* mrtschipp@gmail.com
|
||||
*/
|
||||
|
||||
package tschipp.carryon.networking.clientbound;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import tschipp.carryon.Constants;
|
||||
import tschipp.carryon.common.carry.CarryOnData;
|
||||
import tschipp.carryon.common.carry.CarryOnDataManager;
|
||||
import tschipp.carryon.networking.PacketBase;
|
||||
|
||||
public record ClientboundSyncCarryDataPacket(int iden, CarryOnData data) implements PacketBase {
|
||||
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ClientboundSyncCarryDataPacket> CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.INT, ClientboundSyncCarryDataPacket::iden,
|
||||
CarryOnData.STREAM_CODEC, ClientboundSyncCarryDataPacket::data,
|
||||
ClientboundSyncCarryDataPacket::new
|
||||
);
|
||||
|
||||
public static final CustomPacketPayload.Type<ClientboundSyncCarryDataPacket> TYPE = new Type<>(Constants.PACKET_ID_SYNC_CARRY_ON_DATA);
|
||||
|
||||
@Override
|
||||
public void handle(Player player) {
|
||||
Entity e = player.level().getEntity(this.iden);
|
||||
if(e instanceof Player p) {
|
||||
CarryOnDataManager.setCarryData(p, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -39,9 +39,7 @@ public class CarryOnFabricMod implements ModInitializer {
|
|||
builder -> builder
|
||||
.initializer(() -> new CarryOnData(new CompoundTag()))
|
||||
.persistent(CarryOnData.CODEC)
|
||||
.syncWith(CarryOnData.STREAM_CODEC, (t, p) -> p.connection != null)
|
||||
.copyOnDeath()
|
||||
|
||||
);
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ import tschipp.carryon.compat.ArchitecturyCompat;
|
|||
import tschipp.carryon.config.ConfigLoader;
|
||||
import tschipp.carryon.scripting.IdentifiableScriptReloadListener;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class CommonEvents {
|
||||
|
||||
public static void registerEvents() {
|
||||
|
|
@ -91,9 +93,6 @@ public class CommonEvents {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
UseEntityCallback.EVENT.register((player, level, hand, entity, hitResult) -> {
|
||||
|
||||
if(level.isClientSide)
|
||||
|
|
@ -113,30 +112,58 @@ public class CommonEvents {
|
|||
return InteractionResult.PASS;
|
||||
});
|
||||
|
||||
|
||||
CommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess, environment) -> {
|
||||
CarryOnCommon.registerCommands(dispatcher);
|
||||
}));
|
||||
|
||||
|
||||
ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableScriptReloadListener());
|
||||
|
||||
|
||||
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> {
|
||||
ScriptReloadListener.syncScriptsWithClient(player);
|
||||
});
|
||||
|
||||
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
for(ServerPlayer player : server.getPlayerList().getPlayers())
|
||||
CarryOnCommon.onCarryTick(player);
|
||||
});
|
||||
|
||||
|
||||
// Handle true deaths during COPY_FROM so placement uses the actual death location/world
|
||||
ServerPlayerEvents.COPY_FROM.register(((oldPlayer, newPlayer, alive) -> {
|
||||
PlacementHandler.placeCarriedOnDeath(oldPlayer, newPlayer, !alive);
|
||||
boolean died = !alive;
|
||||
if (died) {
|
||||
boolean keepInv = ((net.minecraft.server.level.ServerLevel) oldPlayer.level()).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_KEEPINVENTORY);
|
||||
if (!keepInv) {
|
||||
// Place immediately at death location before respawn
|
||||
PlacementHandler.placeCarriedOnDeath(oldPlayer, newPlayer, true);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// AFTER_RESPAWN: handle non-death transitions; for deaths with keepInventory, transfer after the player fully exists
|
||||
ServerPlayerEvents.AFTER_RESPAWN.register(((oldPlayer, newPlayer, alive) -> {
|
||||
boolean died = !alive;
|
||||
if (!died) {
|
||||
Objects.requireNonNull(newPlayer.getServer()).execute(() -> PlacementHandler.placeCarriedOnDeath(oldPlayer, newPlayer, false));
|
||||
} else {
|
||||
boolean keepInv = ((net.minecraft.server.level.ServerLevel) oldPlayer.level()).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_KEEPINVENTORY);
|
||||
if (keepInv) {
|
||||
// Transfer one tick later so the client has the new entity registered before any attachment sync
|
||||
Objects.requireNonNull(newPlayer.getServer()).execute(() -> newPlayer.getServer().execute(() -> {
|
||||
var carry = tschipp.carryon.common.carry.CarryOnDataManager.getCarryData(oldPlayer);
|
||||
tschipp.carryon.common.carry.CarryOnDataManager.setCarryData(newPlayer, carry);
|
||||
}));
|
||||
} else {
|
||||
// Ensure no residual carried state remains after respawn when keepInventory is off
|
||||
Objects.requireNonNull(newPlayer.getServer()).execute(() -> newPlayer.getServer().execute(() -> {
|
||||
var carryNew = tschipp.carryon.common.carry.CarryOnDataManager.getCarryData(newPlayer);
|
||||
if (carryNew.isCarrying()) {
|
||||
carryNew.clear();
|
||||
tschipp.carryon.common.carry.CarryOnDataManager.setCarryData(newPlayer, carryNew);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
PlayerBlockBreakEvents.BEFORE.register(((world, player, pos, state, blockEntity) -> {
|
||||
if(!CarryOnCommon.onTryBreakBlock(player))
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import net.minecraft.server.level.ServerPlayer;
|
|||
import net.minecraft.world.entity.player.Player;
|
||||
import tschipp.carryon.CarryOnFabricClientMod;
|
||||
import tschipp.carryon.CarryOnFabricMod;
|
||||
import tschipp.carryon.Constants;
|
||||
import tschipp.carryon.common.carry.CarryOnData;
|
||||
import tschipp.carryon.config.BuiltConfig;
|
||||
import tschipp.carryon.config.fabric.ConfigLoaderImpl;
|
||||
|
|
@ -114,5 +115,8 @@ public class FabricPlatformHelper implements IPlatformHelper {
|
|||
@Override
|
||||
public void setCarryData(Player player, CarryOnData data) {
|
||||
player.setAttached(CarryOnFabricMod.CARRY_ON_DATA_ATTACHMENT_TYPE, data);
|
||||
if (player instanceof ServerPlayer sp) {
|
||||
sendPacketToPlayer(tschipp.carryon.Constants.PACKET_ID_SYNC_CARRY_ON_DATA, new tschipp.carryon.networking.clientbound.ClientboundSyncCarryDataPacket(player.getId(), data), sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user