Skip to content
This repository was archived by the owner on May 27, 2023. It is now read-only.

Commit 9acca85

Browse files
KrystilizeNevaDiesLooFifteen
authored andcommitted
Implement SchematicChunkLoader
1 parent cce96ec commit 9acca85

File tree

5 files changed

+203
-1
lines changed

5 files changed

+203
-1
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package net.crystalgames.scaffolding.instance;
2+
3+
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
4+
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
5+
import net.crystalgames.scaffolding.schematic.Schematic;
6+
import net.minestom.server.instance.Chunk;
7+
import net.minestom.server.instance.DynamicChunk;
8+
import net.minestom.server.instance.IChunkLoader;
9+
import net.minestom.server.instance.Instance;
10+
import net.minestom.server.instance.batch.ChunkBatch;
11+
import net.minestom.server.instance.block.Block;
12+
import net.minestom.server.utils.chunk.ChunkUtils;
13+
import org.jetbrains.annotations.NotNull;
14+
import org.jetbrains.annotations.Nullable;
15+
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
import java.util.concurrent.CompletableFuture;
19+
import java.util.function.Function;
20+
21+
// TODO: Entities?
22+
@SuppressWarnings("UnstableApiUsage")
23+
public class SchematicChunkLoader implements IChunkLoader {
24+
25+
private final @NotNull Function<@NotNull Chunk, @NotNull CompletableFuture<Void>> saveHandler;
26+
private final Long2ObjectMap<ChunkBatch> batches = new Long2ObjectOpenHashMap<>();
27+
28+
private SchematicChunkLoader(
29+
@NotNull Function<@NotNull Chunk, @NotNull CompletableFuture<Void>> saveHandler,
30+
@NotNull List<Schematic> schematics,
31+
int offsetX,
32+
int offsetY,
33+
int offsetZ
34+
) {
35+
this.saveHandler = saveHandler;
36+
37+
// The block setter used for Schematic#apply
38+
Block.Setter setter = (x, y, z, block) -> {
39+
int chunkX = ChunkUtils.getChunkCoordinate(x);
40+
int chunkZ = ChunkUtils.getChunkCoordinate(z);
41+
long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
42+
43+
// Get the batch, create it if it doesn't exist
44+
ChunkBatch batch = batches.computeIfAbsent(index, key -> new ChunkBatch());
45+
46+
// Add the block to the batch
47+
batch.setBlock(x + offsetX, y + offsetY, z + offsetZ, block);
48+
};
49+
50+
// Apply the schematics
51+
for (Schematic schematic : schematics) {
52+
schematic.apply(setter);
53+
}
54+
}
55+
56+
/**
57+
* Creates a builder for a {@link SchematicChunkLoader}.
58+
* @return The builder.
59+
*/
60+
public static @NotNull Builder builder() {
61+
return new Builder();
62+
}
63+
64+
@Override
65+
public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
66+
long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
67+
ChunkBatch batch = batches.get(index);
68+
69+
if (batch == null) {
70+
return CompletableFuture.completedFuture(null);
71+
}
72+
73+
DynamicChunk chunk = new DynamicChunk(instance, chunkX, chunkZ);
74+
CompletableFuture<Chunk> future = new CompletableFuture<>();
75+
batch.apply(instance, chunk, future::complete);
76+
77+
return future;
78+
}
79+
80+
@Override
81+
public @NotNull CompletableFuture<Void> saveChunk(@NotNull Chunk chunk) {
82+
return saveHandler.apply(chunk);
83+
}
84+
85+
public static class Builder {
86+
87+
private final List<Schematic> schematics = new ArrayList<>();
88+
private @NotNull Function<@NotNull Chunk, @NotNull CompletableFuture<Void>> handler = chunk ->
89+
CompletableFuture.completedFuture(null);
90+
private int xOffset;
91+
private int yOffset;
92+
private int zOffset;
93+
94+
private Builder() {
95+
}
96+
97+
/**
98+
* Adds a schematic to this chunk loader.
99+
* <br><br>
100+
* Note that schematics are loaded in the order they are added.
101+
* <br>
102+
* This means that the last added schematic is the only schematic that is guaranteed to have all its data.
103+
* @param schematic The schematic to add.
104+
* @return This builder.
105+
*/
106+
// TODO: Add a way to position schematics within the instance.
107+
public @NotNull Builder addSchematic(@NotNull Schematic schematic) {
108+
schematics.add(schematic);
109+
return this;
110+
}
111+
112+
/**
113+
* Specifies the offset that applies to all schematics added to this chunk loader.
114+
* @param x The x offset.
115+
* @param y The y offset.
116+
* @param z The z offset.
117+
* @return This builder.
118+
*/
119+
public @NotNull Builder offset(int x, int y, int z) {
120+
this.xOffset = x;
121+
this.yOffset = y;
122+
this.zOffset = z;
123+
return this;
124+
}
125+
126+
/**
127+
* Specifies the handler to use to save the chunks.
128+
* @param handler The handler.
129+
* @return This builder.
130+
*/
131+
public @NotNull Builder saveChunkHandler(@NotNull Function<@NotNull Chunk, @NotNull CompletableFuture<Void>> handler) {
132+
this.handler = handler;
133+
return this;
134+
}
135+
136+
public @NotNull SchematicChunkLoader build() {
137+
return new SchematicChunkLoader(handler, List.copyOf(schematics), xOffset, yOffset, zOffset);
138+
}
139+
}
140+
141+
}

