Support for jar mods on desktop
This commit is contained in:
@@ -136,7 +136,7 @@ public interface Platform{
|
||||
* @param extension File extension to filter
|
||||
*/
|
||||
default void showFileChooser(boolean open, String extension, Cons<Fi> cons){
|
||||
new FileChooser(open ? "$open" : "$save", file -> file.extension().toLowerCase().equals(extension), open, file -> {
|
||||
new FileChooser(open ? "$open" : "$save", file -> file.extEquals(extension), open, file -> {
|
||||
if(!open){
|
||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
||||
}else{
|
||||
@@ -145,6 +145,19 @@ public interface Platform{
|
||||
}).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a file chooser for multiple file types. Only supported on desktop.
|
||||
* @param cons Selection listener
|
||||
* @param extensions File extensions to filter
|
||||
*/
|
||||
default void showMultiFileChooser(Cons<Fi> cons, String... extensions){
|
||||
if(mobile){
|
||||
showFileChooser(true, extensions[0], cons);
|
||||
}else{
|
||||
new FileChooser("$open", file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show();
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the app. Android only. */
|
||||
default void hide(){
|
||||
}
|
||||
|
||||
@@ -141,6 +141,8 @@ public class Universe{
|
||||
//TODO events
|
||||
|
||||
Events.fire(new TurnEvent());
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public float secondsMod(float mod, float scale){
|
||||
|
||||
@@ -74,7 +74,6 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
//prepare campaign data for writing
|
||||
if(state.isCampaign()){
|
||||
state.secinfo.prepare();
|
||||
state.getSector().setLastSecond(universe.seconds());
|
||||
}
|
||||
|
||||
//flush tech node progress
|
||||
|
||||
@@ -15,6 +15,11 @@ public class Mod{
|
||||
|
||||
}
|
||||
|
||||
/** Called on clientside mods. Load content here. */
|
||||
public void loadContent(){
|
||||
|
||||
}
|
||||
|
||||
/** Register any commands to be used on the server side, e.g. from the console. */
|
||||
public void registerServerCommands(CommandHandler handler){
|
||||
|
||||
|
||||
@@ -460,6 +460,17 @@ public class Mods implements Loadable{
|
||||
/** Creates all the content found in mod files. */
|
||||
public void loadContent(){
|
||||
|
||||
//load class mod content first
|
||||
for(LoadedMod mod : orderedMods()){
|
||||
//hidden mods can't load content
|
||||
if(mod.main != null && !mod.meta.hidden){
|
||||
content.setCurrentMod(mod);
|
||||
mod.main.loadContent();
|
||||
}
|
||||
}
|
||||
|
||||
content.setCurrentMod(null);
|
||||
|
||||
class LoadRun implements Comparable<LoadRun>{
|
||||
final ContentType type;
|
||||
final Fi file;
|
||||
@@ -583,7 +594,7 @@ public class Mods implements Loadable{
|
||||
Fi metaf = zip.child("mod.json").exists() ? zip.child("mod.json") : zip.child("mod.hjson").exists() ? zip.child("mod.hjson") : zip.child("plugin.json");
|
||||
if(!metaf.exists()){
|
||||
Log.warn("Mod @ doesn't have a 'mod.json'/'mod.hjson'/'plugin.json' file, skipping.", sourceFile);
|
||||
throw new IllegalArgumentException("No mod.json found.");
|
||||
throw new IllegalArgumentException("Invalid file: No mod.json found.");
|
||||
}
|
||||
|
||||
ModMeta meta = json.fromJson(ModMeta.class, Jval.read(metaf.readString()).toString(Jformat.plain));
|
||||
@@ -607,9 +618,9 @@ public class Mods implements Loadable{
|
||||
|
||||
//make sure the main class exists before loading it; if it doesn't just don't put it there
|
||||
if(mainFile.exists()){
|
||||
//other platforms don't have standard java class loaders
|
||||
if(!headless && Version.build != -1){
|
||||
throw new IllegalArgumentException("Java class mods are currently unsupported outside of custom builds.");
|
||||
//mobile versions don't support class mods
|
||||
if(mobile){
|
||||
throw new IllegalArgumentException("Java class mods are not supported on mobile.");
|
||||
}
|
||||
|
||||
URLClassLoader classLoader = new URLClassLoader(new URL[]{sourceFile.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
|
||||
|
||||
@@ -6,7 +6,6 @@ import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.Saves.*;
|
||||
@@ -29,7 +28,7 @@ public class Sector{
|
||||
public @Nullable SectorPreset preset;
|
||||
|
||||
/** Sector enemy hostility from 0 to 1 */
|
||||
public float hostility;
|
||||
//public float hostility;
|
||||
|
||||
//TODO implement a dynamic launch period
|
||||
public int launchPeriod = 10;
|
||||
@@ -57,7 +56,7 @@ public class Sector{
|
||||
|
||||
/** @return whether the enemy has a generated base here. */
|
||||
public boolean hasEnemyBase(){
|
||||
return hostility >= 0.02f && (save == null || save.meta.rules.waves);
|
||||
return is(SectorAttribute.base) && (save == null || save.meta.rules.waves);
|
||||
}
|
||||
|
||||
public boolean isBeingPlayed(){
|
||||
@@ -80,7 +79,7 @@ public class Sector{
|
||||
|
||||
public void generate(){
|
||||
//TODO use simplex and a seed
|
||||
hostility = Math.max(Noise.snoise3(tile.v.x, tile.v.y, tile.v.z, 0.5f, 0.4f), 0);
|
||||
//hostility = Math.max(Noise.snoise3(tile.v.x, tile.v.y, tile.v.z, 0.5f, 0.4f), 0);
|
||||
}
|
||||
|
||||
public boolean locked(){
|
||||
@@ -156,7 +155,8 @@ public class Sector{
|
||||
return (seconds / 60) + ":" + (sf < 10 ? "0" : "") + sf;
|
||||
}
|
||||
|
||||
/** @return the stored amount of time spent in this sector this turn in ticks. */
|
||||
/** @return the stored amount of time spent in this sector this turn in ticks.
|
||||
* Do not use unless you know what you're doing. */
|
||||
public float getStoredTimeSpent(){
|
||||
return Core.settings.getFloat(key("time-spent"));
|
||||
}
|
||||
@@ -165,6 +165,8 @@ public class Sector{
|
||||
put("seconds-passed", number);
|
||||
}
|
||||
|
||||
/** @return how much time has passed in this sector without the player resuming here.
|
||||
* Used for resource production calculations. */
|
||||
public long getSecondsPassed(){
|
||||
return Core.settings.getLong(key("seconds-passed"));
|
||||
}
|
||||
@@ -284,6 +286,8 @@ public class Sector{
|
||||
/** Has snow. */
|
||||
snowy,
|
||||
/** Has sandstorms. */
|
||||
desert
|
||||
desert,
|
||||
/** Has an enemy base. */
|
||||
base
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,15 +77,24 @@ public class ModsDialog extends BaseDialog{
|
||||
t.button("$mod.import.file", Icon.file, bstyle, () -> {
|
||||
dialog.hide();
|
||||
|
||||
platform.showFileChooser(true, "zip", file -> {
|
||||
try{
|
||||
mods.importMod(file);
|
||||
setup();
|
||||
}catch(IOException e){
|
||||
ui.showException(e);
|
||||
e.printStackTrace();
|
||||
platform.showMultiFileChooser(file -> {
|
||||
Runnable go = () -> {
|
||||
try{
|
||||
mods.importMod(file);
|
||||
setup();
|
||||
}catch(IOException e){
|
||||
ui.showException(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
//show unsafe jar file warning
|
||||
if(file.extEquals("jar")){
|
||||
ui.showConfirm("$warning", "$mod.jarwarn", go);
|
||||
}else{
|
||||
go.run();
|
||||
}
|
||||
});
|
||||
}, "zip", "jar");
|
||||
}).margin(12f);
|
||||
|
||||
t.row();
|
||||
|
||||
Reference in New Issue
Block a user