Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
This commit is contained in:
@@ -31,7 +31,7 @@ public class BlockIndexer{
|
|||||||
/** Stores all damaged tile entities by team. */
|
/** Stores all damaged tile entities by team. */
|
||||||
private Seq<Building>[] damagedTiles = new Seq[Team.all.length];
|
private Seq<Building>[] damagedTiles = new Seq[Team.all.length];
|
||||||
/** All ores available on this map. */
|
/** All ores available on this map. */
|
||||||
private ObjectSet<Item> allOres = new ObjectSet<>();
|
private ObjectIntMap<Item> allOres = new ObjectIntMap<>();
|
||||||
/** Stores teams that are present here as tiles. */
|
/** Stores teams that are present here as tiles. */
|
||||||
private Seq<Team> activeTeams = new Seq<>(Team.class);
|
private Seq<Team> activeTeams = new Seq<>(Team.class);
|
||||||
/** Maps teams to a map of flagged tiles by flag. */
|
/** Maps teams to a map of flagged tiles by flag. */
|
||||||
@@ -76,8 +76,6 @@ public class BlockIndexer{
|
|||||||
var drop = tile.drop();
|
var drop = tile.drop();
|
||||||
|
|
||||||
if(drop != null){
|
if(drop != null){
|
||||||
allOres.add(drop);
|
|
||||||
|
|
||||||
int qx = (tile.x / quadrantSize);
|
int qx = (tile.x / quadrantSize);
|
||||||
int qy = (tile.y / quadrantSize);
|
int qy = (tile.y / quadrantSize);
|
||||||
|
|
||||||
@@ -90,6 +88,7 @@ public class BlockIndexer{
|
|||||||
ores[drop.id][qx][qy] = new IntSeq(false, 16);
|
ores[drop.id][qx][qy] = new IntSeq(false, 16);
|
||||||
}
|
}
|
||||||
ores[drop.id][qx][qy].add(tile.pos());
|
ores[drop.id][qx][qy].add(tile.pos());
|
||||||
|
allOres.increment(drop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,9 +147,11 @@ public class BlockIndexer{
|
|||||||
//when the drop can be mined, record the ore position
|
//when the drop can be mined, record the ore position
|
||||||
if(tile.block() == Blocks.air && !seq.contains(pos)){
|
if(tile.block() == Blocks.air && !seq.contains(pos)){
|
||||||
seq.add(pos);
|
seq.add(pos);
|
||||||
|
allOres.increment(drop);
|
||||||
}else{
|
}else{
|
||||||
//otherwise, it likely became blocked, remove it (even if it wasn't there)
|
//otherwise, it likely became blocked, remove it (even if it wasn't there)
|
||||||
seq.removeValue(pos);
|
seq.removeValue(pos);
|
||||||
|
allOres.increment(drop, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +176,7 @@ public class BlockIndexer{
|
|||||||
|
|
||||||
/** @return whether this item is present on this map. */
|
/** @return whether this item is present on this map. */
|
||||||
public boolean hasOre(Item item){
|
public boolean hasOre(Item item){
|
||||||
return allOres.contains(item);
|
return allOres.get(item) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns all damaged tiles by team. */
|
/** Returns all damaged tiles by team. */
|
||||||
|
|||||||
@@ -355,10 +355,13 @@ public class Mods implements Loadable{
|
|||||||
|
|
||||||
/** Loads all mods from the folder, but does not call any methods on them.*/
|
/** Loads all mods from the folder, but does not call any methods on them.*/
|
||||||
public void load(){
|
public void load(){
|
||||||
for(Fi file : modDirectory.list()){
|
var files = resolveDependencies(Seq.with(modDirectory.list()).filter(f ->
|
||||||
if(!file.extension().equals("jar") && !file.extension().equals("zip") && !(file.isDirectory() && (file.child("mod.json").exists() || file.child("mod.hjson").exists()))) continue;
|
f.extension().equals("jar") || f.extension().equals("zip") || (f.isDirectory() && (f.child("mod.json").exists() || f.child("mod.hjson").exists()))
|
||||||
|
));
|
||||||
|
|
||||||
|
for(Fi file : files){
|
||||||
Log.debug("[Mods] Loading mod @", file);
|
Log.debug("[Mods] Loading mod @", file);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
LoadedMod mod = loadMod(file);
|
LoadedMod mod = loadMod(file);
|
||||||
mods.add(mod);
|
mods.add(mod);
|
||||||
@@ -373,7 +376,7 @@ public class Mods implements Loadable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//load workshop mods now
|
//load workshop mods now
|
||||||
for(Fi file : platform.getWorkshopContent(LoadedMod.class)){
|
for(Fi file : resolveDependencies(platform.getWorkshopContent(LoadedMod.class))){
|
||||||
try{
|
try{
|
||||||
LoadedMod mod = loadMod(file);
|
LoadedMod mod = loadMod(file);
|
||||||
mods.add(mod);
|
mods.add(mod);
|
||||||
@@ -708,6 +711,86 @@ public class Mods implements Loadable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tries to find the config file of a mod/plugin. */
|
||||||
|
@Nullable
|
||||||
|
public ModMeta findMeta(Fi file){
|
||||||
|
Fi metaFile =
|
||||||
|
file.child("mod.json").exists() ? file.child("mod.json") :
|
||||||
|
file.child("mod.hjson").exists() ? file.child("mod.hjson") :
|
||||||
|
file.child("plugin.json").exists() ? file.child("plugin.json") :
|
||||||
|
file.child("plugin.hjson");
|
||||||
|
|
||||||
|
if(!metaFile.exists()){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModMeta meta = json.fromJson(ModMeta.class, Jval.read(metaFile.readString()).toString(Jformat.plain));
|
||||||
|
meta.cleanup();
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolves the loading order of a list mods/plugins using their internal names.
|
||||||
|
* It also skips non-mods files or folders. */
|
||||||
|
public Seq<Fi> resolveDependencies(Seq<Fi> files){
|
||||||
|
ObjectMap<String, Fi> fileMapping = new ObjectMap<>();
|
||||||
|
ObjectMap<String, Seq<String>> dependencies = new ObjectMap<>();
|
||||||
|
|
||||||
|
for(Fi file : files){
|
||||||
|
Fi zip = file.isDirectory() ? file : new ZipFi(file);
|
||||||
|
|
||||||
|
if(zip.list().length == 1 && zip.list()[0].isDirectory()){
|
||||||
|
zip = zip.list()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
ModMeta meta = null;
|
||||||
|
try{
|
||||||
|
meta = findMeta(zip);
|
||||||
|
}catch(Exception ignored){
|
||||||
|
}
|
||||||
|
|
||||||
|
if(meta == null) continue;
|
||||||
|
dependencies.put(meta.name, meta.dependencies);
|
||||||
|
fileMapping.put(meta.name, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectSet<String> visited = new ObjectSet<>();
|
||||||
|
OrderedSet<String> ordered = new OrderedSet<>();
|
||||||
|
|
||||||
|
for(String modName : dependencies.keys()){
|
||||||
|
if(!ordered.contains(modName)){
|
||||||
|
// Adds the loaded mods at the beginning of the list
|
||||||
|
ordered.add(modName, 0);
|
||||||
|
resolveDependencies(modName, dependencies, ordered, visited);
|
||||||
|
visited.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the invalid mods
|
||||||
|
for(String missingMod : dependencies.keys()){
|
||||||
|
if(!ordered.contains(missingMod)) ordered.add(missingMod, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Seq<Fi> resolved = ordered.orderedItems().map(fileMapping::get);
|
||||||
|
// Since the resolver explores the dependencies from leaves to the root, reverse the seq
|
||||||
|
resolved.reverse();
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Recursive search of dependencies */
|
||||||
|
public void resolveDependencies(String modName, ObjectMap<String, Seq<String>> dependencies, OrderedSet<String> ordered, ObjectSet<String> visited){
|
||||||
|
visited.add(modName);
|
||||||
|
|
||||||
|
for(String dependency : dependencies.get(modName)){
|
||||||
|
// Checks if the dependency tree isn't circular and that the dependency is not missing
|
||||||
|
if(!visited.contains(dependency) && dependencies.containsKey(dependency)){
|
||||||
|
// Skips if the dependency was already explored in a separate tree
|
||||||
|
if(ordered.contains(dependency)) continue;
|
||||||
|
ordered.add(dependency);
|
||||||
|
resolveDependencies(dependency, dependencies, ordered, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Loads a mod file+meta, but does not add it to the list.
|
/** Loads a mod file+meta, but does not add it to the list.
|
||||||
* Note that directories can be loaded as mods. */
|
* Note that directories can be loaded as mods. */
|
||||||
private LoadedMod loadMod(Fi sourceFile) throws Exception{
|
private LoadedMod loadMod(Fi sourceFile) throws Exception{
|
||||||
@@ -727,19 +810,13 @@ public class Mods implements Loadable{
|
|||||||
zip = zip.list()[0];
|
zip = zip.list()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
Fi metaf =
|
ModMeta meta = findMeta(zip);
|
||||||
zip.child("mod.json").exists() ? zip.child("mod.json") :
|
|
||||||
zip.child("mod.hjson").exists() ? zip.child("mod.hjson") :
|
|
||||||
zip.child("plugin.json").exists() ? zip.child("plugin.json") :
|
|
||||||
zip.child("plugin.hjson");
|
|
||||||
|
|
||||||
if(!metaf.exists()){
|
if(meta == null){
|
||||||
Log.warn("Mod @ doesn't have a '[mod/plugin].[h]json' file, skipping.", sourceFile);
|
Log.warn("Mod @ doesn't have a '[mod/plugin].[h]json' file, skipping.", zip);
|
||||||
throw new ModLoadException("Invalid file: No mod.json found.");
|
throw new ModLoadException("Invalid file: No mod.json found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ModMeta meta = json.fromJson(ModMeta.class, Jval.read(metaf.readString()).toString(Jformat.plain));
|
|
||||||
meta.cleanup();
|
|
||||||
String camelized = meta.name.replace(" ", "");
|
String camelized = meta.name.replace(" ", "");
|
||||||
String mainClass = meta.main == null ? camelized.toLowerCase(Locale.ROOT) + "." + camelized + "Mod" : meta.main;
|
String mainClass = meta.main == null ? camelized.toLowerCase(Locale.ROOT) + "." + camelized + "Mod" : meta.main;
|
||||||
String baseName = meta.name.toLowerCase(Locale.ROOT).replace(" ", "-");
|
String baseName = meta.name.toLowerCase(Locale.ROOT).replace(" ", "-");
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ public class Turret extends ReloadTurret{
|
|||||||
updateReload();
|
updateReload();
|
||||||
|
|
||||||
if(hasAmmo()){
|
if(hasAmmo()){
|
||||||
if(Float.isNaN(reload)) rotation = 0;
|
if(Float.isNaN(reload)) reload = 0;
|
||||||
|
|
||||||
if(timer(timerTarget, targetInterval)){
|
if(timer(timerTarget, targetInterval)){
|
||||||
findTarget();
|
findTarget();
|
||||||
|
|||||||
@@ -105,11 +105,8 @@ public class SNet implements SteamNetworkingCallback, SteamMatchmakingCallback,
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Events.on(WaveEvent.class, e -> {
|
Events.on(WaveEvent.class, e -> updateWave());
|
||||||
if(currentLobby != null && net.server()){
|
Events.run(Trigger.newGame, this::updateWave);
|
||||||
smat.setLobbyData(currentLobby, "wave", state.wave + "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSteamClient(){
|
public boolean isSteamClient(){
|
||||||
@@ -201,6 +198,14 @@ public class SNet implements SteamNetworkingCallback, SteamMatchmakingCallback,
|
|||||||
smat.setLobbyMemberLimit(currentLobby, Core.settings.getInt("playerlimit"));
|
smat.setLobbyMemberLimit(currentLobby, Core.settings.getInt("playerlimit"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateWave(){
|
||||||
|
if(currentLobby != null && net.server()){
|
||||||
|
smat.setLobbyData(currentLobby, "mapname", state.map.name());
|
||||||
|
smat.setLobbyData(currentLobby, "wave", state.wave + "");
|
||||||
|
smat.setLobbyData(currentLobby, "gamemode", state.rules.mode().name() + "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeServer(){
|
public void closeServer(){
|
||||||
|
|||||||
@@ -45,6 +45,14 @@ public class ServerControl implements ApplicationListener{
|
|||||||
public final CommandHandler handler = new CommandHandler("");
|
public final CommandHandler handler = new CommandHandler("");
|
||||||
public final Fi logFolder = Core.settings.getDataDirectory().child("logs/");
|
public final Fi logFolder = Core.settings.getDataDirectory().child("logs/");
|
||||||
|
|
||||||
|
public Runnable serverInput = () -> {
|
||||||
|
Scanner scan = new Scanner(System.in);
|
||||||
|
while(scan.hasNext()){
|
||||||
|
String line = scan.nextLine();
|
||||||
|
Core.app.post(() -> handleCommandString(line));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private Fi currentLogFile;
|
private Fi currentLogFile;
|
||||||
private boolean inExtraRound;
|
private boolean inExtraRound;
|
||||||
private Task lastTask;
|
private Task lastTask;
|
||||||
@@ -147,10 +155,6 @@ public class ServerControl implements ApplicationListener{
|
|||||||
|
|
||||||
customMapDirectory.mkdirs();
|
customMapDirectory.mkdirs();
|
||||||
|
|
||||||
Thread thread = new Thread(this::readCommands, "Server Controls");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
|
|
||||||
if(Version.build == -1){
|
if(Version.build == -1){
|
||||||
warn("&lyYour server is running a custom build, which means that client checking is disabled.");
|
warn("&lyYour server is running a custom build, which means that client checking is disabled.");
|
||||||
warn("&lyIt is highly advised to specify which version you're using by building with gradle args &lb&fb-Pbuildversion=&lr<build>");
|
warn("&lyIt is highly advised to specify which version you're using by building with gradle args &lb&fb-Pbuildversion=&lr<build>");
|
||||||
@@ -258,7 +262,13 @@ public class ServerControl implements ApplicationListener{
|
|||||||
|
|
||||||
toggleSocket(Config.socketInput.bool());
|
toggleSocket(Config.socketInput.bool());
|
||||||
|
|
||||||
info("Server loaded. Type @ for help.", "'help'");
|
Events.on(ServerLoadEvent.class, e -> {
|
||||||
|
Thread thread = new Thread(serverInput, "Server Controls");
|
||||||
|
thread.setDaemon(true);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
info("Server loaded. Type @ for help.", "'help'");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void registerCommands(){
|
protected void registerCommands(){
|
||||||
@@ -397,10 +407,9 @@ public class ServerControl implements ApplicationListener{
|
|||||||
info(" Playing on map &fi@ / Wave @", Strings.capitalize(Strings.stripColors(state.map.name())), state.wave);
|
info(" Playing on map &fi@ / Wave @", Strings.capitalize(Strings.stripColors(state.map.name())), state.wave);
|
||||||
|
|
||||||
if(state.rules.waves){
|
if(state.rules.waves){
|
||||||
info(" @ enemies.", state.enemies);
|
|
||||||
}else{
|
|
||||||
info(" @ seconds until next wave.", (int)(state.wavetime / 60));
|
info(" @ seconds until next wave.", (int)(state.wavetime / 60));
|
||||||
}
|
}
|
||||||
|
info(" @ units / @ enemies", Groups.unit.size(), state.enemies);
|
||||||
|
|
||||||
info(" @ FPS, @ MB used.", Core.graphics.getFramesPerSecond(), Core.app.getJavaHeap() / 1024 / 1024);
|
info(" @ FPS, @ MB used.", Core.graphics.getFramesPerSecond(), Core.app.getJavaHeap() / 1024 / 1024);
|
||||||
|
|
||||||
@@ -456,7 +465,6 @@ public class ServerControl implements ApplicationListener{
|
|||||||
info("&fi&lcServer: &fr@", "&lw" + arg[0]);
|
info("&fi&lcServer: &fr@", "&lw" + arg[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
handler.register("pause", "<on/off>", "Pause or unpause the game.", arg -> {
|
handler.register("pause", "<on/off>", "Pause or unpause the game.", arg -> {
|
||||||
boolean pause = arg[0].equals("on");
|
boolean pause = arg[0].equals("on");
|
||||||
state.serverPaused = pause;
|
state.serverPaused = pause;
|
||||||
@@ -963,15 +971,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
mods.eachClass(p -> p.registerServerCommands(handler));
|
mods.eachClass(p -> p.registerServerCommands(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readCommands(){
|
public void handleCommandString(String line){
|
||||||
Scanner scan = new Scanner(System.in);
|
|
||||||
while(scan.hasNext()){
|
|
||||||
String line = scan.nextLine();
|
|
||||||
Core.app.post(() -> handleCommandString(line));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleCommandString(String line){
|
|
||||||
CommandResponse response = handler.handleMessage(line);
|
CommandResponse response = handler.handleMessage(line);
|
||||||
|
|
||||||
if(response.type == ResponseType.unknownCommand){
|
if(response.type == ResponseType.unknownCommand){
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"name": "Infection",
|
||||||
|
"address": ["plague-continued.ml:5555", "plague-continued.ml:5004"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "RCM",
|
"name": "RCM",
|
||||||
"address": ["185.104.248.61", "easyplay.su"]
|
"address": ["185.104.248.61", "easyplay.su"]
|
||||||
|
|||||||
@@ -7,17 +7,21 @@
|
|||||||
"name": "C.A.M.S.",
|
"name": "C.A.M.S.",
|
||||||
"address": ["baseduser.eu.org:6569", "v7.thedimas.pp.ua", "yeeth.mindustry.me:7000", "yeeth.mindustry.me:4000", "yeeth.mindustry.me:2000", "yeeth.mindustry.me:3000"]
|
"address": ["baseduser.eu.org:6569", "v7.thedimas.pp.ua", "yeeth.mindustry.me:7000", "yeeth.mindustry.me:4000", "yeeth.mindustry.me:2000", "yeeth.mindustry.me:3000"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "SMokeOfAnarchy",
|
||||||
|
"address": "SMokeOfAnarchy.duckdns.org"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hexpvp.ml",
|
"name": "hexpvp.ml",
|
||||||
"address": "hexpvp.ml"
|
"address": "hexpvp.ml"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Omega",
|
"name": "Omega",
|
||||||
"address": ["yeeth.mindustry.me:5002", "yeeth.mindustry.me:5003", "yeeth.mindustry.me:5004","yeeth.mindustry.me:5005", "yeeth.mindustry.me:5006", "yeeth.mindustry.me:5007", "yeeth.mindustry.me", "yeeth.mindustry.me:4006"]
|
"address": ["n3.mindustry.me:5002", "n3.mindustry.me:5003", "n3.mindustry.me:5004","n3.mindustry.me:5005", "n3.mindustry.me:5006", "n3.mindustry.me:5007", "n3.mindustry.me", "n3.mindustry.me:4006"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MeowLand",
|
"name": "MeowLand",
|
||||||
"address": ["34.134.111.15", "fifr4.quackhost.uk:21013", "34.134.111.15:4000", "34.134.111.15:7000"]
|
"address": ["kgstudios.ddns.net:6565"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "DarkDustry",
|
"name": "DarkDustry",
|
||||||
@@ -29,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "XCore",
|
"name": "XCore",
|
||||||
"address": ["skaarjproject.duckdns.org:2000"]
|
"address": ["node.procord.ga:2707"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Obvilion Network",
|
"name": "Obvilion Network",
|
||||||
@@ -74,5 +78,9 @@
|
|||||||
{
|
{
|
||||||
"name": "ALEX",
|
"name": "ALEX",
|
||||||
"address": ["dogemindustry.ddns.net:25588"]
|
"address": ["dogemindustry.ddns.net:25588"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Beyond Anarchy",
|
||||||
|
"address": ["45.156.25.49"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -27,6 +27,6 @@ public class GenericModTest{
|
|||||||
static void checkExistence(String modName){
|
static void checkExistence(String modName){
|
||||||
assertNotEquals(Vars.mods, null);
|
assertNotEquals(Vars.mods, null);
|
||||||
assertNotEquals(Vars.mods.list().size, 0, "At least one mod must be loaded.");
|
assertNotEquals(Vars.mods.list().size, 0, "At least one mod must be loaded.");
|
||||||
assertEquals(Vars.mods.list().first().name, modName, modName + " must be loaded.");
|
assertEquals(modName, Vars.mods.list().first().name, modName + " must be loaded.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user