Skip to content
This repository was archived by the owner on Oct 4, 2024. It is now read-only.

Commit b6b612b

Browse files
committed
Initial support for loading "proto big cube" converted 1.12.2 CC worlds
1 parent 4908c81 commit b6b612b

File tree

4 files changed

+154
-4
lines changed

4 files changed

+154
-4
lines changed

src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ private void onConstruct(ServerLevel worldIn,
239239
this.cubeQueueSorter.createExecutor(delegatedtaskexecutor1, false));
240240

241241
try {
242-
regionCubeIO = new RegionCubeIO(storageFolder, "chunk", "cube");
242+
regionCubeIO = new RegionCubeIO(storageFolder, p_i51538_3_, "chunk", "cube");
243243
} catch (IOException e) {
244244
throw new UncheckedIOException(e);
245245
}

src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/storage/MixinEntityStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public abstract class MixinEntityStorage implements CubicEntityStorage {
4747
@Inject(method = "<init>", at = @At("RETURN"))
4848
private void setupCubeIO(ServerLevel serverLevel, File file, DataFixer dataFixer, boolean bl, Executor executor, CallbackInfo ci) throws IOException {
4949
if (((CubicLevelHeightAccessor) serverLevel).isCubic()) {
50-
cubeWorker = new RegionCubeIO(file, file.getName(), file.getName());
50+
cubeWorker = new RegionCubeIO(file, dataFixer, file.getName(), file.getName());
5151
}
5252
}
5353

src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/storage/MixinSectionStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ private void getServerLevel(File file, Function<Runnable, Codec<R>> function, Fu
7878
LevelHeightAccessor heightAccessor, CallbackInfo ci) throws IOException {
7979

8080
if (((CubicLevelHeightAccessor) levelHeightAccessor).isCubic()) {
81-
cubeWorker = new RegionCubeIO(file, file.getName() + "-chunk", file.getName());
81+
cubeWorker = new RegionCubeIO(file, dataFixer, file.getName() + "-chunk", file.getName());
8282
}
8383
}
8484

src/main/java/io/github/opencubicchunks/cubicchunks/world/storage/RegionCubeIO.java

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
import static net.minecraft.nbt.NbtIo.writeCompressed;
44

5+
import java.io.BufferedInputStream;
56
import java.io.ByteArrayInputStream;
67
import java.io.ByteArrayOutputStream;
8+
import java.io.DataInputStream;
79
import java.io.File;
810
import java.io.IOException;
11+
import java.io.InputStream;
912
import java.nio.ByteBuffer;
1013
import java.util.Iterator;
1114
import java.util.Map;
@@ -15,23 +18,37 @@
1518
import java.util.concurrent.atomic.AtomicBoolean;
1619
import java.util.function.Function;
1720
import java.util.function.Supplier;
21+
import java.util.zip.GZIPInputStream;
1822

1923
import javax.annotation.Nonnull;
2024
import javax.annotation.Nullable;
2125

2226
import com.google.common.collect.Maps;
27+
import com.mojang.datafixers.DataFixer;
2328
import com.mojang.datafixers.util.Either;
2429
import cubicchunks.regionlib.impl.EntryLocation2D;
2530
import cubicchunks.regionlib.impl.EntryLocation3D;
2631
import cubicchunks.regionlib.impl.SaveCubeColumns;
2732
import io.github.opencubicchunks.cubicchunks.CubicChunks;
2833
import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos;
34+
import io.github.opencubicchunks.cubicchunks.utils.Coords;
35+
import net.minecraft.SharedConstants;
2936
import net.minecraft.Util;
3037
import net.minecraft.nbt.CompoundTag;
38+
import net.minecraft.nbt.ListTag;
39+
import net.minecraft.nbt.NbtAccounter;
3140
import net.minecraft.nbt.NbtIo;
41+
import net.minecraft.nbt.NbtUtils;
42+
import net.minecraft.nbt.Tag;
43+
import net.minecraft.resources.ResourceKey;
44+
import net.minecraft.util.datafix.DataFixTypes;
3245
import net.minecraft.util.thread.ProcessorMailbox;
3346
import net.minecraft.util.thread.StrictQueue;
3447
import net.minecraft.world.level.ChunkPos;
48+
import net.minecraft.world.level.Level;
49+
import net.minecraft.world.level.chunk.ChunkStatus;
50+
import net.minecraft.world.level.chunk.storage.ChunkStorage;
51+
import net.minecraft.world.level.storage.DimensionDataStorage;
3552
import org.apache.logging.log4j.Logger;
3653

3754
public class RegionCubeIO {
@@ -46,13 +63,15 @@ public class RegionCubeIO {
4663
private final Map<ChunkPos, SaveEntry> pendingChunkWrites = Maps.newLinkedHashMap();
4764
private final Map<CubePos, SaveEntry> pendingCubeWrites = Maps.newLinkedHashMap();
4865

66+
private final DataFixer fixerUpper;
4967
private final ProcessorMailbox<StrictQueue.IntRunnable> chunkExecutor;
5068
private final ProcessorMailbox<StrictQueue.IntRunnable> cubeExecutor;
5169

5270
private final AtomicBoolean shutdownRequested = new AtomicBoolean();
5371

54-
public RegionCubeIO(File storageFolder, @Nullable String chunkWorkerName, String cubeWorkerName) throws IOException {
72+
public RegionCubeIO(File storageFolder, DataFixer dataFixer, @Nullable String chunkWorkerName, String cubeWorkerName) throws IOException {
5573
this.storageFolder = storageFolder;
74+
this.fixerUpper = dataFixer;
5675

5776

5877
this.chunkExecutor = new ProcessorMailbox<>(new StrictQueue.FixedPriorityQueue(Priority.values().length), Util.ioPool(), "RegionCubeIO-" + chunkWorkerName);
@@ -118,6 +137,22 @@ public CompletableFuture<Void> saveCubeNBT(CubePos cubePos, CompoundTag cubeNBT)
118137
return Either.left(null);
119138
}
120139

140+
boolean isOldData = false;
141+
try (GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(buf.get().array()))) {
142+
// NBT can't begin with byte 255, so this acts as a marker for
143+
// 1.12.2 "proto-big-cube" converted data
144+
if (in.read() == 255) {
145+
// a second byte with value 0 for potential future extension
146+
if (in.read() != 0) {
147+
throw new RuntimeException("Invalid data, expected 0");
148+
}
149+
isOldData = true;
150+
}
151+
}
152+
if (isOldData) {
153+
return Either.left(loadOldNbt(cubePos, buf.get().array()));
154+
}
155+
121156
CompoundTag compoundnbt = NbtIo.readCompressed(new ByteArrayInputStream(buf.get().array()));
122157
return Either.left(compoundnbt);
123158
} catch (Exception exception) {
@@ -138,6 +173,121 @@ public CompletableFuture<Void> saveCubeNBT(CubePos cubePos, CompoundTag cubeNBT)
138173
}
139174
}
140175

176+
@Nullable
177+
private CompoundTag loadOldNbt(CubePos pos, byte[] data) throws IOException {
178+
ByteArrayInputStream in = new ByteArrayInputStream(data);
179+
CompoundTag[] cubeTags = new CompoundTag[8];
180+
try (InputStream gzip = new BufferedInputStream(new GZIPInputStream(in))) {
181+
// skip byte 255 and byte 0
182+
gzip.read();
183+
gzip.read();
184+
for (int i = 0; i < 8; i++) {
185+
cubeTags[i] = NbtIo.read(new DataInputStream(gzip), NbtAccounter.UNLIMITED);
186+
}
187+
}
188+
189+
return mergeOldNbt(pos, cubeTags);
190+
}
191+
192+
@Nullable
193+
private CompoundTag mergeOldNbt(CubePos pos, CompoundTag[] cubeTags) {
194+
CompoundTag outTag = new CompoundTag();
195+
for (int i = 0; i < cubeTags.length; i++) {
196+
CompoundTag level = cubeTags[i].getCompound("Level");
197+
level.put("TerrainPopulated", level.get("populated"));
198+
level.put("LightPopulated", level.get("initLightDone"));
199+
ListTag tileTicks = level.getList("TileTicks", CompoundTag.TAG_COMPOUND);
200+
201+
// prepare tile ticks to that high bits of "y" coordinate are actually cube section index
202+
for (Tag tileTick : tileTicks) {
203+
CompoundTag tick = (CompoundTag) tileTick;
204+
int x = tick.getInt("x");
205+
int y = tick.getInt("y");
206+
int z = tick.getInt("z");
207+
int idx = Coords.blockToIndex(x, y, z);
208+
tick.putInt("y", y & 0xF | idx << 4);
209+
}
210+
211+
int version = ChunkStorage.getVersion(cubeTags[i]);
212+
213+
cubeTags[i] = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, cubeTags[i], version, 1493);
214+
//if (nbt.getCompound("Level").getBoolean("hasLegacyStructureData")) {
215+
// if (this.legacyStructureHandler == null) {
216+
// this.legacyStructureHandler = LegacyStructureDataHandler.getLegacyStructureHandler(worldKey, (DimensionDataStorage)persistentStateManagerFactory.get());
217+
// }
218+
// nbt = this.legacyStructureHandler.updateFromLegacy(nbt);
219+
//}
220+
cubeTags[i] = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, cubeTags[i], Math.max(1493, version));
221+
if (cubeTags[i] == null) {
222+
LOGGER.warn("Dropping incomplete cube at " + pos);
223+
return null;
224+
}
225+
}
226+
CompoundTag levelOut = new CompoundTag();
227+
levelOut.putInt("xPos", pos.getX());
228+
levelOut.putInt("yPos", pos.getY());
229+
levelOut.putInt("zPos", pos.getZ());
230+
231+
outTag.put("ToBeTicked", new ListTag());
232+
outTag.put("Level", levelOut);
233+
// TODO: biomes
234+
outTag.putIntArray("Biomes", new int[8 * 8 * 8]);
235+
for (int i = 0; i < cubeTags.length; i++) {
236+
CompoundTag cube = cubeTags[i];
237+
CompoundTag level = cube.getCompound("Level");
238+
ListTag toBeTicked = cube.getList("ToBeTicked", Tag.TAG_LIST);
239+
ListTag outToBeTicked = outTag.getList("ToBeTicked", Tag.TAG_LIST);
240+
for (int i1 = 0; i1 < toBeTicked.size(); i1++) {
241+
ListTag toTickEntry = toBeTicked.getList(i1);
242+
ListTag toTickEntryOut = outToBeTicked.getList(i1);
243+
toTickEntryOut.addAll(toTickEntry);
244+
outToBeTicked.set(i1, toTickEntryOut);
245+
}
246+
247+
level.putLong("LastUpdate", Math.max(level.getLong("LastUpdate"), levelOut.getLong("LastUpdate")));
248+
level.putLong("InhabitedTime", Math.max(level.getLong("InhabitedTime"), levelOut.getLong("InhabitedTime")));
249+
250+
ChunkStatus status = ChunkStatus.byName(level.getString("Status"));
251+
ChunkStatus oldStatus = levelOut.contains("Status") ? ChunkStatus.byName(level.getString("Status")) : null;
252+
ChunkStatus newStatus = oldStatus == null ? status : oldStatus.isOrAfter(ChunkStatus.SPAWN) ? status : ChunkStatus.EMPTY;
253+
levelOut.putString("Status", newStatus.getName());
254+
255+
ListTag sections = levelOut.getList("Sections", Tag.TAG_COMPOUND);
256+
levelOut.put("Sections", sections);
257+
CompoundTag section = level.getList("Sections", Tag.TAG_COMPOUND).getCompound(0);
258+
section.putShort("i", (short) i);
259+
sections.add(section);
260+
261+
levelOut.putBoolean("isLightOn", true);
262+
263+
ListTag tileEntities = levelOut.getList("TileEntities", Tag.TAG_COMPOUND);
264+
levelOut.put("TileEntities", tileEntities);
265+
ListTag tileEntitiesOld = level.getList("TileEntities", Tag.TAG_COMPOUND);
266+
tileEntities.addAll(tileEntitiesOld);
267+
268+
ListTag entities = levelOut.getList("Entities", Tag.TAG_COMPOUND);
269+
levelOut.put("Entities", entities);
270+
ListTag entitiesOld = level.getList("Entities", Tag.TAG_COMPOUND);
271+
entities.addAll(entitiesOld);
272+
273+
}
274+
return outTag;
275+
}
276+
277+
public CompoundTag upgradeChunkTag(ResourceKey<Level> worldKey, Supplier<DimensionDataStorage> persistentStateManagerFactory, CompoundTag nbt) {
278+
int i = ChunkStorage.getVersion(nbt);
279+
if (i < 1493) {
280+
throw new IllegalArgumentException("Pre-1.17 version handled elsewhere, but trying " + i);
281+
}
282+
283+
nbt = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, nbt, Math.max(1493, i));
284+
if (i < SharedConstants.getCurrentVersion().getWorldVersion()) {
285+
nbt.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
286+
}
287+
288+
return nbt;
289+
}
290+
141291
public CompletableFuture<Void> saveChunkNBT(ChunkPos chunkPos, CompoundTag cubeNBT) {
142292
return this.submitChunkTask(() -> {
143293
SaveEntry entry = this.pendingChunkWrites.computeIfAbsent(chunkPos, (pos) -> new SaveEntry(cubeNBT));

0 commit comments

Comments
 (0)