Skip to content

Commit b6e812c

Browse files
committed
Replace all world loading with worldformat
1 parent 485d772 commit b6e812c

9 files changed

Lines changed: 161 additions & 101 deletions

File tree

chunky/src/java/se/llbit/chunky/map/WorldMapLoader.java

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package se.llbit.chunky.map;
1818

19+
import java.util.Optional;
1920
import java.util.function.BiConsumer;
2021
import se.llbit.chunky.PersistentSettings;
2122
import se.llbit.chunky.renderer.ChunkViewListener;
@@ -25,6 +26,8 @@
2526
import se.llbit.chunky.world.region.RegionParser;
2627
import se.llbit.chunky.world.region.RegionQueue;
2728
import se.llbit.chunky.world.listeners.ChunkTopographyListener;
29+
import se.llbit.chunky.world.worldformat.WorldFormat;
30+
import se.llbit.log.Log;
2831

2932
import java.io.File;
3033
import java.util.ArrayList;
@@ -64,28 +67,44 @@ public WorldMapLoader(ChunkyFxController controller, MapView mapView) {
6467
topographyUpdater.start();
6568
}
6669

70+
public void loadWorldFromDirectory(File worldLocation) {
71+
if (worldLocation == null) {
72+
return;
73+
}
74+
this.loadWorld(WorldFormat.loadWorld(worldLocation).orElse(EmptyWorld.INSTANCE));
75+
}
6776
/**
6877
* This is called when a new world is loaded
6978
*/
70-
public void loadWorld(File worldDir) {
71-
if (JavaWorld.isWorldDir(worldDir)) {
72-
if (world != null) {
73-
world.currentDimension().removeChunkTopographyListener(this);
74-
}
75-
boolean isSameWorld = !(world instanceof EmptyWorld) && worldDir.equals(world.getWorldDirectory());
76-
World newWorld = World.loadWorld(worldDir, currentDimensionId, World.LoggedWarnings.NORMAL);
77-
newWorld.currentDimension().addChunkTopographyListener(this);
78-
synchronized (this) {
79-
world = newWorld;
80-
updateRegionChangeWatcher(newWorld.currentDimension());
81-
82-
File newWorldDir = world.getWorldDirectory();
83-
if (newWorldDir != null && !newWorldDir.equals(PersistentSettings.getLastWorld())) {
84-
PersistentSettings.setLastWorld(newWorldDir);
85-
}
79+
public void loadWorld(World newWorld) {
80+
if (this.world != null) {
81+
this.world.currentDimension().removeChunkTopographyListener(this);
82+
}
83+
boolean isSameWorld = !(this.world instanceof EmptyWorld) && newWorld.getWorldDirectory().equals(this.world.getWorldDirectory());
84+
85+
Optional<String> dimensionToLoad = Optional.of(world.currentDimension())
86+
.map(Dimension::id)
87+
.filter(dimension -> newWorld.availableDimensions().contains(dimension))
88+
.or(newWorld::defaultDimension)
89+
.or(() -> newWorld.availableDimensions().stream().findFirst());
90+
91+
if (dimensionToLoad.isEmpty()) {
92+
Log.infof("No dimension loaded for world %s", newWorld.toString());
93+
return;
94+
}
95+
96+
Dimension loadedDim = newWorld.loadDimension(dimensionToLoad.get());
97+
loadedDim.addChunkTopographyListener(this);
98+
synchronized (this) {
99+
this.world = newWorld;
100+
updateRegionChangeWatcher(loadedDim);
101+
102+
File newWorldDir = this.world.getWorldDirectory();
103+
if (!newWorldDir.equals(PersistentSettings.getLastWorld())) {
104+
PersistentSettings.setLastWorld(newWorldDir);
86105
}
87-
worldLoadListeners.forEach(listener -> listener.accept(newWorld, isSameWorld));
88106
}
107+
worldLoadListeners.forEach(listener -> listener.accept(newWorld, isSameWorld));
89108
}
90109

91110
/**
@@ -151,14 +170,12 @@ public void regionUpdated(RegionPosition region) {
151170
public void reloadWorld() {
152171
topographyUpdater.clearQueue();
153172
world.currentDimension().removeChunkTopographyListener(this);
154-
World newWorld = World.loadWorld(world.getWorldDirectory(), currentDimensionId,
155-
World.LoggedWarnings.NORMAL);
156-
newWorld.currentDimension().addChunkTopographyListener(this);
173+
world.loadDimension(currentDimensionId);
174+
world.currentDimension().addChunkTopographyListener(this);
157175
synchronized (this) {
158-
world = newWorld;
159-
updateRegionChangeWatcher(newWorld.currentDimension());
176+
updateRegionChangeWatcher(world.currentDimension());
160177
}
161-
worldLoadListeners.forEach(listener -> listener.accept(newWorld, true));
178+
worldLoadListeners.forEach(listener -> listener.accept(world, true));
162179
viewUpdated(mapView.getMapView()); // update visible chunks immediately
163180
}
164181

@@ -174,7 +191,7 @@ private void updateRegionChangeWatcher(Dimension dimension) {
174191
/**
175192
* Set the current dimension.
176193
*
177-
* @param value Must be a valid dimension see {@link World#listDimensions()}
194+
* @param value Must be a valid dimension see {@link World#availableDimensions()}
178195
*/
179196
// TODO: change this to show the available dimensions in the UI.
180197
public void setDimension(String value) {

chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import se.llbit.chunky.world.biome.Biome;
5454
import se.llbit.chunky.world.biome.BiomePalette;
5555
import se.llbit.chunky.world.biome.Biomes;
56+
import se.llbit.chunky.world.worldformat.WorldFormat;
5657
import se.llbit.json.*;
5758
import se.llbit.log.Log;
5859
import se.llbit.math.*;
@@ -543,11 +544,8 @@ public synchronized void loadScene(RenderContext context, String sceneName, Task
543544
loadedWorld = EmptyWorld.INSTANCE;
544545
if (!worldPath.isEmpty()) {
545546
File worldDirectory = new File(worldPath);
546-
if (JavaWorld.isWorldDir(worldDirectory)) {
547-
loadedWorld = World.loadWorld(worldDirectory, worldDimension, World.LoggedWarnings.NORMAL);
548-
} else {
549-
Log.info("Could not load world: " + worldPath);
550-
}
547+
loadedWorld = WorldFormat.loadWorld(worldDirectory).orElse(EmptyWorld.INSTANCE);
548+
loadedWorld.loadDimension(this.worldDimension);
551549
}
552550

553551
loadDump(context, taskTracker);

chunky/src/java/se/llbit/chunky/ui/controller/WorldChooserController.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import se.llbit.log.Log;
4141

4242
import java.io.File;
43-
import java.io.IOException;
4443
import java.net.URL;
4544
import java.text.DateFormat;
4645
import java.util.*;
@@ -135,7 +134,7 @@ public void populate(WorldMapLoader mapLoader) {
135134
File directory = chooser.showDialog(stage);
136135
if (directory != null) {
137136
if (directory.isDirectory()) {
138-
this.loadWorld(World.loadWorld(directory, mapLoader.getDimension(), World.LoggedWarnings.NORMAL), mapLoader);
137+
this.loadWorld(WorldFormat.loadWorld(directory).orElse(EmptyWorld.INSTANCE), mapLoader);
139138
stage.close();
140139
} else {
141140
Log.warn("Non-directory selected.");
@@ -171,7 +170,7 @@ private void loadWorld(World world, WorldMapLoader mapLoader) {
171170
}
172171
}
173172
});
174-
mapLoader.loadWorld(world.getWorldDirectory());
173+
mapLoader.loadWorld(world);
175174
}
176175

177176
/**
@@ -196,18 +195,7 @@ protected List<World> call() {
196195
File[] worldDirs = worldSavesDir.listFiles();
197196
if (worldDirs != null) {
198197
for (File dir : worldDirs) {
199-
for (WorldFormat worldFormat : WorldFormat.worldFormats) {
200-
if (worldFormat.isValid(dir.toPath())) {
201-
try {
202-
World world = worldFormat.loadWorld(dir.toPath(), String.valueOf(World.OVERWORLD_DIMENSION));
203-
if (world != EmptyWorld.INSTANCE) {
204-
worlds.add(world);
205-
}
206-
} catch (IOException e) {
207-
throw new RuntimeException(e);
208-
}
209-
}
210-
}
198+
WorldFormat.loadWorld(dir).ifPresent(worlds::add);
211199
}
212200
}
213201
}

chunky/src/java/se/llbit/chunky/world/EmptyWorld.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,15 @@ private EmptyWorld() {
4040
}
4141

4242
@Override
43-
public Set<String> listDimensions() {
43+
public Set<String> availableDimensions() {
4444
return Collections.emptySet();
4545
}
4646

4747
@Override
48+
public Optional<String> defaultDimension() {
49+
return Optional.empty();
50+
}
51+
4852
@Override
4953
public EmptyDimension loadDimension(String dimension) {
5054
return EmptyDimension.INSTANCE;

chunky/src/java/se/llbit/chunky/world/JavaDimension.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import java.io.File;
1212
import java.util.*;
13+
import java.util.stream.Collectors;
1314

1415
public class JavaDimension extends Dimension {
1516
protected final Long2ObjectMap<Region> regionMap = new Long2ObjectOpenHashMap<>();
@@ -29,7 +30,13 @@ protected JavaDimension(JavaWorld world, String dimensionId, File dimensionDirec
2930
* @return {@code true} if player data was reloaded.
3031
*/
3132
public synchronized boolean reloadPlayerData() {
32-
return ((JavaWorld) this.world).reloadPlayerData();
33+
boolean changed = ((JavaWorld) this.world).reloadPlayerData();
34+
if (changed) {
35+
this.setPlayerEntities(((JavaWorld) this.world).playerEntities.stream()
36+
.filter(player -> player.dimension.equals(this.id()))
37+
.collect(Collectors.toSet()));
38+
}
39+
return changed;
3340
}
3441

3542
/**

chunky/src/java/se/llbit/chunky/world/JavaWorld.java

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import se.llbit.chunky.ui.ProgressTracker;
2323
import se.llbit.chunky.world.region.MCRegion;
2424
import se.llbit.log.Log;
25+
import se.llbit.math.Vector3i;
2526
import se.llbit.nbt.NamedTag;
2627
import se.llbit.nbt.Tag;
2728
import se.llbit.util.MinecraftText;
@@ -70,26 +71,48 @@ public class JavaWorld extends World {
7071
new String[] { NETHER_DIMENSION_ID, OVERWORLD_DIMENSION_ID, END_DIMENSION_ID }
7172
));
7273

73-
int versionId;
74+
protected int versionId;
75+
76+
/**
77+
* In a java world player data is per-world and not per-dimension, so we store it here.
78+
*/
79+
protected final Set<PlayerEntityData> playerEntities;
80+
81+
/**
82+
* In a java world spawn position is per-world and not per-dimension, so we store it here.
83+
*/
84+
protected final Vector3i spawnPos;
7485

7586
/**
7687
* @param levelName name of the world (not the world directory).
7788
* @param worldDirectory Minecraft world directory.
7889
* @param seed
7990
* @param timestamp
8091
*/
81-
protected JavaWorld(String levelName, File worldDirectory, long seed, long timestamp) {
92+
protected JavaWorld(String levelName, File worldDirectory, long seed, long timestamp, Set<PlayerEntityData> playerEntities, Vector3i spawnPos) {
8293
super(levelName, worldDirectory, seed, timestamp);
94+
this.playerEntities = playerEntities;
95+
this.spawnPos = spawnPos;
8396
}
8497

8598
@Override
86-
public Set<String> listDimensions() {
99+
public Set<String> availableDimensions() {
87100
return new ObjectArraySet<>(VANILLA_DIMENSION_ID_TO_IDX.keySet());
88101
}
89102

103+
@Override
104+
public Optional<String> defaultDimension() {
105+
return Optional.of(OVERWORLD_DIMENSION_ID);
106+
}
107+
90108
public Dimension loadDimension(String dimension) {
91-
currentDimension = loadDimension(this, this.worldDirectory, dimension, -1, Collections.emptySet());
92-
currentDimension.reloadPlayerData();
109+
currentDimension = loadDimension(
110+
this,
111+
this.worldDirectory,
112+
dimension,
113+
-1,
114+
this.playerEntities.stream().filter(player -> player.dimension.equals(dimension)).collect(Collectors.toSet())
115+
);
93116
return currentDimension;
94117
}
95118

@@ -122,7 +145,7 @@ public static World loadWorld(File worldDirectory, LoggedWarnings warnings) {
122145
}
123146
Tag versionId = result.get(".Data.Version.Id");
124147
Tag player = result.get(".Data.Player");
125-
Tag spawnX = player.get("SpawnX");
148+
Tag spawnX = player.get("SpawnX"); // TODO: not sure what to do with spawn location now. I guess for java worlds: the world should store it and the dimension should set it in the map view when loaded...?
126149
Tag spawnY = player.get("SpawnY");
127150
Tag spawnZ = player.get("SpawnZ");
128151
Tag gameType = result.get(".Data.GameType");
@@ -131,20 +154,19 @@ public static World loadWorld(File worldDirectory, LoggedWarnings warnings) {
131154

132155
long seed = randomSeed.longValue(0);
133156

134-
Set<PlayerEntityData> playerEntities = getPlayerEntityData(worldDirectory, dimensionId, player);
135-
136-
JavaWorld world = new JavaWorld(levelName, worldDirectory, seed, modtime);
137-
world.gameMode = gameType.intValue(0);
138-
world.versionId = versionId.intValue();
139-
140-
Dimension dimension = loadDimension(world, worldDirectory, dimensionId, modtime, playerEntities);
157+
Set<PlayerEntityData> playerEntities = getPlayerEntityData(worldDirectory, player);
141158

142159
boolean haveSpawnPos = !(spawnX.isError() || spawnY.isError() || spawnZ.isError());
160+
Vector3i spawnPos;
143161
if (haveSpawnPos) {
144-
dimension.setSpawnPos(new Vector3i(spawnX.intValue(0), spawnY.intValue(0), spawnZ.intValue(0)));
162+
spawnPos = new Vector3i(spawnX.intValue(0), spawnY.intValue(0), spawnZ.intValue(0));
163+
} else {
164+
spawnPos = new Vector3i(0, 0, 0);
145165
}
146166

147-
world.currentDimension = dimension;
167+
JavaWorld world = new JavaWorld(levelName, worldDirectory, seed, modtime, playerEntities, spawnPos);
168+
world.gameMode = gameType.intValue(0);
169+
world.versionId = versionId.intValue();
148170

149171
return world;
150172
} catch (FileNotFoundException e) {
@@ -172,14 +194,12 @@ protected static JavaDimension loadDimension(JavaWorld world, File worldDirector
172194
}
173195

174196
@NotNull
175-
static Set<PlayerEntityData> getPlayerEntityData(File worldDirectory, String dimensionId, Tag player) {
197+
static Set<PlayerEntityData> getPlayerEntityData(File worldDirectory, Tag player) {
176198
Set<PlayerEntityData> playerEntities = new HashSet<>();
177199
if (!player.isError()) {
178200
playerEntities.add(new PlayerEntityData(player));
179201
}
180202
loadAdditionalPlayers(worldDirectory, playerEntities);
181-
// Filter for the players only within the requested dimension
182-
playerEntities = playerEntities.stream().filter(playerData -> playerData.dimension.equals(dimensionId)).collect(Collectors.toSet());
183203
return playerEntities;
184204
}
185205

@@ -206,8 +226,8 @@ synchronized boolean reloadPlayerData() {
206226
request.add(".Data.Player");
207227
Map<String, Tag> result = NamedTag.quickParse(in, request);
208228
Tag player = result.get(".Data.Player");
209-
210-
currentDimension.setPlayerEntities(getPlayerEntityData(worldDirectory, currentDimension.id(), player));
229+
this.playerEntities.clear();
230+
this.playerEntities.addAll(getPlayerEntityData(worldDirectory, player));
211231
} catch (IOException e) {
212232
Log.infof("Could not read the level.dat file for world %s while trying to reload player data!", levelName);
213233
return false;
@@ -236,25 +256,12 @@ private static void loadPlayerData(File playerdata, Set<PlayerEntityData> player
236256
}
237257
}
238258

239-
@Override
240-
public synchronized JavaDimension currentDimension() {
241-
return (JavaDimension) this.currentDimension;
242-
}
243-
244-
/**
245-
* @deprecated Use {@link JavaWorld#currentDimension()} -> {@link Dimension#getDimensionDirectory()} ()}. Removed once there are no more usages
246-
*/
247-
@Deprecated
248259
protected synchronized File getDataDirectory(int dimension) {
249260
return dimension == 0 ?
250261
worldDirectory :
251262
new File(worldDirectory, "DIM" + dimension);
252263
}
253264

254-
/**
255-
@deprecated Use {@link JavaWorld#currentDimension()} -> {@link JavaDimension#getRegionDirectory()}. Removed once there are no more usages
256-
*/
257-
@Deprecated
258265
protected synchronized File getRegionDirectory(int dimension) {
259266
return new File(getDataDirectory(dimension), "region");
260267
}
@@ -399,7 +406,4 @@ public static boolean isWorldDir(File worldDir) {
399406
return false;
400407
}
401408

402-
public Date getLastModified() {
403-
return new Date(this.worldDirectory.lastModified());
404-
}
405409
}

0 commit comments

Comments
 (0)