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"
gdxVersion = '1.9.8'
aiVersion = '1.8.1'
uCoreVersion = 'ab9f87c'
uCoreVersion = '09bbb56'
}
repositories {
@ -120,6 +120,7 @@ project(":server") {
compile project(":core")
compile project(":kryonet")
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;
import io.anuke.mindustry.core.*;
import io.anuke.mindustry.io.BundleLoader;
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 static io.anuke.mindustry.Vars.*;
@ -23,4 +24,11 @@ public class Mindustry extends ModuleCore {
module(netClient = new NetClient());
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
public static boolean showUI = true;
public static boolean headless = false;
public static float controllerMin = 0.25f;
public static float baseControllerSpeed = 11f;
@ -89,6 +91,7 @@ public class Vars{
public static NetCommon netCommon;
public static NetServer netServer;
public static NetClient netClient;
public static ServerControl serverControl;
public static Player player;

View File

@ -137,8 +137,7 @@ public class Control extends Module{
Events.on(PlayEvent.class, () -> {
renderer.clearTiles();
player.x = world.getCore().worldx();
player.y = world.getCore().worldy() - tilesize*2;
player.set(world.getSpawnX(), world.getSpawnY());
Core.camera.position.set(player.x, player.y, 0);
@ -263,7 +262,6 @@ public class Control extends Module{
@Override
public void update(){
Inputs.update();
if(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(){
if(!Net.client()) return;
if(!state.is(State.menu) && Net.active()){
if(!state.is(State.menu)){
if(gotData) sync();
}else if(!connecting){
Net.disconnect();

View File

@ -22,29 +22,29 @@ public class NetCommon extends Module {
public NetCommon(){
Net.handleServer(ShootPacket.class, (id, packet) -> {
Net.handle(ShootPacket.class, (packet) -> {
Player player = playerGroup.getByID(packet.playerid);
Weapon weapon = (Weapon) Upgrade.getByID(packet.weaponid);
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);
Recipe recipe = Recipes.getByResult(Block.getByID(packet.block));
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);
});
Net.handleServer(ChatPacket.class, (id, packet) -> {
Net.handle(ChatPacket.class, (packet) -> {
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);
if (player == null) return;
@ -53,17 +53,17 @@ public class NetCommon extends Module {
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.block().tapped(tile);
});
Net.handleServer(BlockConfigPacket.class, (id, packet) -> {
Net.handle(BlockConfigPacket.class, (packet) -> {
Tile tile = world.tile(packet.position);
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);
if(player == null) return;
@ -76,7 +76,7 @@ public class NetCommon extends Module {
packet.name = null;
packet.text = message;
Net.send(packet, SendMode.tcp);
ui.chatfrag.addMessage(message, null);
if(!headless) ui.chatfrag.addMessage(message, null);
}
public String colorizeName(int id, String name){

View File

@ -208,7 +208,7 @@ public class NetServer extends Module{
}
public void update(){
if(!closing && Net.active() && state.is(State.menu)){
if(!closing && Net.server() && state.is(State.menu)){
closing = true;
weapons.clear();
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
public void update(){
if(!(Net.client()))
if(!Net.client())
pathfind.update();
}
@ -74,7 +74,7 @@ public class World extends Module{
}
public float getSpawnY(){
return core.worldy() - tilesize/2;
return core.worldy() - tilesize*2;
}
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
for(Map map : arr){
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);
mapNames.put(map.name, map);
lastID = Math.max(lastID, map.id);
@ -164,7 +164,7 @@ public class Maps implements Disposable{
@Override
public void dispose(){
for(Map map : maps.values()){
map.texture.dispose();
if(map.texture != null) map.texture.dispose();
map.pixmap.dispose();
}
maps.clear();

View File

@ -15,6 +15,9 @@ import io.anuke.ucore.function.Consumer;
import java.io.IOException;
import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.ui;
public class Net{
public static final int version = 13;
@ -29,6 +32,15 @@ public class Net{
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.*/
public static void setClientLoaded(boolean loaded){
clientLoaded = loaded;

View File

@ -266,7 +266,7 @@ public class NetworkIO {
world.loadMap(world.maps().getMap(mapid), seed);
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 y = 0; y < world.height(); y ++){

View File

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

View File

@ -18,9 +18,11 @@ public class KryoRegistrator {
}
public static ByteBuffer writeServerData(){
ByteBuffer buffer = ByteBuffer.allocate(1 + Vars.player.name.length() + 4);
buffer.put((byte)Vars.player.name.length());
buffer.put(Vars.player.name.getBytes());
String host = Vars.headless ? "Server" : Vars.player.name;
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);
return buffer;
}

View File

@ -35,8 +35,6 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
import static io.anuke.mindustry.Vars.ui;
public class KryoServer implements ServerProvider {
final boolean debug = false;
final Server server;
@ -434,10 +432,10 @@ public class KryoServer implements ServerProvider {
ex.printStackTrace();
if(ex instanceof BindException){
Net.closeServer();
ui.showError("$text.server.addressinuse");
Net.showError("$text.server.addressinuse");
}else if(ex.getMessage().equals("Permission denied")){
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 io.anuke.kryonet.KryoClient;
import io.anuke.kryonet.KryoServer;
import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.MindustryServer;
import io.anuke.mindustry.net.Net;
public class ServerLauncher{
@ -13,6 +13,6 @@ public class ServerLauncher{
Net.setClientProvider(new KryoClient());
Net.setServerProvider(new KryoServer());
new HeadlessApplication(new Mindustry());
new HeadlessApplication(new MindustryServer());
}
}