Skip to content

Commit 1c469bb

Browse files
authored
fix(world): make new world generation deterministic (#647)
This change updates WorldConfig to contain the system generators and feature generators that were used to generate the system. The world configuration was changed to be held centrally in SolGame and is now used as the authoritive source of truth for world generation. Also updated some occurences of publicly-facing ArrayList<T> methods to the more generic List<T>.
1 parent 100f122 commit 1c469bb

File tree

10 files changed

+158
-60
lines changed

10 files changed

+158
-60
lines changed

engine/src/main/java/org/destinationsol/SolApplication.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ public void play(boolean tut, String shipName, boolean isNewGame, WorldConfig wo
350350
entitySystemManager.initialise();
351351

352352
solGame.createUpdateSystems();
353-
solGame.startGame(shipName, isNewGame, worldConfig, entitySystemManager);
353+
solGame.startGame(shipName, isNewGame, entitySystemManager);
354354

355355
if (!isNewGame) {
356356
try {

engine/src/main/java/org/destinationsol/game/GalaxyFiller.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
import javax.inject.Inject;
4545
import java.util.ArrayList;
46+
import java.util.List;
4647

4748
public class GalaxyFiller {
4849
private static final float STATION_CONSUME_SECTOR = 45f;
@@ -140,7 +141,7 @@ public void fill(SolGame game, HullConfigManager hullConfigManager, ItemManager
140141
return;
141142
}
142143
createStarPorts(game);
143-
ArrayList<SolarSystem> systems = game.getGalaxyBuilder().getBuiltSolarSystems();
144+
List<SolarSystem> systems = game.getGalaxyBuilder().getBuiltSolarSystems();
144145

145146
JSONObject rootNode = Validator.getValidatedJSON(moduleName + ":startingStation", "engine:schemaStartingStation");
146147

engine/src/main/java/org/destinationsol/game/SaveManager.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import com.badlogic.gdx.math.Vector2;
2121
import com.google.gson.Gson;
2222
import com.google.gson.GsonBuilder;
23+
import com.google.gson.JsonArray;
24+
import com.google.gson.JsonElement;
2325
import com.google.gson.JsonObject;
2426
import com.google.gson.JsonParser;
2527
import com.google.gson.stream.JsonReader;
@@ -232,16 +234,29 @@ public static ShipConfig readShip(HullConfigManager hullConfigs, ItemManager ite
232234
}
233235

234236
/**
235-
* Saves the world to a file. Currently stores the seed used to generate the world and the number of systems
236-
* @param numberOfSystems
237+
* Saves the world to a file. Currently stores the seed used to generate the world,
238+
* the number of systems and the generators used.
239+
* @param worldConfig the current world configuration.
237240
*/
238-
public static void saveWorld(int numberOfSystems) {
241+
public static void saveWorld(WorldConfig worldConfig) {
239242
Long seed = SolRandom.getSeed();
240243
String fileName = SaveManager.getResourcePath(Const.WORLD_SAVE_FILE_NAME);
241244

242245
JsonObject world = new JsonObject();
243246
world.addProperty("seed", seed);
244-
world.addProperty("systems", numberOfSystems);
247+
world.addProperty("systems", worldConfig.getNumberOfSystems());
248+
249+
JsonArray solarSystemGenerators = new JsonArray();
250+
for (String solarSystemGenerator : worldConfig.getSolarSystemGenerators()) {
251+
solarSystemGenerators.add(solarSystemGenerator);
252+
}
253+
world.add("solarSystemGenerators", solarSystemGenerators);
254+
255+
JsonArray featureGenerators = new JsonArray();
256+
for (String featureGenerator : worldConfig.getFeatureGenerators()) {
257+
featureGenerators.add(featureGenerator);
258+
}
259+
world.add("featureGenerators", featureGenerators);
245260

246261
Gson gson = new GsonBuilder().setPrettyPrinting().create();
247262
String stringToWrite = gson.toJson(world);
@@ -272,6 +287,26 @@ public static Optional<WorldConfig> loadWorld() {
272287
config.setNumberOfSystems(world.get("systems").getAsInt());
273288
}
274289

290+
if (world.has("solarSystemGenerators")) {
291+
List<String> solarSystemGenerators = new ArrayList<>();
292+
for (JsonElement value : world.getAsJsonArray("solarSystemGenerators")) {
293+
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
294+
solarSystemGenerators.add(value.getAsString());
295+
}
296+
}
297+
config.setSolarSystemGenerators(solarSystemGenerators);
298+
}
299+
300+
if (world.has("featureGenerators")) {
301+
List<String> featureGenerators = new ArrayList<>();
302+
for (JsonElement value : world.getAsJsonArray("featureGenerators")) {
303+
if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
304+
featureGenerators.add(value.getAsString());
305+
}
306+
}
307+
config.setFeatureGenerators(featureGenerators);
308+
}
309+
275310
logger.debug("Successfully loaded the world file");
276311
return Optional.of(config);
277312
} catch (FileNotFoundException e) {

engine/src/main/java/org/destinationsol/game/SolGame.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ public class SolGame {
149149
protected SolCam solCam;
150150
@Inject
151151
protected ModuleManager moduleManager;
152+
@Inject
153+
protected WorldConfig worldConfig;
152154

153155
protected SolApplication solApplication;
154156
private Hero hero;
@@ -236,7 +238,7 @@ public void createUpdateSystems() {
236238
}
237239
}
238240

239-
public void startGame(String shipName, boolean isNewGame, WorldConfig worldConfig, EntitySystemManager entitySystemManager) {
241+
public void startGame(String shipName, boolean isNewGame, EntitySystemManager entitySystemManager) {
240242
this.entitySystemManager = entitySystemManager;
241243

242244
respawnState = new RespawnState();
@@ -331,7 +333,7 @@ public void onGameEnd(Context context) {
331333
if (!hero.isTranscendent()) {
332334
saveShip();
333335
}
334-
SaveManager.saveWorld(getPlanetManager().getSystems().size());
336+
SaveManager.saveWorld(worldConfig);
335337

336338
try {
337339
context.get(SerialisationManager.class).serialise();
@@ -603,6 +605,10 @@ public DrawableManager getDrawableManager() {
603605
return drawableManager;
604606
}
605607

608+
public WorldConfig getWorldConfig() {
609+
return worldConfig;
610+
}
611+
606612
public void setRespawnState() {
607613
respawnState.setRespawnMoney(.75f * hero.getMoney());
608614
if (hero.isNonTranscendent()) {

engine/src/main/java/org/destinationsol/game/StarPort.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public void update(SolGame game) {
125125
ship.setMoney(ship.getMoney() - FARE);
126126
Transcendent transcendent = new Transcendent(ship, fromPlanet, toPlanet, game);
127127
if (transcendent.getShip().getPilot().isPlayer()) {
128-
SaveManager.saveWorld(game.getPlanetManager().getSystems().size());
128+
SaveManager.saveWorld(game.getWorldConfig());
129129
game.getHero().setTranscendent(transcendent);
130130
}
131131
ObjectManager objectManager = game.getObjectManager();
@@ -404,7 +404,7 @@ public void update(SolGame game) {
404404
SolShip ship = this.ship.toObject(game);
405405
if (ship.getPilot().isPlayer()) {
406406
game.getHero().setSolShip(ship, game);
407-
SaveManager.saveWorld(game.getPlanetManager().getSystems().size());
407+
SaveManager.saveWorld(game.getWorldConfig());
408408
}
409409
objectManager.addObjDelayed(ship);
410410
blip(game, ship);

engine/src/main/java/org/destinationsol/game/WorldConfig.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,29 @@
1717

1818
import org.destinationsol.game.planet.SystemsBuilder;
1919

20+
import java.util.ArrayList;
21+
import java.util.List;
22+
2023
public class WorldConfig {
2124
protected long seed;
2225
protected int numberOfSystems;
26+
private List<String> solarSystemGenerators;
27+
private List<String> featureGenerators;
2328

2429
public WorldConfig() {
2530
seed = System.currentTimeMillis();
2631
numberOfSystems = SystemsBuilder.DEFAULT_SYSTEM_COUNT;
32+
solarSystemGenerators = new ArrayList<>();
33+
featureGenerators = new ArrayList<>();
2734
}
2835

29-
public WorldConfig(long seed, int numberOfSystems) {
36+
public WorldConfig(long seed, int numberOfSystems,
37+
List<String> solarSystemGenerators,
38+
List<String> featureGenerators) {
3039
this.seed = seed;
3140
this.numberOfSystems = numberOfSystems;
41+
this.solarSystemGenerators = solarSystemGenerators;
42+
this.featureGenerators = featureGenerators;
3243
}
3344

3445
public long getSeed() {
@@ -46,4 +57,20 @@ public int getNumberOfSystems() {
4657
public void setNumberOfSystems(int numberOfSystems) {
4758
this.numberOfSystems = numberOfSystems;
4859
}
60+
61+
public List<String> getSolarSystemGenerators() {
62+
return solarSystemGenerators;
63+
}
64+
65+
public void setFeatureGenerators(List<String> featureGenerators) {
66+
this.featureGenerators = featureGenerators;
67+
}
68+
69+
public List<String> getFeatureGenerators() {
70+
return featureGenerators;
71+
}
72+
73+
public void setSolarSystemGenerators(List<String> solarSystemGenerators) {
74+
this.solarSystemGenerators = solarSystemGenerators;
75+
}
4976
}

engine/src/main/java/org/destinationsol/world/GalaxyBuilder.java

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,41 +52,78 @@ public class GalaxyBuilder {
5252
private ArrayList<SolarSystem> builtSolarSystems = new ArrayList<>();
5353
private ModuleManager moduleManager;
5454
private SolarSystemConfigManager solarSystemConfigManager;
55-
private final int numberOfSystems;
5655
private BeanContext beanContext;
56+
private WorldConfig worldConfig;
5757

5858
@Inject
5959
public GalaxyBuilder(WorldConfig worldConfig, ModuleManager moduleManager, SolarSystemConfigManager solarSystemConfigManager, BeanContext beanContext) {
6060
this.moduleManager = moduleManager;
6161
this.solarSystemConfigManager = solarSystemConfigManager;
6262
this.beanContext = beanContext;
6363
solarSystemConfigManager.loadDefaultSolarSystemConfigs();
64-
numberOfSystems = worldConfig.getNumberOfSystems();
64+
this.worldConfig = worldConfig;
65+
66+
if (worldConfig.getSolarSystemGenerators().isEmpty()) {
67+
populateSolarSystemGeneratorList();
68+
} else {
69+
for (String typeName : worldConfig.getSolarSystemGenerators()) {
70+
Iterable<Class<? extends SolarSystemGenerator>> generatorTypes =
71+
moduleManager.getEnvironment().getSubtypesOf(SolarSystemGenerator.class, type -> type.getName().equals(typeName));
72+
if (!generatorTypes.iterator().hasNext()) {
73+
logger.error("Unable to find SolarSystemGenerator type {}! World generation will likely be incorrect.", typeName);
74+
continue;
75+
}
76+
77+
for (Class<? extends SolarSystemGenerator> generatorType : generatorTypes) {
78+
solarSystemGeneratorTypes.add(generatorType);
79+
}
80+
}
81+
}
82+
83+
if (worldConfig.getFeatureGenerators().isEmpty()) {
84+
populateFeatureGeneratorList();
85+
} else {
86+
for (String typeName : worldConfig.getFeatureGenerators()) {
87+
Iterable<Class<? extends FeatureGenerator>> generatorTypes =
88+
moduleManager.getEnvironment().getSubtypesOf(FeatureGenerator.class, type -> type.getName().equals(typeName));
89+
if (!generatorTypes.iterator().hasNext()) {
90+
logger.error("Unable to find FeatureGenerator type {}! World generation will likely be incorrect.", typeName);
91+
continue;
92+
}
6593

66-
populateSolarSystemGeneratorList();
67-
populateFeatureGeneratorList();
94+
for (Class<? extends FeatureGenerator> generatorType : generatorTypes) {
95+
featureGeneratorTypes.add(generatorType);
96+
}
97+
}
98+
}
6899
}
69100

70101
/**
71102
* This method uses reflection to retrieve all SolarSystemGenerator classes. They are added to the list
72103
* of SolarSystemGenerators.
73104
*/
74105
private void populateSolarSystemGeneratorList() {
75-
//It is necessary to use an iterator as getSubtypesOf() returns an Iterable
76-
moduleManager.getEnvironment().getSubtypesOf(SolarSystemGenerator.class).iterator().forEachRemaining(solarSystemGeneratorTypes::add);
106+
List<String> systemGeneratorTypeNames = new ArrayList<>();
107+
for (Class<? extends SolarSystemGenerator> systemGeneratorType : moduleManager.getEnvironment().getSubtypesOf(SolarSystemGenerator.class)) {
108+
solarSystemGeneratorTypes.add(systemGeneratorType);
109+
systemGeneratorTypeNames.add(systemGeneratorType.getName());
110+
}
111+
worldConfig.setSolarSystemGenerators(systemGeneratorTypeNames);
77112
}
78113

79114
/**
80115
* This method uses reflection to retrieve all concrete FeatureGenerator classes. They are added to the list
81116
* of FeatureGenerators.
82117
*/
83118
private void populateFeatureGeneratorList() {
84-
119+
List<String> featureGeneratorTypeNames = new ArrayList<>();
85120
for (Class<? extends FeatureGenerator> generator : moduleManager.getEnvironment().getSubtypesOf(FeatureGenerator.class)) {
86121
if (!Modifier.isAbstract(generator.getModifiers())) {
87122
featureGeneratorTypes.add(generator);
123+
featureGeneratorTypeNames.add(generator.getName());
88124
}
89125
}
126+
worldConfig.setFeatureGenerators(featureGeneratorTypeNames);
90127
}
91128

92129
/**
@@ -112,7 +149,7 @@ public void buildWithRandomSolarSystemGenerators() {
112149
*/
113150
public ArrayList<SolarSystemGenerator> initializeRandomSolarSystemGenerators() {
114151
ArrayList<SolarSystemGenerator> generatorArrayList = new ArrayList<>();
115-
for (int i = 0; i < numberOfSystems; i++) {
152+
for (int i = 0; i < worldConfig.getNumberOfSystems(); i++) {
116153
Class<? extends SolarSystemGenerator> solarSystemGenerator = solarSystemGeneratorTypes.get(SolRandom.seededRandomInt(solarSystemGeneratorTypes.size()));
117154
try {
118155
SolarSystemGenerator generator = solarSystemGenerator.newInstance();
@@ -199,19 +236,19 @@ private void calculateRandomWorldPositionAtDistance(Vector2 result, float distan
199236
SolMath.fromAl(result, angle, distance);
200237
}
201238

202-
public ArrayList<Class<? extends SolarSystemGenerator>> getSolarSystemGeneratorTypes() {
239+
public List<Class<? extends SolarSystemGenerator>> getSolarSystemGeneratorTypes() {
203240
return solarSystemGeneratorTypes;
204241
}
205242

206-
public ArrayList<SolarSystemGenerator> getActiveSolarSystemGenerators() {
243+
public List<SolarSystemGenerator> getActiveSolarSystemGenerators() {
207244
return activeSolarSystemGenerators;
208245
}
209246

210-
public ArrayList<Class<? extends FeatureGenerator>> getFeatureGeneratorTypes() {
247+
public List<Class<? extends FeatureGenerator>> getFeatureGeneratorTypes() {
211248
return featureGeneratorTypes;
212249
}
213250

214-
public ArrayList<SolarSystem> getBuiltSolarSystems() {
251+
public List<SolarSystem> getBuiltSolarSystems() {
215252
return builtSolarSystems;
216253
}
217254
}

engine/src/main/java/org/destinationsol/world/generators/SolarSystemGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ private boolean isOtherGeneratorType(int index) {
466466
&& !PlanetGenerator.class.isAssignableFrom(featureGeneratorTypes.get(index));
467467
}
468468

469-
public void setFeatureGeneratorTypes(ArrayList<Class<? extends FeatureGenerator>> generators) {
469+
public void setFeatureGeneratorTypes(List<Class<? extends FeatureGenerator>> generators) {
470470
featureGeneratorTypes.addAll(generators);
471471
}
472472

0 commit comments

Comments
 (0)