mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-20 23:21:24 +01:00
Load level.dat
nbt on instance init
This commit is contained in:
parent
0c52c9eb30
commit
cb7bccf26c
@ -5,15 +5,14 @@ import net.minestom.server.exception.ExceptionManager;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockHandler;
|
||||
import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
import net.minestom.server.world.biomes.Biome;
|
||||
import net.minestom.server.world.biomes.BiomeManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.mca.*;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTTypes;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -22,6 +21,7 @@ import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -37,10 +37,12 @@ public class AnvilLoader implements IChunkLoader {
|
||||
|
||||
private final Map<String, RegionFile> alreadyLoaded = new ConcurrentHashMap<>();
|
||||
private final Path path;
|
||||
private final Path levelPath;
|
||||
private final Path regionPath;
|
||||
|
||||
public AnvilLoader(@NotNull Path path) {
|
||||
this.path = path;
|
||||
this.levelPath = path.resolve("level.dat");
|
||||
this.regionPath = path.resolve("region");
|
||||
}
|
||||
|
||||
@ -48,6 +50,20 @@ public class AnvilLoader implements IChunkLoader {
|
||||
this(Path.of(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadInstance(@NotNull Instance instance) {
|
||||
if (!Files.exists(levelPath)) {
|
||||
return;
|
||||
}
|
||||
try (var reader = new NBTReader(Files.newInputStream(levelPath))) {
|
||||
final NBTCompound tag = (NBTCompound) reader.read();
|
||||
Files.copy(levelPath, path.resolve("level.dat_old"), StandardCopyOption.REPLACE_EXISTING);
|
||||
instance.setTag(Tag.NBT, tag);
|
||||
} catch (IOException | NBTException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||
LOGGER.debug("Attempt loading at {} {}", chunkX, chunkZ);
|
||||
@ -116,17 +132,16 @@ public class AnvilLoader implements IChunkLoader {
|
||||
|
||||
private void loadBlocks(Chunk chunk, ChunkColumn fileChunk) {
|
||||
for (var section : fileChunk.getSections()) {
|
||||
if (section.getEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (section.getEmpty()) continue;
|
||||
final int yOffset = Chunk.CHUNK_SECTION_SIZE * section.getY();
|
||||
for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) {
|
||||
for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
|
||||
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
||||
try {
|
||||
final BlockState blockState = section.get(x, y, z);
|
||||
chunk.setBlock(x, y + yOffset, z,
|
||||
Block.fromNamespaceId(blockState.getName()).withProperties(blockState.getProperties()));
|
||||
final Block block = Objects.requireNonNull(Block.fromNamespaceId(blockState.getName()))
|
||||
.withProperties(blockState.getProperties());
|
||||
chunk.setBlock(x, y + yOffset, z, block);
|
||||
} catch (Exception e) {
|
||||
EXCEPTION_MANAGER.handleException(e);
|
||||
}
|
||||
@ -167,8 +182,20 @@ public class AnvilLoader implements IChunkLoader {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: find a way to unload MCAFiles when an entire region is unloaded
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveInstance(@NotNull Instance instance) {
|
||||
final var nbt = instance.getTag(Tag.NBT);
|
||||
if (nbt == null) {
|
||||
// Instance has no data
|
||||
return AsyncUtils.VOID_FUTURE;
|
||||
}
|
||||
try (NBTWriter writer = new NBTWriter(Files.newOutputStream(levelPath))) {
|
||||
writer.writeNamed("", nbt);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return AsyncUtils.VOID_FUTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveChunk(@NotNull Chunk chunk) {
|
||||
|
@ -18,6 +18,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*/
|
||||
public interface IChunkLoader {
|
||||
|
||||
/**
|
||||
* Loads instance data from the loader.
|
||||
*
|
||||
* @param instance the instance to retrieve the data from
|
||||
*/
|
||||
default void loadInstance(@NotNull Instance instance) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@link Chunk}, all blocks should be set since the {@link ChunkGenerator} is not applied.
|
||||
*
|
||||
@ -28,6 +36,10 @@ public interface IChunkLoader {
|
||||
*/
|
||||
@NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ);
|
||||
|
||||
default @NotNull CompletableFuture<Void> saveInstance(@NotNull Instance instance) {
|
||||
return AsyncUtils.VOID_FUTURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a {@link Chunk} with an optional callback for when it is done.
|
||||
*
|
||||
|
@ -227,6 +227,17 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta
|
||||
*/
|
||||
public abstract @Nullable Chunk getChunk(int chunkX, int chunkZ);
|
||||
|
||||
/**
|
||||
* Saves the current instance tags.
|
||||
* <p>
|
||||
* Warning: only the global instance data will be saved, not chunks.
|
||||
* You would need to call {@link #saveChunksToStorage()} too.
|
||||
*
|
||||
* @return the future called once the instance data has been saved
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public abstract @NotNull CompletableFuture<Void> saveInstance();
|
||||
|
||||
/**
|
||||
* Saves a {@link Chunk} to permanent storage.
|
||||
*
|
||||
|
@ -41,9 +41,6 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public class InstanceContainer extends Instance {
|
||||
|
||||
private static final String UUID_KEY = "uuid";
|
||||
private static final String DATA_KEY = "data";
|
||||
|
||||
// the shared instances assigned to this instance
|
||||
private final List<SharedInstance> sharedInstances = new CopyOnWriteArrayList<>();
|
||||
|
||||
@ -85,6 +82,7 @@ public class InstanceContainer extends Instance {
|
||||
|
||||
// Set the default chunk loader which use the Anvil format
|
||||
setChunkLoader(new AnvilLoader("world"));
|
||||
this.chunkLoader.loadInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -258,6 +256,11 @@ public class InstanceContainer extends Instance {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveInstance() {
|
||||
return chunkLoader.saveInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveChunkToStorage(@NotNull Chunk chunk) {
|
||||
return chunkLoader.saveChunk(chunk);
|
||||
|
@ -15,7 +15,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
* entities are separated.
|
||||
*/
|
||||
public class SharedInstance extends Instance {
|
||||
|
||||
private final InstanceContainer instanceContainer;
|
||||
|
||||
public SharedInstance(@NotNull UUID uniqueId, @NotNull InstanceContainer instanceContainer) {
|
||||
@ -59,6 +58,11 @@ public class SharedInstance extends Instance {
|
||||
return instanceContainer.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveInstance() {
|
||||
return instanceContainer.saveInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveChunkToStorage(@NotNull Chunk chunk) {
|
||||
return instanceContainer.saveChunkToStorage(chunk);
|
||||
|
Loading…
Reference in New Issue
Block a user