Commits
DerFrZocker authored and md_5 committed 1eeba6a0ec9
31 31 | |
32 32 | public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRegion { |
33 33 | |
34 34 | private final WeakReference<GeneratorAccessSeed> weakAccess; |
35 35 | private final int centerChunkX; |
36 36 | private final int centerChunkZ; |
37 37 | // Buffer is one chunk (16 blocks), can be seen in ChunkStatus#q |
38 38 | // there the order is {..., FEATURES, LIQUID_CARVERS, STRUCTURE_STARTS, ...} |
39 39 | private final int buffer = 16; |
40 40 | private final BoundingBox region; |
41 + | boolean entitiesLoaded = false; |
41 42 | // Minecraft saves the entities as NBTTagCompound during chunk generation. This causes that |
42 43 | // changes made to the returned bukkit entity are not saved. To combat this we keep them and |
43 44 | // save them when the population is finished. |
44 45 | private final List<net.minecraft.world.entity.Entity> entities = new ArrayList<>(); |
46 + | // SPIGOT-6891: Save outside Entities extra, since they are not part of the region. |
47 + | // Prevents crash for chunks which are converting from 1.17 to 1.18 |
48 + | private final List<net.minecraft.world.entity.Entity> outsideEntities = new ArrayList<>(); |
45 49 | |
46 50 | public CraftLimitedRegion(GeneratorAccessSeed access, ChunkCoordIntPair center) { |
47 51 | this.weakAccess = new WeakReference<>(access); |
48 52 | centerChunkX = center.x; |
49 53 | centerChunkZ = center.z; |
50 54 | |
51 - | // load entities which are already present |
52 - | for (int x = -(buffer >> 4); x <= (buffer >> 4); x++) { |
53 - | for (int z = -(buffer >> 4); z <= (buffer >> 4); z++) { |
54 - | ProtoChunk chunk = (ProtoChunk) access.getChunk(centerChunkX + x, centerChunkZ + z); |
55 - | for (NBTTagCompound compound : chunk.getEntities()) { |
56 - | EntityTypes.loadEntityRecursive(compound, access.getMinecraftWorld(), (entity) -> { |
57 - | entity.generation = true; |
58 - | entities.add(entity); |
59 - | return entity; |
60 - | }); |
61 - | } |
62 - | } |
63 - | } |
64 - | |
65 55 | World world = access.getMinecraftWorld().getWorld(); |
66 56 | int xCenter = centerChunkX << 4; |
67 57 | int zCenter = centerChunkZ << 4; |
68 58 | int xMin = xCenter - getBuffer(); |
69 59 | int zMin = zCenter - getBuffer(); |
70 60 | int xMax = xCenter + getBuffer() + 16; |
71 61 | int zMax = zCenter + getBuffer() + 16; |
72 62 | |
73 63 | this.region = new BoundingBox(xMin, world.getMinHeight(), zMin, xMax, world.getMaxHeight(), zMax); |
74 64 | } |
75 65 | |
76 66 | public GeneratorAccessSeed getHandle() { |
77 67 | GeneratorAccessSeed handle = weakAccess.get(); |
78 68 | |
79 69 | if (handle == null) { |
80 70 | throw new IllegalStateException("GeneratorAccessSeed no longer present, are you using it in a different tick?"); |
81 71 | } |
82 72 | |
83 73 | return handle; |
84 74 | } |
85 75 | |
86 - | public void saveEntities() { |
76 + | public void loadEntities() { |
77 + | if (entitiesLoaded) { |
78 + | return; |
79 + | } |
80 + | |
87 81 | GeneratorAccessSeed access = getHandle(); |
82 + | // load entities which are already present |
88 83 | for (int x = -(buffer >> 4); x <= (buffer >> 4); x++) { |
89 84 | for (int z = -(buffer >> 4); z <= (buffer >> 4); z++) { |
90 85 | ProtoChunk chunk = (ProtoChunk) access.getChunk(centerChunkX + x, centerChunkZ + z); |
91 - | chunk.getEntities().clear(); |
86 + | for (NBTTagCompound compound : chunk.getEntities()) { |
87 + | EntityTypes.loadEntityRecursive(compound, access.getMinecraftWorld(), (entity) -> { |
88 + | if (region.contains(entity.getX(), entity.getY(), entity.getZ())) { |
89 + | entity.generation = true; |
90 + | entities.add(entity); |
91 + | } else { |
92 + | outsideEntities.add(entity); |
93 + | } |
94 + | return entity; |
95 + | }); |
96 + | } |
97 + | } |
98 + | } |
99 + | |
100 + | entitiesLoaded = true; |
101 + | } |
102 + | |
103 + | public void saveEntities() { |
104 + | GeneratorAccessSeed access = getHandle(); |
105 + | // We don't clear existing entities when they are not loaded and therefore not modified |
106 + | if (entitiesLoaded) { |
107 + | for (int x = -(buffer >> 4); x <= (buffer >> 4); x++) { |
108 + | for (int z = -(buffer >> 4); z <= (buffer >> 4); z++) { |
109 + | ProtoChunk chunk = (ProtoChunk) access.getChunk(centerChunkX + x, centerChunkZ + z); |
110 + | chunk.getEntities().clear(); |
111 + | } |
92 112 | } |
93 113 | } |
94 114 | |
95 115 | for (net.minecraft.world.entity.Entity entity : entities) { |
96 116 | if (entity.isAlive()) { |
97 117 | // check if entity is still in region or if it got teleported outside it |
98 118 | Preconditions.checkState(region.contains(entity.getX(), entity.getY(), entity.getZ()), "Entity %s is not in the region", entity); |
99 119 | access.addFreshEntity(entity); |
100 120 | } |
101 121 | } |
122 + | |
123 + | for (net.minecraft.world.entity.Entity entity : outsideEntities) { |
124 + | access.addFreshEntity(entity); |
125 + | } |
102 126 | } |
103 127 | |
104 128 | public void breakLink() { |
105 129 | weakAccess.clear(); |
106 130 | } |
107 131 | |
108 132 | |
109 133 | public int getBuffer() { |
110 134 | return buffer; |
111 135 | } |
180 204 | } |
181 205 | |
182 206 | |
183 207 | public boolean generateTree(Location location, Random random, TreeType treeType, Consumer<BlockState> consumer) { |
184 208 | Preconditions.checkArgument(isInRegion(location), "Coordinates %s, %s, %s are not in the region", location.getBlockX(), location.getBlockY(), location.getBlockZ()); |
185 209 | return super.generateTree(location, random, treeType, consumer); |
186 210 | } |
187 211 | |
188 212 | |
189 213 | public Collection<net.minecraft.world.entity.Entity> getNMSEntities() { |
214 + | // Only load entities if we need them |
215 + | loadEntities(); |
190 216 | return new ArrayList<>(entities); |
191 217 | } |
192 218 | |
193 219 | |
194 220 | public <T extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> function, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException { |
195 221 | Preconditions.checkArgument(isInRegion(location), "Coordinates %s, %s, %s are not in the region", location.getBlockX(), location.getBlockY(), location.getBlockZ()); |
196 222 | return super.spawn(location, clazz, function, reason); |
197 223 | } |
198 224 | |
199 225 | |