Implemented standalone server, fixed breaking bugs

This commit is contained in:
Anuken
2018-01-28 01:21:33 -05:00
parent 35b6b41f24
commit 5cba3050b9
17 changed files with 218 additions and 32 deletions

View File

@@ -22,7 +22,7 @@ allprojects {
appName = "Mindustry" appName = "Mindustry"
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
aiVersion = '1.8.1' aiVersion = '1.8.1'
uCoreVersion = 'ab9f87c' uCoreVersion = '09bbb56'
} }
repositories { repositories {
@@ -120,6 +120,7 @@ project(":server") {
compile project(":core") compile project(":core")
compile project(":kryonet") compile project(":kryonet")
compile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion" compile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
} }
} }

View File

@@ -1,8 +1,9 @@
package io.anuke.mindustry; package io.anuke.mindustry;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.io.BundleLoader;
import io.anuke.mindustry.io.BlockLoader; import io.anuke.mindustry.io.BlockLoader;
import io.anuke.mindustry.io.BundleLoader;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.modules.ModuleCore; import io.anuke.ucore.modules.ModuleCore;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -23,4 +24,11 @@ public class Mindustry extends ModuleCore {
module(netClient = new NetClient()); module(netClient = new NetClient());
module(netCommon = new NetCommon()); module(netCommon = new NetCommon());
} }
//hack
@Override
public void render() {
super.render();
Inputs.update();
}
} }

View File

@@ -0,0 +1,25 @@
package io.anuke.mindustry;
import io.anuke.mindustry.core.*;
import io.anuke.mindustry.io.BlockLoader;
import io.anuke.mindustry.io.BundleLoader;
import io.anuke.ucore.modules.ModuleCore;
import static io.anuke.mindustry.Vars.*;
public class MindustryServer extends ModuleCore {
@Override
public void init(){
headless = true;
BundleLoader.load();
BlockLoader.load();
module(logic = new Logic());
module(world = new World());
module(netServer = new NetServer());
module(netCommon = new NetCommon());
module(serverControl = new ServerControl());
}
}

View File

