9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-19 14:59:32 +00:00

Update Servux

This commit is contained in:
violetc
2025-02-15 19:33:08 +08:00
parent 0421afcd12
commit 4de9da7f47
6 changed files with 335 additions and 90 deletions

View File

@@ -68,12 +68,6 @@ jobs:
with: with:
name: ${{ env.jar }} name: ${{ env.jar }}
path: ${{ env.jar }} path: ${{ env.jar }}
- name: Delete Draft Releases
if: "!contains(github.event.commits[0].message, '[release-skip]')"
continue-on-error: true
uses: hugo19941994/delete-draft-releases@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release - name: Create Release
if: "!contains(github.event.commits[0].message, '[release-skip]')" if: "!contains(github.event.commits[0].message, '[release-skip]')"
continue-on-error: true continue-on-error: true

View File

@@ -5,14 +5,14 @@ Subject: [PATCH] Servux Protocol
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 1deedd3fc28792fc368580973038c1824b15691d..8dc71b01b9e7970729a82c68402eec9375dbae04 100644 index 1deedd3fc28792fc368580973038c1824b15691d..f25eca541f377ee7b908a900c6a1b50f02a41eef 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -2189,6 +2189,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -2189,6 +2189,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
} }
this.lastSpawnChunkRadius = i; this.lastSpawnChunkRadius = i;
+ org.leavesmc.leaves.protocol.servux.ServuxStructuresProtocol.refreshSpawnMetadata = true; // Leaves - servux + org.leavesmc.leaves.protocol.servux.ServuxHudDataProtocol.refreshSpawnMetadata = true; // Leaves - servux
} }
public LongSet getForcedChunks() { public LongSet getForcedChunks() {

View File

@@ -747,6 +747,12 @@ public final class LeavesConfig {
@GlobalConfig("entity-protocol") @GlobalConfig("entity-protocol")
public boolean entityProtocol = false; public boolean entityProtocol = false;
@GlobalConfig("hud-metadata-protocol")
public boolean hudMetadataProtocol = false;
@GlobalConfig("hud-metadata-protocol-share-seed")
public boolean hudMetadataShareSeed = true;
} }
@GlobalConfig("bbor-protocol") @GlobalConfig("bbor-protocol")

View File

@@ -49,10 +49,16 @@ public class LitematicaEasyPlaceProtocol {
BlockStateProperties.ROTATION_16 BlockStateProperties.ROTATION_16
); );
public static final ImmutableSet<Property<?>> BLACKLISTED_PROPERTIES = ImmutableSet.of(
BlockStateProperties.WATERLOGGED,
BlockStateProperties.POWERED
);
public static BlockState applyPlacementProtocol(BlockState state, BlockPlaceContext context) { public static BlockState applyPlacementProtocol(BlockState state, BlockPlaceContext context) {
return applyPlacementProtocolV3(state, UseContext.from(context, context.getHand())); return applyPlacementProtocolV3(state, UseContext.from(context, context.getHand()));
} }
@SuppressWarnings("unchecked")
@Nullable @Nullable
private static <T extends Comparable<T>> BlockState applyPlacementProtocolV3(BlockState state, @NotNull UseContext context) { private static <T extends Comparable<T>> BlockState applyPlacementProtocolV3(BlockState state, @NotNull UseContext context) {
int protocolValue = (int) (context.getHitVec().x - (double) context.getPos().getX()) - 2; int protocolValue = (int) (context.getHitVec().x - (double) context.getPos().getX()) - 2;
@@ -86,8 +92,13 @@ public class LitematicaEasyPlaceProtocol {
try { try {
for (Property<?> p : propList) { for (Property<?> p : propList) {
if (((p instanceof EnumProperty<?> ep) && !ep.getValueClass().equals(Direction.class)) && WHITELISTED_PROPERTIES.contains(p)) { if (property != null && property.equals(p)) {
@SuppressWarnings("unchecked") continue;
}
if (!WHITELISTED_PROPERTIES.contains(p) || BLACKLISTED_PROPERTIES.contains(p)) {
continue;
}
Property<T> prop = (Property<T>) p; Property<T> prop = (Property<T>) p;
List<T> list = new ArrayList<>(prop.getPossibleValues()); List<T> list = new ArrayList<>(prop.getPossibleValues());
list.sort(Comparable::compareTo); list.sort(Comparable::compareTo);
@@ -112,11 +123,18 @@ public class LitematicaEasyPlaceProtocol {
protocolValue >>>= requiredBits; protocolValue >>>= requiredBits;
} }
} }
}
} catch (Exception e) { } catch (Exception e) {
LeavesLogger.LOGGER.warning("Exception trying to apply placement protocol value", e); LeavesLogger.LOGGER.warning("Exception trying to apply placement protocol value", e);
} }
for (Property<?> p : BLACKLISTED_PROPERTIES) {
if (state.hasProperty(p)) {
Property<T> prop = (Property<T>) p;
BlockState def = state.getBlock().defaultBlockState();
state = state.setValue(prop, def.getValue(prop));
}
}
if (state.canSurvive(context.getWorld(), context.getPos())) { if (state.canSurvive(context.getWorld(), context.getPos())) {
return state; return state;
} else { } else {

View File

@@ -0,0 +1,265 @@
package org.leavesmc.leaves.protocol.servux;
import com.mojang.serialization.DataResult;
import io.netty.buffer.Unpooled;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.level.GameRules;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@LeavesProtocol(namespace = "servux")
public class ServuxHudDataProtocol {
public static final ResourceLocation CHANNEL = ServuxProtocol.id("hud_metadata");
public static final int PROTOCOL_VERSION = 1;
private static final List<ServerPlayer> players = new ArrayList<>();
private static final int updateInterval = 80;
public static boolean refreshSpawnMetadata = false;
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class, payloadId = "hud_metadata")
public static void onPacketReceive(ServerPlayer player, HudDataPayload payload) {
if (!LeavesConfig.protocol.servux.hudMetadataProtocol) {
return;
}
switch (payload.packetType) {
case PACKET_C2S_METADATA_REQUEST -> {
players.add(player);
CompoundTag metadata = new CompoundTag();
metadata.putString("name", "hud_metadata");
metadata.putString("id", CHANNEL.toString());
metadata.putInt("version", PROTOCOL_VERSION);
metadata.putString("servux", ServuxProtocol.SERVUX_STRING);
putWorldData(metadata);
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_METADATA, metadata));
}
case PACKET_C2S_SPAWN_DATA_REQUEST -> refreshSpawnMetadata(player);
case PACKET_C2S_RECIPE_MANAGER_REQUEST -> refreshRecipeManager(player);
}
}
@ProtocolHandler.Ticker
public void onTick() {
if (!LeavesConfig.protocol.servux.hudMetadataProtocol) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
int tickCounter = server.getTickCount();
if ((tickCounter % updateInterval) == 0) {
for (ServerPlayer player : players) {
if (refreshSpawnMetadata) {
refreshSpawnMetadata(player);
}
refreshWeatherData(player);
}
refreshSpawnMetadata = false;
}
}
public static void refreshSpawnMetadata(ServerPlayer player) {
if (!LeavesConfig.protocol.servux.hudMetadataProtocol) {
return;
}
CompoundTag metadata = new CompoundTag();
metadata.putString("id", CHANNEL.toString());
metadata.putString("servux", ServuxProtocol.SERVUX_STRING);
putWorldData(metadata);
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_SPAWN_DATA, metadata));
}
public static void refreshRecipeManager(ServerPlayer player) {
if (!LeavesConfig.protocol.servux.hudMetadataProtocol) {
return;
}
Collection<RecipeHolder<?>> recipes = player.server.getRecipeManager().getRecipes();
CompoundTag nbt = new CompoundTag();
ListTag list = new ListTag();
recipes.forEach((recipeEntry -> {
DataResult<Tag> dr = Recipe.CODEC.encodeStart(NbtOps.INSTANCE, recipeEntry.value());
if (dr.result().isPresent()) {
CompoundTag entry = new CompoundTag();
entry.putString("id_reg", recipeEntry.id().registry().toString());
entry.putString("id_value", recipeEntry.id().location().toString());
entry.put("recipe", dr.result().get());
list.add(entry);
}
}));
nbt.put("RecipeManager", list);
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_START, nbt));
}
public static void refreshWeatherData(ServerPlayer player) {
if (!LeavesConfig.protocol.servux.hudMetadataProtocol) {
return;
}
ServerLevel level = MinecraftServer.getServer().overworld();
if (level.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
return;
}
CompoundTag nbt = new CompoundTag();
nbt.putString("id", CHANNEL.toString());
nbt.putString("servux", ServuxProtocol.SERVUX_STRING);
if (level.serverLevelData.isRaining() && level.serverLevelData.getRainTime() > -1) {
nbt.putInt("SetRaining", level.serverLevelData.getRainTime());
nbt.putBoolean("isRaining", true);
} else {
nbt.putBoolean("isRaining", false);
}
if (level.serverLevelData.isThundering() && level.serverLevelData.getThunderTime() > -1) {
nbt.putInt("SetThundering", level.serverLevelData.getThunderTime());
nbt.putBoolean("isThundering", true);
} else {
nbt.putBoolean("isThundering", false);
}
if (level.serverLevelData.getClearWeatherTime() > -1) {
nbt.putInt("SetClear", level.serverLevelData.getClearWeatherTime());
}
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_WEATHER_TICK, nbt));
}
private static void putWorldData(@NotNull CompoundTag metadata) {
ServerLevel level = MinecraftServer.getServer().overworld();
BlockPos spawnPos = level.levelData.getSpawnPos();
metadata.putInt("spawnPosX", spawnPos.getX());
metadata.putInt("spawnPosY", spawnPos.getY());
metadata.putInt("spawnPosZ", spawnPos.getZ());
metadata.putInt("spawnChunkRadius", level.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS));
if (LeavesConfig.protocol.servux.hudMetadataShareSeed) {
metadata.putLong("worldSeed", level.getSeed());
}
}
public static void sendPacket(ServerPlayer player, HudDataPayload payload) {
if (!LeavesConfig.protocol.servux.hudMetadataProtocol) {
return;
}
if (payload.packetType == HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_START) {
FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
buffer.writeNbt(payload.nbt);
PacketSplitter.send(ServuxHudDataProtocol::sendWithSplitter, buffer, player);
} else {
ProtocolUtils.sendPayloadPacket(player, payload);
}
}
private static void sendWithSplitter(ServerPlayer player, FriendlyByteBuf buf) {
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_DATA, buf));
}
public enum HudDataPayloadType {
PACKET_S2C_METADATA(1),
PACKET_C2S_METADATA_REQUEST(2),
PACKET_S2C_SPAWN_DATA(3),
PACKET_C2S_SPAWN_DATA_REQUEST(4),
PACKET_S2C_WEATHER_TICK(5),
PACKET_C2S_RECIPE_MANAGER_REQUEST(6),
// For Packet Splitter (Oversize Packets, S2C)
PACKET_S2C_NBT_RESPONSE_START(10),
PACKET_S2C_NBT_RESPONSE_DATA(11);
private static final class Helper {
static Map<Integer, HudDataPayloadType> ID_TO_TYPE = new HashMap<>();
}
public final int type;
HudDataPayloadType(int type) {
this.type = type;
HudDataPayloadType.Helper.ID_TO_TYPE.put(type, this);
}
public static HudDataPayloadType fromId(int id) {
return HudDataPayloadType.Helper.ID_TO_TYPE.get(id);
}
}
public record HudDataPayload(HudDataPayloadType packetType, CompoundTag nbt, FriendlyByteBuf buffer) implements LeavesCustomPayload<HudDataPayload> {
public HudDataPayload(HudDataPayloadType type, CompoundTag nbt) {
this(type, nbt, null);
}
public HudDataPayload(HudDataPayloadType type, FriendlyByteBuf buffer) {
this(type, new CompoundTag(), buffer);
}
@New
@NotNull
public static HudDataPayload decode(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
HudDataPayloadType type = HudDataPayloadType.fromId(buf.readVarInt());
if (type == null) {
throw new IllegalStateException("invalid packet type received");
}
switch (type) {
case PACKET_S2C_NBT_RESPONSE_DATA -> {
return new HudDataPayload(type, new FriendlyByteBuf(buf.readBytes(buf.readableBytes())));
}
case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA, PACKET_C2S_SPAWN_DATA_REQUEST, PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK,
PACKET_C2S_RECIPE_MANAGER_REQUEST -> {
return new HudDataPayload(type, buf.readNbt());
}
}
throw new IllegalStateException("invalid packet type received");
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeVarInt(this.packetType.type);
switch (this.packetType) {
case PACKET_S2C_NBT_RESPONSE_DATA -> buf.writeBytes(this.buffer.copy());
case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA, PACKET_C2S_SPAWN_DATA_REQUEST,
PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK, PACKET_C2S_RECIPE_MANAGER_REQUEST -> buf.writeNbt(this.nbt);
}
}
@Override
public ResourceLocation id() {
return CHANNEL;
}
}
}

