mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-02 11:10:50 +03:00
WIP data patch asset system
This commit is contained in:
@@ -142,6 +142,19 @@ public class AssetsProcess extends BaseProcessor{
|
|||||||
.addParameter(int.class, "id")
|
.addParameter(int.class, "id")
|
||||||
.returns(Sound.class)
|
.returns(Sound.class)
|
||||||
.addStatement("return (Sound)idToSound.get(id, () -> Sounds.none)").build());
|
.addStatement("return (Sound)idToSound.get(id, () -> Sounds.none)").build());
|
||||||
|
|
||||||
|
type.addMethod(MethodSpec.methodBuilder("registerSound")
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||||
|
.addParameter(Sound.class, "sound")
|
||||||
|
.addParameter(int.class, "id")
|
||||||
|
.returns(void.class)
|
||||||
|
.addStatement("idToSound.put(id, sound); soundToId.put(sound, id);").build());
|
||||||
|
|
||||||
|
type.addMethod(MethodSpec.methodBuilder("unregisterSound")
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||||
|
.addParameter(Sound.class, "sound")
|
||||||
|
.returns(void.class)
|
||||||
|
.addStatement("int id = soundToId.get(sound); if(id != 0){ soundToId.remove(sound); idToSound.remove(id); }").build());
|
||||||
}
|
}
|
||||||
|
|
||||||
HashSet<String> names = new HashSet<>();
|
HashSet<String> names = new HashSet<>();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class GameState{
|
|||||||
/** Team data. Gets reset every new game. */
|
/** Team data. Gets reset every new game. */
|
||||||
public Teams teams = new Teams();
|
public Teams teams = new Teams();
|
||||||
/** Handles JSON edits of game content. */
|
/** Handles JSON edits of game content. */
|
||||||
public DataPatcher patcher = new DataPatcher();
|
public DataManager data = new DataManager();
|
||||||
/** Number of enemies in the game; only used clientside in servers. */
|
/** Number of enemies in the game; only used clientside in servers. */
|
||||||
public int enemies;
|
public int enemies;
|
||||||
/** Map being playtested (not edited!) */
|
/** Map being playtested (not edited!) */
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ public class Logic implements ApplicationListener{
|
|||||||
|
|
||||||
public void reset(){
|
public void reset(){
|
||||||
State prev = state.getState();
|
State prev = state.getState();
|
||||||
state.patcher.unapply();
|
state.data.unload();
|
||||||
//recreate gamestate - sets state to menu
|
//recreate gamestate - sets state to menu
|
||||||
state = new GameState();
|
state = new GameState();
|
||||||
//fire change event, since it was technically changed
|
//fire change event, since it was technically changed
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import arc.util.*;
|
|||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
import mindustry.ui.*;
|
import mindustry.ui.*;
|
||||||
import mindustry.ui.dialogs.*;
|
import mindustry.ui.dialogs.*;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ import java.util.concurrent.*;
|
|||||||
import java.util.zip.*;
|
import java.util.zip.*;
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
import static mindustry.graphics.DataPatchPacker.*;
|
import static mindustry.mod.DataImagePacker.*;
|
||||||
|
|
||||||
public class MapPatchImagesDialog extends BaseDialog{
|
public class MapPatchImagesDialog extends BaseDialog{
|
||||||
TextField searchField;
|
TextField searchField;
|
||||||
@@ -69,7 +70,7 @@ public class MapPatchImagesDialog extends BaseDialog{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.patcher.images.add(new PatchImage(path, width, height, bytes));
|
state.patcher.images.add(new ImageAsset(path, width, height, bytes));
|
||||||
state.patcher.images.sort();
|
state.patcher.images.sort();
|
||||||
state.patcher.applyImages(state.patcher.images);
|
state.patcher.applyImages(state.patcher.images);
|
||||||
rebuild();
|
rebuild();
|
||||||
@@ -90,7 +91,7 @@ public class MapPatchImagesDialog extends BaseDialog{
|
|||||||
dialog.hide();
|
dialog.hide();
|
||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
try{
|
try{
|
||||||
Seq<Future<PatchImage>> images = new Seq<>();
|
Seq<Future<ImageAsset>> images = new Seq<>();
|
||||||
var errors = new CopyOnWriteArrayList<String>();
|
var errors = new CopyOnWriteArrayList<String>();
|
||||||
|
|
||||||
Fi zipped = new ZipFi(file);
|
Fi zipped = new ZipFi(file);
|
||||||
@@ -106,7 +107,7 @@ public class MapPatchImagesDialog extends BaseDialog{
|
|||||||
bytes = PixmapIO.writePngBytes(pix);
|
bytes = PixmapIO.writePngBytes(pix);
|
||||||
pix.dispose();
|
pix.dispose();
|
||||||
|
|
||||||
return new PatchImage(ifile.pathWithoutExtension(), width, height, bytes);
|
return new ImageAsset(ifile.pathWithoutExtension(), width, height, bytes);
|
||||||
}catch(Throwable error){
|
}catch(Throwable error){
|
||||||
errors.add("[accent]" + ifile.path() + "[white]: " + Strings.getSimpleMessage(error));
|
errors.add("[accent]" + ifile.path() + "[white]: " + Strings.getSimpleMessage(error));
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import mindustry.core.GameState.*;
|
|||||||
import mindustry.ctype.*;
|
import mindustry.ctype.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.graphics.MultiPacker;
|
import mindustry.graphics.MultiPacker;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.data.*;
|
||||||
import mindustry.net.*;
|
import mindustry.net.*;
|
||||||
import mindustry.net.Packets.*;
|
import mindustry.net.Packets.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
@@ -108,14 +108,12 @@ public class EventType{
|
|||||||
/** Called when a game begins and the world tiles are initiated. About to updates tile proximity and sets up physics for the world(Before WorldLoadEvent) */
|
/** Called when a game begins and the world tiles are initiated. About to updates tile proximity and sets up physics for the world(Before WorldLoadEvent) */
|
||||||
public static class WorldLoadEndEvent{}
|
public static class WorldLoadEndEvent{}
|
||||||
|
|
||||||
/** Called when a save loads custom patches. {@link #patches} can be modified in the event handler. The array may be empty. */
|
/** Called when a save loads custom data patches. {@link #assets} can be modified in the event handler. The array may be empty. */
|
||||||
public static class ContentPatchLoadEvent{
|
public static class DataPatchLoadEvent{
|
||||||
public final Seq<String> patches;
|
public final Seq<DataAsset> assets;
|
||||||
public final Seq<PatchImage> images;
|
|
||||||
|
|
||||||
public ContentPatchLoadEvent(Seq<String> patches, Seq<PatchImage> images){
|
public DataPatchLoadEvent(Seq<DataAsset> assets){
|
||||||
this.patches = patches;
|
this.assets = assets;
|
||||||
this.images = images;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class SaveIO{
|
|||||||
/** Save format header. */
|
/** Save format header. */
|
||||||
public static final byte[] header = {'M', 'S', 'A', 'V'};
|
public static final byte[] header = {'M', 'S', 'A', 'V'};
|
||||||
public static final IntMap<SaveVersion> versions = new IntMap<>();
|
public static final IntMap<SaveVersion> versions = new IntMap<>();
|
||||||
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8(), new Save9(), new Save10(), new Save11(), new Save12());
|
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8(), new Save9(), new Save10(), new Save11(), new Save12(), new Save13());
|
||||||
|
|
||||||
static{
|
static{
|
||||||
for(SaveVersion version : versionArray){
|
for(SaveVersion version : versionArray){
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import mindustry.game.Teams.*;
|
|||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.maps.Map;
|
import mindustry.maps.Map;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
import mindustry.world.meta.*;
|
import mindustry.world.meta.*;
|
||||||
@@ -520,79 +521,35 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void skipDataPatches(DataInput stream) throws IOException{
|
public void skipDataPatches(DataInput stream) throws IOException{
|
||||||
stream.readInt(); //version - ignored for now
|
int total = stream.readInt();
|
||||||
int amount = stream.readInt();
|
|
||||||
for(int i = 0; i < amount; i++){
|
|
||||||
int len = stream.readInt();
|
|
||||||
stream.skipBytes(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int imageAmount = stream.readInt();
|
for(int i = 0; i < total; i++){
|
||||||
for(int i = 0; i < imageAmount; i++){
|
//TODO: generates a lot of garbage, but I guess that's fine?
|
||||||
stream.readUTF(); //name
|
DataAsset.readAsset(stream);
|
||||||
stream.skipBytes(4); //w h
|
|
||||||
int len = stream.readInt(); //byte data
|
|
||||||
stream.skipBytes(len);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readDataPatches(DataInput stream) throws IOException{
|
public void readDataPatches(DataInput stream) throws IOException{
|
||||||
stream.readInt(); //version - ignored for now
|
stream.readInt(); //version - ignored for now
|
||||||
|
|
||||||
Seq<String> patches = new Seq<>();
|
int total = stream.readInt();
|
||||||
|
Seq<DataAsset> assets = new Seq<>(total);
|
||||||
|
|
||||||
int patchAmount = stream.readInt();
|
for(int i = 0; i < total; i++){
|
||||||
for(int i = 0; i < patchAmount; i++){
|
assets.add(DataAsset.readAsset(stream));
|
||||||
int len = stream.readInt();
|
|
||||||
byte[] bytes = new byte[len];
|
|
||||||
stream.readFully(bytes);
|
|
||||||
patches.add(new String(bytes, Strings.utf8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Seq<PatchImage> images = new Seq<>();
|
//TODO: actually apply the assets
|
||||||
int imageAmount = stream.readInt();
|
|
||||||
for(int i = 0; i < imageAmount; i++){
|
|
||||||
String name = stream.readUTF();
|
|
||||||
short w = stream.readShort(), h = stream.readShort();
|
|
||||||
byte[] bytes = new byte[stream.readInt()];
|
|
||||||
stream.readFully(bytes);
|
|
||||||
images.add(new PatchImage(name, w, h, bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
Events.fire(new ContentPatchLoadEvent(patches, images));
|
|
||||||
|
|
||||||
if(images.size > 0){
|
|
||||||
state.patcher.applyImages(images);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patches.size > 0){
|
|
||||||
try{
|
|
||||||
state.patcher.apply(patches);
|
|
||||||
}catch(Throwable e){
|
|
||||||
Log.err("Failed to apply patches: " + patches, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeDataPatches(DataOutput stream) throws IOException{
|
public void writeDataPatches(DataOutput stream) throws IOException{
|
||||||
stream.writeInt(DataPatcher.patchFormatVersion);
|
stream.writeInt(DataPatcher.patchFormatVersion);
|
||||||
|
|
||||||
var patches = state.patcher.patches;
|
var assets = state.data.getAllAssets();
|
||||||
stream.writeInt(patches.size);
|
stream.writeInt(assets.size);
|
||||||
for(var patchset : patches){
|
|
||||||
byte[] bytes = patchset.patch.getBytes(Strings.utf8);
|
|
||||||
stream.writeInt(bytes.length);
|
|
||||||
stream.write(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
var images = state.patcher.images;
|
for(var asset : assets){
|
||||||
stream.writeInt(images.size);
|
DataAsset.writeAsset(asset, stream);
|
||||||
for(var image : images){
|
|
||||||
stream.writeUTF(image.path);
|
|
||||||
stream.writeShort(image.width);
|
|
||||||
stream.writeShort(image.height);
|
|
||||||
stream.writeInt(image.data.length);
|
|
||||||
stream.write(image.data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,21 +575,10 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
//HACK: versions below 11 don't read the patch chunk, which means the event for reading patches is never triggered.
|
//HACK: versions below 11 don't read the patch chunk, which means the event for reading patches is never triggered.
|
||||||
//manually fire the event here for older versions.
|
//manually fire the event here for older versions.
|
||||||
if(version < 11){
|
if(version < 11){
|
||||||
Seq<String> patches = new Seq<>();
|
Seq<DataAsset> assets = new Seq<>();
|
||||||
Seq<PatchImage> images = new Seq<>();
|
Events.fire(new DataPatchLoadEvent(assets));
|
||||||
Events.fire(new ContentPatchLoadEvent(patches, images));
|
|
||||||
|
|
||||||
if(images.size > 0){
|
state.data.load(assets);
|
||||||
state.patcher.applyImages(images);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patches.size > 0){
|
|
||||||
try{
|
|
||||||
state.patcher.apply(patches);
|
|
||||||
}catch(Throwable e){
|
|
||||||
Log.err("Failed to apply patches: " + patches, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import arc.util.*;
|
|||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import mindustry.game.EventType.*;
|
import mindustry.game.EventType.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.data.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -50,30 +50,18 @@ 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) throws IOException{
|
||||||
Seq<String> patches = new Seq<>();
|
Seq<DataAsset> assets = new Seq<>();
|
||||||
|
|
||||||
int amount = stream.readUnsignedByte();
|
int amount = stream.readUnsignedByte();
|
||||||
for(int i = 0; i < amount; i++){
|
for(int i = 0; i < amount; i++){
|
||||||
int len = stream.readInt();
|
int len = stream.readInt();
|
||||||
byte[] bytes = new byte[len];
|
byte[] bytes = new byte[len];
|
||||||
stream.readFully(bytes);
|
stream.readFully(bytes);
|
||||||
patches.add(new String(bytes, Strings.utf8));
|
assets.add(new PatchAsset(new String(bytes, Strings.utf8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Seq<PatchImage> images = new Seq<>();
|
Events.fire(new DataPatchLoadEvent(assets));
|
||||||
|
|
||||||
Events.fire(new ContentPatchLoadEvent(patches, images));
|
state.data.load(assets);
|
||||||
|
|
||||||
if(images.size > 0){
|
|
||||||
state.patcher.applyImages(images);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patches.size > 0){
|
|
||||||
try{
|
|
||||||
state.patcher.apply(patches);
|
|
||||||
}catch(Throwable e){
|
|
||||||
Log.err("Failed to apply patches: " + patches, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
|
import arc.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.game.EventType.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
/** Changes data patches to be read before content, and adds support for more complex data patch IO. */
|
/** Changes data patches to be read before content, and adds support for more complex data patch IO. */
|
||||||
public class Save12 extends SaveVersion{
|
public class Save12 extends SaveVersion{
|
||||||
@@ -8,4 +17,50 @@ public class Save12 extends SaveVersion{
|
|||||||
public Save12(){
|
public Save12(){
|
||||||
super(12);
|
super(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skipDataPatches(DataInput stream) throws IOException{
|
||||||
|
stream.readInt(); //version - ignored for now
|
||||||
|
int amount = stream.readInt();
|
||||||
|
for(int i = 0; i < amount; i++){
|
||||||
|
int len = stream.readInt();
|
||||||
|
stream.skipBytes(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int imageAmount = stream.readInt();
|
||||||
|
for(int i = 0; i < imageAmount; i++){
|
||||||
|
stream.readUTF(); //name
|
||||||
|
stream.skipBytes(4); //w h
|
||||||
|
int len = stream.readInt(); //byte data
|
||||||
|
stream.skipBytes(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readDataPatches(DataInput stream) throws IOException{
|
||||||
|
stream.readInt(); //version - ignored for now
|
||||||
|
|
||||||
|
Seq<DataAsset> assets = new Seq<>();
|
||||||
|
|
||||||
|
int patchAmount = stream.readInt();
|
||||||
|
for(int i = 0; i < patchAmount; i++){
|
||||||
|
int len = stream.readInt();
|
||||||
|
byte[] bytes = new byte[len];
|
||||||
|
stream.readFully(bytes);
|
||||||
|
assets.add(new PatchAsset(new String(bytes, Strings.utf8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int imageAmount = stream.readInt();
|
||||||
|
for(int i = 0; i < imageAmount; i++){
|
||||||
|
String name = stream.readUTF();
|
||||||
|
short w = stream.readShort(), h = stream.readShort();
|
||||||
|
byte[] bytes = new byte[stream.readInt()];
|
||||||
|
stream.readFully(bytes);
|
||||||
|
assets.add(new ImageAsset(name, w, h, bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
Events.fire(new DataPatchLoadEvent(assets));
|
||||||
|
|
||||||
|
state.data.load(assets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package mindustry.io.versions;
|
||||||
|
|
||||||
|
import mindustry.io.*;
|
||||||
|
|
||||||
|
/** New asset-based data patch system. */
|
||||||
|
public class Save13 extends SaveVersion{
|
||||||
|
|
||||||
|
public Save13(){
|
||||||
|
super(13);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -309,6 +309,10 @@ public class ContentParser{
|
|||||||
if(data.isArray()) return new RandomSound(parser.readValue(Sound[].class, data));
|
if(data.isArray()) return new RandomSound(parser.readValue(Sound[].class, data));
|
||||||
|
|
||||||
var field = fieldOpt(Sounds.class, data);
|
var field = fieldOpt(Sounds.class, data);
|
||||||
|
//try grabbing it from the asset manager directly (relevant for data patches)
|
||||||
|
if(field == null) field = Core.assets.getOrNull(data.name + ".ogg", Sound.class);
|
||||||
|
if(field == null) field = Core.assets.getOrNull(data.name + ".mp3", Sound.class);
|
||||||
|
if(field == null) field = Core.assets.getOrNull(data.name, Sound.class);
|
||||||
|
|
||||||
if(!allowAssetLoading && field == null){
|
if(!allowAssetLoading && field == null){
|
||||||
warn("Sound not found: @", data.asString());
|
warn("Sound not found: @", data.asString());
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package mindustry.mod;
|
||||||
|
|
||||||
|
import arc.*;
|
||||||
|
import arc.files.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import arc.util.io.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
|
|
||||||
|
public class DataBundleLoader{
|
||||||
|
|
||||||
|
public void load(Seq<BundleAsset> assets){
|
||||||
|
//add new keys to each bundle
|
||||||
|
I18NBundle bundle = Core.bundle;
|
||||||
|
while(bundle != null){
|
||||||
|
String str = bundle.getLocale().toString();
|
||||||
|
String locale = "bundle" + (str.isEmpty() ? "" : "_" + str);
|
||||||
|
for(Fi file : bundles.get(locale, Seq::new)){
|
||||||
|
try{
|
||||||
|
PropertiesUtils.load(bundle.getProperties(), file.reader());
|
||||||
|
}catch(Throwable e){
|
||||||
|
Log.err("Error loading bundle: " + file + "/" + locale, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bundle = bundle.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unload(){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-6
@@ -1,4 +1,4 @@
|
|||||||
package mindustry.graphics;
|
package mindustry.mod;
|
||||||
|
|
||||||
import arc.*;
|
import arc.*;
|
||||||
import arc.graphics.*;
|
import arc.graphics.*;
|
||||||
@@ -10,20 +10,20 @@ import arc.struct.*;
|
|||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import arc.util.Log.*;
|
import arc.util.Log.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.data.*;
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/** Manages data patch images. */
|
/** Manages data patch images. */
|
||||||
public class DataPatchPacker{
|
public class DataImagePacker{
|
||||||
public static final String regionPrefix = "dp-";
|
public static final String regionPrefix = "dp-";
|
||||||
|
|
||||||
private @Nullable TextureAtlas patchAtlas;
|
private @Nullable TextureAtlas patchAtlas;
|
||||||
|
|
||||||
/** Packs a new set of images. If images are already packed, disposes of the old ones. */
|
/** Packs a new set of images. If images are already packed, disposes of the old ones. */
|
||||||
public void pack(Seq<PatchImage> images){
|
public void pack(Seq<ImageAsset> images){
|
||||||
if(patchAtlas != null){
|
if(patchAtlas != null){
|
||||||
unapply();
|
unload();
|
||||||
}
|
}
|
||||||
if(images.isEmpty()) return;
|
if(images.isEmpty()) return;
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ public class DataPatchPacker{
|
|||||||
Log.debug("[Patch Atlas] Time to pack: @ms", Time.elapsed());
|
Log.debug("[Patch Atlas] Time to pack: @ms", Time.elapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unapply(){
|
public void unload(){
|
||||||
if(patchAtlas != null){
|
if(patchAtlas != null){
|
||||||
for(var texture : patchAtlas.getTextures()){
|
for(var texture : patchAtlas.getTextures()){
|
||||||
patchAtlas.getTextures().remove(texture);
|
patchAtlas.getTextures().remove(texture);
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package mindustry.mod;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import mindustry.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
|
|
||||||
|
public class DataManager{
|
||||||
|
private DataPatcher patcher = new DataPatcher();
|
||||||
|
private DataImagePacker packer = new DataImagePacker();
|
||||||
|
private DataSoundLoader soundLoader = new DataSoundLoader();
|
||||||
|
private DataBundleLoader bundleLoader = new DataBundleLoader();
|
||||||
|
|
||||||
|
private ObjectMap<DataAssetType, Seq<DataAsset>> assets = new ObjectMap<>();
|
||||||
|
|
||||||
|
public void load(Seq<DataAsset> newAssets){
|
||||||
|
unload(); //if already loaded
|
||||||
|
|
||||||
|
if(newAssets.isEmpty()) return;
|
||||||
|
|
||||||
|
for(var asset : newAssets){
|
||||||
|
assets.get(asset.getType(), Seq::new).add(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bundleLoader.load(getBundles());
|
||||||
|
|
||||||
|
soundLoader.load(getSounds(), getMusic());
|
||||||
|
|
||||||
|
if(!Vars.headless){
|
||||||
|
packer.pack(getImages());
|
||||||
|
}
|
||||||
|
|
||||||
|
patcher.apply(getPatches());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unload(){
|
||||||
|
patcher.unapply();
|
||||||
|
if(!Vars.headless) packer.unload();
|
||||||
|
soundLoader.unload();
|
||||||
|
|
||||||
|
assets.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<DataAsset> getAllAssets(){
|
||||||
|
Seq<DataAsset> result = new Seq<>();
|
||||||
|
assets.each((key, seq) -> result.addAll(seq));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPatched(Object content){
|
||||||
|
return patcher.isPatched(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends DataAsset> Seq<T> getAssets(DataAssetType type){
|
||||||
|
return assets.get(type, Seq::new).as();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<ImageAsset> getImages(){
|
||||||
|
return getAssets(DataAssetType.image);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<PatchAsset> getPatches(){
|
||||||
|
return getAssets(DataAssetType.patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<MusicAsset> getMusic(){
|
||||||
|
return getAssets(DataAssetType.music);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<SoundAsset> getSounds(){
|
||||||
|
return getAssets(DataAssetType.sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<BundleAsset> getBundles(){
|
||||||
|
return getAssets(DataAssetType.bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq<ContentAsset> getContent(){
|
||||||
|
return getAssets(DataAssetType.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import arc.struct.*;
|
|||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import arc.util.serialization.Json.*;
|
import arc.util.serialization.Json.*;
|
||||||
import arc.util.serialization.*;
|
import arc.util.serialization.*;
|
||||||
import arc.util.serialization.JsonWriter.*;
|
|
||||||
import arc.util.serialization.Jval.*;
|
import arc.util.serialization.Jval.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.core.*;
|
import mindustry.core.*;
|
||||||
@@ -13,7 +12,7 @@ import mindustry.ctype.*;
|
|||||||
import mindustry.entities.part.*;
|
import mindustry.entities.part.*;
|
||||||
import mindustry.entities.units.*;
|
import mindustry.entities.units.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.graphics.*;
|
import mindustry.mod.data.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
import mindustry.world.blocks.*;
|
import mindustry.world.blocks.*;
|
||||||
@@ -28,10 +27,11 @@ import java.util.*;
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class DataPatcher{
|
public class DataPatcher{
|
||||||
public static final int maxImageSize = 1024;
|
public static final int maxImageSize = 1024;
|
||||||
public static final int patchFormatVersion = 1;
|
public static final int patchFormatVersion = 2;
|
||||||
|
|
||||||
private static final Object root = new Object();
|
private static final Object root = new Object();
|
||||||
private static final ObjectMap<String, ContentType> nameToType = new ObjectMap<>();
|
private static final ObjectMap<String, ContentType> nameToType = new ObjectMap<>();
|
||||||
|
private static DataPatcher currentDataPatcher;
|
||||||
private static ContentParser parser = createParser();
|
private static ContentParser parser = createParser();
|
||||||
|
|
||||||
private boolean applied;
|
private boolean applied;
|
||||||
@@ -40,13 +40,7 @@ public class DataPatcher{
|
|||||||
private Seq<Runnable> resetters = new Seq<>();
|
private Seq<Runnable> resetters = new Seq<>();
|
||||||
private Seq<Runnable> afterCallbacks = new Seq<>();
|
private Seq<Runnable> afterCallbacks = new Seq<>();
|
||||||
private Seq<Object> visitStack = new Seq<>();
|
private Seq<Object> visitStack = new Seq<>();
|
||||||
private @Nullable PatchSet currentlyApplying;
|
private @Nullable PatchAsset currentlyApplying;
|
||||||
private DataPatchPacker packer = new DataPatchPacker();
|
|
||||||
|
|
||||||
/** Currently active patches. Note that apply() should be called after modification. */
|
|
||||||
public Seq<PatchSet> patches = new Seq<>();
|
|
||||||
/** Currently loaded patch images. */
|
|
||||||
public Seq<PatchImage> images = new Seq<>();
|
|
||||||
|
|
||||||
static{
|
static{
|
||||||
for(var type : ContentType.all){
|
for(var type : ContentType.all){
|
||||||
@@ -59,8 +53,8 @@ public class DataPatcher{
|
|||||||
@Override
|
@Override
|
||||||
void warn(String string, Object... format){
|
void warn(String string, Object... format){
|
||||||
//forward warnings to the current patcher - this is a bit hacky, but I do not want to re-initialize the parser every time
|
//forward warnings to the current patcher - this is a bit hacky, but I do not want to re-initialize the parser every time
|
||||||
if(Vars.state.patcher != null){
|
if(currentDataPatcher!= null){
|
||||||
Vars.state.patcher.warn(string, format);
|
currentDataPatcher.warn(string, format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -74,78 +68,74 @@ public class DataPatcher{
|
|||||||
return usedpatches.contains(object);
|
return usedpatches.contains(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyImages(Seq<PatchImage> images){
|
|
||||||
this.images = images;
|
|
||||||
|
|
||||||
if(!Vars.headless) packer.pack(images);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Applies the specified patches. If patches were already applied, the previous ones are un-applied - they do not stack! */
|
/** Applies the specified patches. If patches were already applied, the previous ones are un-applied - they do not stack! */
|
||||||
public void apply(Seq<String> patchArray) throws Exception{
|
public void apply(Seq<PatchAsset> patchArray){
|
||||||
|
//if you're un-applying data patches, and it throws an error, just crash. this is not recoverable.
|
||||||
if(applied){
|
if(applied){
|
||||||
unapply();
|
unapply();
|
||||||
applied = false;
|
applied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
applied = true;
|
if(patchArray.isEmpty()) return;
|
||||||
contentLoader = Vars.content.copy();
|
|
||||||
patches.clear();
|
|
||||||
|
|
||||||
Attribute[] oldAttributes = Attribute.all.clone();
|
try{
|
||||||
var oldAttributeMap = Attribute.map.copy();
|
currentDataPatcher = this;
|
||||||
reset(() -> {
|
applied = true;
|
||||||
Attribute.all = oldAttributes;
|
contentLoader = Vars.content.copy();
|
||||||
Attribute.map = oldAttributeMap;
|
|
||||||
});
|
|
||||||
|
|
||||||
for(String patch : patchArray){
|
Attribute[] oldAttributes = Attribute.all.clone();
|
||||||
PatchSet set = new PatchSet(patch, new JsonValue("error"));
|
var oldAttributeMap = Attribute.map.copy();
|
||||||
|
reset(() -> {
|
||||||
|
Attribute.all = oldAttributes;
|
||||||
|
Attribute.map = oldAttributeMap;
|
||||||
|
});
|
||||||
|
|
||||||
try{
|
for(var set : patchArray){
|
||||||
JsonValue value = parser.getJson().fromJson(null, Jval.read(patch).toString(Jformat.plain));
|
|
||||||
if(Vars.state.rules.planet != null && value.has("requiredPlanets")){
|
|
||||||
JsonValue req = value.get("requiredPlanets");
|
|
||||||
value.remove("requiredPlanets");
|
|
||||||
|
|
||||||
//this should be ignored unless this instance is a dedicated server
|
try{
|
||||||
if(Vars.headless){
|
JsonValue value = parser.getJson().fromJson(null, Jval.read(set.patch).toString(Jformat.plain));
|
||||||
String[] planets = req.isArray() ? req.asStringArray() : new String[]{req.asString()};
|
if(Vars.state.rules.planet != null && value.has("requiredPlanets")){
|
||||||
if(!Structs.contains(planets, Vars.state.rules.planet.name)){
|
JsonValue req = value.get("requiredPlanets");
|
||||||
continue;
|
value.remove("requiredPlanets");
|
||||||
|
|
||||||
|
//this should be ignored unless this instance is a dedicated server
|
||||||
|
if(Vars.headless){
|
||||||
|
String[] planets = req.isArray() ? req.asStringArray() : new String[]{req.asString()};
|
||||||
|
if(!Structs.contains(planets, Vars.state.rules.planet.name)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set.json = value;
|
||||||
|
currentlyApplying = set;
|
||||||
|
visitStack.clear();
|
||||||
|
|
||||||
|
set.name = value.getString("name", "");
|
||||||
|
value.remove("name"); //patchsets can have a name, ignore it if present
|
||||||
|
for(var child : value){
|
||||||
|
assign(root, child.name, child, null, null, null);
|
||||||
|
}
|
||||||
|
currentlyApplying = null;
|
||||||
|
|
||||||
|
}catch(Exception e){
|
||||||
|
set.error = true;
|
||||||
|
set.warnings.add(Strings.getSimpleMessage(e));
|
||||||
|
currentlyApplying = null;
|
||||||
|
|
||||||
|
Log.err("Failed to apply patch: " + set.patch, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
set.json = value;
|
|
||||||
currentlyApplying = set;
|
|
||||||
visitStack.clear();
|
|
||||||
|
|
||||||
set.name = value.getString("name", "");
|
|
||||||
value.remove("name"); //patchsets can have a name, ignore it if present
|
|
||||||
for(var child : value){
|
|
||||||
assign(root, child.name, child, null, null, null);
|
|
||||||
}
|
|
||||||
currentlyApplying = null;
|
|
||||||
|
|
||||||
}catch(Exception e){
|
|
||||||
set.error = true;
|
|
||||||
set.warnings.add(Strings.getSimpleMessage(e));
|
|
||||||
currentlyApplying = null;
|
|
||||||
|
|
||||||
Log.err("Failed to apply patch: " + patch, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patches.add(set);
|
afterCallbacks.each(Runnable::run);
|
||||||
|
}catch(Exception e){
|
||||||
|
Log.err("Failed to apply data patches: " + patchArray.map(p -> p.patch));
|
||||||
}
|
}
|
||||||
|
|
||||||
afterCallbacks.each(Runnable::run);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unapply(){
|
public void unapply(){
|
||||||
if(!applied) return;
|
if(!applied) return;
|
||||||
|
|
||||||
if(!Vars.headless) packer.unapply();
|
|
||||||
|
|
||||||
Vars.content = contentLoader;
|
Vars.content = contentLoader;
|
||||||
applied = false;
|
applied = false;
|
||||||
|
|
||||||
@@ -642,30 +632,6 @@ public class DataPatcher{
|
|||||||
else return ((Object[])object).clone();
|
else return ((Object[])object).clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PatchSet{
|
|
||||||
/** Raw string value, containing original formatting. */
|
|
||||||
public String patch;
|
|
||||||
/** Parsed JSON value. Can be an empty error value if parsing failed. */
|
|
||||||
public JsonValue json;
|
|
||||||
/** Named obtained from patch. */
|
|
||||||
public String name = "";
|
|
||||||
/** True if an error was encountered. */
|
|
||||||
public boolean error;
|
|
||||||
/** Warnings encountered during patching. */
|
|
||||||
public Seq<String> warnings = new Seq<>();
|
|
||||||
|
|
||||||
public PatchSet(String patch, JsonValue json){
|
|
||||||
this.patch = patch;
|
|
||||||
this.json = json;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(){
|
|
||||||
//the json can be a single 'error' value if it failed to parse
|
|
||||||
return !json.isObject() ? patch : json.prettyPrint(OutputType.minimal, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FieldData{
|
private static class FieldData{
|
||||||
Class type, elementType, keyType;
|
Class type, elementType, keyType;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package mindustry.mod;
|
||||||
|
|
||||||
|
import arc.*;
|
||||||
|
import arc.audio.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import mindustry.*;
|
||||||
|
import mindustry.gen.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
|
|
||||||
|
public class DataSoundLoader{
|
||||||
|
private static final int soundIdOffset = 100_000;
|
||||||
|
private static final String prefix = "dp-";
|
||||||
|
|
||||||
|
private Seq<Sound> loadedSounds = new Seq<>();
|
||||||
|
private Seq<Music> loadedMusic = new Seq<>();
|
||||||
|
|
||||||
|
public void load(Seq<SoundAsset> sounds, Seq<MusicAsset> musics){
|
||||||
|
|
||||||
|
int nextSoundId = soundIdOffset + 1;
|
||||||
|
|
||||||
|
for(var asset : sounds){
|
||||||
|
Sound sound = Vars.headless ? new Sound() : Sound.createLazy(prefix + asset.name, asset.data);
|
||||||
|
loadedSounds.add(sound);
|
||||||
|
|
||||||
|
Sounds.registerSound(sound, nextSoundId ++);
|
||||||
|
|
||||||
|
if(Vars.headless || !Core.audio.initialized()) continue;
|
||||||
|
|
||||||
|
Core.assets.addAsset(sound.file.toString(), Sound.class, sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var asset : musics){
|
||||||
|
Music music = Vars.headless ? new Music() : Music.createLazy(prefix + asset.name, asset.data);
|
||||||
|
loadedMusic.add(music);
|
||||||
|
|
||||||
|
if(Vars.headless || !Core.audio.initialized()) continue;
|
||||||
|
|
||||||
|
Core.assets.addAsset(music.file.toString(), Music.class, music);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unload(){
|
||||||
|
for(var sound : loadedSounds){
|
||||||
|
if(!Vars.headless) Core.assets.unload(sound.file.toString());
|
||||||
|
sound.dispose();
|
||||||
|
Sounds.unregisterSound(sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var music : loadedMusic){
|
||||||
|
if(!Vars.headless) Core.assets.unload(music.file.toString());
|
||||||
|
music.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedSounds.clear();
|
||||||
|
loadedMusic.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public abstract class AudioAsset extends DataAsset{
|
||||||
|
public byte[] data;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void read(DataInput stream) throws IOException{
|
||||||
|
data = new byte[stream.readInt()];
|
||||||
|
stream.readFully(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(DataOutput stream) throws IOException{
|
||||||
|
stream.writeInt(data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
import arc.util.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class BundleAsset extends DataAsset{
|
||||||
|
public String string = "";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataAssetType getType(){
|
||||||
|
return DataAssetType.bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void read(DataInput stream) throws IOException{
|
||||||
|
int len = stream.readInt();
|
||||||
|
byte[] bytes = new byte[len];
|
||||||
|
stream.readFully(bytes);
|
||||||
|
string = new String(bytes, Strings.utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(DataOutput stream) throws IOException{
|
||||||
|
byte[] bytes = string.getBytes(Strings.utf8);
|
||||||
|
stream.writeInt(bytes.length);
|
||||||
|
stream.write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.ctype.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class ContentAsset extends DataAsset{
|
||||||
|
public ContentType type = ContentType.unit;
|
||||||
|
public String data = "";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataAssetType getType(){
|
||||||
|
return DataAssetType.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void read(DataInput stream) throws IOException{
|
||||||
|
type = ContentType.all[stream.readShort()];
|
||||||
|
int len = stream.readInt();
|
||||||
|
byte[] bytes = new byte[len];
|
||||||
|
stream.readFully(bytes);
|
||||||
|
data = new String(bytes, Strings.utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(DataOutput stream) throws IOException{
|
||||||
|
stream.writeShort(type.ordinal());
|
||||||
|
byte[] bytes = data.getBytes(Strings.utf8);
|
||||||
|
stream.writeInt(bytes.length);
|
||||||
|
stream.write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
import arc.util.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/** Abstract class for a kind of asset used in an asset mod. */
|
||||||
|
public abstract class DataAsset{
|
||||||
|
/** File path, including name and extension, but excluding base folder prefix. */
|
||||||
|
public String path;
|
||||||
|
/** File name, excluding extension. This is taken from the path. */
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
public void setPath(String path){
|
||||||
|
this.path = path.replace('\\', '/');
|
||||||
|
this.name = Strings.getFileNameWithoutExtension(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract DataAssetType getType();
|
||||||
|
|
||||||
|
abstract void read(DataInput stream) throws IOException;
|
||||||
|
|
||||||
|
abstract void write(DataOutput stream) throws IOException;
|
||||||
|
|
||||||
|
public static DataAsset readAsset(DataInput input) throws IOException{
|
||||||
|
short typeId = input.readShort();
|
||||||
|
if(typeId < 0 || typeId >= DataAssetType.all.length) throw new IOException("Invalid asset type ID: " + typeId);
|
||||||
|
|
||||||
|
String path = input.readUTF();
|
||||||
|
var type = DataAssetType.all[typeId];
|
||||||
|
var asset = type.create();
|
||||||
|
|
||||||
|
asset.setPath(path);
|
||||||
|
asset.read(input);
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeAsset(DataAsset asset, DataOutput output) throws IOException{
|
||||||
|
var type = asset.getType();
|
||||||
|
output.writeShort(type.ordinal());
|
||||||
|
output.writeUTF(asset.path);
|
||||||
|
asset.write(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
import arc.func.*;
|
||||||
|
|
||||||
|
public enum DataAssetType{
|
||||||
|
patch("patches", PatchAsset::new),
|
||||||
|
content("content", ContentAsset::new),
|
||||||
|
image("sprites", ImageAsset::new),
|
||||||
|
sound("sounds", SoundAsset::new),
|
||||||
|
music("music", MusicAsset::new),
|
||||||
|
bundle("bundles", BundleAsset::new);
|
||||||
|
|
||||||
|
public final String folder;
|
||||||
|
public final Prov<DataAsset> constructor;
|
||||||
|
|
||||||
|
public static final DataAssetType[] all = values();
|
||||||
|
|
||||||
|
DataAssetType(String folder, Prov<DataAsset> constructor){
|
||||||
|
this.folder = folder;
|
||||||
|
this.constructor = constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataAsset create(){
|
||||||
|
return constructor.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
-11
@@ -1,20 +1,17 @@
|
|||||||
package mindustry.mod;
|
package mindustry.mod.data;
|
||||||
|
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
|
import mindustry.mod.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
public class PatchImage implements Comparable<PatchImage>{
|
public class ImageAsset extends DataAsset implements Comparable<ImageAsset>{
|
||||||
/** Image name without extension; does not contain packing prefix. */
|
|
||||||
public String name;
|
|
||||||
/** Image path, excluding extension. */
|
|
||||||
public String path;
|
|
||||||
/** Size of encoded image. */
|
/** Size of encoded image. */
|
||||||
public int width, height;
|
public int width, height;
|
||||||
/** Encoded PNG data. */
|
/** Encoded PNG data. */
|
||||||
public byte[] data;
|
public byte[] data;
|
||||||
|
|
||||||
public PatchImage(String path, int width, int height, byte[] data){
|
public ImageAsset(String path, int width, int height, byte[] data){
|
||||||
Fi file = new Fi(path);
|
Fi file = new Fi(path);
|
||||||
this.name = file.nameWithoutExtension();
|
this.name = file.nameWithoutExtension();
|
||||||
this.path = file.pathWithoutExtension();
|
this.path = file.pathWithoutExtension();
|
||||||
@@ -23,7 +20,9 @@ public class PatchImage implements Comparable<PatchImage>{
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PatchImage fromFile(String relativePath, Fi file) throws IOException{
|
ImageAsset(){}
|
||||||
|
|
||||||
|
public static ImageAsset fromFile(String relativePath, Fi file) throws IOException{
|
||||||
byte[] data = file.readBytes();
|
byte[] data = file.readBytes();
|
||||||
int width, height;
|
int width, height;
|
||||||
//perform basic validation and fetch size from IHDR chunk
|
//perform basic validation and fetch size from IHDR chunk
|
||||||
@@ -40,11 +39,32 @@ public class PatchImage implements Comparable<PatchImage>{
|
|||||||
if(width <= 0 || height <= 0) throw new IOException("PNG size must be positive.");
|
if(width <= 0 || height <= 0) throw new IOException("PNG size must be positive.");
|
||||||
if(width > DataPatcher.maxImageSize || height > DataPatcher.maxImageSize) throw new IOException("PNG is larger than maximum image size (" + DataPatcher.maxImageSize + "x" + DataPatcher.maxImageSize + ")");
|
if(width > DataPatcher.maxImageSize || height > DataPatcher.maxImageSize) throw new IOException("PNG is larger than maximum image size (" + DataPatcher.maxImageSize + "x" + DataPatcher.maxImageSize + ")");
|
||||||
}
|
}
|
||||||
return new PatchImage(relativePath, width, height, data);
|
return new ImageAsset(relativePath, width, height, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(PatchImage patchImage){
|
void read(DataInput stream) throws IOException{
|
||||||
return path.compareTo(patchImage.path);
|
width = stream.readShort();
|
||||||
|
height = stream.readShort();
|
||||||
|
data = new byte[stream.readInt()];
|
||||||
|
stream.readFully(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(DataOutput stream) throws IOException{
|
||||||
|
stream.writeShort(width);
|
||||||
|
stream.writeShort(height);
|
||||||
|
stream.writeInt(data.length);
|
||||||
|
stream.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataAssetType getType(){
|
||||||
|
return DataAssetType.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ImageAsset imageAsset){
|
||||||
|
return path.compareTo(imageAsset.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
public class MusicAsset extends AudioAsset{
|
||||||
|
@Override
|
||||||
|
public DataAssetType getType(){
|
||||||
|
return DataAssetType.music;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import arc.util.serialization.*;
|
||||||
|
import arc.util.serialization.JsonWriter.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class PatchAsset extends DataAsset{
|
||||||
|
private static final JsonValue emptyValue = new JsonValue("error");
|
||||||
|
|
||||||
|
/** Raw string value, containing original formatting. */
|
||||||
|
public String patch = "";
|
||||||
|
/** Parsed JSON value. Can be an empty error value if parsing failed. */
|
||||||
|
public JsonValue json = emptyValue;
|
||||||
|
/** Named obtained from patch. */
|
||||||
|
public String name = "";
|
||||||
|
/** True if an error was encountered. */
|
||||||
|
public boolean error;
|
||||||
|
/** Warnings encountered during patching. */
|
||||||
|
public Seq<String> warnings = new Seq<>();
|
||||||
|
|
||||||
|
public PatchAsset(String patch){
|
||||||
|
this.patch = patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchAsset(){}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataAssetType getType(){
|
||||||
|
return DataAssetType.patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void read(DataInput stream) throws IOException{
|
||||||
|
int len = stream.readInt();
|
||||||
|
byte[] bytes = new byte[len];
|
||||||
|
stream.readFully(bytes);
|
||||||
|
patch = new String(bytes, Strings.utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(DataOutput stream) throws IOException{
|
||||||
|
byte[] bytes = patch.getBytes(Strings.utf8);
|
||||||
|
stream.writeInt(bytes.length);
|
||||||
|
stream.write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
//the json can be a single 'error' value if it failed to parse
|
||||||
|
return !json.isObject() ? patch : json.prettyPrint(OutputType.minimal, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package mindustry.mod.data;
|
||||||
|
|
||||||
|
public class SoundAsset extends AudioAsset{
|
||||||
|
@Override
|
||||||
|
public DataAssetType getType(){
|
||||||
|
return DataAssetType.sound;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ public class CrashHandler{
|
|||||||
+ "Cores: " + OS.cores + "\n"
|
+ "Cores: " + OS.cores + "\n"
|
||||||
+ (cause == null ? "" : "Likely Cause: " + cause.meta.displayName + " (" + cause.name + " v" + cause.meta.version + ")\n")
|
+ (cause == null ? "" : "Likely Cause: " + cause.meta.displayName + " (" + cause.name + " v" + cause.meta.version + ")\n")
|
||||||
+ (enabledMods == null ? "<no mod init>" : "Mods: " + (enabledMods.isEmpty() ? "none (vanilla)" : enabledMods.toString(", ", mod -> mod.name + ":" + mod.meta.version))) + "\n"
|
+ (enabledMods == null ? "<no mod init>" : "Mods: " + (enabledMods.isEmpty() ? "none (vanilla)" : enabledMods.toString(", ", mod -> mod.name + ":" + mod.meta.version))) + "\n"
|
||||||
+ (state != null && state.patcher != null && state.patcher.patches != null && state.patcher.patches.size > 0 ? "Patches: \n" + state.patcher.patches.toString("\n---\n", p -> p.patch) + "\n" : "")
|
+ (state != null && state.data != null && state.data.getPatches().size > 0 ? "Patches: \n" + state.data.getPatches().toString("\n---\n", p -> p.patch) + "\n" : "")
|
||||||
+ "\n\n" + error;
|
+ "\n\n" + error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class ContentInfoDialog extends BaseDialog{
|
|||||||
|
|
||||||
table.row();
|
table.row();
|
||||||
|
|
||||||
if(state.isGame() && state.patcher.isPatched(content)){
|
if(state.isGame() && state.data.isPatched(content)){
|
||||||
table.table(t -> {
|
table.table(t -> {
|
||||||
t.image(Icon.info).color(Pal.lightishGray);
|
t.image(Icon.info).color(Pal.lightishGray);
|
||||||
t.add("@database.patched").color(Pal.lightishGray).padLeft(4f);
|
t.add("@database.patched").color(Pal.lightishGray).padLeft(4f);
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public class DatabaseDialog extends BaseDialog{
|
|||||||
setColor(Color.scarlet);
|
setColor(Color.scarlet);
|
||||||
touchable = Touchable.disabled;
|
touchable = Touchable.disabled;
|
||||||
}}).size(8 * 4).pad(3);
|
}}).size(8 * 4).pad(3);
|
||||||
}else if(state.isGame() && state.patcher.isPatched(unlock)){
|
}else if(state.isGame() && state.data.isPatched(unlock)){
|
||||||
list.stack(image, new Table(){{
|
list.stack(image, new Table(){{
|
||||||
right().bottom().touchable = Touchable.disabled;
|
right().bottom().touchable = Touchable.disabled;
|
||||||
// Interpolated color (lerp lightishGray and white) for better contrast
|
// Interpolated color (lerp lightishGray and white) for better contrast
|
||||||
|
|||||||
+1
-1
@@ -26,4 +26,4 @@ org.gradle.caching=true
|
|||||||
org.gradle.internal.http.socketTimeout=100000
|
org.gradle.internal.http.socketTimeout=100000
|
||||||
org.gradle.internal.http.connectionTimeout=100000
|
org.gradle.internal.http.connectionTimeout=100000
|
||||||
android.enableR8.fullMode=false
|
android.enableR8.fullMode=false
|
||||||
archash=c5139df8d4
|
archash=9979f6e052
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import mindustry.io.*;
|
|||||||
import mindustry.maps.*;
|
import mindustry.maps.*;
|
||||||
import mindustry.maps.Maps.*;
|
import mindustry.maps.Maps.*;
|
||||||
import mindustry.mod.Mods.*;
|
import mindustry.mod.Mods.*;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.data.*;
|
||||||
import mindustry.net.Administration.*;
|
import mindustry.net.Administration.*;
|
||||||
import mindustry.net.Packets.*;
|
import mindustry.net.Packets.*;
|
||||||
import mindustry.net.*;
|
import mindustry.net.*;
|
||||||
@@ -68,7 +68,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
private boolean autoPaused = false;
|
private boolean autoPaused = false;
|
||||||
private Fi patchDirectory, patchImageDirectory;
|
private Fi patchDirectory, patchImageDirectory;
|
||||||
private Seq<String> contentPatches = new Seq<>();
|
private Seq<String> contentPatches = new Seq<>();
|
||||||
private Seq<PatchImage> contentPatchImages = new Seq<>();
|
private Seq<ImageAsset> contentPatchImages = new Seq<>();
|
||||||
|
|
||||||
private LineReader lineReader;
|
private LineReader lineReader;
|
||||||
|
|
||||||
@@ -344,7 +344,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
info("Server loaded. Type @ for help.", "'help'");
|
info("Server loaded. Type @ for help.", "'help'");
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(ContentPatchLoadEvent.class, event -> {
|
Events.on(DataPatchLoadEvent.class, event -> {
|
||||||
//NOTE: if patches change, and an older save is loaded, the patches will be applied twice; the old ones won't be removed.
|
//NOTE: if patches change, and an older save is loaded, the patches will be applied twice; the old ones won't be removed.
|
||||||
for(String patch : contentPatches){
|
for(String patch : contentPatches){
|
||||||
event.patches.addUnique(patch);
|
event.patches.addUnique(patch);
|
||||||
@@ -352,7 +352,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
|
|
||||||
//add images that aren't already in the patch
|
//add images that aren't already in the patch
|
||||||
ObjectSet<String> usedImages = event.images.map(i -> i.path).asSet();
|
ObjectSet<String> usedImages = event.images.map(i -> i.path).asSet();
|
||||||
for(PatchImage image : contentPatchImages){
|
for(ImageAsset image : contentPatchImages){
|
||||||
if(usedImages.add(image.path)){
|
if(usedImages.add(image.path)){
|
||||||
event.images.add(image);
|
event.images.add(image);
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
|
|
||||||
for(Fi image : imageFiles){
|
for(Fi image : imageFiles){
|
||||||
try{
|
try{
|
||||||
contentPatchImages.add(PatchImage.fromFile(image.absolutePath().substring(patchImageDirectory.absolutePath().length() + 1), image));
|
contentPatchImages.add(ImageAsset.fromFile(image.absolutePath().substring(patchImageDirectory.absolutePath().length() + 1), image));
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
Log.err("Invalid patch image file: " + image.path(), e);
|
Log.err("Invalid patch image file: " + image.path(), e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import mindustry.content.*;
|
|||||||
import mindustry.entities.abilities.*;
|
import mindustry.entities.abilities.*;
|
||||||
import mindustry.entities.bullet.*;
|
import mindustry.entities.bullet.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
|
import mindustry.mod.data.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.world.blocks.defense.turrets.*;
|
import mindustry.world.blocks.defense.turrets.*;
|
||||||
import mindustry.world.blocks.production.*;
|
import mindustry.world.blocks.production.*;
|
||||||
@@ -19,6 +20,18 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
|
|
||||||
public class PatcherTests{
|
public class PatcherTests{
|
||||||
|
|
||||||
|
static void apply(String... patches){
|
||||||
|
Vars.state.data.load(Seq.with(patches).map(PatchAsset::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Seq<PatchAsset> getPatches(){
|
||||||
|
return Vars.state.data.getPatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertNoWarnings(){
|
||||||
|
assertEquals(new Seq<>(), getPatches().first().warnings);
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void init(){
|
static void init(){
|
||||||
ApplicationTests.launchApplication(false);
|
ApplicationTests.launchApplication(false);
|
||||||
@@ -55,7 +68,7 @@ public class PatcherTests{
|
|||||||
"""
|
"""
|
||||||
})
|
})
|
||||||
void unitFactoryPlans(String value) throws Exception{
|
void unitFactoryPlans(String value) throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with(value));
|
apply(value);
|
||||||
|
|
||||||
var plan = ((UnitFactory)Blocks.groundFactory).plans.find(u -> u.unit == UnitTypes.flare);
|
var plan = ((UnitFactory)Blocks.groundFactory).plans.find(u -> u.unit == UnitTypes.flare);
|
||||||
assertNotNull(plan, "A plan for flares must have been added.");
|
assertNotNull(plan, "A plan for flares must have been added.");
|
||||||
@@ -76,7 +89,7 @@ public class PatcherTests{
|
|||||||
var prev = reconstructor.upgrades.copy();
|
var prev = reconstructor.upgrades.copy();
|
||||||
var prevConsumes = reconstructor.<ConsumeItems>findConsumer(c -> c instanceof ConsumeItems).items;
|
var prevConsumes = reconstructor.<ConsumeItems>findConsumer(c -> c instanceof ConsumeItems).items;
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with(
|
apply(
|
||||||
"""
|
"""
|
||||||
block.additive-reconstructor.upgrades: [[dagger, flare]]
|
block.additive-reconstructor.upgrades: [[dagger, flare]]
|
||||||
block.additive-reconstructor.consumes: {
|
block.additive-reconstructor.consumes: {
|
||||||
@@ -84,7 +97,7 @@ public class PatcherTests{
|
|||||||
items: [surge-alloy/10, copper/20]
|
items: [surge-alloy/10, copper/20]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
));
|
);
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
var plan = reconstructor.upgrades.get(0);
|
var plan = reconstructor.upgrades.get(0);
|
||||||
@@ -103,11 +116,11 @@ public class PatcherTests{
|
|||||||
var reconstructor = ((Reconstructor)Blocks.additiveReconstructor);
|
var reconstructor = ((Reconstructor)Blocks.additiveReconstructor);
|
||||||
var prev = reconstructor.upgrades.copy();
|
var prev = reconstructor.upgrades.copy();
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with(
|
apply(
|
||||||
"""
|
"""
|
||||||
block.additive-reconstructor.upgrades.1: [dagger, flare]
|
block.additive-reconstructor.upgrades.1: [dagger, flare]
|
||||||
"""
|
"""
|
||||||
));
|
);
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
var plan = reconstructor.upgrades.get(1);
|
var plan = reconstructor.upgrades.get(1);
|
||||||
@@ -123,11 +136,11 @@ public class PatcherTests{
|
|||||||
var reconstructor = ((Reconstructor)Blocks.additiveReconstructor);
|
var reconstructor = ((Reconstructor)Blocks.additiveReconstructor);
|
||||||
var prev = reconstructor.upgrades.copy();
|
var prev = reconstructor.upgrades.copy();
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with(
|
apply(
|
||||||
"""
|
"""
|
||||||
block.additive-reconstructor.upgrades.+: [[dagger, flare]]
|
block.additive-reconstructor.upgrades.+: [[dagger, flare]]
|
||||||
"""
|
"""
|
||||||
));
|
);
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
var plan = reconstructor.upgrades.peek();
|
var plan = reconstructor.upgrades.peek();
|
||||||
@@ -140,11 +153,11 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void consumeApply() throws Exception{
|
void consumeApply() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with(
|
apply(
|
||||||
"""
|
"""
|
||||||
block.conveyor.consumes: {power: 1}
|
block.conveyor.consumes: {power: 1}
|
||||||
"""
|
"""
|
||||||
));
|
);
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertTrue(Blocks.conveyor.hasPower);
|
assertTrue(Blocks.conveyor.hasPower);
|
||||||
@@ -164,7 +177,7 @@ public class PatcherTests{
|
|||||||
UnitTypes.dagger.stats.add(Stat.charge, 999);
|
UnitTypes.dagger.stats.add(Stat.charge, 999);
|
||||||
assertNotNull(UnitTypes.dagger.stats.toMap().get(StatCat.general).get(Stat.charge));
|
assertNotNull(UnitTypes.dagger.stats.toMap().get(StatCat.general).get(Stat.charge));
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.weapons.+: {
|
unit.dagger.weapons.+: {
|
||||||
name: navanax-weapon
|
name: navanax-weapon
|
||||||
bullet: {
|
bullet: {
|
||||||
@@ -172,7 +185,7 @@ public class PatcherTests{
|
|||||||
lightningLength: 999
|
lightningLength: 999
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(3, UnitTypes.dagger.weapons.size);
|
assertEquals(3, UnitTypes.dagger.weapons.size);
|
||||||
@@ -188,7 +201,7 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void uUnitWeaponReassign() throws Exception{
|
void uUnitWeaponReassign() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.weapons: [
|
unit.dagger.weapons: [
|
||||||
{
|
{
|
||||||
name: megapoop
|
name: megapoop
|
||||||
@@ -198,7 +211,7 @@ public class PatcherTests{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(1, UnitTypes.dagger.weapons.size);
|
assertEquals(1, UnitTypes.dagger.weapons.size);
|
||||||
assertEquals("megapoop", UnitTypes.dagger.weapons.get(0).name);
|
assertEquals("megapoop", UnitTypes.dagger.weapons.get(0).name);
|
||||||
@@ -213,12 +226,12 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unitAbilities() throws Exception{
|
void unitAbilities() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.abilities.+: {
|
unit.dagger.abilities.+: {
|
||||||
type: ShieldArcAbility
|
type: ShieldArcAbility
|
||||||
max: 1000
|
max: 1000
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(1, UnitTypes.dagger.abilities.size);
|
assertEquals(1, UnitTypes.dagger.abilities.size);
|
||||||
assertEquals(ShieldArcAbility.class, UnitTypes.dagger.abilities.get(0).getClass());
|
assertEquals(ShieldArcAbility.class, UnitTypes.dagger.abilities.get(0).getClass());
|
||||||
@@ -231,7 +244,7 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unitAbilitiesArray() throws Exception{
|
void unitAbilitiesArray() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.abilities.+: [
|
unit.dagger.abilities.+: [
|
||||||
{
|
{
|
||||||
type: ShieldArcAbility
|
type: ShieldArcAbility
|
||||||
@@ -242,7 +255,7 @@ public class PatcherTests{
|
|||||||
amount: 10
|
amount: 10
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(2, UnitTypes.dagger.abilities.size);
|
assertEquals(2, UnitTypes.dagger.abilities.size);
|
||||||
assertEquals(ShieldArcAbility.class, UnitTypes.dagger.abilities.get(0).getClass());
|
assertEquals(ShieldArcAbility.class, UnitTypes.dagger.abilities.get(0).getClass());
|
||||||
@@ -258,14 +271,14 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unitTypeObject() throws Exception{
|
void unitTypeObject() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
{
|
{
|
||||||
"name": "object syntax",
|
"name": "object syntax",
|
||||||
"unit.dagger": {
|
"unit.dagger": {
|
||||||
"type": "legs"
|
"type": "legs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
}
|
}
|
||||||
@@ -274,11 +287,11 @@ public class PatcherTests{
|
|||||||
void unitFlagsArray() throws Exception{
|
void unitFlagsArray() throws Exception{
|
||||||
int oldLength = UnitTypes.dagger.targetFlags.length;
|
int oldLength = UnitTypes.dagger.targetFlags.length;
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.targetFlags.+: [
|
unit.dagger.targetFlags.+: [
|
||||||
shield, drill
|
shield, drill
|
||||||
]
|
]
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(oldLength + 2, UnitTypes.dagger.targetFlags.length);
|
assertEquals(oldLength + 2, UnitTypes.dagger.targetFlags.length);
|
||||||
assertEquals(BlockFlag.shield, UnitTypes.dagger.targetFlags[UnitTypes.dagger.targetFlags.length - 2]);
|
assertEquals(BlockFlag.shield, UnitTypes.dagger.targetFlags[UnitTypes.dagger.targetFlags.length - 2]);
|
||||||
@@ -293,9 +306,9 @@ public class PatcherTests{
|
|||||||
void unitFlags() throws Exception{
|
void unitFlags() throws Exception{
|
||||||
int oldLength = UnitTypes.dagger.targetFlags.length;
|
int oldLength = UnitTypes.dagger.targetFlags.length;
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.targetFlags.+: shield
|
unit.dagger.targetFlags.+: shield
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(oldLength + 1, UnitTypes.dagger.targetFlags.length);
|
assertEquals(oldLength + 1, UnitTypes.dagger.targetFlags.length);
|
||||||
assertEquals(BlockFlag.shield, UnitTypes.dagger.targetFlags[UnitTypes.dagger.targetFlags.length - 1]);
|
assertEquals(BlockFlag.shield, UnitTypes.dagger.targetFlags[UnitTypes.dagger.targetFlags.length - 1]);
|
||||||
@@ -307,11 +320,11 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unitType() throws Exception{
|
void unitType() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.type: legs
|
unit.dagger.type: legs
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(0, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(0, getPatches().first().warnings.size);
|
||||||
assertEquals(LegsUnit.class, UnitTypes.dagger.constructor.get().getClass());
|
assertEquals(LegsUnit.class, UnitTypes.dagger.constructor.get().getClass());
|
||||||
|
|
||||||
Vars.logic.reset();
|
Vars.logic.reset();
|
||||||
@@ -321,61 +334,61 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cannotPatch() throws Exception{
|
void cannotPatch() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.conveyor.size: 2
|
block.conveyor.size: 2
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(1, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(1, getPatches().first().warnings.size);
|
||||||
assertEquals(1, Blocks.conveyor.size);
|
assertEquals(1, Blocks.conveyor.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void assignStringToObject() throws Exception{
|
void assignStringToObject() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.weapons: ["frog"]
|
unit.dagger.weapons: ["frog"]
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(1, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(1, getPatches().first().warnings.size);
|
||||||
assertEquals(2, UnitTypes.dagger.weapons.size);
|
assertEquals(2, UnitTypes.dagger.weapons.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void gibberish() throws Exception{
|
void gibberish() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
}[35209509()jfkjhadsf,
|
}[35209509()jfkjhadsf,
|
||||||
,,,,,[]
|
,,,,,[]
|
||||||
]{
|
]{
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(1, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(1, getPatches().first().warnings.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void noIdAssign() throws Exception{
|
void noIdAssign() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.router.id: 9231
|
block.router.id: 9231
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(1, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(1, getPatches().first().warnings.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unknownFieldWarn() throws Exception{
|
void unknownFieldWarn() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.weapons.+: {
|
unit.dagger.weapons.+: {
|
||||||
bullet: {
|
bullet: {
|
||||||
frogs: 99
|
frogs: 99
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unit.dagger.frogs: 10
|
unit.dagger.frogs: 10
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(2, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(2, getPatches().first().warnings.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void objectFloatMap() throws Exception{
|
void objectFloatMap() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.mechanical-drill.drillMultipliers: {
|
block.mechanical-drill.drillMultipliers: {
|
||||||
titanium: 2.0
|
titanium: 2.0
|
||||||
}
|
}
|
||||||
@@ -386,7 +399,7 @@ public class PatcherTests{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
block.mechanical-drill.drillMultipliers.surge-alloy: 10
|
block.mechanical-drill.drillMultipliers.surge-alloy: 10
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(2f, ((Drill)Blocks.mechanicalDrill).drillMultipliers.get(Items.titanium, 0f));
|
assertEquals(2f, ((Drill)Blocks.mechanicalDrill).drillMultipliers.get(Items.titanium, 0f));
|
||||||
@@ -402,12 +415,12 @@ public class PatcherTests{
|
|||||||
@Test
|
@Test
|
||||||
void specificArrayRequirements() throws Exception{
|
void specificArrayRequirements() throws Exception{
|
||||||
ItemStack[] reqs = Blocks.scatter.requirements.clone();
|
ItemStack[] reqs = Blocks.scatter.requirements.clone();
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.scatter.requirements: {
|
block.scatter.requirements: {
|
||||||
0: surge-alloy/10
|
0: surge-alloy/10
|
||||||
}
|
}
|
||||||
block.duo.requirements: [titanium/5, surge-alloy/20]
|
block.duo.requirements: [titanium/5, surge-alloy/20]
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(Blocks.scatter.requirements[0], new ItemStack(Items.surgeAlloy, 10));
|
assertEquals(Blocks.scatter.requirements[0], new ItemStack(Items.surgeAlloy, 10));
|
||||||
@@ -421,12 +434,12 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void attributes() throws Exception{
|
void attributes() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.grass.attributes: {
|
block.grass.attributes: {
|
||||||
oil: 99
|
oil: 99
|
||||||
}
|
}
|
||||||
block.grass.attributes.heat: 77
|
block.grass.attributes.heat: 77
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(99, Blocks.grass.attributes.get(Attribute.oil));
|
assertEquals(99, Blocks.grass.attributes.get(Attribute.oil));
|
||||||
@@ -440,11 +453,11 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void singleValue() throws Exception{
|
void singleValue() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block: {
|
block: {
|
||||||
graphite-press.craftTime: 1
|
graphite-press.craftTime: 1
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(1f, ((GenericCrafter)Blocks.graphitePress).craftTime);
|
assertEquals(1f, ((GenericCrafter)Blocks.graphitePress).craftTime);
|
||||||
@@ -452,13 +465,13 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void singleValue2() throws Exception{
|
void singleValue2() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block: {
|
block: {
|
||||||
graphite-press: {
|
graphite-press: {
|
||||||
craftTime: 1
|
craftTime: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(1f, ((GenericCrafter)Blocks.graphitePress).craftTime);
|
assertEquals(1f, ((GenericCrafter)Blocks.graphitePress).craftTime);
|
||||||
@@ -468,20 +481,20 @@ public class PatcherTests{
|
|||||||
void noResolution() throws Exception{
|
void noResolution() throws Exception{
|
||||||
String name = Pathfinder.class.getCanonicalName();
|
String name = Pathfinder.class.getCanonicalName();
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.conveyor.lastConfig: {
|
block.conveyor.lastConfig: {
|
||||||
class: %theClass%
|
class: %theClass%
|
||||||
}
|
}
|
||||||
""".replace("%theClass%", name)));
|
""".replace("%theClass%", name));
|
||||||
|
|
||||||
assertEquals(1, Vars.state.patcher.patches.first().warnings.size);
|
assertEquals(1, getPatches().first().warnings.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void setMultiAdd() throws Exception{
|
void setMultiAdd() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.immunities.+: [slow, fast]
|
unit.dagger.immunities.+: [slow, fast]
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertTrue(UnitTypes.dagger.immunities.contains(StatusEffects.slow));
|
assertTrue(UnitTypes.dagger.immunities.contains(StatusEffects.slow));
|
||||||
@@ -495,7 +508,7 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void ammoReassign() throws Exception{
|
void ammoReassign() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.fuse.ammoTypes: {
|
block.fuse.ammoTypes: {
|
||||||
titanium: "-"
|
titanium: "-"
|
||||||
surge-alloy: {
|
surge-alloy: {
|
||||||
@@ -506,7 +519,7 @@ public class PatcherTests{
|
|||||||
colors: ["000000", "ff0000", "ffffff"]
|
colors: ["000000", "ff0000", "ffffff"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertTrue(((ItemTurret)Blocks.fuse).ammoTypes.containsKey(Items.surgeAlloy));
|
assertTrue(((ItemTurret)Blocks.fuse).ammoTypes.containsKey(Items.surgeAlloy));
|
||||||
@@ -522,9 +535,9 @@ public class PatcherTests{
|
|||||||
@Test
|
@Test
|
||||||
void indexAccess() throws Exception{
|
void indexAccess() throws Exception{
|
||||||
float oldDamage = UnitTypes.dagger.weapons.first().bullet.damage;
|
float oldDamage = UnitTypes.dagger.weapons.first().bullet.damage;
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.dagger.weapons.0.bullet.damage: 100
|
unit.dagger.weapons.0.bullet.damage: 100
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(100, UnitTypes.dagger.weapons.first().bullet.damage);
|
assertEquals(100, UnitTypes.dagger.weapons.first().bullet.damage);
|
||||||
@@ -537,14 +550,14 @@ public class PatcherTests{
|
|||||||
@Test
|
@Test
|
||||||
void nestedArrays() throws Exception{
|
void nestedArrays() throws Exception{
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
{
|
{
|
||||||
"block.ship-refabricator.upgrades.0": {
|
"block.ship-refabricator.upgrades.0": {
|
||||||
"0": "dagger",
|
"0": "dagger",
|
||||||
"1": "mace"
|
"1": "mace"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
|
|
||||||
assertEquals(UnitTypes.dagger, ((Reconstructor)Blocks.shipRefabricator).upgrades.get(0)[0]);
|
assertEquals(UnitTypes.dagger, ((Reconstructor)Blocks.shipRefabricator).upgrades.get(0)[0]);
|
||||||
@@ -559,14 +572,14 @@ public class PatcherTests{
|
|||||||
@Test
|
@Test
|
||||||
void nestedArrays2() throws Exception{
|
void nestedArrays2() throws Exception{
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
{
|
{
|
||||||
"block.ship-refabricator": {
|
"block.ship-refabricator": {
|
||||||
"upgrades.0.0": "dagger",
|
"upgrades.0.0": "dagger",
|
||||||
"upgrades.0.1": "mace"
|
"upgrades.0.1": "mace"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
|
|
||||||
assertEquals(UnitTypes.dagger, ((Reconstructor)Blocks.shipRefabricator).upgrades.get(0)[0]);
|
assertEquals(UnitTypes.dagger, ((Reconstructor)Blocks.shipRefabricator).upgrades.get(0)[0]);
|
||||||
@@ -582,9 +595,9 @@ public class PatcherTests{
|
|||||||
void arrayMulti() throws Exception{
|
void arrayMulti() throws Exception{
|
||||||
int size = UnitTypes.emanate.weapons.size;
|
int size = UnitTypes.emanate.weapons.size;
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
{"name":"Patch0","unit":{"emanate":{"weapons":{"0":{"type":"Weapon","name":"toxopid-cannon"}},"weapons.+":[{"name":"sei-launcher"}]}}}
|
{"name":"Patch0","unit":{"emanate":{"weapons":{"0":{"type":"Weapon","name":"toxopid-cannon"}},"weapons.+":[{"name":"sei-launcher"}]}}}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertEquals(UnitTypes.emanate.weapons.size, size + 1);
|
assertEquals(UnitTypes.emanate.weapons.size, size + 1);
|
||||||
|
|
||||||
@@ -597,11 +610,11 @@ public class PatcherTests{
|
|||||||
void customAttribute() throws Exception{
|
void customAttribute() throws Exception{
|
||||||
int amount = Attribute.all.length;
|
int amount = Attribute.all.length;
|
||||||
|
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
block.grass.attributes: {
|
block.grass.attributes: {
|
||||||
frogs: 10
|
frogs: 10
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertTrue(Attribute.exists("frogs"));
|
assertTrue(Attribute.exists("frogs"));
|
||||||
assertEquals(amount + 1, Attribute.all.length);
|
assertEquals(amount + 1, Attribute.all.length);
|
||||||
@@ -616,7 +629,7 @@ public class PatcherTests{
|
|||||||
@Test
|
@Test
|
||||||
void addWeapon() throws Exception{
|
void addWeapon() throws Exception{
|
||||||
int oldSize = UnitTypes.flare.weapons.size;
|
int oldSize = UnitTypes.flare.weapons.size;
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
unit.flare.weapons.+: {
|
unit.flare.weapons.+: {
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
@@ -626,7 +639,7 @@ public class PatcherTests{
|
|||||||
damage: 100
|
damage: 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
assertEquals(oldSize + 1, UnitTypes.flare.weapons.size);
|
assertEquals(oldSize + 1, UnitTypes.flare.weapons.size);
|
||||||
@@ -635,7 +648,7 @@ public class PatcherTests{
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void bigPatch() throws Exception{
|
void bigPatch() throws Exception{
|
||||||
Vars.state.patcher.apply(Seq.with("""
|
apply("""
|
||||||
item: {
|
item: {
|
||||||
fissile-matter: {
|
fissile-matter: {
|
||||||
localizedName: Duo
|
localizedName: Duo
|
||||||
@@ -690,12 +703,8 @@ public class PatcherTests{
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""));
|
""");
|
||||||
|
|
||||||
assertNoWarnings();
|
assertNoWarnings();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void assertNoWarnings(){
|
|
||||||
assertEquals(new Seq<>(), Vars.state.patcher.patches.first().warnings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user