@@ -62,6 +62,8 @@ public class Vars{
//whether to hide ui, only on debug //whether to hide ui, only on debug
public static boolean showUI = true; public static boolean showUI = true;
public static boolean headless = false;
public static float controllerMin = 0.25f; public static float controllerMin = 0.25f;
public static float baseControllerSpeed = 11f; public static float baseControllerSpeed = 11f;
@@ -89,6 +91,7 @@ public class Vars{
public static NetCommon netCommon; public static NetCommon netCommon;
public static NetServer netServer; public static NetServer netServer;
public static NetClient netClient; public static NetClient netClient;
public static ServerControl serverControl;
public static Player player; public static Player player;

View File

@@ -137,8 +137,7 @@ public class Control extends Module{
Events.on(PlayEvent.class, () -> { Events.on(PlayEvent.class, () -> {
renderer.clearTiles(); renderer.clearTiles();
player.x = world.getCore().worldx(); player.set(world.getSpawnX(), world.getSpawnY());
player.y = world.getCore().worldy() - tilesize*2;
Core.camera.position.set(player.x, player.y, 0); Core.camera.position.set(player.x, player.y, 0);
@@ -263,7 +262,6 @@ public class Control extends Module{
@Override @Override
public void update(){ public void update(){
Inputs.update();
if(Gdx.input != proxy){ if(Gdx.input != proxy){
Gdx.input = proxy; Gdx.input = proxy;
@@ -352,5 +350,6 @@ public class Control extends Module{
} }
} }
} }
} }
} }

View File

@@ -287,7 +287,7 @@ public class NetClient extends Module {
public void update(){ public void update(){
if(!Net.client()) return; if(!Net.client()) return;
if(!state.is(State.menu) && Net.active()){ if(!state.is(State.menu)){
if(gotData) sync(); if(gotData) sync();
}else if(!connecting){ }else if(!connecting){
Net.disconnect(); Net.disconnect();

View File

@@ -22,29 +22,29 @@ public class NetCommon extends Module {
public NetCommon(){ public NetCommon(){
Net.handleServer(ShootPacket.class, (id, packet) -> { Net.handle(ShootPacket.class, (packet) -> {
Player player = playerGroup.getByID(packet.playerid); Player player = playerGroup.getByID(packet.playerid);
Weapon weapon = (Weapon) Upgrade.getByID(packet.weaponid); Weapon weapon = (Weapon) Upgrade.getByID(packet.weaponid);
weapon.shoot(player, packet.x, packet.y, packet.rotation); weapon.shoot(player, packet.x, packet.y, packet.rotation);
}); });
Net.handleServer(PlacePacket.class, (id, packet) -> { Net.handle(PlacePacket.class, (packet) -> {
control.input().placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); control.input().placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false);
Recipe recipe = Recipes.getByResult(Block.getByID(packet.block)); Recipe recipe = Recipes.getByResult(Block.getByID(packet.block));
if (recipe != null) state.inventory.removeItems(recipe.requirements); if (recipe != null) state.inventory.removeItems(recipe.requirements);
}); });
Net.handleServer(BreakPacket.class, (id, packet) -> { Net.handle(BreakPacket.class, (packet) -> {
control.input().breakBlockInternal(packet.x, packet.y, false); control.input().breakBlockInternal(packet.x, packet.y, false);
}); });
Net.handleServer(ChatPacket.class, (id, packet) -> { Net.handle(ChatPacket.class, (packet) -> {
ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name)); ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name));
}); });
Net.handleServer(WeaponSwitchPacket.class, (id, packet) -> { Net.handle(WeaponSwitchPacket.class, (packet) -> {
Player player = playerGroup.getByID(packet.playerid); Player player = playerGroup.getByID(packet.playerid);
if (player == null) return; if (player == null) return;
@@ -53,17 +53,17 @@ public class NetCommon extends Module {
player.weaponRight = (Weapon) Upgrade.getByID(packet.right); player.weaponRight = (Weapon) Upgrade.getByID(packet.right);
}); });
Net.handleServer(BlockTapPacket.class, (id, packet) -> { Net.handle(BlockTapPacket.class, (packet) -> {
Tile tile = world.tile(packet.position); Tile tile = world.tile(packet.position);
tile.block().tapped(tile); tile.block().tapped(tile);
}); });
Net.handleServer(BlockConfigPacket.class, (id, packet) -> { Net.handle(BlockConfigPacket.class, (packet) -> {
Tile tile = world.tile(packet.position); Tile tile = world.tile(packet.position);
if (tile != null) tile.block().configure(tile, packet.data); if (tile != null) tile.block().configure(tile, packet.data);
}); });
Net.handleServer(PlayerDeathPacket.class, (id, packet) -> { Net.handle(PlayerDeathPacket.class, (packet) -> {
Player player = playerGroup.getByID(packet.id); Player player = playerGroup.getByID(packet.id);
if(player == null) return; if(player == null) return;
@@ -76,7 +76,7 @@ public class NetCommon extends Module {
packet.name = null; packet.name = null;
packet.text = message; packet.text = message;
Net.send(packet, SendMode.tcp); Net.send(packet, SendMode.tcp);
ui.chatfrag.addMessage(message, null); if(!headless) ui.chatfrag.addMessage(message, null);
} }
public String colorizeName(int id, String name){ public String colorizeName(int id, String name){

View File

@@ -208,7 +208,7 @@ public class NetServer extends Module{
} }
public void update(){ public void update(){
if(!closing && Net.active() && state.is(State.menu)){ if(!closing && Net.server() && state.is(State.menu)){
closing = true; closing = true;
weapons.clear(); weapons.clear();
ui.loadfrag.show("$text.server.closing"); ui.loadfrag.show("$text.server.closing");

View File

@@ -0,0 +1,138 @@
package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.ChatPacket;
import io.anuke.mindustry.world.Map;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Sounds;
import io.anuke.ucore.modules.Module;
import io.anuke.ucore.util.ColorCodes;
import io.anuke.ucore.util.CommandHandler;
import io.anuke.ucore.util.CommandHandler.Command;
import io.anuke.ucore.util.CommandHandler.Response;
import io.anuke.ucore.util.CommandHandler.ResponseType;
import java.io.IOException;
import java.util.Scanner;
import static io.anuke.mindustry.Vars.*;
import static io.anuke.ucore.util.ColorCodes.*;
public class ServerControl extends Module {
private final CommandHandler handler = new CommandHandler("");
public ServerControl(){
Effects.setScreenShakeProvider((a, b) -> {});
Effects.setEffectProvider((a, b, c, d, e) -> {});
Sounds.setHeadless(true);
//override default handling
Net.handle(ChatPacket.class, (packet) -> {
info("&y" + (packet.name == null ? "" : packet.name) + ": &lb{0}", packet.text);
});
registerCommands();
Thread thread = new Thread(this::readCommands, "Server Controls");
thread.setDaemon(true);
thread.start();
info("&lcServer loaded. Type &ly'help'&lc for help.");
}
private void registerCommands(){
handler.register("help", "", "Displays this command list.", arg -> {
info("Commands:");
for(Command command : handler.getCommandList()){
print(" &y" + command.text + (command.params.isEmpty() ? "" : " ") + command.params + " - &lm" + command.description);
}
});
handler.register("exit", "", "Exit the server application.", arg -> {
info("Shutting down server.");
Net.dispose();
Gdx.app.exit();
});
handler.register("stop", "", "Stop hosting the server.", arg -> {
Net.closeServer();
state.set(State.menu);
});
handler.register("host", "<mapname>", "Open the server with a specific map.", arg -> {
if(state.is(State.playing)){
err("Already hosting. Type 'stop' to stop hosting first.");
}
String search = arg[0];
Map result = null;
for(Map map : world.maps().list()){
if(map.name.equalsIgnoreCase(search))
result = map;
}
if(result == null){
err("No map with name &y'{0}'&lg found.", search);
return;
}
info("Loading map...");
logic.reset();
world.loadMap(result);
state.set(State.playing);
info("Map loaded.");
try {
Net.host(port);
info("Server opened.");
}catch (IOException e){
UCore.error(e);
}
});
}
private void readCommands(){
Scanner scan = new Scanner(System.in);
System.out.print(LIGHT_BLUE + "> " + RESET);
while(true){
String line = scan.nextLine();
Gdx.app.postRunnable(() -> {
Response response = handler.handleMessage(line);
if (response.type == ResponseType.unknownCommand) {
err("Invalid command. Type 'help' for help.");
} else if (response.type == ResponseType.invalidArguments) {
err("Invalid command arguments. Usage: " + response.command.text + " " + response.command.params);
}
System.out.print(LIGHT_BLUE + "> " + RESET);
});
}
}
private void print(String text, Object... args){
System.out.println(format(text, args) + RESET);
}
private void info(String text, Object... args){
print(LIGHT_GREEN + BOLD + format(text, args));
}
private void err(String text, Object... args){
print(LIGHT_RED + BOLD + format(text, args));
}
private String format(String text, Object... args){
for(int i = 0; i < args.length; i ++){
text = text.replace("{" + i + "}", args[i].toString());
}
for(String color : ColorCodes.getColorCodes()){
text = text.replace("&" + color, ColorCodes.getColorText(color));
}
return text;
}
}

View File

@@ -44,7 +44,7 @@ public class World extends Module{
@Override @Override
public void update(){ public void update(){
if(!(Net.client())) if(!Net.client())
pathfind.update(); pathfind.update();
} }
@@ -74,7 +74,7 @@ public class World extends Module{
} }
public float getSpawnY(){ public float getSpawnY(){
return core.worldy() - tilesize/2; return core.worldy() - tilesize*2;
} }
public boolean solid(int x, int y){ public boolean solid(int x, int y){

View File

@@ -147,7 +147,7 @@ public class Maps implements Disposable{
if(arr != null){ //can be an empty map file if(arr != null){ //can be an empty map file
for(Map map : arr){ for(Map map : arr){
map.pixmap = new Pixmap(file.sibling(map.name + ".png")); map.pixmap = new Pixmap(file.sibling(map.name + ".png"));
map.texture = new Texture(map.pixmap); if(!headless) map.texture = new Texture(map.pixmap);
maps.put(map.id, map); maps.put(map.id, map);
mapNames.put(map.name, map); mapNames.put(map.name, map);
lastID = Math.max(lastID, map.id); lastID = Math.max(lastID, map.id);
@@ -164,7 +164,7 @@ public class Maps implements Disposable{
@Override @Override
public void dispose(){ public void dispose(){
for(Map map : maps.values()){ for(Map map : maps.values()){
map.texture.dispose(); if(map.texture != null) map.texture.dispose();
map.pixmap.dispose(); map.pixmap.dispose();
} }
maps.clear(); maps.clear();

View File

@@ -15,6 +15,9 @@ import io.anuke.ucore.function.Consumer;
import java.io.IOException; import java.io.IOException;
import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.ui;
public class Net{ public class Net{
public static final int version = 13; public static final int version = 13;
@@ -29,6 +32,15 @@ public class Net{
private static IntMap<StreamBuilder> streams = new IntMap<>(); private static IntMap<StreamBuilder> streams = new IntMap<>();
/**Display a network error.*/
public static void showError(String text){
if(!headless){
ui.showError(text);
}else{
UCore.log(text);
}
}
/**Sets the client loaded status, or whether it will recieve normal packets from the server.*/ /**Sets the client loaded status, or whether it will recieve normal packets from the server.*/
public static void setClientLoaded(boolean loaded){ public static void setClientLoaded(boolean loaded){
clientLoaded = loaded; clientLoaded = loaded;

View File

@@ -266,7 +266,7 @@ public class NetworkIO {
world.loadMap(world.maps().getMap(mapid), seed); world.loadMap(world.maps().getMap(mapid), seed);
renderer.clearTiles(); renderer.clearTiles();
player.set(world.getCore().worldx(), world.getCore().worldy()); player.set(world.getSpawnX(), world.getSpawnY());
for(int x = 0; x < world.width(); x ++){ for(int x = 0; x < world.width(); x ++){
for(int y = 0; y < world.height(); y ++){ for(int y = 0; y < world.height(); y ++){

View File

@@ -79,7 +79,7 @@ public class KryoClient implements ClientProvider{
Net.handleClientReceived(object); Net.handleClientReceived(object);
}catch (Exception e){ }catch (Exception e){
if(e instanceof KryoNetException && e.getMessage() != null && e.getMessage().toLowerCase().contains("incorrect")) { if(e instanceof KryoNetException && e.getMessage() != null && e.getMessage().toLowerCase().contains("incorrect")) {
ui.showError("$text.server.mismatch"); Net.showError("$text.server.mismatch");
netClient.disconnectQuietly(); netClient.disconnectQuietly();
}else{ }else{
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -207,7 +207,7 @@ public class KryoClient implements ClientProvider{
private void handleException(Exception e){ private void handleException(Exception e){
e.printStackTrace(); e.printStackTrace();
if(e instanceof KryoNetException){ if(e instanceof KryoNetException){
Gdx.app.postRunnable(() -> ui.showError("$text.server.mismatch")); Gdx.app.postRunnable(() -> Net.showError("$text.server.mismatch"));
}else{ }else{
//TODO better exception handling. //TODO better exception handling.
disconnect(); disconnect();

View File

@@ -18,9 +18,11 @@ public class KryoRegistrator {
} }
public static ByteBuffer writeServerData(){ public static ByteBuffer writeServerData(){
ByteBuffer buffer = ByteBuffer.allocate(1 + Vars.player.name.length() + 4); String host = Vars.headless ? "Server" : Vars.player.name;
buffer.put((byte)Vars.player.name.length());
buffer.put(Vars.player.name.getBytes()); ByteBuffer buffer = ByteBuffer.allocate(1 + host.getBytes().length + 4);
buffer.put((byte)host.getBytes().length);
buffer.put(host.getBytes());
buffer.putInt(Net.getConnections().size + 1); buffer.putInt(Net.getConnections().size + 1);
return buffer; return buffer;
} }

View File

@@ -35,8 +35,6 @@ import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import static io.anuke.mindustry.Vars.ui;
public class KryoServer implements ServerProvider { public class KryoServer implements ServerProvider {
final boolean debug = false; final boolean debug = false;
final Server server; final Server server;
@@ -434,10 +432,10 @@ public class KryoServer implements ServerProvider {
ex.printStackTrace(); ex.printStackTrace();
if(ex instanceof BindException){ if(ex instanceof BindException){
Net.closeServer(); Net.closeServer();
ui.showError("$text.server.addressinuse"); Net.showError("$text.server.addressinuse");
}else if(ex.getMessage().equals("Permission denied")){ }else if(ex.getMessage().equals("Permission denied")){
Net.closeServer(); Net.closeServer();
ui.showError("Permission denied."); Net.showError("Permission denied.");
} }
} }

View File

@@ -3,7 +3,7 @@ package io.anuke.mindustry.server;
import com.badlogic.gdx.backends.headless.HeadlessApplication; import com.badlogic.gdx.backends.headless.HeadlessApplication;
import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoClient;
import io.anuke.kryonet.KryoServer; import io.anuke.kryonet.KryoServer;
import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.MindustryServer;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
public class ServerLauncher{ public class ServerLauncher{
@@ -13,6 +13,6 @@ public class ServerLauncher{
Net.setClientProvider(new KryoClient()); Net.setClientProvider(new KryoClient());
Net.setServerProvider(new KryoServer()); Net.setServerProvider(new KryoServer());
new HeadlessApplication(new Mindustry()); new HeadlessApplication(new MindustryServer());
} }
} }