diff --git a/java/src/game/Game.java b/java/src/game/Game.java index 8c01bcc..7fe0b61 100755 --- a/java/src/game/Game.java +++ b/java/src/game/Game.java @@ -1,34 +1,25 @@ package game; -import java.awt.Desktop; -import java.awt.GraphicsEnvironment; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.io.PrintStream; -import java.lang.Thread.UncaughtExceptionHandler; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.net.IDN; import java.net.InetAddress; -import java.net.SocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.security.SecureRandom; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; @@ -40,8 +31,6 @@ import java.util.concurrent.FutureTask; import java.util.function.Function; import javax.imageio.ImageIO; -import javax.swing.JOptionPane; - import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; @@ -92,7 +81,6 @@ import game.item.ItemStack; import game.log.Log; import game.log.LogLevel; import game.log.Message; -import game.log.NettyLogger; import game.material.Material; import game.model.ModelManager; import game.network.IThreadListener; @@ -167,7 +155,6 @@ import game.world.Region; import game.world.State; import game.world.World; import game.world.WorldClient; -import game.world.WorldServer; /* Een net ganz funktionierndes Programm ... @@ -409,8 +396,7 @@ public class Game implements IThreadListener { @Variable(name = "srv_port", category = CVarCategory.SYSTEM, min = 0, max = 65535, display = "Standard Hosting-Port") public int port = Config.PORT; - public int bind = -1; - + @Variable(name = "snd_enabled", category = CVarCategory.SOUND, display = "Tonausgabe") public boolean soundEnabled = true; @Variable(name = "snd_buffer_size", category = CVarCategory.SOUND, min = 0, max = 1048576, display = "Puffergröße") @@ -418,12 +404,13 @@ public class Game implements IThreadListener { @Variable(name = "snd_frame_size", category = CVarCategory.SOUND, min = 2, max = 8192, display = "PCM-Intervall") public int soundFrameSize = 32; - private Server server; + private ServerProcess server; + private String serverInfo; + private int lastServerTick; private AudioInterface audio; private long start; private boolean cfgDirty; private String buffer = ""; - private boolean crashed; private boolean waitingForFile; private boolean refreshing; @@ -450,20 +437,20 @@ public class Game implements IThreadListener { return INSTANCE; } - public void connect(String address, int port, String user, String pass, String access) { - Log.JNI.info("Verbinde zu " + address + ":" + port); + public void connect(String address, int port, String user, String pass, String access, byte[] local) { + Log.JNI.info("Verbinde zu " + (address == null ? "localhost" : address) + ":" + port); NetConnection connection = null; try { - connection = NetConnection.createNetworkManagerAndConnect(InetAddress.getByName(IDN.toASCII(address)), port); + connection = NetConnection.createNetworkManagerAndConnect(address == null ? InetAddress.getLoopbackAddress() : InetAddress.getByName(IDN.toASCII(address)), port); connection.setNetHandler(new ClientLoginHandler(connection, this)); connection.sendPacket(new HPacketHandshake(Config.PROTOCOL)); - connection.sendPacket(new LPacketPasswordResponse(user, access, pass)); + connection.sendPacket(new LPacketPasswordResponse(user, access, pass, local)); } catch (UnknownHostException u) { Log.JNI.error(u, "Konnte nicht zu Server verbinden"); - this.disconnected("Unbekannter Hostname: " + address); + this.disconnected("Unbekannter Hostname: " + (address == null ? "localhost" : address)); return; } catch (Exception e) @@ -475,17 +462,6 @@ public class Game implements IThreadListener { this.connection = connection; } - public void connectToIntegrated(Server server, String user) { - this.debugWorld = server.getFolder() == null; -// this.displayGuiScreen(null); - SocketAddress socket = server.setLocalEndpoint(); - NetConnection connection = NetConnection.provideLocalClient(socket); - connection.setNetHandler(new ClientLoginHandler(connection, this)); - connection.sendPacket(new HPacketHandshake(Config.PROTOCOL)); - connection.sendPacket(new LPacketPasswordResponse(user, "", "")); - this.connection = connection; - } - public void unloadWorld() { ClientPlayer netHandler = this.getNetHandler(); if(netHandler != null) @@ -496,6 +472,7 @@ public class Game implements IThreadListener { this.connection = null; this.theWorld = null; this.thePlayer = null; + this.serverInfo = null; this.soundManager.stopSounds(); } @@ -1013,7 +990,7 @@ public class Game implements IThreadListener { int y1 = 0; int y2 = this.fb_y; // int pos = 0; - String str = this.getLeft(this.server); + String str = this.getLeft(); // if(jstr) // str = (*jsys.env)->GetStringUTFChars(jsys.env, jstr, NULL); // elem->r_dirty = 1; @@ -1624,8 +1601,14 @@ public class Game implements IThreadListener { return this.itemRenderer; } - public void setTicked() { + public void setTicked(String info) { this.lastTicked = System.currentTimeMillis(); + this.serverInfo = info; + } + + public void setLastTick(int tick) { + this.lastTicked = System.currentTimeMillis(); + this.lastServerTick = tick; } public void updatePlayerMoveState() @@ -1664,7 +1647,7 @@ public class Game implements IThreadListener { } } - public String getLeft(Server server) { + public String getLeft() { long maxMem = Runtime.getRuntime().maxMemory(); long totalMem = Runtime.getRuntime().totalMemory(); long freeMem = Runtime.getRuntime().freeMemory(); @@ -1767,13 +1750,8 @@ public class Game implements IThreadListener { ) + "\n" + String.format("Letzte Zeitsynch.: + %d.%d s", ticked / 1000L, (ticked / 100L) % 10L - ) + "\n" + - (server != null ? - String.format("Server: %.3f T/s (%.1fx), Z: %.1f T/s, D: %.3f ms", server.getTpsRate(), - server.getTpsRate() / 20.0f, - server.getTpsTarget(), server.getAverageTps()) : "Server: ") + "\n" + - (server != null ? "Geladen: " + server.getWorlds().size() + " Welten, " + WorldServer.getLoadedInfo(server) : - "Geladen: ") + ) + + (this.serverInfo != null ? "\n" + this.serverInfo : "") // WorldServer world = this.server.getWorld(this.theWorld.dimension.getDimensionId()); // if(world != null) // list.add("Seed: " + world.getSeed()); @@ -2100,7 +2078,7 @@ public class Game implements IThreadListener { return; this.frameWait = 50000000L - ((n - (this.frameLast + this.frameWait)) < 50000000L ? (n - (this.frameLast + this.frameWait)) : 0L); this.frameLast = n; - this.tickTimes[this.tickIndex++] = this.server != null ? (int)this.server.getLastTick() : 0; + this.tickTimes[this.tickIndex++] = this.lastServerTick; if(this.tickIndex == 240) { this.tickIndex = 0; } @@ -2369,11 +2347,13 @@ public class Game implements IThreadListener { public void unload(boolean loading) { if(this.theWorld != null) { + if(server != null) + this.performAction(Action.SHUTDOWN); if(server == null && this.getNetHandler() != null) this.getNetHandler().getNetworkManager().closeChannel("Quitting"); this.unloadWorld(); - if(server != null) - server.shutdown(); +// if(server != null) +// server.shutdown(); } if(server != null) { if(loading) { @@ -2396,9 +2376,11 @@ public class Game implements IThreadListener { } public void startServer(File dir, String user) { - server = new Server(dir); + byte[] key = new byte[256]; + new SecureRandom().nextBytes(key); + server = new ServerProcess(dir, 1024, 4096, this.port, this.renderDistance, key); server.start(); - this.displayGuiScreen(GuiLoading.makeLoadTask(server, user)); + this.displayGuiScreen(GuiLoading.makeLoadTask(server, user, key)); // while(server != null && !server.isStarted()) { // try { // Thread.sleep(10L); @@ -2450,17 +2432,12 @@ public class Game implements IThreadListener { public double ftime() { return ((double)rtime()) / 1000000.0; } - - public void bind(int port) { - if(server != null) - server.bind(port); - } public void distance(int distance) { if(this.renderGlobal != null) this.renderGlobal.setDisplayListEntitiesDirty(); - if(this.server != null) - this.server.setVar("viewDistance", "" + distance); + if(this.server != null && this.thePlayer != null) + this.thePlayer.sendQueue.addToSendQueue(new CPacketAction(Action.SET_VIEWDIST, distance)); } private void registerDebug(Keysym key, String help, DebugRunner runner) { @@ -2575,13 +2552,7 @@ public class Game implements IThreadListener { }); this.registerDebug(Keysym.W, "Server-Tick-Limit umschalten (Welt beschleunigen / Warpmodus)", new DebugRunner() { public void execute(Keysym key) { - if(server != null) { - server.schedule(new Runnable() { - public void run() { - server.setTpsTarget(server.getTpsTarget() < 10000.0f ? 10000.0f : 20.0f); - } - }); - } + Game.this.performAction(Action.WARP_MODE); } }); this.registerDebug(Keysym.M, "Alle Gegenstände herbei ziehen (Magnetmodus)", new DebugRunner() { @@ -2662,116 +2633,9 @@ public class Game implements IThreadListener { public static void main(String[] args) { - if(System.getProperty("os.name").startsWith("Windows") || System.getProperty("os.name").startsWith("Mac")) { - String info = "Inkompatibles Betriebssystem"; - String msg = "Linux oder *BSD ist erforderlich, um dieses Programm auszuführen.\n" + - "Alle Versionen von Windows und Mac OS (X) sind nicht kompatibel."; - System.err.println("#################################################################"); - System.err.println("*** " + info + " ***"); - System.err.println(msg); - System.err.println("#################################################################"); - if(!GraphicsEnvironment.isHeadless()) - JOptionPane.showMessageDialog(null, msg, info, JOptionPane.ERROR_MESSAGE); - return; - } + Util.checkOs(); Window.init(); - Locale.setDefault(Locale.ROOT); - Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { - public void uncaughtException(Thread thread, Throwable e) { - System.err.println("Fehler in Thread '" + thread.getName() + "'"); - e.printStackTrace(System.err); - if(INSTANCE.crashed) - System.exit(1); - if(e instanceof OutOfMemoryError) { - System.gc(); - System.gc(); - } - if(!thread.getName().startsWith("Thread-") || e instanceof OutOfMemoryError) { - System.err.println("Beende!"); - INSTANCE.crashed = true; - if(System.getProperty("crash.nodump") == null) { - PrintStream ps = null; - File report = null; - try { - Date date = new Date(); - File file = new File("crash-" + new SimpleDateFormat("dd.MM.yyyy_HH.mm.ss").format(date) + ".txt"); - FileOutputStream out = new FileOutputStream(file); - ps = new PrintStream(out); - ThreadMXBean bean = ManagementFactory.getThreadMXBean(); - ThreadInfo[] info = bean.dumpAllThreads(true, true); - StringBuilder sb = new StringBuilder(); - Error error = new Error(); - for(ThreadInfo threadinfo : info) { - if(threadinfo.getThreadId() == thread.getId()) - error.setStackTrace(threadinfo.getStackTrace()); - sb.append(threadinfo); - } - ps.println("************************************************ " + new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date) + " ************************************************"); - ps.println(); - ps.println("\"Wie haste das denn nu wieder geschafft, Bursche?\""); - ps.println("Unerwarteter Fehler in Thread '" + thread.getName() + "':"); - e.printStackTrace(ps); - ps.println(); - ps.println("---------------------------------------------- Thread-Dump (" + info.length + " Threads) ---------------------------------------------"); - ps.println(); - ps.print(sb.toString()); - ps.println("*********************************************************************************************************************"); - report = file; - } - catch(Throwable t) { - System.err.println("Konnte Absturzbericht nicht speichern:"); - t.printStackTrace(System.err); - } - finally { - if(ps != null) - ps.close(); - } - if(report != null) { - System.err.println("Absturzbericht gespeichert unter " + report.getPath()); - try { - Desktop.getDesktop().browse(report.toURI()); - } - catch(Throwable e1) { - System.err.println("Konnte " + report + " nicht öffnen: " + e1); - } - } - } - System.exit(1); - } - } - }); - ImageIO.setUseCache(false); - Thread timer = new Thread("Timer Hack Thread") { - public void run() { - while(true) { - try { - Thread.sleep(2147483647L); - } - catch(InterruptedException e) { - ; - } - } - } - }; - timer.setDaemon(true); - timer.start(); - System.setProperty("java.net.preferIPv4Stack", "true"); - NettyLogger.init(); - Registry.register(); -// game = new Game(); - Runtime.getRuntime().addShutdownHook(new Thread("Game Shutdown Thread") { - public void run() { - if(!INSTANCE.crashed && INSTANCE.server != null) { - try { - INSTANCE.server.stopServer(); - } - catch(Throwable e) { - e.printStackTrace(); - } - } - } - }); - Thread.currentThread().setName("Render thread"); + Registry.setup("Render thread"); INSTANCE.run(); Window.end(); } diff --git a/java/src/game/Server.java b/java/src/game/Server.java index 9f2828f..d775668 100755 --- a/java/src/game/Server.java +++ b/java/src/game/Server.java @@ -3,7 +3,6 @@ package game; import java.io.File; import java.io.IOException; import java.net.InetAddress; -import java.net.SocketAddress; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -32,6 +31,7 @@ import game.entity.npc.EntityHuman; import game.entity.npc.EntityNPC; import game.init.Config; import game.init.EntityRegistry; +import game.init.Registry; import game.init.UniverseRegistry; import game.log.Log; import game.nbt.NBTLoader; @@ -41,8 +41,7 @@ import game.network.IThreadListener; import game.network.LazyLoadBase; import game.network.NetConnection; import game.network.NetHandler.ThreadQuickExitException; -import game.network.HandshakeHandlerMemory; -import game.network.HandshakeHandlerTCP; +import game.network.HandshakeHandler; import game.network.Player; import game.network.Packet; import game.network.PacketDecoder; @@ -65,6 +64,7 @@ import game.packet.SPacketTimeUpdate; import game.packet.SPacketWorld; import game.potion.PotionEffect; import game.util.ExtMath; +import game.util.Util; import game.world.BlockPos; import game.world.PortalType; import game.world.Position; @@ -81,23 +81,20 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalServerChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; -public final class Server implements Runnable, IThreadListener { +public final class Server implements IThreadListener { private static final LazyLoadBase SERVER_NIO_EVENTLOOP = new LazyLoadBase() { protected NioEventLoopGroup load() { return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build()); } }; - private final Thread serverThread; + private final Thread serverThread = Thread.currentThread(); private final List clients = Collections.synchronizedList(Lists.newArrayList()); private final List players = Lists.newArrayList(); private final Map usermap = Maps.newHashMap(); @@ -109,17 +106,11 @@ public final class Server implements Runnable, IThreadListener { private final List unload = Lists.newArrayList(); private final Map warps = Maps.newTreeMap(); private final CommandEnvironment scriptEnv = new CommandEnvironment(this); -// private final String owner; - private final File playerDir; - private final File baseDir; -// private final int port; private final boolean debug; private WorldServer space; private ChannelFuture endpoint; - private ChannelFuture localEndpoint; private String localUser; - private String message; private boolean running = true; private boolean stopped; @@ -140,28 +131,29 @@ public final class Server implements Runnable, IThreadListener { private int pingTimer; private int syncTimer; private int perfTimer; - private int progress = -1; - private int total = 0; + + public static void main(String[] args) { + Util.checkOs(); + Registry.setup("Server thread"); + final Server server = new Server(false); + Registry.addShutdownHook(new Runnable() { + public void run() { + server.stopServer(); + } + }); + server.run(); + } - Server(File dir) { -// this.owner = owner; - this.serverThread = new Thread(this, "Server thread"); - this.baseDir = dir; // dir != null ? new File(dir) : null; - this.playerDir = new File(this.baseDir, "players"); - this.debug = this.baseDir == null; -// this.port = port; + private Server(boolean debug) { + this.debug = debug; } public CommandEnvironment getScriptEnvironment() { return this.scriptEnv; } - public boolean isStarted() { - return this.started; - } - public String[] getUsers() { - String[] list = this.debug ? null : this.playerDir.list(); + String[] list = this.debug ? null : new File("players").list(); if(list == null) { list = new String[0]; } @@ -182,8 +174,8 @@ public final class Server implements Runnable, IThreadListener { public void saveWorldInfo() { if(!this.debug) { - Region.saveWorldInfo(this.baseDir, this.space.getDayTime(), this.localUser); - WorldServer.saveWarps(this.baseDir, this.warps); + Region.saveWorldInfo(null, this.space.getDayTime(), this.localUser); + WorldServer.saveWarps(this.warps); } } @@ -192,7 +184,7 @@ public final class Server implements Runnable, IThreadListener { return null; NBTTagCompound tag = null; try { - File dat = new File(this.playerDir, user + ".nbt"); + File dat = new File(new File("players"), user + ".nbt"); if(dat.exists() && dat.isFile()) { tag = NBTLoader.readGZip(dat); @@ -235,7 +227,7 @@ public final class Server implements Runnable, IThreadListener { private void startProgress(boolean save, int amount) { this.setTotal(amount); this.setProgress(0); - Log.JNI.info((save ? "Speichere" : "Generiere und lade") + " Level " + (this.baseDir == null ? "." : this.baseDir.getName())); + Log.JNI.info((save ? "Speichere" : "Generiere und lade") + " Welt"); } public void unloadWorld(WorldServer world) { @@ -258,15 +250,15 @@ public final class Server implements Runnable, IThreadListener { Log.JNI.info("Starte Server Version " + Config.VERSION); if(!this.debug) { this.setMessage("Welt wird erstellt und geladen"); - FolderInfo info = Region.loadWorldInfo(this.baseDir); + FolderInfo info = Region.loadWorldInfo(null); // if(dtime == -1L) // { // dtime = World.START_TIME; //// Config.set("spawnDim", "1", null); //// } - this.worlds.add(this.space = new WorldServer(this, this.baseDir, info == null ? World.START_TIME : info.time, + this.worlds.add(this.space = new WorldServer(this, info == null ? World.START_TIME : info.time, Space.INSTANCE, false)); this.dimensions.put(this.space.dimension.getDimensionId(), this.space); - this.playerDir.mkdirs(); + new File("players").mkdirs(); // if(Config.spawnY < 0) { // WorldServer world = this.getWorld(Config.spawnDim); // world = world == null ? this.space : world; @@ -297,17 +289,17 @@ public final class Server implements Runnable, IThreadListener { Config.set("weatherChanges", "false", null); Config.set("mobSpawning", "false", null); Config.set("spawnRadius", "0", null); - this.worlds.add(this.space = new WorldServer(this, null, World.START_TIME, + this.worlds.add(this.space = new WorldServer(this, World.START_TIME, Space.INSTANCE, true)); this.dimensions.put(this.space.dimension.getDimensionId(), this.space); } this.setTpsTarget(20.0f); if(!this.debug) { for(Dimension dim : UniverseRegistry.getDimensions()) { - if(WorldServer.needsLoading(this.baseDir, dim)) { + if(WorldServer.needsLoading(dim)) { this.getWorld(dim.getDimensionId()).loadForcedChunks(); } - WorldServer.loadWarps(this.baseDir, dim, this.warps); + WorldServer.loadWarps(dim, this.warps); } // this.openLAN(); } @@ -339,17 +331,22 @@ public final class Server implements Runnable, IThreadListener { this.lastPoll = this.currentTime; this.ticksDone = 0L; } - this.started = true; + if(!this.started) { + this.started = true; + this.sendPipeIPC("running", true); + } } try { this.stopServer(); this.stopped = true; + this.sendPipeIPC("running", false); } catch(Throwable e) { Log.JNI.error(e, "Fehler beim Beenden des Servers"); } finally { this.stopped = true; + this.sendPipeIPC("running", false); Log.JNI.info("Server wurde beendet"); } } @@ -365,8 +362,8 @@ public final class Server implements Runnable, IThreadListener { bx = bx >> 4; bz = bz >> 4; long last = System.currentTimeMillis(); - for(int x = -Config.distance; x <= Config.distance && this.running; x++) { - for(int z = -Config.distance; z <= Config.distance && this.running; z++) { + for(int x = -Config.distance; x <= Config.distance; x++) { + for(int z = -Config.distance; z <= Config.distance; z++) { long time = System.currentTimeMillis(); if(time - last >= 10L) { this.setProgress(done); @@ -436,7 +433,7 @@ public final class Server implements Runnable, IThreadListener { } } if(++this.syncTimer == 20) { - this.sendPacket(new SPacketTimeUpdate(this.space.getDayTime())); + this.sendPacket(new SPacketTimeUpdate(this.space.getDayTime(), this.getInfo())); this.syncTimer = 0; } this.ticked.clear(); @@ -477,7 +474,7 @@ public final class Server implements Runnable, IThreadListener { Dimension dim = UniverseRegistry.getDimension(dimension); if(dim == null) return null; - world = new WorldServer(this, this.baseDir, this.space.getDayTime(), + world = new WorldServer(this, this.space.getDayTime(), dim, this.debug); this.worlds.add(world); this.dimensions.put(dimension, world); @@ -497,11 +494,7 @@ public final class Server implements Runnable, IThreadListener { public List getWorlds() { return this.worlds; } - - public boolean isStopped() { - return this.stopped; - } - + public long[] getTickTimes() { return this.tickTimes; } @@ -547,41 +540,17 @@ public final class Server implements Runnable, IThreadListener { public Player getPlayer(String user) { return this.usermap.get(user); } - - public String getMessage() { - synchronized(this) { - return this.message; - } - } - - public int getProgress() { - synchronized(this) { - return this.progress; - } - } - - public int getTotal() { - synchronized(this) { - return this.total; - } - } - + private void setMessage(String message) { - synchronized(this) { - this.message = message; - } + this.sendPipeIPC("message", message); } private void setProgress(int progress) { - synchronized(this) { - this.progress = progress; - } + this.sendPipeIPC("progress", progress); } private void setTotal(int total) { - synchronized(this) { - this.total = total; - } + this.sendPipeIPC("total", total); } public void setVar(String cv, String value) { @@ -591,11 +560,7 @@ public final class Server implements Runnable, IThreadListener { } }); } - - public File getFolder() { - return this.baseDir; - } - + private ListenableFuture callFromMainThread(Callable callable) { if(!this.isMainThread() && !this.stopped) { ListenableFutureTask task = ListenableFutureTask.create(callable); @@ -661,35 +626,33 @@ public final class Server implements Runnable, IThreadListener { Player conn = new Player(this, connection, loginUser); if(tag != null) conn.readFromNBT(tag); - if(!connection.isLocalChannel()) { - if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit && !conn.getAdmin()) - return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit); - if(Config.auth) { - if(conn.getPassword() == null) { - if(!Config.register) - return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; - if(loginPass.length() == 0) - return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens 8 Zeichen)"; - if(loginPass.length() < 8) - return "Passwort ist zu kurz, mindestens 8 Zeichen"; - conn.setPassword(loginPass); - Log.JNI.info(loginUser + " registrierte sich mit Passwort"); - } - else if(!conn.getPassword().equals(loginPass)) { - return "Falsches Passwort"; - } - else { - Log.JNI.info(loginUser + " loggte sich mit Passwort ein"); - } - } - if(Config.compression >= 0) { - connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() { - public void operationComplete(ChannelFuture future) throws Exception { - connection.setCompressionTreshold(Config.compression); - } - }); - } - } + if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit && !conn.isAdmin()) + return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit); + if(!connection.isLocalChannel() && Config.auth) { + if(conn.getPassword() == null) { + if(!Config.register) + return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; + if(loginPass.length() == 0) + return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens 8 Zeichen)"; + if(loginPass.length() < 8) + return "Passwort ist zu kurz, mindestens 8 Zeichen"; + conn.setPassword(loginPass); + Log.JNI.info(loginUser + " registrierte sich mit Passwort"); + } + else if(!conn.getPassword().equals(loginPass)) { + return "Falsches Passwort"; + } + else { + Log.JNI.info(loginUser + " loggte sich mit Passwort ein"); + } + } + if(Config.compression >= 0) { + connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) throws Exception { + connection.setCompressionTreshold(Config.compression); + } + }); + } connection.sendPacket(new RPacketLoginSuccess()); connection.setNetHandler(conn); this.players.add(conn); @@ -766,7 +729,7 @@ public final class Server implements Runnable, IThreadListener { return null; NBTTagCompound tag = null; try { - File dat = new File(this.playerDir, user + ".nbt"); + File dat = new File(new File("players"), user + ".nbt"); if(dat.exists() && dat.isFile()) { tag = NBTLoader.readGZip(dat); @@ -794,8 +757,8 @@ public final class Server implements Runnable, IThreadListener { // else // etag = new NBTTagCompound(); conn.writeToNBT(tag); - File tmp = new File(this.playerDir, conn.getUser() + ".nbt.tmp"); - File dat = new File(this.playerDir, conn.getUser() + ".nbt"); + File tmp = new File(new File("players"), conn.getUser() + ".nbt.tmp"); + File dat = new File(new File("players"), conn.getUser() + ".nbt"); NBTLoader.writeGZip(tag, tmp); if(dat.exists()) { dat.delete(); @@ -1020,7 +983,7 @@ public final class Server implements Runnable, IThreadListener { } private void updateTimeAndWeatherForPlayer(Player conn, WorldServer world) { - conn.sendPacket(new SPacketTimeUpdate(world.getDayTime())); + conn.sendPacket(new SPacketTimeUpdate(world.getDayTime(), this.getInfo())); conn.sendPacket(new S2BPacketChangeGameState(S2BPacketChangeGameState.Action.SET_WEATHER, world.getWeather().getID())); conn.sendPacket(new S2BPacketChangeGameState(S2BPacketChangeGameState.Action.RAIN_STRENGTH, world.getRainStrength())); conn.sendPacket(new S2BPacketChangeGameState(S2BPacketChangeGameState.Action.DARKNESS, world.getDarkness())); @@ -1055,41 +1018,21 @@ public final class Server implements Runnable, IThreadListener { NetConnection manager = new NetConnection(); Server.this.clients.add(manager); channel.pipeline().addLast((String)"packet_handler", (ChannelHandler)manager); - manager.setNetHandler(new HandshakeHandlerTCP(Server.this, manager)); + manager.setNetHandler(new HandshakeHandler(Server.this, manager)); } }).group(SERVER_NIO_EVENTLOOP.getValue()).localAddress((InetAddress)null, port)).bind().syncUninterruptibly(); } } - - public SocketAddress setLocalEndpoint() { -// ChannelFuture future; - synchronized(this.serverThread) { - if(this.localEndpoint == null) { -// throw new IllegalStateException("Lokaler Eingangspunkt bereits gesetzt"); -// } - this.localEndpoint = ((ServerBootstrap)((ServerBootstrap)(new ServerBootstrap()).channel(LocalServerChannel.class)).childHandler(new ChannelInitializer() { - protected void initChannel(Channel channel) throws Exception { - NetConnection manager = new NetConnection(); - manager.setNetHandler(new HandshakeHandlerMemory(Server.this, manager)); - Server.this.clients.add(manager); - channel.pipeline().addLast((String)"packet_handler", (ChannelHandler)manager); - } - }).group((EventLoopGroup)SERVER_NIO_EVENTLOOP.getValue()).localAddress(LocalAddress.ANY)).bind().syncUninterruptibly(); -// this.localEndpoint = future; - } - } - return this.localEndpoint.channel().localAddress(); - } private void unsetLanEndpoint() { for(Player conn : Lists.newArrayList(this.players)) { if(!conn.isLocal()) conn.disconnect(); } - this.terminateEndpoints(false); + this.terminateEndpoint(); } - private void terminateEndpoints(boolean local) { + private void terminateEndpoint() { synchronized(this.serverThread) { if(this.endpoint != null) { Log.JNI.info("Schließe Port"); @@ -1101,15 +1044,6 @@ public final class Server implements Runnable, IThreadListener { Log.JNI.warn("Unterbrochen beim Schließen des Kanals"); } } - if(local && this.localEndpoint != null) { - try { - this.localEndpoint.channel().close().sync(); - this.localEndpoint = null; - } - catch(InterruptedException e) { - Log.JNI.warn("Unterbrochen beim Schließen des lokalen Kanals"); - } - } } } @@ -1128,9 +1062,6 @@ public final class Server implements Runnable, IThreadListener { manager.processReceivedPackets(); } catch(Exception e) { - if(manager.isLocalChannel()) { - throw e; - } Log.JNI.error(e, "Konnte Paket von " + manager.getCutAddress() + " nicht verarbeiten"); manager.sendPacket(new SPacketDisconnect(), new GenericFutureListener>() { public void operationComplete(Future future) throws Exception { @@ -1148,12 +1079,12 @@ public final class Server implements Runnable, IThreadListener { public Map getWarps() { return this.warps; } - - public void start() { - this.serverThread.start(); - } +// +// public void start() { +// this.serverThread.start(); +// } - void stopServer() { + private void stopServer() { if(!this.stopped) { this.setProgress(-1); this.setMessage("Stoppe server"); @@ -1162,7 +1093,7 @@ public final class Server implements Runnable, IThreadListener { for(Player conn : Lists.newArrayList(this.players)) { conn.disconnect(); } - this.terminateEndpoints(true); + this.terminateEndpoint(); if(this.started) { Log.JNI.info("Speichere Spieler"); this.saveAllPlayerData(true); @@ -1203,4 +1134,14 @@ public final class Server implements Runnable, IThreadListener { })); this.running = false; } + + public String getInfo() { + return String.format("Server: %.3f T/s (%.1fx), Z: %.1f T/s, D: %.3f ms", this.getTpsRate(), this.getTpsRate() / 20.0f, this.getTpsTarget(), this.getAverageTps()) + "\n" + + "Geladen: " + this.getWorlds().size() + " Welten, " + WorldServer.getLoadedInfo(this); + } + + private void sendPipeIPC(String key, Object value) { + System.out.println("#" + key + (value == null ? "" : " " + String.valueOf(value))); + System.out.flush(); + } } diff --git a/java/src/game/ServerProcess.java b/java/src/game/ServerProcess.java new file mode 100644 index 0000000..efdbb4f --- /dev/null +++ b/java/src/game/ServerProcess.java @@ -0,0 +1,114 @@ +package game; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +import game.log.Log; +import game.util.Tuple; +import game.util.Util; + +public class ServerProcess { + private final ProcessBuilder builder; + + private Process process; + private BufferedWriter writer; + private boolean running; + private String message; + private int total; + private int progress = -1; + + public ServerProcess(File dir, int minMem, int maxMem, int port, int distance, byte[] key) { + StringBuilder sb = new StringBuilder(); + for(int z = 0; z < key.length; z++) { + sb.append(String.format("%02x", key[z] & 255)); + } + this.builder = new ProcessBuilder("java", "-Xms", minMem + "M", "-Xmx", maxMem + "M", "-jar", "game.jar", "game.Server", + "-p", "" + port, "-d", "" + distance, "-t", sb.toString()).directory(dir).redirectErrorStream(true); + } + + public void start() { + this.builder.directory().mkdirs(); + try { + this.process = this.builder.start(); + } + catch(IOException e) { + Log.SYSTEM.error(e, "Konnte Server nicht starten"); + this.process = null; + } +// this.builder = null; + if(this.process == null) + return; + Thread con = new Thread(new Runnable() { + private final BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(ServerProcess.this.process.getInputStream()))); + + public void run() { + while(true) { + String line; + try { + line = this.reader.readLine(); + } + catch(IOException e) { + line = null; + } + if(line == null) + break; + if(line.startsWith("#")) { + line = line.substring(1); + Tuple data = Util.getKeyValue(line); + if(data.first.equals("running")) + ServerProcess.this.running = Boolean.parseBoolean(data.second); + if(data.first.equals("message")) + ServerProcess.this.message = data.second; + if(data.first.equals("total")) + ServerProcess.this.total = Integer.parseInt(data.second); + if(data.first.equals("progress")) + ServerProcess.this.progress = Integer.parseInt(data.second); + continue; + } + Log.SYSTEM.info("==SERVER== %s", line); + } + } + }, "Server console listener"); + con.setDaemon(true); + con.start(); + this.writer = new BufferedWriter(new OutputStreamWriter(new BufferedOutputStream(this.process.getOutputStream()))); + } + + public File getFolder() { + return this.builder.directory(); + } + + public boolean isStarted() { + return this.running; + } + + public boolean isStopped() { + return !this.running; + } + + public String getMessage() { + return this.message; + } + + public int getProgress() { + return this.progress; + } + + public int getTotal() { + return this.total; + } + + public void shutdown() { + try { + this.writer.write("end\n"); + } + catch(IOException e) { + } + } +} diff --git a/java/src/game/gui/GuiLoading.java b/java/src/game/gui/GuiLoading.java index 5ec0ddc..eb12daf 100644 --- a/java/src/game/gui/GuiLoading.java +++ b/java/src/game/gui/GuiLoading.java @@ -1,7 +1,7 @@ package game.gui; import game.Game; -import game.Server; +import game.ServerProcess; import game.gui.element.Bar; import game.gui.element.Label; @@ -18,7 +18,7 @@ public class GuiLoading extends Gui { private Bar progressBar1; private Bar progressBar2; - public static GuiLoading makeLoadTask(final Server server, final String user) { + public static GuiLoading makeLoadTask(final ServerProcess server, final String user, final byte[] key) { return new GuiLoading("Lade Welt ...", new Callback() { boolean started = false; @@ -26,8 +26,8 @@ public class GuiLoading extends Gui { if(!this.started && server.isStarted()) { this.started = true; // gm.displayGuiScreen(null); - server.setVar("viewDistance", "" + gm.renderDistance); - gm.connectToIntegrated(server, user); + gm.debugWorld = server.getFolder() == null; + gm.connect(null, gm.port, user, "", "", key); // return; } int progress = server.getProgress(); @@ -43,7 +43,7 @@ public class GuiLoading extends Gui { }); } - public static GuiLoading makeIntermittentTask(final Server server) { + public static GuiLoading makeIntermittentTask(final ServerProcess server) { return new GuiLoading("Lade Welt ...", new Callback() { public void poll(Game gm, GuiLoading gui) { int progress = server.getProgress(); @@ -63,7 +63,7 @@ public class GuiLoading extends Gui { }); } - public static GuiLoading makeSaveTask(final Server server) { + public static GuiLoading makeSaveTask(final ServerProcess server) { return new GuiLoading("Speichere Welt ...", new Callback() { public void poll(Game gm, GuiLoading gui) { if(server.isStopped()) { diff --git a/java/src/game/gui/GuiMenu.java b/java/src/game/gui/GuiMenu.java index ebacc1b..e4a6768 100644 --- a/java/src/game/gui/GuiMenu.java +++ b/java/src/game/gui/GuiMenu.java @@ -8,7 +8,6 @@ import game.gui.element.ActButton.Mode; import game.gui.element.Label; import game.gui.element.NavButton; import game.gui.element.Textbox; -import game.gui.element.Textbox.Action; import game.gui.options.GuiOptions; import game.gui.server.GuiConnect; import game.gui.world.GuiWorlds; @@ -114,32 +113,6 @@ public class GuiMenu extends Gui { this.add(new NavButton(0, 28, this.gm.charEditor ? 400 : 198, 24, GuiOptions.getPage(), "Einstellungen")); if(!this.gm.charEditor) this.add(new NavButton(202, 28, 198, 24, GuiCharacters.INSTANCE, "Charakter")); - if(!this.gm.isRemote() && !this.gm.debugWorld) { - final Textbox portBox = this.add(new Textbox(0, 56, 96, 24, 5, true, new Textbox.Callback() { - public void use(Textbox elem, Action value) { - if(value == Action.SEND || value == Action.UNFOCUS) { - int port = -1; - try { - port = Integer.parseInt(elem.getText()); - } - catch(NumberFormatException e) { - } - if(port < 0 || port > 65535) - elem.setText("" + GuiMenu.this.gm.port); - else - GuiMenu.this.gm.port = port; - } - } - }, "" + this.gm.port)); // "srv_port" - portBox.enabled = this.gm.bind == -1; - this.add(new ActButton(100, 56, 300, 24, new ActButton.Callback() { - public void use(ActButton elem, ActButton.Mode action) { - GuiMenu.this.gm.bind(GuiMenu.this.gm.bind = (GuiMenu.this.gm.bind != -1 ? -1 : GuiMenu.this.gm.port)); - elem.setText(GuiMenu.this.gm.bind != -1 ? "LAN-Welt schließen" : "LAN-Welt öffnen"); - portBox.enabled = GuiMenu.this.gm.bind == -1; - } - }, this.gm.bind != -1 ? "LAN-Welt schließen" : "LAN-Welt öffnen")); - } this.add(new ActButton(0, 102, 400, 24, new ActButton.Callback() { public void use(ActButton elem, ActButton.Mode action) { GuiMenu.this.gm.unload(true); diff --git a/java/src/game/gui/options/GuiNetwork.java b/java/src/game/gui/options/GuiNetwork.java new file mode 100644 index 0000000..ecefe41 --- /dev/null +++ b/java/src/game/gui/options/GuiNetwork.java @@ -0,0 +1,38 @@ +package game.gui.options; + +import game.gui.element.Textbox; +import game.gui.element.Label; +import game.gui.element.Textbox.Action; + +public class GuiNetwork extends GuiOptions { + protected GuiNetwork() { + } + + public void init(int width, int height) { + this.add(new Label(30, 360, 440, 20, "Server-Port für lokale Welten", true)); + final Textbox portBox = this.add(new Textbox(30, 380, 440, 24, 5, true, new Textbox.Callback() { + public void use(Textbox elem, Action value) { + if(value == Action.SEND || value == Action.UNFOCUS) { + int port = -1; + try { + port = Integer.parseInt(elem.getText()); + } + catch(NumberFormatException e) { + } + if(port < 0 || port > 65535) { + elem.setText("" + GuiNetwork.this.gm.port); + } + else { + GuiNetwork.this.gm.port = port; + GuiNetwork.this.gm.setDirty(); + } + } + } + }, "" + this.gm.port)); + super.init(width, height); + } + + public String getTitle() { + return "Server und Netzwerk"; + } +} diff --git a/java/src/game/gui/options/GuiOptions.java b/java/src/game/gui/options/GuiOptions.java index e50ed60..3d5b8ed 100644 --- a/java/src/game/gui/options/GuiOptions.java +++ b/java/src/game/gui/options/GuiOptions.java @@ -5,7 +5,7 @@ import game.gui.GuiMenu; import game.gui.element.NavButton; public abstract class GuiOptions extends Gui { - private static final GuiOptions[] PAGES = {lastPage = new GuiBinds(), new GuiStyle(), new GuiDisplay(), new GuiSound()}; + private static final GuiOptions[] PAGES = {lastPage = new GuiBinds(), new GuiStyle(), new GuiDisplay(), new GuiSound(), new GuiNetwork()}; private static GuiOptions lastPage; diff --git a/java/src/game/gui/server/GuiConnect.java b/java/src/game/gui/server/GuiConnect.java index b8731d3..bcdc5a1 100644 --- a/java/src/game/gui/server/GuiConnect.java +++ b/java/src/game/gui/server/GuiConnect.java @@ -105,7 +105,7 @@ public class GuiConnect extends Gui implements Textbox.Callback { this.lastPass = pass; this.lastAcc = acc; this.gm.setDirty(); - this.gm.connect(addr, port, user, pass, acc); + this.gm.connect(addr, port, user, pass, acc, null); } public void use(Textbox elem, Action value) { diff --git a/java/src/game/init/Registry.java b/java/src/game/init/Registry.java index b7476e2..7623cd8 100755 --- a/java/src/game/init/Registry.java +++ b/java/src/game/init/Registry.java @@ -1,7 +1,25 @@ package game.init; +import java.awt.Desktop; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import javax.imageio.ImageIO; + +import game.log.NettyLogger; + public abstract class Registry { - public static void register() { + private static boolean crashed; + + private static void register() { NameRegistry.register(); BlockRegistry.register(); FlammabilityRegistry.register(); @@ -17,4 +35,106 @@ public abstract class Registry { RotationRegistry.register(); ReorderRegistry.register(); } + + public static void setup(String thread) { + Thread.currentThread().setName(thread); + Locale.setDefault(Locale.ROOT); + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable e) { + System.err.println("Fehler in Thread '" + thread.getName() + "'"); + e.printStackTrace(System.err); + if(crashed) + System.exit(1); + if(e instanceof OutOfMemoryError) { + System.gc(); + System.gc(); + } + if(!thread.getName().startsWith("Thread-") || e instanceof OutOfMemoryError) { + System.err.println("Beende!"); + crashed = true; + if(System.getProperty("crash.nodump") == null) { + PrintStream ps = null; + File report = null; + try { + Date date = new Date(); + File file = new File("crash-" + new SimpleDateFormat("dd.MM.yyyy_HH.mm.ss").format(date) + ".txt"); + FileOutputStream out = new FileOutputStream(file); + ps = new PrintStream(out); + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] info = bean.dumpAllThreads(true, true); + StringBuilder sb = new StringBuilder(); + Error error = new Error(); + for(ThreadInfo threadinfo : info) { + if(threadinfo.getThreadId() == thread.getId()) + error.setStackTrace(threadinfo.getStackTrace()); + sb.append(threadinfo); + } + ps.println("************************************************ " + new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date) + " ************************************************"); + ps.println(); + ps.println("\"Wie haste das denn nu wieder geschafft, Bursche?\""); + ps.println("Unerwarteter Fehler in Thread '" + thread.getName() + "':"); + e.printStackTrace(ps); + ps.println(); + ps.println("---------------------------------------------- Thread-Dump (" + info.length + " Threads) ---------------------------------------------"); + ps.println(); + ps.print(sb.toString()); + ps.println("*********************************************************************************************************************"); + report = file; + } + catch(Throwable t) { + System.err.println("Konnte Absturzbericht nicht speichern:"); + t.printStackTrace(System.err); + } + finally { + if(ps != null) + ps.close(); + } + if(report != null) { + System.err.println("Absturzbericht gespeichert unter " + report.getPath()); + try { + Desktop.getDesktop().browse(report.toURI()); + } + catch(Throwable e1) { + System.err.println("Konnte " + report + " nicht öffnen: " + e1); + } + } + } + System.exit(1); + } + } + }); + ImageIO.setUseCache(false); + Thread timer = new Thread("Timer Hack Thread") { + public void run() { + while(true) { + try { + Thread.sleep(2147483647L); + } + catch(InterruptedException e) { + ; + } + } + } + }; + timer.setDaemon(true); + timer.start(); + System.setProperty("java.net.preferIPv4Stack", "true"); + NettyLogger.init(); + register(); + } + + public static void addShutdownHook(final Runnable hook) { + Runtime.getRuntime().addShutdownHook(new Thread("Game Shutdown Thread") { + public void run() { + if(!crashed) { + try { + hook.run(); + } + catch(Throwable e) { + e.printStackTrace(); + } + } + } + }); + } } diff --git a/java/src/game/network/ClientLoginHandler.java b/java/src/game/network/ClientLoginHandler.java index ef4dd53..4122100 100755 --- a/java/src/game/network/ClientLoginHandler.java +++ b/java/src/game/network/ClientLoginHandler.java @@ -32,10 +32,7 @@ public class ClientLoginHandler extends NetHandler { public final void handleEnableCompression(RPacketEnableCompression packetIn) { - if (!this.networkManager.isLocalChannel()) - { - this.networkManager.setCompressionTreshold(packetIn.getValue()); - } + this.networkManager.setCompressionTreshold(packetIn.getValue()); } // public void handlePasswordRequest(RPacketPasswordRequest packetIn) { diff --git a/java/src/game/network/ClientPlayer.java b/java/src/game/network/ClientPlayer.java index 441f705..7ee2c0b 100755 --- a/java/src/game/network/ClientPlayer.java +++ b/java/src/game/network/ClientPlayer.java @@ -94,6 +94,7 @@ import game.packet.SPacketMessage; import game.packet.SPacketMultiBlockChange; import game.packet.SPacketPlayerPosLook; import game.packet.SPacketRespawn; +import game.packet.SPacketServerTick; import game.packet.SPacketSetExperience; import game.packet.SPacketSkin; import game.packet.SPacketSpawnMob; @@ -903,7 +904,7 @@ public class ClientPlayer extends NetHandler NetHandler.checkThread(packetIn, this, this.gameController, this.clientWorldController); // this.gameController.theWorld.getWorldInfo().setTime(packetIn.getTotalWorldTime()); this.gameController.theWorld.setDayTime(packetIn.getWorldTime()); - this.gameController.setTicked(); + this.gameController.setTicked(packetIn.getServerinfo()); } // public void handleCompass(SPacketCompass packetIn) @@ -1881,6 +1882,11 @@ public class ClientPlayer extends NetHandler this.clientWorldController.dimension.setFullName(packetIn.getFullName()); this.clientWorldController.dimension.setCustomName(packetIn.getCustomName()); } + + public void handleServerTick(SPacketServerTick packet) { + NetHandler.checkThread(packet, this, this.gameController); + this.gameController.setLastTick(packet.getTime()); + } /** * Returns this the NetworkManager instance registered with this NetworkHandlerPlayClient diff --git a/java/src/game/network/HandshakeHandler.java b/java/src/game/network/HandshakeHandler.java index 4d0a76f..dee4f5b 100755 --- a/java/src/game/network/HandshakeHandler.java +++ b/java/src/game/network/HandshakeHandler.java @@ -1,12 +1,45 @@ package game.network; +import game.Server; +import game.init.Config; import game.packet.HPacketHandshake; +import game.packet.RPacketDisconnect; -public abstract class HandshakeHandler extends NetHandler +public class HandshakeHandler extends NetHandler { - public abstract void processHandshake(HPacketHandshake packetIn); + private final Server server; + private final NetConnection networkManager; + + public HandshakeHandler(Server serverIn, NetConnection netManager) + { + this.server = serverIn; + this.networkManager = netManager; + } public void onDisconnect(String reason) { } + + public void processHandshake(HPacketHandshake packetIn) + { + if(packetIn.getProtocolVersion() == 0) { + this.networkManager.closeChannel("Inkompatibel!"); + return; + } + this.networkManager.setConnectionState(PacketRegistry.LOGIN); + if (packetIn.getProtocolVersion() != Config.PROTOCOL) + { + String comp; + if(packetIn.getProtocolVersion() < Config.PROTOCOL) + comp = "Der Server nutzt eine neuere Version: " + Config.VERSION; + else + comp = "Der Server nutzt eine ältere Version: " + Config.VERSION; + this.networkManager.sendPacket(new RPacketDisconnect(comp)); + this.networkManager.closeChannel(comp); + } + else + { + this.networkManager.setNetHandler(new LoginHandler(this.server, this.networkManager)); + } + } } diff --git a/java/src/game/network/HandshakeHandlerMemory.java b/java/src/game/network/HandshakeHandlerMemory.java deleted file mode 100755 index 2ae8da6..0000000 --- a/java/src/game/network/HandshakeHandlerMemory.java +++ /dev/null @@ -1,22 +0,0 @@ -package game.network; - -import game.Server; -import game.packet.HPacketHandshake; - -public class HandshakeHandlerMemory extends HandshakeHandler -{ - private final Server gmServer; - private final NetConnection networkManager; - - public HandshakeHandlerMemory(Server gmServerIn, NetConnection networkManagerIn) - { - this.gmServer = gmServerIn; - this.networkManager = networkManagerIn; - } - - public void processHandshake(HPacketHandshake packetIn) - { - this.networkManager.setConnectionState(PacketRegistry.LOGIN); - this.networkManager.setNetHandler(new LoginHandler(this.gmServer, this.networkManager)); - } -} diff --git a/java/src/game/network/HandshakeHandlerTCP.java b/java/src/game/network/HandshakeHandlerTCP.java deleted file mode 100755 index c0e1405..0000000 --- a/java/src/game/network/HandshakeHandlerTCP.java +++ /dev/null @@ -1,41 +0,0 @@ -package game.network; - -import game.Server; -import game.init.Config; -import game.packet.HPacketHandshake; -import game.packet.RPacketDisconnect; - -public class HandshakeHandlerTCP extends HandshakeHandler -{ - private final Server server; - private final NetConnection networkManager; - - public HandshakeHandlerTCP(Server serverIn, NetConnection netManager) - { - this.server = serverIn; - this.networkManager = netManager; - } - - public void processHandshake(HPacketHandshake packetIn) - { - if(packetIn.getProtocolVersion() == 0) { - this.networkManager.closeChannel("Inkompatibel!"); - return; - } - this.networkManager.setConnectionState(PacketRegistry.LOGIN); - if (packetIn.getProtocolVersion() != Config.PROTOCOL) - { - String comp; - if(packetIn.getProtocolVersion() < Config.PROTOCOL) - comp = "Der Server nutzt eine neuere Version: " + Config.VERSION; - else - comp = "Der Server nutzt eine ältere Version: " + Config.VERSION; - this.networkManager.sendPacket(new RPacketDisconnect(comp)); - this.networkManager.closeChannel(comp); - } - else - { - this.networkManager.setNetHandler(new LoginHandler(this.server, this.networkManager)); - } - } -} diff --git a/java/src/game/network/LoginHandler.java b/java/src/game/network/LoginHandler.java index 5f12f47..55027f1 100755 --- a/java/src/game/network/LoginHandler.java +++ b/java/src/game/network/LoginHandler.java @@ -72,23 +72,10 @@ public class LoginHandler extends NetHandler private void tryAcceptPlayer() { - if(!this.netManager.isLocalChannel()) { -// SocketAddress address = this.netManager.getRemoteAddress(); -// if(this.server.isBanned(address)) { -//// Ban ban = this.server.getBanEntryIp(address); -// this.closeConnection("Deine IP-Adresse ist von diesem Server gebannt"); -// return; -// } -// if(this.server.isBanned(this.loginUser)) { -// Ban ban = this.server.getBanEntry(this.loginUser); -// this.closeConnection(ban.format(true, 0L)); -// return; -// } - if(this.server.getPlayer(this.loginUser) != null) { - this.closeConnection("Nutzername '" + this.loginUser + "' ist bereits vergeben."); - return; - } - } + if(this.server.getPlayer(this.loginUser) != null) { + this.closeConnection("Nutzername '" + this.loginUser + "' ist bereits vergeben."); + return; + } // if (this.server.getPlayer(this.loginUser) != null) // { // this.closeConnection("Nutzername '" + this.loginUser + "' ist bereits vergeben."); @@ -118,7 +105,9 @@ public class LoginHandler extends NetHandler if(this.state != LoginState.PASSWORD) throw new IllegalStateException("Unerwartetes Passwort-Paket"); this.loginUser = packetIn.getUser(); - if(this.netManager.isLocalChannel()) { + if(packetIn.getLocal() != null) { + // TODO: key verification + this.netManager.setLocal(); this.loginPass = ""; // this.loginUser = Config.localUser; if(this.loginUser.length() > Player.MAX_USER_LENGTH || (!this.loginUser.isEmpty() && !Player.isValidUser(this.loginUser))) { diff --git a/java/src/game/network/NetConnection.java b/java/src/game/network/NetConnection.java index fb8e219..6ec4c02 100755 --- a/java/src/game/network/NetConnection.java +++ b/java/src/game/network/NetConnection.java @@ -22,9 +22,6 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalEventLoopGroup; -import io.netty.channel.local.LocalServerChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; @@ -45,21 +42,6 @@ public class NetConnection extends SimpleChannelInboundHandler return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build()); } }; -// public static final LazyLoadBase CLIENT_EPOLL_EVENTLOOP = new LazyLoadBase() -// { -// protected EpollEventLoopGroup load() -// { -// return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build()); -// } -// }; - public static final LazyLoadBase CLIENT_LOCAL_EVENTLOOP = new LazyLoadBase() - { - protected LocalEventLoopGroup load() - { - return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build()); - } - }; -// private final PacketDirection direction; private final Queue outboundPacketsQueue = new ConcurrentLinkedQueue(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); @@ -69,17 +51,12 @@ public class NetConnection extends SimpleChannelInboundHandler private String terminationReason; private boolean disconnected; private boolean local; - -// public NetConnection(PacketDirection packetDirection) -// { -// this.direction = packetDirection; -// } - + public void channelActive(ChannelHandlerContext p_channelActive_1_) throws Exception { super.channelActive(p_channelActive_1_); this.channel = p_channelActive_1_.channel(); - this.local = this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel; + this.local = false; // this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel; this.socketAddress = this.channel.remoteAddress(); try @@ -324,6 +301,11 @@ public class NetConnection extends SimpleChannelInboundHandler return this.local; } + public void setLocal() + { + this.local = true; + } + /** * Create a new NetworkManager from the server host and connect it to the server * @@ -366,26 +348,6 @@ public class NetConnection extends SimpleChannelInboundHandler return networkmanager; } - /** - * Prepares a clientside NetworkManager: establishes a connection to the socket supplied and configures the channel - * pipeline. Returns the newly created instance. - */ - public static NetConnection provideLocalClient(SocketAddress address) - { - final NetConnection networkmanager = new NetConnection(); - ((Bootstrap)((Bootstrap)((Bootstrap)(new Bootstrap()).group((EventLoopGroup)CLIENT_LOCAL_EVENTLOOP.getValue())).handler(new ChannelInitializer() - { - protected void initChannel(Channel p_initChannel_1_) throws Exception - { - p_initChannel_1_.pipeline().addLast((String)"packet_handler", (ChannelHandler)networkmanager); - } - })).channel(LocalChannel.class)).connect(address).syncUninterruptibly(); - return networkmanager; - } - - /** - * Returns true if this NetworkManager has an active channel, false otherwise - */ public boolean isChannelOpen() { return this.channel != null && this.channel.isOpen(); diff --git a/java/src/game/network/PacketRegistry.java b/java/src/game/network/PacketRegistry.java index 918a383..0b752ba 100755 --- a/java/src/game/network/PacketRegistry.java +++ b/java/src/game/network/PacketRegistry.java @@ -74,6 +74,7 @@ import game.packet.SPacketMessage; import game.packet.SPacketMultiBlockChange; import game.packet.SPacketPlayerPosLook; import game.packet.SPacketRespawn; +import game.packet.SPacketServerTick; import game.packet.SPacketSetExperience; import game.packet.SPacketSkin; import game.packet.SPacketSpawnMob; @@ -178,6 +179,7 @@ public enum PacketRegistry // this.server(SPacketNotify.class); this.server(SPacketDimensionName.class); this.server(SPacketCharacterList.class); + this.server(SPacketServerTick.class); this.client(CPacketKeepAlive.class); this.client(CPacketMessage.class); diff --git a/java/src/game/network/Player.java b/java/src/game/network/Player.java index 8200a16..d14d4bf 100755 --- a/java/src/game/network/Player.java +++ b/java/src/game/network/Player.java @@ -274,6 +274,8 @@ public class Player extends NetHandler implements ICrafting, Executor this.pingKey = (int)this.lastPingTime; this.sendPacket(new SPacketKeepAlive(this.pingKey)); } + if(this.local) + if(this.respawnTimer > 0) { if(--this.respawnTimer == 0) { this.respawnPlayer(); @@ -2511,7 +2513,7 @@ public class Player extends NetHandler implements ICrafting, Executor NetHandler.checkThread(packetIn, this, this.server); CPacketAction.Action action = packetIn.getAction(); - if(this.charEditor != (action == Action.SET_ALIGN || action == Action.SET_SPECIES || action == Action.SET_CLASS || action == Action.SET_HEIGHT || action == Action.CLOSE_EDITOR)) // { + if(action != Action.SET_VIEWDIST && action != Action.SHUTDOWN && (this.charEditor != (action == Action.SET_ALIGN || action == Action.SET_SPECIES || action == Action.SET_CLASS || action == Action.SET_HEIGHT || action == Action.CLOSE_EDITOR))) // { // if(this.local && action == Action.CLOSE_EDITOR) // this.server.setDone(); return; @@ -2873,7 +2875,27 @@ public class Player extends NetHandler implements ICrafting, Executor this.entity.setNpcClass(classes[packetIn.getAuxData()]); } break; - + + case WARP_MODE: + if(this.isLocal()) { + this.server.schedule(new Runnable() { + public void run() { + Player.this.server.setTpsTarget(Player.this.server.getTpsTarget() < 10000.0f ? 10000.0f : 20.0f); + } + }); + } + break; + + case SET_VIEWDIST: + if(this.isLocal()) + this.server.setVar("viewDistance", "" + packetIn.getAuxData()); + break; + + case SHUTDOWN: + if(this.isLocal()) + this.server.shutdown(); + break; + default: throw new IllegalArgumentException("Ungültige Aktion!"); } diff --git a/java/src/game/packet/CPacketAction.java b/java/src/game/packet/CPacketAction.java index 5f46eee..3deb5d9 100755 --- a/java/src/game/packet/CPacketAction.java +++ b/java/src/game/packet/CPacketAction.java @@ -93,6 +93,9 @@ public class CPacketAction implements Packet HEAL, REPAIR, PERF, - MAGNET; + MAGNET, + SET_VIEWDIST, + WARP_MODE, + SHUTDOWN; } } diff --git a/java/src/game/packet/LPacketPasswordResponse.java b/java/src/game/packet/LPacketPasswordResponse.java index 4d445df..ced95f6 100755 --- a/java/src/game/packet/LPacketPasswordResponse.java +++ b/java/src/game/packet/LPacketPasswordResponse.java @@ -12,16 +12,18 @@ public class LPacketPasswordResponse implements Packet private String user; private String access; private String password; + private byte[] local; public LPacketPasswordResponse() { } - public LPacketPasswordResponse(String userIn, String accessIn, String passwordIn) + public LPacketPasswordResponse(String userIn, String accessIn, String passwordIn, byte[] local) { this.user = userIn; this.access = accessIn; this.password = passwordIn; + this.local = local; } /** @@ -32,6 +34,8 @@ public class LPacketPasswordResponse implements Packet this.user = buf.readStringFromBuffer(Player.MAX_USER_LENGTH); this.access = buf.readStringFromBuffer(Player.MAX_PASS_LENGTH); this.password = buf.readStringFromBuffer(Player.MAX_PASS_LENGTH); + this.local = buf.readByteArray(); + this.local = this.local.length == 0 ? null : this.local; } /** @@ -42,6 +46,7 @@ public class LPacketPasswordResponse implements Packet buf.writeString(this.user); buf.writeString(this.access); buf.writeString(this.password); + buf.writeByteArray(this.local == null ? new byte[0] : this.local); } /** @@ -66,4 +71,9 @@ public class LPacketPasswordResponse implements Packet { return this.password; } + + public byte[] getLocal() + { + return this.local; + } } diff --git a/java/src/game/packet/SPacketServerTick.java b/java/src/game/packet/SPacketServerTick.java new file mode 100644 index 0000000..14dcaea --- /dev/null +++ b/java/src/game/packet/SPacketServerTick.java @@ -0,0 +1,34 @@ +package game.packet; + +import java.io.IOException; + +import game.network.ClientPlayer; +import game.network.Packet; +import game.network.PacketBuffer; + +public class SPacketServerTick implements Packet { + private int time; + + public SPacketServerTick() { + } + + public SPacketServerTick(int time) { + this.time = time; + } + + public void readPacketData(PacketBuffer buf) throws IOException { + this.time = buf.readInt(); + } + + public void writePacketData(PacketBuffer buf) throws IOException { + buf.writeInt(this.time); + } + + public void processPacket(ClientPlayer handler) { + handler.handleServerTick(this); + } + + public int getTime() { + return this.time; + } +} diff --git a/java/src/game/packet/SPacketTimeUpdate.java b/java/src/game/packet/SPacketTimeUpdate.java index 06e5ec1..47ec0ae 100755 --- a/java/src/game/packet/SPacketTimeUpdate.java +++ b/java/src/game/packet/SPacketTimeUpdate.java @@ -7,36 +7,36 @@ import game.network.Packet; import game.network.PacketBuffer; public class SPacketTimeUpdate implements Packet { -// private long totalWorldTime; private long worldTime; + private String serverInfo; public SPacketTimeUpdate() { } - public SPacketTimeUpdate(long totalTimeIn) { -// this.totalWorldTime = totalWorldTimeIn; - this.worldTime = totalTimeIn; + public SPacketTimeUpdate(long time, String info) { + this.worldTime = time; + this.serverInfo = info; } public void readPacketData(PacketBuffer buf) throws IOException { -// this.totalWorldTime = buf.readLong(); this.worldTime = buf.readLong(); + this.serverInfo = buf.readStringFromBuffer(512); } public void writePacketData(PacketBuffer buf) throws IOException { -// buf.writeLong(this.totalWorldTime); buf.writeLong(this.worldTime); + buf.writeString(this.serverInfo); } public void processPacket(ClientPlayer handler) { handler.handleTimeUpdate(this); } -// public long getTotalWorldTime() { -// return this.totalWorldTime; -// } - public long getWorldTime() { return this.worldTime; } + + public String getServerinfo() { + return this.serverInfo; + } } diff --git a/java/src/game/util/Tuple.java b/java/src/game/util/Tuple.java new file mode 100644 index 0000000..f41da46 --- /dev/null +++ b/java/src/game/util/Tuple.java @@ -0,0 +1,11 @@ +package game.util; + +public class Tuple { + public final S first; + public final T second; + + public Tuple(S first, T second) { + this.first = first; + this.second = second; + } +} diff --git a/java/src/game/util/Util.java b/java/src/game/util/Util.java index cf5a2c9..a5d079c 100644 --- a/java/src/game/util/Util.java +++ b/java/src/game/util/Util.java @@ -1,7 +1,10 @@ package game.util; +import java.awt.GraphicsEnvironment; import java.util.function.Function; +import javax.swing.JOptionPane; + import game.log.Log; import game.properties.IStringSerializable; @@ -293,4 +296,30 @@ int utf_len(const char *str) { return (color & 0xff000000) | ((((color >> 16 & 255) * mul) / 255) << 16) | ((((color >> 8 & 255) * mul) / 255) << 8) | (((color & 255) * mul) / 255); } + + public static void checkOs() { + if(System.getProperty("os.name").startsWith("Windows") || System.getProperty("os.name").startsWith("Mac")) { + String info = "Inkompatibles Betriebssystem"; + String msg = "Linux oder *BSD ist erforderlich, um dieses Programm auszuführen.\n" + + "Alle Versionen von Windows und Mac OS (X) sind nicht kompatibel."; + System.err.println("#################################################################"); + System.err.println("*** " + info + " ***"); + System.err.println(msg); + System.err.println("#################################################################"); + if(!GraphicsEnvironment.isHeadless()) + JOptionPane.showMessageDialog(null, msg, info, JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + } + + public static Tuple getKeyValue(String text, char separator) { + int index = text.indexOf(separator); + if(index == -1) + return new Tuple(text, null); + return new Tuple(text.substring(0, index), text.substring(index + 1)); + } + + public static Tuple getKeyValue(String text) { + return getKeyValue(text, ' '); + } } diff --git a/java/src/game/world/WorldServer.java b/java/src/game/world/WorldServer.java index 4ae9610..150ba5b 100755 --- a/java/src/game/world/WorldServer.java +++ b/java/src/game/world/WorldServer.java @@ -93,7 +93,6 @@ public final class WorldServer extends World { private static final int[][] XZ_DIRS = new int[][] {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; private final Server server; - private final File dir; private final File chunkDir; private final Random grng; private final Set ticks = Sets.newHashSet(); @@ -156,14 +155,13 @@ public final class WorldServer extends World { private long prevUpdate; private long time; - public WorldServer(Server server, File dir, long dtime, Dimension dim, boolean debug) { + public WorldServer(Server server, long dtime, Dimension dim, boolean debug) { super(dim, false, debug); this.server = server; - this.dir = dir; // this.time = time; this.daytime = dtime; this.updateViewRadius(); - this.chunkDir = new File(new File(this.dir, "chunk"), dim.getDimensionName()); + this.chunkDir = new File(new File("chunk"), dim.getDimensionName()); if(!debug) { this.chunkDir.mkdirs(); this.seed = this.rand.longv(); @@ -800,10 +798,10 @@ public final class WorldServer extends World { return list; } - public static boolean needsLoading(File dir, Dimension dim) { + public static boolean needsLoading(Dimension dim) { NBTTagCompound tag = null; try { - File dat = new File(new File(new File(dir, "chunk"), dim.getDimensionName()), "loaders.nbt"); + File dat = new File(new File(new File("chunk"), dim.getDimensionName()), "loaders.nbt"); if(dat.exists() && dat.isFile()) tag = NBTLoader.readGZip(dat); } @@ -813,10 +811,10 @@ public final class WorldServer extends World { return tag != null && tag.hasKey("Loaders", 9) && !tag.getTagList("Loaders", 10).hasNoTags(); } - public static void loadWarps(File dir, Dimension dim, Map warps) { + public static void loadWarps(Dimension dim, Map warps) { NBTTagCompound tag = null; try { - File dat = new File(new File(new File(dir, "chunk"), dim.getDimensionName()), "warps.nbt"); + File dat = new File(new File(new File("chunk"), dim.getDimensionName()), "warps.nbt"); if(dat.exists() && dat.isFile()) tag = NBTLoader.readGZip(dat); } @@ -834,7 +832,7 @@ public final class WorldServer extends World { } } - public static void saveWarps(File dir, Map warps) { + public static void saveWarps(Map warps) { Map map = Maps.newHashMap(); for(Entry pos : warps.entrySet()) { Dimension dim = UniverseRegistry.getDimension(pos.getValue().dim); @@ -854,7 +852,7 @@ public final class WorldServer extends World { } for(Dimension dim : UniverseRegistry.getDimensions()) { NBTTagList list = map.get(dim.getDimensionId()); - File file = new File(new File(new File(dir, "chunk"), dim.getDimensionName()), "warps.nbt"); + File file = new File(new File(new File("chunk"), dim.getDimensionName()), "warps.nbt"); if(list == null) { file.delete(); }