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
|
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(
|
Services.PLATFORM.registerClientboundPacket(
|
||||||
ClientboundStartRidingOtherPlayerPacket.TYPE,
|
ClientboundStartRidingOtherPlayerPacket.TYPE,
|
||||||
ClientboundStartRidingOtherPlayerPacket.class,
|
ClientboundStartRidingOtherPlayerPacket.class,
|
||||||
|
|
|
||||||
|
|
@ -70,4 +70,4 @@ public class CarryOnCommonClient
|
||||||
{
|
{
|
||||||
return Minecraft.getInstance().player;
|
return Minecraft.getInstance().player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -290,15 +290,46 @@ public class PlacementHandler
|
||||||
public static void placeCarriedOnDeath(ServerPlayer oldPlayer, ServerPlayer newPlayer, boolean died)
|
public static void placeCarriedOnDeath(ServerPlayer oldPlayer, ServerPlayer newPlayer, boolean died)
|
||||||
{
|
{
|
||||||
CarryOnData carry = CarryOnDataManager.getCarryData(oldPlayer);
|
CarryOnData carry = CarryOnDataManager.getCarryData(oldPlayer);
|
||||||
if (((ServerLevel) oldPlayer.level()).getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || !died) {
|
if (died) {
|
||||||
if (!carry.isCarrying(CarryType.PLAYER)) {
|
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);
|
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)
|
public static void placeCarried(ServerPlayer player)
|
||||||
|
|
@ -357,6 +388,39 @@ public class PlacementHandler
|
||||||
return p;
|
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)
|
private static int getPassengerCount(Entity entity)
|
||||||
{
|
{
|
||||||
int passengers = 0;
|
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
|
builder -> builder
|
||||||
.initializer(() -> new CarryOnData(new CompoundTag()))
|
.initializer(() -> new CarryOnData(new CompoundTag()))
|
||||||
.persistent(CarryOnData.CODEC)
|
.persistent(CarryOnData.CODEC)
|
||||||
.syncWith(CarryOnData.STREAM_CODEC, (t, p) -> p.connection != null)
|
|
||||||
.copyOnDeath()
|
.copyOnDeath()
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ import tschipp.carryon.compat.ArchitecturyCompat;
|
||||||
import tschipp.carryon.config.ConfigLoader;
|
import tschipp.carryon.config.ConfigLoader;
|
||||||
import tschipp.carryon.scripting.IdentifiableScriptReloadListener;
|
import tschipp.carryon.scripting.IdentifiableScriptReloadListener;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class CommonEvents {
|
public class CommonEvents {
|
||||||
|
|
||||||
public static void registerEvents() {
|
public static void registerEvents() {
|
||||||
|
|
@ -91,9 +93,6 @@ public class CommonEvents {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UseEntityCallback.EVENT.register((player, level, hand, entity, hitResult) -> {
|
UseEntityCallback.EVENT.register((player, level, hand, entity, hitResult) -> {
|
||||||
|
|
||||||
if(level.isClientSide)
|
if(level.isClientSide)
|
||||||
|
|
@ -113,30 +112,58 @@ public class CommonEvents {
|
||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
CommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess, environment) -> {
|
CommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess, environment) -> {
|
||||||
CarryOnCommon.registerCommands(dispatcher);
|
CarryOnCommon.registerCommands(dispatcher);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableScriptReloadListener());
|
ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableScriptReloadListener());
|
||||||
|
|
||||||
|
|
||||||
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> {
|
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> {
|
||||||
ScriptReloadListener.syncScriptsWithClient(player);
|
ScriptReloadListener.syncScriptsWithClient(player);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||||
for(ServerPlayer player : server.getPlayerList().getPlayers())
|
for(ServerPlayer player : server.getPlayerList().getPlayers())
|
||||||
CarryOnCommon.onCarryTick(player);
|
CarryOnCommon.onCarryTick(player);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle true deaths during COPY_FROM so placement uses the actual death location/world
|
||||||
ServerPlayerEvents.COPY_FROM.register(((oldPlayer, newPlayer, alive) -> {
|
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) -> {
|
PlayerBlockBreakEvents.BEFORE.register(((world, player, pos, state, blockEntity) -> {
|
||||||
if(!CarryOnCommon.onTryBreakBlock(player))
|
if(!CarryOnCommon.onTryBreakBlock(player))
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import tschipp.carryon.CarryOnFabricClientMod;
|
import tschipp.carryon.CarryOnFabricClientMod;
|
||||||
import tschipp.carryon.CarryOnFabricMod;
|
import tschipp.carryon.CarryOnFabricMod;
|
||||||
|
import tschipp.carryon.Constants;
|
||||||
import tschipp.carryon.common.carry.CarryOnData;
|
import tschipp.carryon.common.carry.CarryOnData;
|
||||||
import tschipp.carryon.config.BuiltConfig;
|
import tschipp.carryon.config.BuiltConfig;
|
||||||
import tschipp.carryon.config.fabric.ConfigLoaderImpl;
|
import tschipp.carryon.config.fabric.ConfigLoaderImpl;
|
||||||
|
|
@ -114,5 +115,8 @@ public class FabricPlatformHelper implements IPlatformHelper {
|
||||||
@Override
|
@Override
|
||||||
public void setCarryData(Player player, CarryOnData data) {
|
public void setCarryData(Player player, CarryOnData data) {
|
||||||
player.setAttached(CarryOnFabricMod.CARRY_ON_DATA_ATTACHMENT_TYPE, 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