Added BE auto-updater / Server config / Fixed #1266

This commit is contained in:
Anuken 2019-12-28 16:30:40 -05:00
parent c0c0ffa682
commit e1bf8bdab1
17 changed files with 349 additions and 144 deletions

View File

@ -29,6 +29,13 @@ load.system = System
load.mod = Mods
load.scripts = Scripts
be.update = A new Bleeding Edge build is available:
be.update.confirm = Download it and restart now?
be.updating = Updating...
be.ignore = Ignore
be.noupdates = No updates found.
be.check = Check for updates
schematic = Schematic
schematic.add = Save Schematic...
schematics = Schematics

View File

@ -21,6 +21,7 @@ import mindustry.gen.*;
import mindustry.input.*;
import mindustry.maps.*;
import mindustry.mod.*;
import mindustry.net.*;
import mindustry.net.Net;
import mindustry.world.blocks.defense.ForceProjector.*;
@ -136,6 +137,8 @@ public class Vars implements Loadable{
public static Fi modDirectory;
/** data subdirectory used for schematics */
public static Fi schematicDirectory;
/** data subdirectory used for bleeding edge build versions */
public static Fi bebuildDirectory;
/** map file extension */
public static final String mapExtension = "msav";
/** save file extension */
@ -157,6 +160,7 @@ public class Vars implements Loadable{
public static Platform platform = new Platform(){};
public static Mods mods;
public static Schematics schematics = new Schematics();
public static BeControl becontrol;
public static World world;
public static Maps maps;
@ -220,6 +224,7 @@ public class Vars implements Loadable{
defaultWaves = new DefaultWaves();
collisions = new EntityCollisions();
world = new World();
becontrol = new BeControl();
maps = new Maps();
spawner = new WaveSpawner();
@ -260,6 +265,7 @@ public class Vars implements Loadable{
tmpDirectory = dataDirectory.child("tmp/");
modDirectory = dataDirectory.child("mods/");
schematicDirectory = dataDirectory.child("schematics/");
bebuildDirectory = dataDirectory.child("be_builds/");
modDirectory.mkdirs();

View File

@ -139,7 +139,7 @@ public class Pathfinder implements Runnable{
//stop looping when interrupted externally
return;
}
}catch(Exception e){
}catch(Throwable e){
e.printStackTrace();
}
}

View File

@ -9,9 +9,9 @@ import arc.util.io.*;
public class Version{
/** Build type. 'official' for official releases; 'custom' or 'bleeding edge' are also used. */
public static String type;
public static String type = "unknown";
/** Build modifier, e.g. 'alpha' or 'release' */
public static String modifier;
public static String modifier = "unknown";
/** Number specifying the major version, e.g. '4' */
public static int number;
/** Build number, e.g. '43'. set to '-1' for custom builds. */

View File

@ -217,7 +217,7 @@ public class World{
public void loadMap(Map map, Rules checkRules){
try{
SaveIO.load(map.file, new FilterContext(map));
}catch(Exception e){
}catch(Throwable e){
Log.err(e);
if(!headless){
ui.showErrorMessage("$map.invalid");

View File

@ -28,7 +28,8 @@ public class EventType{
exclusionDeath,
suicideBomb,
openWiki,
teamCoreDamage
teamCoreDamage,
socketConfigChanged
}
public static class WinEvent{}

View File

@ -18,11 +18,6 @@ public class Administration{
private Array<ChatFilter> chatFilters = new Array<>();
public Administration(){
Core.settings.defaults(
"strict", true,
"servername", "Server"
);
load();
}
@ -51,21 +46,12 @@ public class Administration{
Core.settings.putSave("playerlimit", limit);
}
public void setStrict(boolean on){
Core.settings.putSave("strict", on);
}
public boolean getStrict(){
return Core.settings.getBool("strict");
return Config.strict.bool();
}
public boolean allowsCustomClients(){
return Core.settings.getBool("allow-custom", !headless);
}
public void setCustomClients(boolean allowed){
Core.settings.put("allow-custom", allowed);
Core.settings.save();
return Config.allowCustomClients.bool();
}
/** Call when a player joins to update their information here. */
@ -219,11 +205,7 @@ public class Administration{
}
public boolean isWhitelistEnabled(){
return Core.settings.getBool("whitelist", false);
}
public void setWhitelist(boolean enabled){
Core.settings.putSave("whitelist", enabled);
return Config.whitelist.bool();
}
public boolean isWhitelisted(String id, String usid){
@ -333,6 +315,79 @@ public class Administration{
whitelist = Core.settings.getObject("whitelisted", Array.class, Array::new);
}
/** Server configuration definition. Each config value can be a string, boolean or number. */
public enum Config{
name("The server name as displayed on clients.", "Server", "servername"),
port("The port to host on.", Vars.port),
autoUpdate("Whether to auto-restart when a new update arrives.", false),
crashReport("Whether to send crash reports.", false, "crashreport"),
logging("Whether to log everything to files.", true),
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
socketInputPort("The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)),
socketInputAddress("The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)),
allowCustomClients("Whether custom clients are allowed to connect.", !headless, "allow-custom"),
whitelist("Whether the whitelist is used.", false);
public static final Config[] all = values();
public final Object defaultValue;
public final String key, description;
final Runnable changed;
Config(String description, Object def){
this(description, def, null, null);
}
Config(String description, Object def, String key){
this(description, def, key, null);
}
Config(String description, Object def, Runnable changed){
this(description, def, null, changed);
}
Config(String description, Object def, String key, Runnable changed){
this.description = description;
this.key = key == null ? name() : key;
this.defaultValue = def;
this.changed = changed == null ? () -> {} : changed;
}
public boolean isNum(){
return defaultValue instanceof Integer;
}
public boolean isBool(){
return defaultValue instanceof Boolean;
}
public boolean isString(){
return defaultValue instanceof String;
}
public Object get(){
return Core.settings.get(key, defaultValue);
}
public boolean bool(){
return Core.settings.getBool(key, (Boolean)defaultValue);
}
public int num(){
return Core.settings.getInt(key, (Integer)defaultValue);
}
public String string(){
return Core.settings.getString(key, (String)defaultValue);
}
public void set(Object value){
Core.settings.putSave(key, value);
changed.run();
}
}
@Serialize
public static class PlayerInfo{
public String id;

View File

@ -0,0 +1,168 @@
package mindustry.net;
import arc.*;
import arc.Net.*;
import arc.files.*;
import arc.func.*;
import arc.util.*;
import arc.util.async.*;
import arc.util.serialization.*;
import mindustry.core.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.net.Administration.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*;
import java.io.*;
import java.net.*;
import static mindustry.Vars.*;
/** Handles control of bleeding edge builds. */
public class BeControl{
private static final int updateInterval = 60;
private AsyncExecutor executor = new AsyncExecutor(1);
private boolean checkUpdates = true;
private boolean updateAvailable;
private String updateUrl;
private int updateBuild;
/** @return whether this is a bleeding edge build. */
public boolean active(){
return Version.type.equals("bleeding-edge");
}
public BeControl(){
if(active()){
Timer.schedule(() -> {
if(checkUpdates && !mobile){
checkUpdate(t -> {});
}
}, 1, updateInterval);
}
}
/** asynchronously checks for updates. */
public void checkUpdate(Boolc done){
Core.net.httpGet("https://api.github.com/repos/Anuken/MindustryBuilds/releases/latest", res -> {
if(res.getStatus() == HttpStatus.OK){
Jval val = Jval.read(res.getResultAsString());
int newBuild = Strings.parseInt(val.getString("tag_name", "0"));
if(newBuild > Version.build){
Jval asset = val.get("assets").asArray().find(v -> v.getString("name", "").startsWith(headless ? "Mindustry-BE-Server" : "Mindustry-BE-Desktop"));
String url = asset.getString("browser_download_url", "");
updateAvailable = true;
updateBuild = newBuild;
updateUrl = url;
showUpdateDialog();
Core.app.post(() -> done.get(true));
}else{
Core.app.post(() -> done.get(false));
}
}else{
Core.app.post(() -> done.get(false));
Log.err("Update check responded with: {0}", res.getStatus());
}
}, error -> {
if(!headless){
ui.showException(error);
}else{
error.printStackTrace();
}
});
}
/** @return whether a new update is available */
public boolean isUpdateAvailable(){
return updateAvailable;
}
/** shows the dialog for updating the game on desktop, or a prompt for doing so on the server */
public void showUpdateDialog(){
if(!updateAvailable) return;
if(!headless){
ui.showCustomConfirm(Core.bundle.format("be.update", "") + " " + updateBuild, "$be.update.confirm", "$ok", "$be.ignore", () -> {
boolean[] cancel = {false};
float[] progress = {0};
int[] length = {0};
Fi file = bebuildDirectory.child("client-be-" + updateBuild + ".jar");
FloatingDialog dialog = new FloatingDialog("$be.updating");
download(updateUrl, file, i -> length[0] = i, v -> progress[0] = v, () -> cancel[0], () -> {
try{
Runtime.getRuntime().exec(new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()});
System.exit(0);
}catch(IOException e){
ui.showException(e);
}
}, e -> {
dialog.hide();
ui.showException(e);
});
dialog.cont.add(new Bar(() -> length[0] == 0 ? Core.bundle.get("be.updating") : (int)(progress[0] * length[0]) / 1024/ 1024 + "/" + length[0]/1024/1024 + " MB", () -> Pal.accent, () -> progress[0])).width(400f).height(70f);
dialog.buttons.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
cancel[0] = true;
dialog.hide();
}).size(210f, 64f);
dialog.setFillParent(false);
dialog.show();
}, () -> checkUpdates = false);
}else{
Log.info("&lcA new update is available: &lyBleeding Edge build {0}", updateBuild);
if(Config.autoUpdate.bool()){
Log.info("&lcAuto-downloading next version...");
try{
Fi source = Fi.get(BeControl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
Fi dest = source.sibling("server-be-" + updateBuild + ".jar");
download(updateUrl, dest,
len -> Core.app.post(() -> Log.info("&ly| Size: {0} MB.", Strings.fixed((float)len / 1024 / 1024, 2))),
progress -> {},
() -> false,
() -> {
Log.info("&lcVersion downloaded, exiting. Note that if you are not using the run-server script, the server will not restart automatically.");
dest.copyTo(source);
dest.delete();
System.exit(2); //this will cause a restart if using the script
},
Throwable::printStackTrace);
}catch(Exception e){
e.printStackTrace();
}
}
checkUpdates = false;
//todo server updates
}
}
private void download(String furl, Fi dest, Intc length, Floatc progressor, Boolp canceled, Runnable done, Cons<Throwable> error){
executor.submit(() -> {
try{
HttpURLConnection con = (HttpURLConnection)new URL(furl).openConnection();
BufferedInputStream in = new BufferedInputStream(con.getInputStream());
OutputStream out = dest.write(false, 4096);
byte[] data = new byte[4096];
long size = con.getContentLength();
long counter = 0;
length.get((int)size);
int x;
while((x = in.read(data, 0, data.length)) >= 0 && !canceled.get()){
counter += x;
progressor.get((float)counter / (float)size);
out.write(data, 0, x);
}
out.close();
in.close();
if(!canceled.get()) done.run();
}catch(Throwable e){
error.get(e);
}
});
}
}

View File

@ -27,7 +27,9 @@ public class CrashSender{
exception.printStackTrace();
//don't create crash logs for custom builds, as it's expected
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))) return;
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))){
ret();
}
//attempt to load version regardless
if(Version.number == 0){
@ -63,7 +65,7 @@ public class CrashSender{
try{
//check crash report setting
if(!Core.settings.getBool("crashreport", true)){
return;
ret();
}
}catch(Throwable ignored){
//if there's no settings init we don't know what the user wants but chances are it's an important crash, so send it anyway
@ -72,14 +74,14 @@ public class CrashSender{
try{
//check any mods - if there are any, don't send reports
if(Vars.mods != null && !Vars.mods.list().isEmpty()){
return;
ret();
}
}catch(Throwable ignored){
}
//do not send exceptions that occur for versions that can't be parsed
if(Version.number == 0){
return;
ret();
}
boolean netActive = false, netServer = false;
@ -130,12 +132,16 @@ public class CrashSender{
while(!sent[0]){
Thread.sleep(30);
}
}catch(InterruptedException ignored){
}
}catch(InterruptedException ignored){}
}catch(Throwable death){
death.printStackTrace();
System.exit(1);
}
ret();
}
private static void ret(){
System.exit(1);
}
private static void httpPost(String url, String content, Cons<HttpResponse> success, Cons<Throwable> failure){

View File

@ -1,12 +1,12 @@
package mindustry.net;
import arc.*;
import arc.util.*;
import mindustry.core.*;
import mindustry.entities.type.*;
import mindustry.game.*;
import mindustry.io.*;
import mindustry.maps.Map;
import mindustry.net.Administration.*;
import java.io.*;
import java.nio.*;
@ -62,7 +62,7 @@ public class NetworkIO{
}
public static ByteBuffer writeServerData(){
String name = (headless ? Core.settings.getString("servername") : player.name);
String name = (headless ? Config.name.string() : player.name);
String map = world.getMap() == null ? "None" : world.getMap().name();
ByteBuffer buffer = ByteBuffer.allocate(256);

View File

@ -32,7 +32,7 @@ public class ModsDialog extends FloatingDialog{
buttons.row();
buttons.addImageTextButton("$mods.guide", Icon.wiki,
buttons.addImageTextButton("$mods.guide", Icon.link,
() -> Core.net.openURI(modGuideURL))
.size(210, 64f);

View File

@ -59,6 +59,18 @@ public class MenuFragment extends Fragment{
if(mobile){
parent.fill(c -> c.bottom().left().addButton("", Styles.infot, ui.about::show).size(84, 45));
parent.fill(c -> c.bottom().right().addButton("", Styles.discordt, ui.discord::show).size(84, 45));
}else if(becontrol.active()){
parent.fill(c -> c.bottom().right().addImageTextButton("$be.check", Icon.refreshSmall, () -> {
ui.loadfrag.show();
becontrol.checkUpdate(result -> {
ui.loadfrag.hide();
if(!result){
ui.showInfo("$be.noupdates");
}
});
}).size(200, 60).update(t -> {
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
}));
}
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build + (Version.revision == 0 ? "" : "." + Version.revision));

View File

@ -257,7 +257,7 @@ public class Tile implements Position, TargetTrait{
}
public boolean solid(){
return block.solid || block.isSolidFor(this) || (isLinked() && link().solid());
return block.solid || block.isSolidFor(this) || (isLinked() && link() != this && link().solid());
}
public boolean breakable(){

View File

@ -4,6 +4,7 @@ import arc.Core;
import arc.graphics.g2d.Draw;
import arc.graphics.g2d.TextureRegion;
import arc.math.Mathf;
import arc.util.*;
import mindustry.content.Fx;
import mindustry.content.Liquids;
import mindustry.entities.Effects;
@ -51,8 +52,8 @@ public class SolidPump extends Pump{
public void setBars(){
super.setBars();
bars.add("efficiency", entity -> new Bar(() ->
Core.bundle.formatFloat("bar.efficiency",
((((SolidPumpEntity)entity).boost + 1f) * ((SolidPumpEntity)entity).warmup) * 100 * percentSolid(entity.tile.x, entity.tile.y), 1),
Core.bundle.formatFloat("bar.pumpspeed",
((SolidPumpEntity)entity).lastPump / Time.delta() * 60, 1),
() -> Pal.ammo,
() -> ((SolidPumpEntity)entity).warmup));
}
@ -104,11 +105,13 @@ public class SolidPump extends Pump{
if(tile.entity.cons.valid() && typeLiquid(tile) < liquidCapacity - 0.001f){
float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction * entity.efficiency());
tile.entity.liquids.add(result, maxPump);
entity.lastPump = maxPump;
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
if(Mathf.chance(entity.delta() * updateEffectChance))
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
}else{
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f);
entity.lastPump = 0f;
}
entity.pumpTime += entity.warmup * entity.delta();
@ -153,5 +156,6 @@ public class SolidPump extends Pump{
public float warmup;
public float pumpTime;
public float boost;
public float lastPump;
}
}

View File

@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=60a9ebe264f92f2c3082596c77b9ab29474c4a7f
archash=0e25944f7f3a065ed6707f0dbe48548980cad8a6

View File

@ -5,4 +5,12 @@ if [[ $# -eq 0 ]] ; then
fi
./gradlew server:dist -Pbuildversion=$1
while true; do
#auto-restart until ctrl-c or exit 0
java -jar -XX:+HeapDumpOnOutOfMemoryError server/build/libs/server-release.jar
excode=$?
if [ $excode -eq 0 ] || [ $excode -eq 130 ]; then
exit 0
fi
done

View File

@ -40,7 +40,6 @@ import static mindustry.Vars.*;
public class ServerControl implements ApplicationListener{
private static final int roundExtraTime = 12;
private static final int maxLogLength = 1024 * 512;
private static final int commandSocketPort = 6859;
protected static String[] tags = {"&lc&fb[D]", "&lg&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""};
protected static DateTimeFormatter dateTime = DateTimeFormatter.ofPattern("MM-dd-yyyy | HH:mm:ss");
@ -64,10 +63,6 @@ public class ServerControl implements ApplicationListener{
"bans", "",
"admins", "",
"shufflemode", "custom",
"crashreport", false,
"port", port,
"logging", true,
"socket", false,
"globalrules", "{reactorExplosions: false}"
);
@ -75,7 +70,7 @@ public class ServerControl implements ApplicationListener{
String result = "[" + dateTime.format(LocalDateTime.now()) + "] " + format(tags[level.ordinal()] + " " + text + "&fr", args1);
System.out.println(result);
if(Core.settings.getBool("logging")){
if(Config.logging.bool()){
logToFile("[" + dateTime.format(LocalDateTime.now()) + "] " + format(tags[level.ordinal()] + " " + text + "&fr", false, args1));
}
@ -158,16 +153,19 @@ public class ServerControl implements ApplicationListener{
}
});
Events.on(Trigger.socketConfigChanged, () -> {
toggleSocket(false);
toggleSocket(Config.socketInput.bool());
});
if(!mods.list().isEmpty()){
info("&lc{0} mods loaded.", mods.list().size);
}
toggleSocket(Config.socketInput.bool());
info("&lcServer loaded. Type &ly'help'&lc for help.");
System.out.print("> ");
if(Core.settings.getBool("socket")){
toggleSocket(true);
}
}
private void registerCommands(){
@ -247,21 +245,6 @@ public class ServerControl implements ApplicationListener{
}
});
handler.register("port", "[port]", "Sets or displays the port for hosting the server.", arg -> {
if(arg.length == 0){
info("&lyPort: &lc{0}", Core.settings.getInt("port"));
}else{
int port = Strings.parseInt(arg[0]);
if(port < 0 || port > 65535){
err("Port must be a number between 0 and 65535.");
return;
}
info("&lyPort set to {0}.", port);
Core.settings.put("port", port);
Core.settings.save();
}
});
handler.register("maps", "Display all available maps.", arg -> {
if(!maps.all().isEmpty()){
info("Maps:");
@ -440,16 +423,6 @@ public class ServerControl implements ApplicationListener{
});
handler.register("name", "[name...]", "Change the server display name.", arg -> {
if(arg.length == 0){
info("Server name is currently &lc'{0}'.", Core.settings.getString("servername"));
return;
}
Core.settings.put("servername", arg[0]);
Core.settings.save();
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());
@ -470,14 +443,40 @@ public class ServerControl implements ApplicationListener{
}
});
handler.register("whitelist", "[on/off...]", "Enable/disable whitelisting.", arg -> {
handler.register("config", "[name] [value]", "Configure server settings.", arg -> {
if(arg.length == 0){
info("Whitelist is currently &lc{0}.", netServer.admins.isWhitelistEnabled() ? "on" : "off");
info("&lyAll config values:");
for(Config c : Config.all){
Log.info("&ly| &lc{0}:&lm {1}", c.name(), c.get());
Log.info("&ly| | {0}", c.description);
Log.info("&ly|");
}
return;
}
boolean on = arg[0].equalsIgnoreCase("on");
netServer.admins.setWhitelist(on);
info("Whitelist is now &lc{0}.", on ? "on" : "off");
try{
Config c = Config.valueOf(arg[0]);
if(arg.length == 1){
Log.info("&lc'{0}'&lg is currently &lc{0}.", c.name(), c.get());
}else{
if(c.isBool()){
c.set(arg[1].equals("on") || arg[1].equals("true"));
}else if(c.isNum()){
try{
c.set(Integer.parseInt(arg[1]));
}catch(NumberFormatException e){
Log.err("Not a valid number: {0}", arg[1]);
return;
}
}else if(c.isString()){
c.set(arg[1]);
}
Log.info("&lc{0}&lg set to &lc{1}.", c.name(), c.get());
}
}catch(IllegalArgumentException e){
err("Unknown config: '{0}'. Run the command with no arguments to get a list of valid configs.", arg[0]);
}
});
handler.register("whitelisted", "List the entire whitelist.", arg -> {
@ -512,67 +511,6 @@ public class ServerControl implements ApplicationListener{
info("Player &ly'{0}'&lg has been un-whitelisted.", info.lastName);
});
handler.register("sync", "[on/off...]", "Enable/disable block sync. Experimental.", arg -> {
if(arg.length == 0){
info("Block sync is currently &lc{0}.", Core.settings.getBool("blocksync") ? "enabled" : "disabled");
return;
}
boolean on = arg[0].equalsIgnoreCase("on");
Core.settings.putSave("blocksync", on);
info("Block syncing is now &lc{0}.", on ? "on" : "off");
});
handler.register("crashreport", "<on/off>", "Disables or enables automatic crash reporting", arg -> {
boolean value = arg[0].equalsIgnoreCase("on");
Core.settings.put("crashreport", value);
Core.settings.save();
info("Crash reporting is now {0}.", value ? "on" : "off");
});
handler.register("logging", "<on/off>", "Disables or enables server logs", arg -> {
boolean value = arg[0].equalsIgnoreCase("on");
Core.settings.put("logging", value);
Core.settings.save();
info("Logging is now {0}.", value ? "on" : "off");
});
handler.register("strict", "<on/off>", "Disables or enables strict mode", arg -> {
boolean value = arg[0].equalsIgnoreCase("on");
netServer.admins.setStrict(value);
info("Strict mode is now {0}.", netServer.admins.getStrict() ? "on" : "off");
});
handler.register("socketinput", "[on/off]", "Disables or enables a local TCP socket at port "+commandSocketPort+" to recieve commands from other applications", arg -> {
if(arg.length == 0){
info("Socket input is currently &lc{0}.", Core.settings.getBool("socket") ? "on" : "off");
return;
}
boolean value = arg[0].equalsIgnoreCase("on");
toggleSocket(value);
Core.settings.put("socket", value);
Core.settings.save();
info("Socket input is now &lc{0}.", value ? "on" : "off");
});
handler.register("allow-custom-clients", "[on/off]", "Allow or disallow custom clients.", arg -> {
if(arg.length == 0){
info("Custom clients are currently &lc{0}.", netServer.admins.allowsCustomClients() ? "allowed" : "disallowed");
return;
}
String s = arg[0];
if(s.equalsIgnoreCase("on")){
netServer.admins.setCustomClients(true);
info("Custom clients enabled.");
}else if(s.equalsIgnoreCase("off")){
netServer.admins.setCustomClients(false);
info("Custom clients disabled.");
}else{
err("Incorrect command usage.");
}
});
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());
@ -933,8 +871,8 @@ public class ServerControl implements ApplicationListener{
private void host(){
try{
net.host(Core.settings.getInt("port"));
info("&lcOpened a server on port {0}.", Core.settings.getInt("port"));
net.host(Config.port.num());
info("&lcOpened a server on port {0}.", Config.port.num());
}catch(BindException e){
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");
state.set(State.menu);
@ -968,7 +906,7 @@ public class ServerControl implements ApplicationListener{
socketThread = new Thread(() -> {
try{
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("localhost", commandSocketPort));
serverSocket.bind(new InetSocketAddress(Config.socketInputAddress.string(), Config.socketInputPort.num()));
while(true){
Socket client = serverSocket.accept();
info("&lmRecieved command socket connection: &lb{0}", serverSocket.getLocalSocketAddress());