diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index ee369301d1..a2f1aa8138 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -64,8 +64,8 @@ techtree = Tech Tree research.list = [lightgray]Research: research = Research researched = [lightgray]{0} researched. -players = {0} players online -players.single = {0} player online +players = {0} players +players.single = {0} player server.closing = [accent]Closing server... server.kicked.kick = You have been kicked from the server! server.kicked.whitelist = You are not whitelisted here. @@ -75,6 +75,7 @@ server.kicked.clientOutdated = Outdated client! Update your game! server.kicked.serverOutdated = Outdated server! Ask the host to update! server.kicked.banned = You are banned on this server. server.kicked.typeMismatch = This server is not compatible with your build type. +server.kicked.playerLimit = This server is full. Wait for an empty slot. server.kicked.recentKick = You have been kicked recently.\nWait before connecting again. server.kicked.nameInUse = There is someone with that name\nalready on this server. server.kicked.nameEmpty = Your chosen name is invalid. @@ -110,7 +111,7 @@ server.edit = Edit Server server.outdated = [crimson]Outdated Server![] server.outdated.client = [crimson]Outdated Client![] server.version = [gray]v{0} {1} -server.custombuild = [yellow]Custom Build +server.custombuild = [accent]Custom Build confirmban = Are you sure you want to ban this player? confirmkick = Are you sure you want to kick this player? confirmunban = Are you sure you want to unban this player? @@ -155,7 +156,7 @@ off = Off save.autosave = Autosave: {0} save.map = Map: {0} save.wave = Wave {0} -save.difficulty = Difficulty: {0} +save.mode = Gamemode: {0} save.date = Last Saved: {0} save.playtime = Playtime: {0} warning = Warning. diff --git a/core/src/io/anuke/mindustry/ClientLauncher.java b/core/src/io/anuke/mindustry/ClientLauncher.java index c34c6fe754..8e57f24058 100644 --- a/core/src/io/anuke/mindustry/ClientLauncher.java +++ b/core/src/io/anuke/mindustry/ClientLauncher.java @@ -84,6 +84,8 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform @Override public void resize(int width, int height){ + if(assets == null) return; + if(!assets.isFinished()){ Draw.proj().setOrtho(0, 0, width, height); }else{ diff --git a/core/src/io/anuke/mindustry/content/Blocks.java b/core/src/io/anuke/mindustry/content/Blocks.java index c5784aeeab..c0ec123d96 100644 --- a/core/src/io/anuke/mindustry/content/Blocks.java +++ b/core/src/io/anuke/mindustry/content/Blocks.java @@ -500,6 +500,7 @@ public class Blocks implements ContentList{ consumes.items(new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10)); consumes.power(5f); + itemCapacity = 20; int bottomRegion = reg("-bottom"), weaveRegion = reg("-weave"); diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 049053b9c0..1ffb6badde 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -48,6 +48,10 @@ public class Control implements ApplicationListener, Loadable{ private boolean wasPaused = false; public Control(){ + saves = new Saves(); + tutorial = new Tutorial(); + music = new MusicControl(); + Events.on(StateChangeEvent.class, event -> { if((event.from == State.playing && event.to == State.menu) || (event.from == State.menu && event.to != State.menu)){ Time.runTask(5f, platform::updateRPC); @@ -152,10 +156,6 @@ public class Control implements ApplicationListener, Loadable{ @Override public void loadAsync(){ - saves = new Saves(); - tutorial = new Tutorial(); - music = new MusicControl(); - Draw.scl = 1f / Core.atlas.find("scale_marker").getWidth(); Core.input.setCatch(KeyCode.BACK, true); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 3a4ae64eaf..975520aa09 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -108,6 +108,11 @@ public class NetServer implements ApplicationListener{ return; } + if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){ + kick(id, KickReason.playerLimit); + return; + } + if(!admins.isWhitelisted(packet.uuid, packet.usid)){ info.adminUsid = packet.usid; info.lastName = packet.name; diff --git a/core/src/io/anuke/mindustry/game/Gamemode.java b/core/src/io/anuke/mindustry/game/Gamemode.java index 16f129f065..2dd0c31348 100644 --- a/core/src/io/anuke/mindustry/game/Gamemode.java +++ b/core/src/io/anuke/mindustry/game/Gamemode.java @@ -69,6 +69,20 @@ public enum Gamemode{ this.validator = validator; } + public static Gamemode bestFit(Rules rules){ + if(rules.pvp){ + return pvp; + }else if(rules.editor){ + return editor; + }else if(rules.attackMode){ + return attack; + }else if(rules.infiniteResources){ + return sandbox; + }else{ + return survival; + } + } + /** Applies this preset to this ruleset. */ public Rules apply(Rules in){ rules.accept(in); diff --git a/core/src/io/anuke/mindustry/game/Saves.java b/core/src/io/anuke/mindustry/game/Saves.java index 284de3572b..7f3cb40f59 100644 --- a/core/src/io/anuke/mindustry/game/Saves.java +++ b/core/src/io/anuke/mindustry/game/Saves.java @@ -266,6 +266,10 @@ public class Saves{ return meta == null || meta.rules == null ? null : meta.rules.zone; } + public Gamemode mode(){ + return Gamemode.bestFit(meta.rules); + } + public int getBuild(){ return meta.build; } diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index 81de174e73..f611fd9fcf 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -59,7 +59,8 @@ public abstract class InputHandler implements InputProcessor{ @Remote(targets = Loc.both, forward = true, called = Loc.server) public static void transferInventory(Player player, Tile tile){ - if(Net.server() && (player.item().amount <= 0 || player.isTransferring || !player.timer.get(Player.timerTransfer, 40))){ + if(!player.timer.get(Player.timerTransfer, 40)) return; + if(Net.server() && (player.item().amount <= 0 || player.isTransferring)){ throw new ValidateException(player, "Player cannot transfer an item."); } diff --git a/core/src/io/anuke/mindustry/maps/Maps.java b/core/src/io/anuke/mindustry/maps/Maps.java index 22d90303c4..81a4e6da49 100644 --- a/core/src/io/anuke/mindustry/maps/Maps.java +++ b/core/src/io/anuke/mindustry/maps/Maps.java @@ -184,7 +184,7 @@ public class Maps{ FileHandle dest = findFile(); file.copyTo(dest); - createNewPreview(loadMap(dest, true), true); + createNewPreview(loadMap(dest, true)); } /** Attempts to run the following code; @@ -341,7 +341,7 @@ public class Maps{ private void createAllPreviews(){ Core.app.post(() -> { for(Map map : previewList){ - createNewPreview(map, false); + createNewPreview(map); } previewList.clear(); }); @@ -351,16 +351,12 @@ public class Maps{ Core.app.post(() -> previewList.add(map)); } - private void createNewPreview(Map map, boolean immediate){ + private void createNewPreview(Map map){ try{ //if it's here, then the preview failed to load or doesn't exist, make it //this has to be done synchronously! Pixmap pix = MapIO.generatePreview(map); - if(immediate){ - map.texture = new Texture(pix); - }else{ - Core.app.post(() -> map.texture = new Texture(pix)); - } + map.texture = new Texture(pix); executor.submit(() -> { try{ map.previewFile().writePNG(pix); diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index afaff51561..0aaeb8542f 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -21,9 +21,16 @@ public class Administration{ load(); } + public int getPlayerLimit(){ + return Core.settings.getInt("playerlimit", 0); + } + + public void setPlayerLimit(int limit){ + Core.settings.putSave("playerlimit", limit); + } + public void setStrict(boolean on){ - Core.settings.put("strict", on); - Core.settings.save(); + Core.settings.putSave("strict", on); } public boolean getStrict(){ diff --git a/core/src/io/anuke/mindustry/net/Host.java b/core/src/io/anuke/mindustry/net/Host.java index 77a281d8ec..44b6c6a665 100644 --- a/core/src/io/anuke/mindustry/net/Host.java +++ b/core/src/io/anuke/mindustry/net/Host.java @@ -1,15 +1,18 @@ package io.anuke.mindustry.net; +import io.anuke.mindustry.game.*; + public class Host{ public final String name; public final String address; public final String mapname; public final int wave; - public final int players; + public final int players, playerLimit; public final int version; public final String versionType; + public final Gamemode mode; - public Host(String name, String address, String mapname, int wave, int players, int version, String versionType){ + public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit){ this.name = name; this.address = address; this.players = players; @@ -17,5 +20,7 @@ public class Host{ this.wave = wave; this.version = version; this.versionType = versionType; + this.playerLimit = playerLimit; + this.mode = mode; } } diff --git a/core/src/io/anuke/mindustry/net/NetworkIO.java b/core/src/io/anuke/mindustry/net/NetworkIO.java index 663cfcdf0d..9bc881f08c 100644 --- a/core/src/io/anuke/mindustry/net/NetworkIO.java +++ b/core/src/io/anuke/mindustry/net/NetworkIO.java @@ -1,17 +1,15 @@ package io.anuke.mindustry.net; -import io.anuke.arc.Core; -import io.anuke.arc.util.Time; -import io.anuke.mindustry.entities.Entities; -import io.anuke.mindustry.entities.type.Player; +import io.anuke.arc.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; -import io.anuke.mindustry.io.JsonIO; -import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.io.*; import io.anuke.mindustry.maps.Map; import java.io.*; -import java.nio.ByteBuffer; -import java.util.Arrays; +import java.nio.*; +import java.util.*; import static io.anuke.mindustry.Vars.*; @@ -71,8 +69,9 @@ public class NetworkIO{ buffer.putInt(state.wave); buffer.putInt(Version.build); writeString(buffer, Version.type); - //TODO additional information: - // - gamemode ID/name (just pick the closest one?) + + buffer.put((byte)Gamemode.bestFit(state.rules).ordinal()); + buffer.putInt(netServer.admins.getPlayerLimit()); return buffer; } @@ -83,13 +82,15 @@ public class NetworkIO{ int wave = buffer.getInt(); int version = buffer.getInt(); String vertype = readString(buffer); + Gamemode gamemode = Gamemode.all[buffer.get()]; + int limit = buffer.getInt(); - return new Host(host, hostAddress, map, wave, players, version, vertype); + return new Host(host, hostAddress, map, wave, players, version, vertype, gamemode, limit); } private static void writeString(ByteBuffer buffer, String string, int maxlen){ byte[] bytes = string.getBytes(charset); - //truncating this way may lead to wierd encoding errors at the ends of strings... + //todo truncating this way may lead to wierd encoding errors at the ends of strings... if(bytes.length > maxlen){ bytes = Arrays.copyOfRange(bytes, 0, maxlen); } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 458780631f..d7d79bfbb3 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -14,7 +14,7 @@ public class Packets{ public enum KickReason{ kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick, - nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist; + nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit; public final boolean quiet; diff --git a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java index 0a30c476bf..473addf0ed 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java @@ -197,9 +197,9 @@ public class JoinDialog extends FloatingDialog{ server.content.table(t -> { t.add("[lightgray]" + host.name + " " + versionString).width(targetWidth() - 10f).left().get().setEllipsis(true); t.row(); - t.add("[lightgray]" + (host.players != 1 ? Core.bundle.format("players", host.players == 0 ? host.players : "[accent]" + host.players + "[lightgray]") : Core.bundle.format("players.single", "[accent]" + host.players + "[lightgray]"))).left(); + t.add("[lightgray]" + (Core.bundle.format("players" + (host.players == 1 ? ".single" : ""), (host.players == 0 ? "[lightgray]" : "[accent]") + host.players + (host.playerLimit > 0 ? "[lightgray]/[accent]" + host.playerLimit : "")+ "[lightgray]"))).left(); t.row(); - t.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[lightgray] / " + Core.bundle.format("save.wave", host.wave)).width(targetWidth() - 10f).left().get().setEllipsis(true); + t.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[lightgray] / " + host.mode.toString()).width(targetWidth() - 10f).left().get().setEllipsis(true); }).expand().left().bottom().padLeft(12f).padBottom(8); } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java index b8b17e803e..f535d65d0a 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java @@ -137,13 +137,13 @@ public class LoadDialog extends FloatingDialog{ meta.row(); meta.labelWrap(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().name()))); meta.row(); - meta.labelWrap(Core.bundle.format("save.wave", color + slot.getWave())); + meta.labelWrap(slot.mode().toString() + " /" + color + " " + Core.bundle.format("save.wave", color + slot.getWave())); meta.row(); meta.labelWrap(() -> Core.bundle.format("save.autosave", color + Core.bundle.get(slot.isAutosave() ? "on" : "off"))); meta.row(); meta.labelWrap(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime())); meta.row(); - meta.labelWrap(Core.bundle.format("save.date", color + slot.getDate())); + meta.labelWrap(color + slot.getDate()); meta.row(); }).left().growX().width(250f); diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java index 2ee2359185..a1853ee300 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java @@ -71,6 +71,8 @@ public class BlockInventoryFragment extends Fragment{ } public void hide(){ + if(table == null) return; + table.actions(Actions.scaleTo(0f, 1f, 0.06f, Interpolation.pow3Out), Actions.run(() -> { table.clearChildren(); table.clearListeners(); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 64a9922363..7e22d8ad67 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -401,6 +401,26 @@ public class ServerControl implements ApplicationListener{ info("Server name is now &lc'{0}'.", arg[0]); }); + handler.register("playerlimit", "[off/somenumber]", "Set the server player limit.", arg -> { + if(arg.length == 0){ + info("Player limit is currently &lc{0}.", netServer.admins.getPlayerLimit() == 0 ? "off" : netServer.admins.getPlayerLimit()); + return; + } + if(arg[0].equals("off")){ + netServer.admins.setPlayerLimit(0); + info("Player limit disabled."); + return; + } + + if(Strings.canParsePostiveInt(arg[0]) && Strings.parseInt(arg[0]) > 0){ + int lim = Strings.parseInt(arg[0]); + netServer.admins.setPlayerLimit(lim); + info("Player limit is now &lc{0}.", lim); + }else{ + err("Limit must be a number above 0."); + } + }); + handler.register("whitelist", "[on/off...]", "Enable/disable whitelisting.", arg -> { if(arg.length == 0){ info("Whitelist is currently &lc{0}.", netServer.admins.isWhitelistEnabled() ? "on" : "off");