mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-03 03:31:28 +03:00
Fixed #12267
This commit is contained in:
@@ -401,6 +401,8 @@ public class Control implements ApplicationListener, Loadable{
|
|||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
logic.reset();
|
logic.reset();
|
||||||
world.loadMap(map, rules);
|
world.loadMap(map, rules);
|
||||||
|
var oldRules = state.rules;
|
||||||
|
rules.retainContentFields(oldRules);
|
||||||
state.rules = rules;
|
state.rules = rules;
|
||||||
if(playtest) state.playtestingMap = map;
|
if(playtest) state.playtestingMap = map;
|
||||||
state.rules.sector = null;
|
state.rules.sector = null;
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ public abstract class Content implements Comparable<Content>{
|
|||||||
return !isVanilla();
|
return !isVanilla();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return whether this content is from a map data patch. */
|
||||||
|
public boolean isPatchContent(){
|
||||||
|
return minfo.mod == DataPatcher.dpMod;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Content c){
|
public int compareTo(Content c){
|
||||||
return Integer.compare(id, c.id);
|
return Integer.compare(id, c.id);
|
||||||
|
|||||||
@@ -245,6 +245,23 @@ public class Rules{
|
|||||||
return JsonIO.copy(this);
|
return JsonIO.copy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a map is played, it uses rules from the rules dialog, which cannot contain patched content, since it doesn't exist at that point in time.
|
||||||
|
* This means that any existing rules containing patched content will contain garbage or empty data.
|
||||||
|
* This function copies original map rule data from {@param source} (obtained after map load) that may contain new content into this ruleset.
|
||||||
|
* */
|
||||||
|
public void retainContentFields(Rules source){
|
||||||
|
//these fields can't be modified in the custom rules anyway, so force-overwriting them is fine
|
||||||
|
spawns = source.spawns;
|
||||||
|
objectives = source.objectives;
|
||||||
|
weather = source.weather;
|
||||||
|
|
||||||
|
//TODO: this overwrites banned blocks/units and loadouts if someone set it in custom rules when playing; there isn't a good way to avoid this
|
||||||
|
if(Seq.with(source.bannedBlocks).contains(Content::isPatchContent)) bannedBlocks = source.bannedBlocks;
|
||||||
|
if(Seq.with(source.bannedUnits).contains(Content::isPatchContent)) bannedUnits = source.bannedUnits;
|
||||||
|
if(source.loadout.contains(i -> i.item.isPatchContent())) loadout = source.loadout;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the gamemode that best fits these rules. */
|
/** Returns the gamemode that best fits these rules. */
|
||||||
public Gamemode mode(){
|
public Gamemode mode(){
|
||||||
if(pvp){
|
if(pvp){
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package mindustry.io;
|
|||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.struct.ObjectMap.*;
|
import arc.struct.ObjectMap.*;
|
||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import mindustry.world.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@@ -190,7 +189,7 @@ public abstract class SaveFileReader{
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException;
|
public abstract void read(DataInputStream stream, CounterInputStream counter, SaveReadState state) throws IOException;
|
||||||
|
|
||||||
public abstract void write(DataOutputStream stream, SaveOptions options) throws IOException;
|
public abstract void write(DataOutputStream stream, SaveOptions options) throws IOException;
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public class SaveIO{
|
|||||||
|
|
||||||
if(ver == null) throw new IOException("Unknown save version: " + version + ". Are you trying to load a save from a newer version?");
|
if(ver == null) throw new IOException("Unknown save version: " + version + ". Are you trying to load a save from a newer version?");
|
||||||
|
|
||||||
ver.read(stream, counter, context);
|
ver.read(stream, counter, new SaveReadState(context));
|
||||||
Events.fire(new SaveLoadEvent(context.isMap()));
|
Events.fire(new SaveLoadEvent(context.isMap()));
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
throw new SaveException(e);
|
throw new SaveException(e);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package mindustry.io;
|
||||||
|
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.world.*;
|
||||||
|
|
||||||
|
public class SaveReadState{
|
||||||
|
public final WorldContext context;
|
||||||
|
public @Nullable String ruleString;
|
||||||
|
|
||||||
|
public SaveReadState(WorldContext context){
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,13 +60,13 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
public void read(DataInputStream stream, CounterInputStream counter, SaveReadState saveState) throws IOException{
|
||||||
readRegion("meta", stream, counter, in -> readMeta(in, context));
|
readRegion("meta", stream, counter, in -> readMeta(in, saveState));
|
||||||
if(version >= 12) readRegion("patches", stream, counter, this::readDataPatches);
|
if(version >= 12) readRegion("patches", stream, counter, in -> readDataPatches(in, saveState));
|
||||||
|
|
||||||
try{
|
try{
|
||||||
readRegion("content", stream, counter, this::readContentHeader);
|
readRegion("content", stream, counter, this::readContentHeader);
|
||||||
readRegion("map", stream, counter, in -> readMap(in, context));
|
readRegion("map", stream, counter, in -> readMap(in, saveState.context));
|
||||||
readRegion("entities", stream, counter, this::readEntities);
|
readRegion("entities", stream, counter, this::readEntities);
|
||||||
if(version >= 8) readRegion("markers", stream, counter, this::readMarkers);
|
if(version >= 8) readRegion("markers", stream, counter, this::readMarkers);
|
||||||
readRegion("custom", stream, counter, this::readCustomChunks);
|
readRegion("custom", stream, counter, this::readCustomChunks);
|
||||||
@@ -75,6 +75,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void write(DataOutputStream stream, SaveOptions options) throws IOException{
|
public void write(DataOutputStream stream, SaveOptions options) throws IOException{
|
||||||
writeRegion("meta", stream, out -> writeMeta(out, options.extraTags));
|
writeRegion("meta", stream, out -> writeMeta(out, options.extraTags));
|
||||||
writeRegion("patches", stream, out -> writeDataPatches(out, options.embedAssets));
|
writeRegion("patches", stream, out -> writeDataPatches(out, options.embedAssets));
|
||||||
@@ -148,27 +149,20 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readMeta(DataInput stream, WorldContext context) throws IOException{
|
public void readMeta(DataInput stream, SaveReadState saveState) throws IOException{
|
||||||
StringMap map = readStringMap(stream);
|
StringMap map = readStringMap(stream);
|
||||||
|
|
||||||
state.wave = map.getInt("wave");
|
state.wave = map.getInt("wave");
|
||||||
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
|
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
|
||||||
state.tick = map.getFloat("tick");
|
state.tick = map.getFloat("tick");
|
||||||
state.stats = JsonIO.read(GameStats.class, map.get("stats", "{}"));
|
state.stats = JsonIO.read(GameStats.class, map.get("stats", "{}"));
|
||||||
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
|
|
||||||
state.mapLocales = JsonIO.read(MapLocales.class, map.get("locales", "{}"));
|
state.mapLocales = JsonIO.read(MapLocales.class, map.get("locales", "{}"));
|
||||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = waves.get();
|
|
||||||
|
|
||||||
if(context.getSector() != null){
|
saveState.ruleString = map.get("rules", "{}");
|
||||||
state.rules.sector = context.getSector();
|
|
||||||
if(state.rules.sector != null){
|
|
||||||
state.rules.sector.planet.applyRules(state.rules);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//replace the default serpulo env with erekir
|
//for versions >= 13, rules are parsed after data patches are loaded
|
||||||
if(state.rules.planet == Planets.serpulo && state.rules.hasEnv(Env.scorching)){
|
if(version < 13){
|
||||||
state.rules.planet = Planets.erekir;
|
readRules(saveState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!headless){
|
if(!headless){
|
||||||
@@ -196,6 +190,25 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
)) : worldmap;
|
)) : worldmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void readRules(SaveReadState saveState){
|
||||||
|
if(saveState.ruleString == null) return; //in NetworkIO, rules are null, not read here
|
||||||
|
state.rules = JsonIO.read(Rules.class, saveState.ruleString);
|
||||||
|
|
||||||
|
if(state.rules.spawns.isEmpty()) state.rules.spawns = waves.get();
|
||||||
|
|
||||||
|
if(saveState.context.getSector() != null){
|
||||||
|
state.rules.sector = saveState.context.getSector();
|
||||||
|
if(state.rules.sector != null){
|
||||||
|
state.rules.sector.planet.applyRules(state.rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//replace the default serpulo env with erekir
|
||||||
|
if(state.rules.planet == Planets.serpulo && state.rules.hasEnv(Env.scorching)){
|
||||||
|
state.rules.planet = Planets.erekir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void writeMap(DataOutput stream) throws IOException{
|
public void writeMap(DataOutput stream) throws IOException{
|
||||||
//write world size
|
//write world size
|
||||||
stream.writeShort(world.width());
|
stream.writeShort(world.width());
|
||||||
@@ -527,7 +540,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
readWorldEntities(stream, mapping);
|
readWorldEntities(stream, mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readDataPatches(DataInput stream) throws IOException{
|
public void readDataPatches(DataInput stream, SaveReadState saveState) throws IOException{
|
||||||
stream.readInt(); //version - ignored for now
|
stream.readInt(); //version - ignored for now
|
||||||
|
|
||||||
int total = stream.readInt();
|
int total = stream.readInt();
|
||||||
@@ -563,6 +576,9 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
Events.fire(new DataPatchLoadEvent(assets));
|
Events.fire(new DataPatchLoadEvent(assets));
|
||||||
|
|
||||||
state.data.load(assets);
|
state.data.load(assets);
|
||||||
|
|
||||||
|
//now that patches are loaded, the rules can actually be read
|
||||||
|
readRules(saveState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeDataPatches(DataOutput stream, boolean forceEmbed) throws IOException{
|
public void writeDataPatches(DataOutput stream, boolean forceEmbed) throws IOException{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import mindustry.world.*;
|
import mindustry.io.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@@ -15,12 +15,12 @@ public class LegacyRegionSaveVersion extends ShortChunkSaveVersion{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
public void read(DataInputStream stream, CounterInputStream counter, SaveReadState saveState) throws IOException{
|
||||||
readRegion("meta", stream, counter, in -> readMeta(in, context));
|
readRegion("meta", stream, counter, in -> readMeta(in, saveState));
|
||||||
readRegion("content", stream, counter, this::readContentHeader);
|
readRegion("content", stream, counter, this::readContentHeader);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
readRegion("map", stream, counter, in -> readMap(in, context));
|
readRegion("map", stream, counter, in -> readMap(in, saveState.context));
|
||||||
readRegion("entities", stream, counter, this::readEntities);
|
readRegion("entities", stream, counter, this::readEntities);
|
||||||
}finally{
|
}finally{
|
||||||
content.setTemporaryMapper(null);
|
content.setTemporaryMapper(null);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import arc.util.io.*;
|
|||||||
import mindustry.game.EventType.*;
|
import mindustry.game.EventType.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
import mindustry.mod.data.*;
|
import mindustry.mod.data.*;
|
||||||
import mindustry.world.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@@ -23,13 +22,13 @@ public class Save11 extends SaveVersion{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
public void read(DataInputStream stream, CounterInputStream counter, SaveReadState saveState) throws IOException{
|
||||||
readRegion("meta", stream, counter, in -> readMeta(in, context));
|
readRegion("meta", stream, counter, in -> readMeta(in, saveState));
|
||||||
readRegion("content", stream, counter, this::readContentHeader);
|
readRegion("content", stream, counter, this::readContentHeader);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
readRegion("patches", stream, counter, this::readDataPatches);
|
readRegion("patches", stream, counter, in -> readDataPatches(in, saveState));
|
||||||
readRegion("map", stream, counter, in -> readMap(in, context));
|
readRegion("map", stream, counter, in -> readMap(in, saveState.context));
|
||||||
readRegion("entities", stream, counter, this::readEntities);
|
readRegion("entities", stream, counter, this::readEntities);
|
||||||
readRegion("markers", stream, counter, this::readMarkers);
|
readRegion("markers", stream, counter, this::readMarkers);
|
||||||
readRegion("custom", stream, counter, this::readCustomChunks);
|
readRegion("custom", stream, counter, this::readCustomChunks);
|
||||||
@@ -40,7 +39,7 @@ public class Save11 extends SaveVersion{
|
|||||||
|
|
||||||
//old, simplified string-only data patches
|
//old, simplified string-only data patches
|
||||||
@Override
|
@Override
|
||||||
public void readDataPatches(DataInput stream) throws IOException{
|
public void readDataPatches(DataInput stream, SaveReadState saveState) throws IOException{
|
||||||
Seq<DataAsset> assets = new Seq<>();
|
Seq<DataAsset> assets = new Seq<>();
|
||||||
|
|
||||||
int amount = stream.readUnsignedByte();
|
int amount = stream.readUnsignedByte();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class Save12 extends SaveVersion{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readDataPatches(DataInput stream) throws IOException{
|
public void readDataPatches(DataInput stream, SaveReadState saveState) throws IOException{
|
||||||
stream.readInt(); //version - ignored for now
|
stream.readInt(); //version - ignored for now
|
||||||
|
|
||||||
Seq<DataAsset> assets = new Seq<>();
|
Seq<DataAsset> assets = new Seq<>();
|
||||||
|
|||||||
@@ -100,9 +100,7 @@ public class Map implements Comparable<Map>, Publishable{
|
|||||||
|
|
||||||
public Rules rules(Rules base){
|
public Rules rules(Rules base){
|
||||||
try{
|
try{
|
||||||
//this replacement is a MASSIVE hack but it fixes some incorrect overwriting of team-specific rules.
|
Rules result = JsonIO.read(Rules.class, base, tags.get("rules", "{}"));
|
||||||
//may need to be tweaked later
|
|
||||||
Rules result = JsonIO.read(Rules.class, base, tags.get("rules", "{}").replace("teams:{2:{infiniteAmmo:true}},", ""));
|
|
||||||
//replace the default serpulo env with erekir
|
//replace the default serpulo env with erekir
|
||||||
if(result.planet == Planets.serpulo && result.hasEnv(Env.scorching)){
|
if(result.planet == Planets.serpulo && result.hasEnv(Env.scorching)){
|
||||||
result.planet = Planets.erekir;
|
result.planet = Planets.erekir;
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ import java.util.*;
|
|||||||
/** The current implementation is awful. Consider it a proof of concept. */
|
/** The current implementation is awful. Consider it a proof of concept. */
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class DataPatcher{
|
public class DataPatcher{
|
||||||
|
private static ModMeta dpModMeta = new ModMeta(){{
|
||||||
|
name = internalName = "dp";
|
||||||
|
}};
|
||||||
|
|
||||||
|
public static final LoadedMod dpMod = new LoadedMod(new Fi("dp"), new Fi(""), null, null, dpModMeta);
|
||||||
|
|
||||||
public static final int maxImageSize = 2000;
|
public static final int maxImageSize = 2000;
|
||||||
public static final int patchFormatVersion = 2;
|
public static final int patchFormatVersion = 2;
|
||||||
|
|
||||||
@@ -37,10 +43,7 @@ public class DataPatcher{
|
|||||||
private static final ObjectMap<String, ContentType> nameToType = new ObjectMap<>();
|
private static final ObjectMap<String, ContentType> nameToType = new ObjectMap<>();
|
||||||
private static DataPatcher currentDataPatcher;
|
private static DataPatcher currentDataPatcher;
|
||||||
private static ContentParser parser = createParser();
|
private static ContentParser parser = createParser();
|
||||||
private static ModMeta dpModMeta = new ModMeta(){{
|
|
||||||
name = internalName = "dp";
|
|
||||||
}};
|
|
||||||
private static LoadedMod dpMod = new LoadedMod(new Fi("dp"), new Fi(""), null, null, dpModMeta);
|
|
||||||
|
|
||||||
private boolean applied;
|
private boolean applied;
|
||||||
private ContentLoader contentLoader;
|
private ContentLoader contentLoader;
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ public class NetworkIO{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//data patches must be first, as rules can involve patched content
|
||||||
|
SaveIO.getSaveWriter().writeDataPatches(stream, false);
|
||||||
|
|
||||||
stream.writeUTF(JsonIO.write(state.rules));
|
stream.writeUTF(JsonIO.write(state.rules));
|
||||||
stream.writeUTF(JsonIO.write(state.mapLocales));
|
stream.writeUTF(JsonIO.write(state.mapLocales));
|
||||||
SaveIO.getSaveWriter().writeStringMap(stream, state.map.tags);
|
SaveIO.getSaveWriter().writeStringMap(stream, state.map.tags);
|
||||||
@@ -55,7 +58,6 @@ public class NetworkIO{
|
|||||||
stream.writeInt(player.id);
|
stream.writeInt(player.id);
|
||||||
player.write(new Writes(stream));
|
player.write(new Writes(stream));
|
||||||
|
|
||||||
SaveIO.getSaveWriter().writeDataPatches(stream, false);
|
|
||||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
SaveIO.getSaveWriter().writeContentHeader(stream);
|
||||||
SaveIO.getSaveWriter().writeMap(stream);
|
SaveIO.getSaveWriter().writeMap(stream);
|
||||||
SaveIO.getSaveWriter().writeTeamBlocks(stream);
|
SaveIO.getSaveWriter().writeTeamBlocks(stream);
|
||||||
@@ -70,6 +72,8 @@ public class NetworkIO{
|
|||||||
|
|
||||||
try(DataInputStream stream = new DataInputStream(is)){
|
try(DataInputStream stream = new DataInputStream(is)){
|
||||||
Time.clear();
|
Time.clear();
|
||||||
|
SaveIO.getSaveWriter().readDataPatches(stream, new SaveReadState(world.context));
|
||||||
|
|
||||||
state.rules = JsonIO.read(Rules.class, stream.readUTF());
|
state.rules = JsonIO.read(Rules.class, stream.readUTF());
|
||||||
state.mapLocales = JsonIO.read(MapLocales.class, stream.readUTF());
|
state.mapLocales = JsonIO.read(MapLocales.class, stream.readUTF());
|
||||||
state.map = new Map(SaveIO.getSaveWriter().readStringMap(stream));
|
state.map = new Map(SaveIO.getSaveWriter().readStringMap(stream));
|
||||||
@@ -89,7 +93,6 @@ public class NetworkIO{
|
|||||||
player.id = id;
|
player.id = id;
|
||||||
player.add();
|
player.add();
|
||||||
|
|
||||||
SaveIO.getSaveWriter().readDataPatches(stream);
|
|
||||||
SaveIO.getSaveWriter().readContentHeader(stream);
|
SaveIO.getSaveWriter().readContentHeader(stream);
|
||||||
SaveIO.getSaveWriter().readMap(stream, world.context);
|
SaveIO.getSaveWriter().readMap(stream, world.context);
|
||||||
SaveIO.getSaveWriter().readTeamBlocks(stream);
|
SaveIO.getSaveWriter().readTeamBlocks(stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user