src/main/java/net/crystalgames/scaffolding/schematic/Schematic.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import net.crystalgames.scaffolding.region.Region;
44
import net.minestom.server.coordinate.Pos;
55
import net.minestom.server.instance.Instance;
6+
import net.minestom.server.instance.block.Block;
7+
import org.jetbrains.annotations.NotNull;
68
import org.jglrxavpok.hephaistos.nbt.CompressedProcesser;
79
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
810
import org.jglrxavpok.hephaistos.nbt.NBTException;
@@ -34,4 +36,9 @@ default void read(InputStream inputStream) throws IOException, NBTException {
3436
int getOffsetY();
3537
int getOffsetZ();
3638

39+
/**
40+
* Applies the schematic to the given block setter.
41+
* @param setter the block setter
42+
*/
43+
void apply(@NotNull Block.Setter setter);
3744
}

src/main/java/net/crystalgames/scaffolding/schematic/impl/MCEditSchematic.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,16 @@ public int getOffsetZ() {
169169
return offsetZ;
170170
}
171171

172+
@Override
173+
public void apply(Block.@NotNull Setter setter) {
174+
for (Region.Block block : regionBlocks) {
175+
Pos pos = block.position();
176+
Block minestomBlock = Block.fromStateId(block.stateId());
177+
if (minestomBlock != null) {
178+
setter.setBlock(pos, minestomBlock);
179+
} else {
180+
throw new IllegalStateException("Invalid block state id: " + block.stateId());
181+
}
182+
}
183+
}
172184
}

src/main/java/net/crystalgames/scaffolding/schematic/impl/SpongeSchematic.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,17 @@ public int getOffsetZ() {
216216
return offsetZ;
217217
}
218218

219+
@Override
220+
public void apply(Block.@NotNull Setter setter) {
221+
for (Region.Block block : regionBlocks) {
222+
Pos pos = block.position();
223+
Block minestomBlock = Block.fromStateId(block.stateId());
224+
if (minestomBlock != null) {
225+
setter.setBlock(pos, minestomBlock);
226+
} else {
227+
throw new IllegalStateException("Invalid block state id: " + block.stateId());
228+
}
229+
}
230+
}
231+
219232
}

src/test/java/dev/sllcoding/scaffolding/test/Server.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,53 @@
22

33
import dev.sllcoding.scaffolding.test.commands.TestCommand;
44
import dev.sllcoding.scaffolding.test.generator.Generator;
5+
import net.crystalgames.scaffolding.Scaffolding;
6+
import net.crystalgames.scaffolding.instance.SchematicChunkLoader;
7+
import net.crystalgames.scaffolding.schematic.Schematic;
58
import net.minestom.server.MinecraftServer;
69
import net.minestom.server.coordinate.Pos;
10+
import net.minestom.server.entity.GameMode;
711
import net.minestom.server.event.player.PlayerLoginEvent;
12+
import net.minestom.server.event.player.PlayerSpawnEvent;
813
import net.minestom.server.instance.InstanceContainer;
14+
import net.minestom.server.utils.NamespaceID;
15+
import net.minestom.server.world.DimensionType;
16+
import org.jglrxavpok.hephaistos.nbt.NBTException;
17+
18+
import java.io.File;
19+
import java.io.IOException;
920

1021
public class Server {
1122

23+
private static final DimensionType FULLBRIGHT_DIMENSTION = DimensionType.builder(NamespaceID.from("scaffolding:fullbright"))
24+
.ambientLight(2.0f)
25+
.build();
26+
1227
public static void main(String[] args) {
1328
MinecraftServer server = MinecraftServer.init();
1429

15-
InstanceContainer instance = MinecraftServer.getInstanceManager().createInstanceContainer();
30+
MinecraftServer.getDimensionTypeManager().addDimension(FULLBRIGHT_DIMENSTION);
31+
InstanceContainer instance = MinecraftServer.getInstanceManager().createInstanceContainer(FULLBRIGHT_DIMENSTION);
1632
instance.setChunkGenerator(new Generator());
33+
// Load schematic for schematic chunk loader
34+
try {
35+
Schematic schematic = Scaffolding.fromFile(new File("schematic.schematic"));
36+
SchematicChunkLoader chunkLoader = SchematicChunkLoader.builder()
37+
.addSchematic(schematic)
38+
.build();
39+
instance.setChunkLoader(chunkLoader);
40+
} catch (IOException | NBTException e) {
41+
e.printStackTrace();
42+
}
43+
1744

1845
MinecraftServer.getCommandManager().register(new TestCommand());
1946

2047
MinecraftServer.getGlobalEventHandler().addListener(PlayerLoginEvent.class, event -> {
2148
event.setSpawningInstance(instance);
2249
event.getPlayer().setRespawnPoint(new Pos(0, 42, 0));
50+
}).addListener(PlayerSpawnEvent.class, event -> {
51+
event.getPlayer().setGameMode(GameMode.CREATIVE);
2352
});
2453

2554
server.start("0.0.0.0", 25565);

0 commit comments

Comments
 (0)