diff --git a/core/src/io/anuke/mindustry/maps/Maps.java b/core/src/io/anuke/mindustry/maps/Maps.java index beceeb1382..077cd71f1a 100644 --- a/core/src/io/anuke/mindustry/maps/Maps.java +++ b/core/src/io/anuke/mindustry/maps/Maps.java @@ -8,10 +8,12 @@ import io.anuke.arc.collection.IntSet.*; import io.anuke.arc.files.*; import io.anuke.arc.func.*; import io.anuke.arc.graphics.*; +import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.*; import io.anuke.arc.util.async.*; import io.anuke.arc.util.io.*; import io.anuke.arc.util.serialization.*; +import io.anuke.mindustry.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.ctype.*; import io.anuke.mindustry.game.EventType.*; @@ -34,9 +36,30 @@ public class Maps{ /** Serializer for meta. */ private Json json = new Json(); + private ShuffleMode shuffleMode = ShuffleMode.all; + private @Nullable MapProvider shuffler; + private AsyncExecutor executor = new AsyncExecutor(2); private ObjectSet previewList = new ObjectSet<>(); + public ShuffleMode getShuffleMode(){ + return shuffleMode; + } + + public void setShuffleMode(ShuffleMode mode){ + this.shuffleMode = mode; + } + + /** Set the provider for the map(s) to be played on. Will override the default shuffle mode setting.*/ + public void setMapProvider(MapProvider provider){ + this.shuffler = provider; + } + + /** @return the next map to shuffle to. May be null, in which case the server should be stopped. */ + public @Nullable Map getNextMap(@Nullable Map previous){ + return shuffler != null ? shuffler.next(previous) : shuffleMode.next(previous); + } + /** Returns a list of all maps, including custom ones. */ public Array all(){ return maps; @@ -422,4 +445,37 @@ public class Maps{ return map; } + public interface MapProvider{ + @Nullable Map next(@Nullable Map previous); + } + + public enum ShuffleMode implements MapProvider{ + none(map -> null), + all(prev -> { + Array maps = Array.withArrays(Vars.maps.defaultMaps(), Vars.maps.customMaps()); + maps.shuffle(); + return maps.find(m -> m != prev || maps.size == 1); + }), + custom(prev -> { + Array maps = Array.withArrays(Vars.maps.customMaps()); + maps.shuffle(); + return maps.find(m -> m != prev || maps.size == 1); + }), + builtin(prev -> { + Array maps = Array.withArrays(Vars.maps.defaultMaps()); + maps.shuffle(); + return maps.find(m -> m != prev || maps.size == 1); + }); + + private final MapProvider provider; + + ShuffleMode(MapProvider provider){ + this.provider = provider; + } + + @Override + public Map next(@Nullable Map previous){ + return provider.next(previous); + } + } } \ No newline at end of file diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 953b02ae36..570d3a8414 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -5,6 +5,7 @@ import io.anuke.arc.collection.*; import io.anuke.arc.collection.Array.*; import io.anuke.arc.files.*; import io.anuke.arc.util.*; +import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.Timer; import io.anuke.arc.util.CommandHandler.*; import io.anuke.arc.util.Timer.*; @@ -21,6 +22,7 @@ import io.anuke.mindustry.gen.*; import io.anuke.mindustry.io.*; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.*; +import io.anuke.mindustry.maps.Maps.*; import io.anuke.mindustry.mod.Mods.*; import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.Packets.*; @@ -48,6 +50,7 @@ public class ServerControl implements ApplicationListener{ private boolean inExtraRound; private Task lastTask; private Gamemode lastMode = Gamemode.survival; + private @Nullable Map nextMapOverride; private Thread socketThread; private PrintWriter socketOutput; @@ -57,7 +60,7 @@ public class ServerControl implements ApplicationListener{ "shufflemode", "normal", "bans", "", "admins", "", - "shuffle", true, + "shufflemode", "all", "crashreport", false, "port", port, "logging", true, @@ -142,34 +145,34 @@ public class ServerControl implements ApplicationListener{ warn("&lyIt is highly advised to specify which version you're using by building with gradle args &lc-Pbuildversion=&lm&ly."); } + //set up default shuffle mode + try{ + maps.setShuffleMode(ShuffleMode.valueOf(Core.settings.getString("shufflemode"))); + }catch(Exception e){ + maps.setShuffleMode(ShuffleMode.all); + } + Events.on(GameOverEvent.class, event -> { if(inExtraRound) return; - info("Game over!"); - displayStatus(); + if(state.rules.waves){ + info("&lcGame over! Reached wave &ly{0}&lc with &ly{1}&lc players online on map &ly{2}&lc.", state.wave, playerGroup.size(), Strings.capitalize(world.getMap().name())); + }else{ + info("&lcGame over! Team &ly{0}&lc is victorious with &ly{1}&lc players online on map &ly{2}&lc.", event.winner.name(), playerGroup.size(), Strings.capitalize(world.getMap().name())); + } - if(Core.settings.getBool("shuffle")){ - if(maps.all().size > 0){ - Array maps = Array.with(Vars.maps.customMaps().size == 0 ? Vars.maps.defaultMaps() : Vars.maps.customMaps()); - maps.shuffle(); + //set next map to be played + Map map = nextMapOverride != null ? nextMapOverride : maps.getNextMap(world.getMap()); + nextMapOverride = null; + if(map != null){ + Call.onInfoMessage((state.rules.pvp + ? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]") + + "\nNext selected map:[accent] " + map.name() + "[]" + + (map.tags.containsKey("author") && !map.tags.get("author").trim().isEmpty() ? " by[accent] " + map.author() + "[]" : "") + "." + + "\nNew game begins in " + roundExtraTime + "[] seconds."); - Map previous = world.getMap(); - Map map = maps.find(m -> m != previous || maps.size == 1); + info("Selected next map to be {0}.", map.name()); - if(map != null){ - - Call.onInfoMessage((state.rules.pvp - ? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]") - + "\nNext selected map:[accent] " + map.name() + "[]" - + (map.tags.containsKey("author") && !map.tags.get("author").trim().isEmpty() ? " by[accent] " + map.author() + "[]" : "") + "." + - "\nNew game begins in " + roundExtraTime + "[] seconds."); - - info("Selected next map to be {0}.", map.name()); - - play(true, () -> world.loadMap(map, map.applyRules(lastMode))); - }else{ - Log.err("No suitable map found."); - } - } + play(true, () -> world.loadMap(map, map.applyRules(lastMode))); }else{ netServer.kickAll(KickReason.gameover); state.set(State.menu); @@ -304,7 +307,29 @@ public class ServerControl implements ApplicationListener{ }); handler.register("status", "Display server status.", arg -> { - displayStatus(); + if(state.is(State.menu)){ + info("Status: &rserver closed"); + }else{ + info("Status:"); + info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1}", Strings.capitalize(world.getMap().name()), state.wave); + + if(state.rules.waves){ + info("&ly {0} enemies.", unitGroups[Team.crux.ordinal()].size()); + }else{ + info("&ly {0} seconds until next wave.", (int)(state.wavetime / 60)); + } + + info(" &ly{0} FPS, {1} MB used.", (int)(60f / Time.delta()), Core.app.getJavaHeap() / 1024 / 1024); + + if(playerGroup.size() > 0){ + info(" &lyPlayers: {0}", playerGroup.size()); + for(Player p : playerGroup.all()){ + info(" &y{0} / {1}", p.name, p.uuid); + } + }else{ + info(" &lyNo players connected."); + } + } }); handler.register("mods", "Display all loaded mods.", arg -> { @@ -553,14 +578,29 @@ public class ServerControl implements ApplicationListener{ } }); - handler.register("shuffle", "", "Set map shuffling.", arg -> { - if(!arg[0].equals("on") && !arg[0].equals("off")){ - err("Invalid shuffle mode."); - return; + handler.register("shuffle", "[none/all/custom/builtin]", "Set map shuffling mode.", arg -> { + if(arg.length == 0){ + info("Shuffle mode current set to &ly'{0}'&lg.", maps.getShuffleMode()); + }else{ + try{ + ShuffleMode mode = ShuffleMode.valueOf(arg[0]); + Core.settings.putSave("shufflemode", mode.name()); + maps.setShuffleMode(mode); + info("Shuffle mode set to &ly'{0}'&lg.", arg[0]); + }catch(Exception e){ + err("Invalid shuffle mode."); + } + } + }); + + handler.register("nextmap", "", "Set the next map to be played after a game-over. Overrides shuffling.", arg -> { + Map res = maps.all().find(map -> map.name().equalsIgnoreCase(arg[0].replace('_', ' ')) || map.name().equalsIgnoreCase(arg[0])); + if(res != null){ + nextMapOverride = res; + Log.info("Next map set to &ly'{0}'.", res.name()); + }else{ + Log.err("No map '{0}' found.", arg[0]); } - Core.settings.put("shuffle", arg[0].equals("on")); - Core.settings.save(); - info("Shuffle mode set to '{0}'.", arg[0]); }); handler.register("kick", "", "Kick a person by name.", arg -> { @@ -759,7 +799,7 @@ public class ServerControl implements ApplicationListener{ handler.register("gameover", "Force a game over.", arg -> { if(state.is(State.menu)){ - info("Not playing a map."); + err("Not playing a map."); return; } @@ -809,32 +849,6 @@ public class ServerControl implements ApplicationListener{ } } - private void displayStatus() { - if(state.is(State.menu)){ - info("Status: &rserver closed"); - }else{ - info("Status:"); - info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1}", Strings.capitalize(world.getMap().name()), state.wave); - - if(state.rules.waves){ - info("&ly {0} enemies.", unitGroups[Team.crux.ordinal()].size()); - }else{ - info("&ly {0} seconds until next wave.", (int)(state.wavetime / 60)); - } - - info(" &ly{0} FPS, {1} MB used.", (int)(60f / Time.delta()), Core.app.getJavaHeap() / 1024 / 1024); - - if(playerGroup.size() > 0){ - info(" &lyPlayers: {0}", playerGroup.size()); - for(Player p : playerGroup.all()){ - info(" &y{0} / {1}", p.name, p.uuid); - } - }else{ - info(" &lyNo players connected."); - } - } - } - private void readCommands(){ Scanner scan = new Scanner(System.in);