Compare commits
2 Commits
54c9024615
...
57f0f340a9
| Author | SHA1 | Date | |
|---|---|---|---|
| 57f0f340a9 | |||
| 881ba1a9a2 |
@ -57,6 +57,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
import io.lampnet.travelerssuitcase.world.RuntimeWorldHandle;
|
import io.lampnet.travelerssuitcase.world.RuntimeWorldHandle;
|
||||||
|
|
||||||
@ -120,6 +121,14 @@ public class TravelersSuitcase {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onServerStarting(ServerStartingEvent event) {
|
public static void onServerStarting(ServerStartingEvent event) {
|
||||||
SuitcaseRegistryState.onServerStart(event.getServer());
|
SuitcaseRegistryState.onServerStart(event.getServer());
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
try {
|
||||||
|
loadConfig(event.getServer());
|
||||||
|
LOGGER.info("Configuration loaded successfully");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.warn("Failed to load configuration, using defaults", e);
|
||||||
|
}
|
||||||
|
|
||||||
Path registryFile = event.getServer().getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT)
|
Path registryFile = event.getServer().getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT)
|
||||||
.resolve("data")
|
.resolve("data")
|
||||||
@ -196,6 +205,52 @@ public class TravelersSuitcase {
|
|||||||
), false);
|
), false);
|
||||||
return 1;
|
return 1;
|
||||||
}))
|
}))
|
||||||
|
.then(Commands.literal("getPlayerEntry")
|
||||||
|
.executes(ctx -> {
|
||||||
|
var src = ctx.getSource();
|
||||||
|
var world = src.getLevel();
|
||||||
|
var id = world.dimension().location();
|
||||||
|
if (!id.getNamespace().equals(MODID)
|
||||||
|
|| !id.getPath().startsWith("pocket_dimension_")) {
|
||||||
|
src.sendFailure(Component.literal("§cNot in a pocket dimension"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerEntryData data = PlayerEntryData.get(world);
|
||||||
|
Vec3 pos = data.getEntryPos();
|
||||||
|
float yaw = data.getEntryYaw();
|
||||||
|
float pitch = data.getEntryPitch();
|
||||||
|
|
||||||
|
src.sendSuccess(() -> Component.literal(
|
||||||
|
String.format("§aPlayer entry: %.2f, %.2f, %.2f (yaw: %.1f, pitch: %.1f)",
|
||||||
|
pos.x(), pos.y(), pos.z(), yaw, pitch)
|
||||||
|
), false);
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(Commands.literal("getMobEntry")
|
||||||
|
.executes(ctx -> {
|
||||||
|
var src = ctx.getSource();
|
||||||
|
var world = src.getLevel();
|
||||||
|
var id = world.dimension().location();
|
||||||
|
if (!id.getNamespace().equals(MODID)
|
||||||
|
|| !id.getPath().startsWith("pocket_dimension_")) {
|
||||||
|
src.sendFailure(Component.literal("§cNot in a pocket dimension"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MobEntryData data = MobEntryData.get(world);
|
||||||
|
Vec3 pos = data.getEntryPos();
|
||||||
|
float yaw = data.getEntryYaw();
|
||||||
|
float pitch = data.getEntryPitch();
|
||||||
|
|
||||||
|
src.sendSuccess(() -> Component.literal(
|
||||||
|
String.format("§aMob entry: %.2f, %.2f, %.2f (yaw: %.1f, pitch: %.1f)",
|
||||||
|
pos.x(), pos.y(), pos.z(), yaw, pitch)
|
||||||
|
), false);
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
.then(Commands.literal("resetPlayerEntry")
|
.then(Commands.literal("resetPlayerEntry")
|
||||||
.then(Commands.argument("dimension", StringArgumentType.word())
|
.then(Commands.argument("dimension", StringArgumentType.word())
|
||||||
.executes(ctx -> {
|
.executes(ctx -> {
|
||||||
@ -213,7 +268,7 @@ public class TravelersSuitcase {
|
|||||||
|
|
||||||
// Reset player entry to default position
|
// Reset player entry to default position
|
||||||
PlayerEntryData playerData = PlayerEntryData.get(targetWorld);
|
PlayerEntryData playerData = PlayerEntryData.get(targetWorld);
|
||||||
playerData.setEntry(new Vec3(17.5, 97.0, 9.5), 0f, 0f);
|
playerData.resetToDefault();
|
||||||
|
|
||||||
src.sendSuccess(() -> Component.literal(
|
src.sendSuccess(() -> Component.literal(
|
||||||
"§aPlayer entry reset for pocket dimension '" + dimSuffix + "'"
|
"§aPlayer entry reset for pocket dimension '" + dimSuffix + "'"
|
||||||
@ -222,6 +277,32 @@ public class TravelersSuitcase {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.then(Commands.literal("resetMobEntry")
|
||||||
|
.then(Commands.argument("dimension", StringArgumentType.word())
|
||||||
|
.executes(ctx -> {
|
||||||
|
var src = ctx.getSource();
|
||||||
|
String dimSuffix = StringArgumentType.getString(ctx, "dimension");
|
||||||
|
|
||||||
|
ResourceLocation dimId = ResourceLocation.fromNamespaceAndPath(MODID, "pocket_dimension_" + dimSuffix);
|
||||||
|
ResourceKey<Level> worldKey = ResourceKey.create(Registries.DIMENSION, dimId);
|
||||||
|
ServerLevel targetWorld = src.getServer().getLevel(worldKey);
|
||||||
|
|
||||||
|
if (targetWorld == null) {
|
||||||
|
src.sendFailure(Component.literal("§cPocket dimension '" + dimSuffix + "' not found"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset mob entry to default position
|
||||||
|
MobEntryData mobData = MobEntryData.get(targetWorld);
|
||||||
|
mobData.setEntry(new Vec3(35.5, 85.0, 16.5), 0f, 0f);
|
||||||
|
|
||||||
|
src.sendSuccess(() -> Component.literal(
|
||||||
|
"§aMob entry reset for pocket dimension '" + dimSuffix + "'"
|
||||||
|
), false);
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
.then(Commands.literal("listDimensions")
|
.then(Commands.literal("listDimensions")
|
||||||
.requires(src -> src.hasPermission(2))
|
.requires(src -> src.hasPermission(2))
|
||||||
.executes(ctx -> {
|
.executes(ctx -> {
|
||||||
@ -342,10 +423,120 @@ public class TravelersSuitcase {
|
|||||||
return 1;
|
return 1;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
.then(Commands.literal("config")
|
||||||
|
.requires(src -> src.hasPermission(2))
|
||||||
|
.then(Commands.literal("save")
|
||||||
|
.executes(ctx -> {
|
||||||
|
var src = ctx.getSource();
|
||||||
|
try {
|
||||||
|
saveConfig(src.getServer());
|
||||||
|
src.sendSuccess(() -> Component.literal("§aConfiguration saved successfully"), false);
|
||||||
|
return 1;
|
||||||
|
} catch (Exception e) {
|
||||||
|
src.sendFailure(Component.literal("§cFailed to save configuration: " + e.getMessage()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(Commands.literal("load")
|
||||||
|
.executes(ctx -> {
|
||||||
|
var src = ctx.getSource();
|
||||||
|
try {
|
||||||
|
loadConfig(src.getServer());
|
||||||
|
src.sendSuccess(() -> Component.literal("§aConfiguration loaded successfully"), false);
|
||||||
|
return 1;
|
||||||
|
} catch (Exception e) {
|
||||||
|
src.sendFailure(Component.literal("§cFailed to load configuration: " + e.getMessage()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(Commands.literal("reload")
|
||||||
|
.executes(ctx -> {
|
||||||
|
var src = ctx.getSource();
|
||||||
|
try {
|
||||||
|
loadConfig(src.getServer());
|
||||||
|
src.sendSuccess(() -> Component.literal("§aConfiguration reloaded successfully"), false);
|
||||||
|
return 1;
|
||||||
|
} catch (Exception e) {
|
||||||
|
src.sendFailure(Component.literal("§cFailed to reload configuration: " + e.getMessage()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
event.getDispatcher().register(builder);
|
event.getDispatcher().register(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void saveConfig(MinecraftServer server) throws IOException {
|
||||||
|
Path configDir = server.getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT)
|
||||||
|
.resolve("data")
|
||||||
|
.resolve(MODID);
|
||||||
|
|
||||||
|
Files.createDirectories(configDir);
|
||||||
|
|
||||||
|
Path configFile = configDir.resolve("config.properties");
|
||||||
|
|
||||||
|
StringBuilder content = new StringBuilder();
|
||||||
|
content.append("canCaptureHostile=").append(canCaptureHostile).append("\n");
|
||||||
|
content.append("blacklistedEntities=");
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
for (EntityType<?> entityType : entityBlacklist) {
|
||||||
|
if (!first) {
|
||||||
|
content.append(",");
|
||||||
|
}
|
||||||
|
content.append(BuiltInRegistries.ENTITY_TYPE.getKey(entityType));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
content.append("\n");
|
||||||
|
|
||||||
|
Files.writeString(configFile, content.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadConfig(MinecraftServer server) throws IOException {
|
||||||
|
Path configFile = server.getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT)
|
||||||
|
.resolve("data")
|
||||||
|
.resolve(MODID)
|
||||||
|
.resolve("config.properties");
|
||||||
|
|
||||||
|
if (!Files.exists(configFile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entityBlacklist.clear();
|
||||||
|
|
||||||
|
for (String line : Files.readAllLines(configFile)) {
|
||||||
|
String[] parts = line.split("=", 2);
|
||||||
|
if (parts.length != 2) continue;
|
||||||
|
|
||||||
|
String key = parts[0].trim();
|
||||||
|
String value = parts[1].trim();
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "canCaptureHostile":
|
||||||
|
canCaptureHostile = Boolean.parseBoolean(value);
|
||||||
|
break;
|
||||||
|
case "blacklistedEntities":
|
||||||
|
if (!value.isEmpty()) {
|
||||||
|
for (String entityId : value.split(",")) {
|
||||||
|
try {
|
||||||
|
ResourceLocation id = ResourceLocation.parse(entityId.trim());
|
||||||
|
EntityType<?> entityType = BuiltInRegistries.ENTITY_TYPE.get(id);
|
||||||
|
if (entityType != EntityType.PIG || entityId.trim().equals("minecraft:pig")) {
|
||||||
|
entityBlacklist.add(entityType);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.warn("Failed to parse entity ID from config: " + entityId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void onUseEntity(PlayerInteractEvent.EntityInteract event) {
|
public void onUseEntity(PlayerInteractEvent.EntityInteract event) {
|
||||||
Level world = event.getLevel();
|
Level world = event.getLevel();
|
||||||
@ -402,8 +593,6 @@ public class TravelersSuitcase {
|
|||||||
}
|
}
|
||||||
MobEntryData data = MobEntryData.get(targetWorld);
|
MobEntryData data = MobEntryData.get(targetWorld);
|
||||||
Vec3 dest = data.getEntryPos();
|
Vec3 dest = data.getEntryPos();
|
||||||
float yaw = data.getEntryYaw();
|
|
||||||
float pitch = data.getEntryPitch();
|
|
||||||
mob.changeDimension(targetWorld, new net.minecraftforge.common.util.ITeleporter() {
|
mob.changeDimension(targetWorld, new net.minecraftforge.common.util.ITeleporter() {
|
||||||
@Override
|
@Override
|
||||||
public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float yaw, java.util.function.Function<Boolean, Entity> repositionEntity) {
|
public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float yaw, java.util.function.Function<Boolean, Entity> repositionEntity) {
|
||||||
|
|||||||
@ -4,12 +4,7 @@ import io.lampnet.travelerssuitcase.block.entity.SuitcaseBlockEntity;
|
|||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.entity.item.ItemEntity;
|
|
||||||
import net.minecraft.world.item.BlockItem;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.nbt.StringTag;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.sounds.SoundEvents;
|
import net.minecraft.sounds.SoundEvents;
|
||||||
@ -17,43 +12,20 @@ import net.minecraft.sounds.SoundSource;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.phys.AABB;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import net.minecraft.world.level.storage.loot.LootParams;
|
import net.minecraft.world.level.storage.loot.LootParams;
|
||||||
|
|
||||||
public class PocketPortalBlock extends Block {
|
public class PocketPortalBlock extends Block {
|
||||||
private static final Map<String, PlayerPositionData> LAST_KNOWN_POSITIONS = new HashMap<>();
|
|
||||||
private static final int SEARCH_RADIUS_CHUNKS = 12;
|
|
||||||
public static class PlayerPositionData {
|
|
||||||
public final double x;
|
|
||||||
public final double y;
|
|
||||||
public final double z;
|
|
||||||
public final float yaw;
|
|
||||||
public final float pitch;
|
|
||||||
public final long timestamp;
|
|
||||||
public PlayerPositionData(double x, double y, double z, float yaw, float pitch) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.yaw = yaw;
|
|
||||||
this.pitch = pitch;
|
|
||||||
this.timestamp = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PocketPortalBlock(BlockBehaviour.Properties properties) {
|
public PocketPortalBlock(BlockBehaviour.Properties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
@ -65,76 +37,9 @@ public class PocketPortalBlock extends Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void storePlayerPosition(ServerPlayer player) {
|
|
||||||
LAST_KNOWN_POSITIONS.put(
|
|
||||||
player.getUUID().toString(),
|
|
||||||
new PlayerPositionData(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean attemptPlayerInventorySuitcaseTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) {
|
|
||||||
for (ServerPlayer serverPlayer : Objects.requireNonNull(world.getServer()).getPlayerList().getPlayers()) {
|
|
||||||
if (scanPlayerInventoryForSuitcase(serverPlayer, player, keystoneName, world, overworld)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean scanPlayerInventoryForSuitcase(ServerPlayer inventoryOwner, ServerPlayer exitingPlayer,
|
|
||||||
String keystoneName, Level world, ServerLevel overworld) {
|
|
||||||
for (int i = 0; i < inventoryOwner.getInventory().getContainerSize(); i++) {
|
|
||||||
ItemStack stack = inventoryOwner.getInventory().getItem(i);
|
|
||||||
if (isSuitcaseItemWithKeystone(stack, keystoneName)) {
|
|
||||||
cleanUpSuitcaseItemNbt(stack, exitingPlayer, keystoneName);
|
|
||||||
inventoryOwner.getInventory().setItem(i, stack);
|
|
||||||
teleportToPosition(world, exitingPlayer, overworld,
|
|
||||||
inventoryOwner.getX(), inventoryOwner.getY() + 1.0, inventoryOwner.getZ(),
|
|
||||||
exitingPlayer.getYRot(), exitingPlayer.getXRot());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuitcaseItemWithKeystone(ItemStack stack, String keystoneName) {
|
|
||||||
if (stack.isEmpty() || !(stack.getItem() instanceof BlockItem) ||
|
|
||||||
!(((BlockItem) stack.getItem()).getBlock() instanceof SuitcaseBlock)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stack.hasTag()) return false;
|
|
||||||
CompoundTag beTag = BlockItem.getBlockEntityData(stack);
|
|
||||||
if (beTag == null) return false;
|
|
||||||
|
|
||||||
return beTag.contains("BoundKeystone") && keystoneName.equals(beTag.getString("BoundKeystone"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanUpSuitcaseItemNbt(ItemStack stack, ServerPlayer player, String keystoneName) {
|
|
||||||
if (!stack.hasTag()) return;
|
|
||||||
CompoundTag beTag = BlockItem.getBlockEntityData(stack);
|
|
||||||
if (beTag == null) return;
|
|
||||||
if (beTag.contains("EnteredPlayers", Tag.TAG_LIST)) {
|
|
||||||
ListTag playersList = beTag.getList("EnteredPlayers", Tag.TAG_COMPOUND);
|
|
||||||
ListTag newPlayersList = new ListTag();
|
|
||||||
boolean playerFound = false;
|
|
||||||
for (int i = 0; i < playersList.size(); i++) {
|
|
||||||
CompoundTag playerData = playersList.getCompound(i);
|
|
||||||
if (!player.getUUID().toString().equals(playerData.getString("UUID"))) {
|
|
||||||
newPlayersList.add(playerData);
|
|
||||||
} else {
|
|
||||||
playerFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (playerFound) {
|
|
||||||
beTag.put("EnteredPlayers", newPlayersList);
|
|
||||||
int remainingPlayers = newPlayersList.size();
|
|
||||||
updateItemLore(stack, remainingPlayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SuitcaseBlockEntity.removeSuitcaseEntry(keystoneName, player.getUUID().toString(), player.getServer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void entityInside(@NotNull BlockState state, Level world, @NotNull BlockPos pos, @NotNull Entity entity) {
|
public void entityInside(@NotNull BlockState state, Level world, @NotNull BlockPos pos, @NotNull Entity entity) {
|
||||||
@ -148,28 +53,9 @@ public class PocketPortalBlock extends Block {
|
|||||||
ServerLevel overworld = Objects.requireNonNull(world.getServer()).getLevel(Level.OVERWORLD);
|
ServerLevel overworld = Objects.requireNonNull(world.getServer()).getLevel(Level.OVERWORLD);
|
||||||
if (overworld == null) return;
|
if (overworld == null) return;
|
||||||
|
|
||||||
boolean teleported = false;
|
// Primary method: Try to teleport to the suitcase block they entered from
|
||||||
|
if (!attemptSuitcaseTeleport(world, overworld, player, keystoneName)) {
|
||||||
// Method 1: Try to teleport to the original suitcase block entity
|
// Fallback: Take them to spawn if suitcase block cannot be found
|
||||||
teleported = attemptSuitcaseTeleport(world, overworld, player, keystoneName);
|
|
||||||
|
|
||||||
// Method 2: Try to find suitcase in a player's inventory (new method)
|
|
||||||
if (!teleported) {
|
|
||||||
teleported = attemptPlayerInventorySuitcaseTeleport(world, overworld, player, keystoneName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method 3: Try to find the suitcase as an item entity in the world
|
|
||||||
if (!teleported) {
|
|
||||||
teleported = attemptSuitcaseItemTeleport(world, overworld, player, keystoneName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method 4: Try to use player's last known position
|
|
||||||
if (!teleported) {
|
|
||||||
teleported = attemptLastKnownPositionTeleport(world, overworld, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: Take them to spawn
|
|
||||||
if (!teleported) {
|
|
||||||
player.displayClientMessage(Component.literal("§c...").withStyle(ChatFormatting.RED), false);
|
player.displayClientMessage(Component.literal("§c...").withStyle(ChatFormatting.RED), false);
|
||||||
teleportToPosition(world, player, overworld,
|
teleportToPosition(world, player, overworld,
|
||||||
overworld.getSharedSpawnPos().getX() + 0.5,
|
overworld.getSharedSpawnPos().getX() + 0.5,
|
||||||
@ -177,7 +63,6 @@ public class PocketPortalBlock extends Block {
|
|||||||
overworld.getSharedSpawnPos().getZ() + 0.5, 0, 0);
|
overworld.getSharedSpawnPos().getZ() + 0.5, 0, 0);
|
||||||
}
|
}
|
||||||
SuitcaseBlockEntity.removeSuitcaseEntry(keystoneName, player.getUUID().toString(), world.getServer());
|
SuitcaseBlockEntity.removeSuitcaseEntry(keystoneName, player.getUUID().toString(), world.getServer());
|
||||||
LAST_KNOWN_POSITIONS.remove(player.getUUID().toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,10 +73,11 @@ public class PocketPortalBlock extends Block {
|
|||||||
player.fallDistance = 0f;
|
player.fallDistance = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method 1: Try to teleport to the original suitcase block entity
|
// Teleport to the suitcase block coordinates they entered from
|
||||||
private boolean attemptSuitcaseTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) {
|
private boolean attemptSuitcaseTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) {
|
||||||
BlockPos suitcasePos = SuitcaseBlockEntity.findSuitcasePosition(keystoneName, player.getUUID().toString());
|
BlockPos suitcasePos = SuitcaseBlockEntity.findSuitcasePosition(keystoneName, player.getUUID().toString());
|
||||||
if (suitcasePos == null) return false;
|
if (suitcasePos == null) return false;
|
||||||
|
|
||||||
ChunkPos suitcaseChunkPos = new ChunkPos(suitcasePos);
|
ChunkPos suitcaseChunkPos = new ChunkPos(suitcasePos);
|
||||||
overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, true);
|
overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, true);
|
||||||
try {
|
try {
|
||||||
@ -199,7 +85,10 @@ public class PocketPortalBlock extends Block {
|
|||||||
if (targetEntity instanceof SuitcaseBlockEntity suitcase) {
|
if (targetEntity instanceof SuitcaseBlockEntity suitcase) {
|
||||||
SuitcaseBlockEntity.EnteredPlayerData exitData = suitcase.getExitPosition(player.getUUID().toString());
|
SuitcaseBlockEntity.EnteredPlayerData exitData = suitcase.getExitPosition(player.getUUID().toString());
|
||||||
if (exitData != null) {
|
if (exitData != null) {
|
||||||
teleportToPosition(world, player, overworld, exitData.x(), exitData.y(), exitData.z(), exitData.yaw(), player.getXRot());
|
// Teleport to the suitcase block position (not the original player position)
|
||||||
|
teleportToPosition(world, player, overworld,
|
||||||
|
suitcasePos.getX() + 0.5, suitcasePos.getY() + 1.0, suitcasePos.getZ() + 0.5,
|
||||||
|
exitData.yaw(), player.getXRot());
|
||||||
Objects.requireNonNull(world.getServer()).execute(() -> {
|
Objects.requireNonNull(world.getServer()).execute(() -> {
|
||||||
overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, false);
|
overworld.setChunkForced(suitcaseChunkPos.x, suitcaseChunkPos.z, false);
|
||||||
});
|
});
|
||||||
@ -212,118 +101,24 @@ public class PocketPortalBlock extends Block {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method 2: Try to find the suitcase as an item entity in the world
|
|
||||||
private boolean attemptSuitcaseItemTeleport(Level world, ServerLevel overworld, ServerPlayer player, String keystoneName) {
|
|
||||||
BlockPos searchCenter;
|
|
||||||
BlockPos suitcasePos = SuitcaseBlockEntity.findSuitcasePosition(keystoneName, player.getUUID().toString());
|
|
||||||
if (suitcasePos != null) {
|
|
||||||
searchCenter = suitcasePos;
|
|
||||||
} else {
|
|
||||||
PlayerPositionData lastPos = LAST_KNOWN_POSITIONS.get(player.getUUID().toString());
|
|
||||||
if (lastPos != null) {
|
|
||||||
searchCenter = new BlockPos((int)lastPos.x, (int)lastPos.y, (int)lastPos.z);
|
|
||||||
} else {
|
|
||||||
searchCenter = overworld.getSharedSpawnPos();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int centerX = searchCenter.getX() >> 4;
|
|
||||||
int centerZ = searchCenter.getZ() >> 4;
|
|
||||||
for (int radius = 0; radius <= SEARCH_RADIUS_CHUNKS; radius++) {
|
|
||||||
for (int x = centerX - radius; x <= centerX + radius; x++) {
|
|
||||||
for (int z = centerZ - radius; z <= centerZ + radius; z++) {
|
|
||||||
if (radius > 0 && x > centerX - radius && x < centerX + radius &&
|
|
||||||
z > centerZ - radius && z < centerZ + radius) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!overworld.hasChunk(x, z)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LevelChunk chunk = overworld.getChunk(x, z);
|
|
||||||
List<ItemEntity> itemEntities = overworld.getEntitiesOfClass(
|
|
||||||
ItemEntity.class,
|
|
||||||
new AABB(chunk.getPos().getMinBlockX(), overworld.getMinBuildHeight(), chunk.getPos().getMinBlockZ(),
|
|
||||||
chunk.getPos().getMaxBlockX(), overworld.getMaxBuildHeight(), chunk.getPos().getMaxBlockZ()),
|
|
||||||
itemEntity -> {
|
|
||||||
ItemStack stack = itemEntity.getItem();
|
|
||||||
CompoundTag beTag = BlockItem.getBlockEntityData(stack);
|
|
||||||
if (beTag == null) return false;
|
|
||||||
|
|
||||||
return beTag.contains("BoundKeystone") &&
|
|
||||||
keystoneName.equals(beTag.getString("BoundKeystone"));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!itemEntities.isEmpty()) {
|
|
||||||
ItemEntity suitcaseItemEntity = itemEntities.get(0);
|
|
||||||
cleanUpSuitcaseItemNbt(suitcaseItemEntity.getItem(), player, keystoneName);
|
|
||||||
suitcaseItemEntity.setItem(suitcaseItemEntity.getItem());
|
|
||||||
teleportToPosition(world, player, overworld,
|
|
||||||
suitcaseItemEntity.getX(), suitcaseItemEntity.getY() + 1.0, suitcaseItemEntity.getZ(),
|
|
||||||
player.getYRot(), player.getXRot());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method 3: Try to use player's last known position
|
|
||||||
private boolean attemptLastKnownPositionTeleport(Level world, ServerLevel overworld, ServerPlayer player) {
|
|
||||||
PlayerPositionData lastPos = LAST_KNOWN_POSITIONS.get(player.getUUID().toString());
|
|
||||||
if (lastPos != null) {
|
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
if (currentTime - lastPos.timestamp > 10 * 60 * 1000) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
player.displayClientMessage(Component.literal("§6Returning to your last known position."), false);
|
|
||||||
teleportToPosition(world, player, overworld,
|
|
||||||
lastPos.x, lastPos.y, lastPos.z,
|
|
||||||
lastPos.yaw, lastPos.pitch);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void teleportToPosition(Level world, ServerPlayer player, ServerLevel targetWorld,
|
private void teleportToPosition(Level world, ServerPlayer player, ServerLevel targetWorld,
|
||||||
double x, double y, double z, float yaw, float pitch) {
|
double x, double y, double z, float yaw, float pitch) {
|
||||||
if (!world.isClientSide) {
|
if (!world.isClientSide) {
|
||||||
player.teleportTo(targetWorld, x, y, z, yaw, pitch);
|
// Use changeDimension instead of teleportTo to avoid "Player moved wrongly" errors
|
||||||
|
player.changeDimension(targetWorld, new net.minecraftforge.common.util.ITeleporter() {
|
||||||
|
@Override
|
||||||
|
public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float placementYaw, java.util.function.Function<Boolean, Entity> repositionEntity) {
|
||||||
|
Entity e = repositionEntity.apply(false);
|
||||||
|
e.teleportTo(x, y, z);
|
||||||
|
e.setYRot(yaw);
|
||||||
|
e.setXRot(pitch);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateItemLore(ItemStack stack, int playerCount) {
|
|
||||||
if (stack.hasTag()) {
|
|
||||||
assert stack.getTag() != null;
|
|
||||||
if (stack.getTag().contains("display")) {
|
|
||||||
CompoundTag display = stack.getTag().getCompound("display");
|
|
||||||
if (display.contains("Lore", Tag.TAG_LIST)) {
|
|
||||||
ListTag lore = display.getList("Lore", Tag.TAG_STRING);
|
|
||||||
ListTag newLore = new ListTag();
|
|
||||||
for (int i = 0; i < lore.size(); i++) {
|
|
||||||
String loreStr = lore.getString(i);
|
|
||||||
if (!loreStr.contains("traveler")) {
|
|
||||||
newLore.add(lore.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (playerCount > 0) {
|
|
||||||
Component warningText = Component.literal("§c⚠ Contains " + playerCount + " traveler(s)!")
|
|
||||||
.withStyle(ChatFormatting.RED);
|
|
||||||
newLore.add(0, StringTag.valueOf(Component.Serializer.toJson(warningText)));
|
|
||||||
}
|
|
||||||
display.put("Lore", newLore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (playerCount > 0) {
|
|
||||||
Component lore = Component.literal(playerCount + " player" + (playerCount > 1 ? "s" : "") + " inside").withStyle(ChatFormatting.GRAY);
|
|
||||||
CompoundTag displayTag = stack.getOrCreateTagElement("display");
|
|
||||||
ListTag loreList = new ListTag();
|
|
||||||
loreList.add(StringTag.valueOf(Component.Serializer.toJson(lore)));
|
|
||||||
displayTag.put("Lore", loreList);
|
|
||||||
} else {
|
|
||||||
if (stack.hasTag() && stack.getTag().contains("display")) {
|
|
||||||
stack.getTag().getCompound("display").remove("Lore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -45,13 +45,11 @@ import net.minecraft.world.phys.Vec3;
|
|||||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.Containers;
|
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundStopSoundPacket;
|
import net.minecraft.network.protocol.game.ClientboundStopSoundPacket;
|
||||||
import net.minecraft.sounds.SoundSource;
|
|
||||||
import net.minecraft.sounds.SoundEvents;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -66,6 +64,8 @@ public class SuitcaseBlock extends BaseEntityBlock {
|
|||||||
private final static VoxelShape SHAPE_S = Block.box(0, 0, 2, 16, 4, 14);
|
private final static VoxelShape SHAPE_S = Block.box(0, 0, 2, 16, 4, 14);
|
||||||
private final static VoxelShape SHAPE_E = Block.box(2, 0, 0, 14, 4, 16);
|
private final static VoxelShape SHAPE_E = Block.box(2, 0, 0, 14, 4, 16);
|
||||||
private final static VoxelShape SHAPE_W = Block.box(2, 0, 0, 14, 4, 16);
|
private final static VoxelShape SHAPE_W = Block.box(2, 0, 0, 14, 4, 16);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SuitcaseBlock(BlockBehaviour.Properties properties) {
|
public SuitcaseBlock(BlockBehaviour.Properties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
@ -119,42 +119,6 @@ public class SuitcaseBlock extends BaseEntityBlock {
|
|||||||
@Override
|
@Override
|
||||||
public void onRemove(BlockState state, @NotNull Level world, @NotNull BlockPos pos, BlockState newState, boolean isMoving) {
|
public void onRemove(BlockState state, @NotNull Level world, @NotNull BlockPos pos, BlockState newState, boolean isMoving) {
|
||||||
if (!state.is(newState.getBlock())) {
|
if (!state.is(newState.getBlock())) {
|
||||||
BlockEntity blockEntity = world.getBlockEntity(pos);
|
|
||||||
if (blockEntity instanceof SuitcaseBlockEntity suitcase) {
|
|
||||||
ItemStack itemStack = new ItemStack(this);
|
|
||||||
String boundKeystone = suitcase.getBoundKeystoneName();
|
|
||||||
if (boundKeystone != null) {
|
|
||||||
CompoundTag beNbt = new CompoundTag();
|
|
||||||
suitcase.saveAdditional(beNbt);
|
|
||||||
|
|
||||||
if (!beNbt.isEmpty()) {
|
|
||||||
BlockItem.setBlockEntityData(itemStack, ModBlockEntities.SUITCASE_BLOCK_ENTITY.get(), beNbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompoundTag display = itemStack.getOrCreateTagElement("display");
|
|
||||||
ListTag lore = new ListTag();
|
|
||||||
String displayName = boundKeystone.replace("_", " ");
|
|
||||||
Component boundText;
|
|
||||||
if (suitcase.isLocked()) {
|
|
||||||
boundText = Component.literal("Bound to: §k" + displayName)
|
|
||||||
.withStyle(ChatFormatting.GRAY);
|
|
||||||
} else {
|
|
||||||
boundText = Component.literal("Bound to: " + displayName)
|
|
||||||
.withStyle(ChatFormatting.GRAY);
|
|
||||||
}
|
|
||||||
Component lockText = Component.literal(suitcase.isLocked() ? "§cLocked" : "§aUnlocked")
|
|
||||||
.withStyle(ChatFormatting.GRAY);
|
|
||||||
if (!suitcase.getEnteredPlayers().isEmpty()) {
|
|
||||||
Component warningText = Component.literal("§c⚠ Contains " + suitcase.getEnteredPlayers().size() + " Traveler's!")
|
|
||||||
.withStyle(ChatFormatting.RED);
|
|
||||||
lore.add(StringTag.valueOf(Component.Serializer.toJson(warningText)));
|
|
||||||
}
|
|
||||||
lore.add(StringTag.valueOf(Component.Serializer.toJson(boundText)));
|
|
||||||
lore.add(StringTag.valueOf(Component.Serializer.toJson(lockText)));
|
|
||||||
display.put("Lore", lore);
|
|
||||||
}
|
|
||||||
Containers.dropItemStack(world, pos.getX(), pos.getY(), pos.getZ(), itemStack);
|
|
||||||
}
|
|
||||||
super.onRemove(state, world, pos, newState, isMoving);
|
super.onRemove(state, world, pos, newState, isMoving);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,12 +227,41 @@ public class SuitcaseBlock extends BaseEntityBlock {
|
|||||||
player.fallDistance = 0f;
|
player.fallDistance = 0f;
|
||||||
|
|
||||||
PlayerEntryData ped = PlayerEntryData.get(targetWorld);
|
PlayerEntryData ped = PlayerEntryData.get(targetWorld);
|
||||||
|
|
||||||
|
// Use default position (17, 97, 9) unless manually configured
|
||||||
|
if (!ped.isManuallySet()) {
|
||||||
|
ped.setEntryAutomatic(new Vec3(17.5, 97.0, 9.5), 0f, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
Vec3 dest = ped.getEntryPos();
|
Vec3 dest = ped.getEntryPos();
|
||||||
|
float entryYaw = ped.getEntryYaw();
|
||||||
float yaw = ped.getEntryYaw();
|
|
||||||
float pitch = player.getXRot();
|
float pitch = player.getXRot();
|
||||||
|
|
||||||
player.teleportTo(targetWorld, dest.x, dest.y, dest.z, yaw, pitch);
|
// Ensure destination chunk is loaded before teleportation
|
||||||
|
ChunkPos destChunkPos = new ChunkPos(BlockPos.containing(dest));
|
||||||
|
targetWorld.setChunkForced(destChunkPos.x, destChunkPos.z, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use changeDimension instead of teleportTo to avoid "Player moved wrongly" errors
|
||||||
|
player.changeDimension(targetWorld, new net.minecraftforge.common.util.ITeleporter() {
|
||||||
|
@Override
|
||||||
|
public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float placementYaw, java.util.function.Function<Boolean, Entity> repositionEntity) {
|
||||||
|
Entity e = repositionEntity.apply(false);
|
||||||
|
e.teleportTo(dest.x, dest.y, dest.z);
|
||||||
|
e.setYRot(entryYaw);
|
||||||
|
e.setXRot(pitch);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schedule chunk unforcing on next tick to allow proper entity placement
|
||||||
|
Objects.requireNonNull(world.getServer()).execute(() -> {
|
||||||
|
targetWorld.setChunkForced(destChunkPos.x, destChunkPos.z, false);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// Ensure chunk is unforced even if teleportation fails
|
||||||
|
targetWorld.setChunkForced(destChunkPos.x, destChunkPos.z, false);
|
||||||
|
}
|
||||||
|
|
||||||
player.connection.send(new ClientboundStopSoundPacket(null, null));
|
player.connection.send(new ClientboundStopSoundPacket(null, null));
|
||||||
|
|
||||||
@ -283,6 +276,8 @@ public class SuitcaseBlock extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull VoxelShape getShape(BlockState state, @NotNull BlockGetter world, @NotNull BlockPos pos, @NotNull CollisionContext context) {
|
public @NotNull VoxelShape getShape(BlockState state, @NotNull BlockGetter world, @NotNull BlockPos pos, @NotNull CollisionContext context) {
|
||||||
return switch (state.getValue(FACING)) {
|
return switch (state.getValue(FACING)) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package io.lampnet.travelerssuitcase.block.entity;
|
package io.lampnet.travelerssuitcase.block.entity;
|
||||||
|
|
||||||
import io.lampnet.travelerssuitcase.block.PocketPortalBlock;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
@ -96,7 +96,7 @@ public class SuitcaseBlockEntity extends BlockEntity {
|
|||||||
);
|
);
|
||||||
suitcases.put(player.getUUID().toString(), this.worldPosition);
|
suitcases.put(player.getUUID().toString(), this.worldPosition);
|
||||||
}
|
}
|
||||||
PocketPortalBlock.storePlayerPosition(player);
|
|
||||||
setChangedAndNotify();
|
setChangedAndNotify();
|
||||||
|
|
||||||
MinecraftServer server = player.getServer();
|
MinecraftServer server = player.getServer();
|
||||||
@ -111,9 +111,9 @@ public class SuitcaseBlockEntity extends BlockEntity {
|
|||||||
if (data.uuid.equals(playerUuid)) {
|
if (data.uuid.equals(playerUuid)) {
|
||||||
EnteredPlayerData exitData = new EnteredPlayerData(
|
EnteredPlayerData exitData = new EnteredPlayerData(
|
||||||
data.uuid,
|
data.uuid,
|
||||||
this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 1.0, this.worldPosition.getZ() + 0.5,
|
data.x, data.y, data.z,
|
||||||
data.pitch, data.yaw,
|
data.pitch, data.yaw,
|
||||||
this.worldPosition
|
data.suitcasePos != null ? data.suitcasePos : this.worldPosition
|
||||||
);
|
);
|
||||||
enteredPlayers.remove(i);
|
enteredPlayers.remove(i);
|
||||||
setChangedAndNotify();
|
setChangedAndNotify();
|
||||||
@ -187,7 +187,13 @@ public class SuitcaseBlockEntity extends BlockEntity {
|
|||||||
@Override
|
@Override
|
||||||
public @NotNull CompoundTag getUpdateTag() {
|
public @NotNull CompoundTag getUpdateTag() {
|
||||||
CompoundTag nbt = new CompoundTag();
|
CompoundTag nbt = new CompoundTag();
|
||||||
this.saveAdditional(nbt);
|
// Only sync essential data to client, not player entry data which could cause position conflicts
|
||||||
|
if (boundKeystoneName != null) {
|
||||||
|
nbt.putString("BoundKeystone", boundKeystoneName);
|
||||||
|
}
|
||||||
|
nbt.putBoolean("Locked", isLocked);
|
||||||
|
nbt.putBoolean("DimensionLocked", dimensionLocked);
|
||||||
|
// Deliberately exclude EnteredPlayers from client sync to prevent coordinate conflicts
|
||||||
return nbt;
|
return nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ public class PlayerEntryData extends SavedData {
|
|||||||
|
|
||||||
private Vec3 entryPos = Vec3.ZERO;
|
private Vec3 entryPos = Vec3.ZERO;
|
||||||
private float entryYaw = 0f, entryPitch = 0f;
|
private float entryYaw = 0f, entryPitch = 0f;
|
||||||
|
private boolean isManuallySet = false;
|
||||||
|
|
||||||
public PlayerEntryData() {
|
public PlayerEntryData() {
|
||||||
super();
|
super();
|
||||||
@ -27,6 +28,7 @@ public class PlayerEntryData extends SavedData {
|
|||||||
);
|
);
|
||||||
data.entryYaw = nbt.getFloat("pyaw");
|
data.entryYaw = nbt.getFloat("pyaw");
|
||||||
data.entryPitch = nbt.getFloat("ppitch");
|
data.entryPitch = nbt.getFloat("ppitch");
|
||||||
|
data.isManuallySet = nbt.getBoolean("manually_set");
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +43,7 @@ public class PlayerEntryData extends SavedData {
|
|||||||
nbt.putDouble("pz", entryPos.z);
|
nbt.putDouble("pz", entryPos.z);
|
||||||
nbt.putFloat( "pyaw", entryYaw);
|
nbt.putFloat( "pyaw", entryYaw);
|
||||||
nbt.putFloat( "ppitch", entryPitch);
|
nbt.putFloat( "ppitch", entryPitch);
|
||||||
|
nbt.putBoolean("manually_set", isManuallySet);
|
||||||
return nbt;
|
return nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,10 +51,27 @@ public class PlayerEntryData extends SavedData {
|
|||||||
this.entryPos = pos;
|
this.entryPos = pos;
|
||||||
this.entryYaw = yaw;
|
this.entryYaw = yaw;
|
||||||
this.entryPitch = pitch;
|
this.entryPitch = pitch;
|
||||||
|
this.isManuallySet = true;
|
||||||
|
this.setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntryAutomatic(Vec3 pos, float yaw, float pitch) {
|
||||||
|
this.entryPos = pos;
|
||||||
|
this.entryYaw = yaw;
|
||||||
|
this.entryPitch = pitch;
|
||||||
|
this.setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetToDefault() {
|
||||||
|
this.entryPos = new Vec3(17.5, 97.0, 9.5);
|
||||||
|
this.entryYaw = 0f;
|
||||||
|
this.entryPitch = 0f;
|
||||||
|
this.isManuallySet = false;
|
||||||
this.setDirty();
|
this.setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3 getEntryPos() { return entryPos; }
|
public Vec3 getEntryPos() { return entryPos; }
|
||||||
public float getEntryYaw() { return entryYaw; }
|
public float getEntryYaw() { return entryYaw; }
|
||||||
public float getEntryPitch() { return entryPitch; }
|
public float getEntryPitch() { return entryPitch; }
|
||||||
|
public boolean isManuallySet() { return isManuallySet; }
|
||||||
}
|
}
|
||||||
@ -21,9 +21,14 @@ public class ServerChunkManagerMixin {
|
|||||||
private void onRunDistanceManagerUpdates(CallbackInfoReturnable<Boolean> cir) {
|
private void onRunDistanceManagerUpdates(CallbackInfoReturnable<Boolean> cir) {
|
||||||
// Only apply special chunk processing logic to RuntimeWorld instances (fantasy dimensions)
|
// Only apply special chunk processing logic to RuntimeWorld instances (fantasy dimensions)
|
||||||
if (this.level instanceof RuntimeWorld) {
|
if (this.level instanceof RuntimeWorld) {
|
||||||
if (!((FantasyWorldAccess) this.level).fantasy$shouldTick()) {
|
FantasyWorldAccess worldAccess = (FantasyWorldAccess) this.level;
|
||||||
cir.setReturnValue(false);
|
// Always allow distance manager updates if chunks are loaded (prevents breaking entity initialization)
|
||||||
|
// Only block when world is truly empty AND configured not to tick when empty
|
||||||
|
if (this.level.getChunkSource().getLoadedChunksCount() > 0 || worldAccess.fantasy$shouldTick()) {
|
||||||
|
// Allow distance manager to run - this is critical for entity systems
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
cir.setReturnValue(false);
|
||||||
}
|
}
|
||||||
// Regular worlds (overworld, nether, end) process chunks normally without interference
|
// Regular worlds (overworld, nether, end) process chunks normally without interference
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user