View File

@@ -4,7 +4,6 @@ import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
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;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
@@ -13,7 +12,6 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
@@ -46,7 +44,6 @@ public class ServuxStructuresProtocol {
private static final int updateInterval = 40; private static final int updateInterval = 40;
private static final int timeout = 30 * 20; private static final int timeout = 30 * 20;
public static boolean refreshSpawnMetadata = false;
private static int retainDistance; private static int retainDistance;
public static final ResourceLocation CHANNEL = ServuxProtocol.id("structures"); public static final ResourceLocation CHANNEL = ServuxProtocol.id("structures");
@@ -62,22 +59,10 @@ public class ServuxStructuresProtocol {
switch (payload.packetType()) { switch (payload.packetType()) {
case PACKET_C2S_STRUCTURES_REGISTER -> onPlayerSubscribed(player); case PACKET_C2S_STRUCTURES_REGISTER -> onPlayerSubscribed(player);
case PACKET_C2S_REQUEST_SPAWN_METADATA -> refreshSpawnMetadata(player); case PACKET_C2S_REQUEST_SPAWN_METADATA -> ServuxHudDataProtocol.refreshSpawnMetadata(player); // move to
case PACKET_C2S_STRUCTURES_UNREGISTER -> { case PACKET_C2S_STRUCTURES_UNREGISTER -> onPlayerLoggedOut(player);
onPlayerLoggedOut(player);
refreshSpawnMetadata(player);
} }
} }
}
@ProtocolHandler.PlayerJoin
public static void onPlayerLoggedIn(ServerPlayer player) {
if (!LeavesConfig.protocol.servux.structureProtocol) {
return;
}
onPlayerSubscribed(player);
}
@ProtocolHandler.PlayerLeave @ProtocolHandler.PlayerLeave
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) { public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
@@ -99,17 +84,9 @@ public class ServuxStructuresProtocol {
if ((tickCounter % updateInterval) == 0) { if ((tickCounter % updateInterval) == 0) {
retainDistance = server.getPlayerList().getViewDistance() + 2; retainDistance = server.getPlayerList().getViewDistance() + 2;
for (ServerPlayer player : players.values()) { for (ServerPlayer player : players.values()) {
if (refreshSpawnMetadata) {
refreshSpawnMetadata(player);
}
// TODO DimensionChange // TODO DimensionChange
refreshTrackedChunks(player, tickCounter); refreshTrackedChunks(player, tickCounter);
} }
if (refreshSpawnMetadata) {
refreshSpawnMetadata = false;
}
} }
} }
@@ -134,23 +111,24 @@ public class ServuxStructuresProtocol {
} }
} }
private static boolean chunkHasStructureReferences(int chunkX, int chunkZ, Level world) { private static boolean chunkHasStructureReferences(int chunkX, int chunkZ, @NotNull Level world) {
if (!world.hasChunk(chunkX, chunkZ)) { if (!world.hasChunk(chunkX, chunkZ)) {
return false; return false;
} }
ChunkAccess chunk = world.getChunk(chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS, false); ChunkAccess chunk = world.getChunk(chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS, false);
if (chunk != null) {
for (Map.Entry<Structure, LongSet> entry : chunk.getAllReferences().entrySet()) { for (Map.Entry<Structure, LongSet> entry : chunk.getAllReferences().entrySet()) {
if (!entry.getValue().isEmpty()) { if (!entry.getValue().isEmpty()) {
return true; return true;
} }
} }
}
return false; return false;
} }
public static void onPlayerSubscribed(@NotNull ServerPlayer player) { public static void onPlayerSubscribed(@NotNull ServerPlayer player) {
if (!players.containsKey(player.getId())) { if (!players.containsKey(player.getId())) {
players.put(player.getId(), player); players.put(player.getId(), player);
@@ -158,6 +136,7 @@ public class ServuxStructuresProtocol {
LeavesLogger.LOGGER.warning(player.getScoreboardName() + " re-register servux:structures"); LeavesLogger.LOGGER.warning(player.getScoreboardName() + " re-register servux:structures");
} }
MinecraftServer server = MinecraftServer.getServer();
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.putString("name", "structure_bounding_boxes"); tag.putString("name", "structure_bounding_boxes");
tag.putString("id", CHANNEL.toString()); tag.putString("id", CHANNEL.toString());
@@ -165,32 +144,10 @@ public class ServuxStructuresProtocol {
tag.putString("servux", ServuxProtocol.SERVUX_STRING); tag.putString("servux", ServuxProtocol.SERVUX_STRING);
tag.putInt("timeout", timeout); tag.putInt("timeout", timeout);
MinecraftServer server = MinecraftServer.getServer();
BlockPos spawnPos = server.overworld().levelData.getSpawnPos();
tag.putInt("spawnPosX", spawnPos.getX());
tag.putInt("spawnPosY", spawnPos.getY());
tag.putInt("spawnPosZ", spawnPos.getZ());
tag.putInt("spawnChunkRadius", server.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS));
sendPacket(player, new StructuresPayload(StructuresPayloadType.PACKET_S2C_METADATA, tag)); sendPacket(player, new StructuresPayload(StructuresPayloadType.PACKET_S2C_METADATA, tag));
initialSyncStructures(player, player.moonrise$getViewDistanceHolder().getViewDistances().sendViewDistance() + 2, server.getTickCount()); initialSyncStructures(player, player.moonrise$getViewDistanceHolder().getViewDistances().sendViewDistance() + 2, server.getTickCount());
} }
public static void refreshSpawnMetadata(ServerPlayer player) {
CompoundTag tag = new CompoundTag();
tag.putString("id", CHANNEL.toString());
tag.putString("servux", ServuxProtocol.SERVUX_STRING);
MinecraftServer server = MinecraftServer.getServer();
BlockPos spawnPos = server.overworld().levelData.getSpawnPos();
tag.putInt("spawnPosX", spawnPos.getX());
tag.putInt("spawnPosY", spawnPos.getY());
tag.putInt("spawnPosZ", spawnPos.getZ());
tag.putInt("spawnChunkRadius", server.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS));
sendPacket(player, new StructuresPayload(StructuresPayloadType.PACKET_S2C_SPAWN_METADATA, tag));
}
public static void initialSyncStructures(ServerPlayer player, int chunkRadius, int tickCounter) { public static void initialSyncStructures(ServerPlayer player, int chunkRadius, int tickCounter) {
UUID uuid = player.getUUID(); UUID uuid = player.getUUID();
ChunkPos center = player.getLastSectionPos().chunk(); ChunkPos center = player.getLastSectionPos().chunk();
@@ -220,6 +177,7 @@ public class ServuxStructuresProtocol {
ChunkAccess chunk = world.getChunk(chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS, false); ChunkAccess chunk = world.getChunk(chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS, false);
if (chunk != null) {
for (Map.Entry<Structure, LongSet> entry : chunk.getAllReferences().entrySet()) { for (Map.Entry<Structure, LongSet> entry : chunk.getAllReferences().entrySet()) {
Structure feature = entry.getKey(); Structure feature = entry.getKey();
LongSet startChunks = entry.getValue(); LongSet startChunks = entry.getValue();
@@ -234,6 +192,7 @@ public class ServuxStructuresProtocol {
} }
} }
} }
}
public static void sendStructures(ServerPlayer player, Map<Structure, LongSet> references, int tickCounter) { public static void sendStructures(ServerPlayer player, Map<Structure, LongSet> references, int tickCounter) {
ServerLevel world = player.serverLevel(); ServerLevel world = player.serverLevel();
@@ -280,7 +239,10 @@ public class ServuxStructuresProtocol {
} }
ChunkAccess chunk = world.getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_STARTS, false); ChunkAccess chunk = world.getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_STARTS, false);
StructureStart start = chunk.getStartForStructure(structure); StructureStart start = null;
if (chunk != null) {
start = chunk.getStartForStructure(structure);
}
if (start != null) { if (start != null) {
starts.put(pos, start); starts.put(pos, start);