Parcourir la source

Working chat system; start of command system; refactored storing plain x and y in player to using Vector2; (fix: not rendering default lobby under the world received from server)

jan il y a 1 mois
Parent
commit
b2d6df951d
39 fichiers modifiés avec 1086 ajouts et 144 suppressions
  1. 2 2
      core/src/main/java/me/lethunderhawk/camera/CameraController.java
  2. 0 4
      core/src/main/java/me/lethunderhawk/controller/CameraController.java
  3. 29 0
      core/src/main/java/me/lethunderhawk/controller/ChatInputProcessor.java
  4. 33 13
      core/src/main/java/me/lethunderhawk/controller/PlayerInputController.java
  5. 147 0
      core/src/main/java/me/lethunderhawk/manager/ChatManager.java
  6. 20 11
      core/src/main/java/me/lethunderhawk/network/ClientPacketListener.java
  7. 22 6
      core/src/main/java/me/lethunderhawk/network/GameClient.java
  8. 1 1
      core/src/main/java/me/lethunderhawk/render/MapManager.java
  9. 4 4
      core/src/main/java/me/lethunderhawk/render/PlayerRenderer.java
  10. 207 0
      core/src/main/java/me/lethunderhawk/render/chat/ChatRenderer.java
  11. 55 0
      core/src/main/java/me/lethunderhawk/render/chat/RenderedChatMessage.java
  12. 48 17
      core/src/main/java/me/lethunderhawk/screen/GameScreen.java
  13. 13 2
      core/src/main/java/me/lethunderhawk/screen/SettingsScreen.java
  14. 1 0
      core/src/main/java/me/lethunderhawk/settings/Keybind.java
  15. 2 0
      core/src/main/java/me/lethunderhawk/settings/KeybindSettings.java
  16. 9 0
      server/src/main/java/me/lethunderhawk/server/GameServer.java
  17. 51 16
      server/src/main/java/me/lethunderhawk/server/GameServerPacketListener.java
  18. 0 2
      server/src/main/java/me/lethunderhawk/server/ServerLauncher.java
  19. 16 0
      shared/src/main/java/me/lethunderhawk/command/Command.java
  20. 36 0
      shared/src/main/java/me/lethunderhawk/command/CommandContext.java
  21. 18 0
      shared/src/main/java/me/lethunderhawk/command/CommandRegistry.java
  22. 32 0
      shared/src/main/java/me/lethunderhawk/command/impl/HelpCommand.java
  23. 22 0
      shared/src/main/java/me/lethunderhawk/command/impl/TeleportCommand.java
  24. 16 0
      shared/src/main/java/me/lethunderhawk/constant/StringProperties.java
  25. 19 11
      shared/src/main/java/me/lethunderhawk/event/impl/PlayerMoveEvent.java
  26. 29 0
      shared/src/main/java/me/lethunderhawk/messages/BaseMessage.java
  27. 13 0
      shared/src/main/java/me/lethunderhawk/messages/Message.java
  28. 14 0
      shared/src/main/java/me/lethunderhawk/messages/PlayerChatMessage.java
  29. 11 0
      shared/src/main/java/me/lethunderhawk/messages/ServerChatMessage.java
  30. 0 26
      shared/src/main/java/me/lethunderhawk/network/NetworkRegister.java
  31. 12 10
      shared/src/main/java/me/lethunderhawk/network/packet/PacketRegistry.java
  32. 31 0
      shared/src/main/java/me/lethunderhawk/network/packet/impl/MessagePacket.java
  33. 26 3
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLoginPacket.java
  34. 10 1
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLogoutPacket.java
  35. 14 5
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerPositionPacket.java
  36. 21 0
      shared/src/main/java/me/lethunderhawk/network/packet/serialization/OptionalSerializer.java
  37. 18 0
      shared/src/main/java/me/lethunderhawk/network/packet/serialization/UUIDSerializer.java
  38. 44 10
      shared/src/main/java/me/lethunderhawk/world/entity/player/Player.java
  39. 40 0
      shared/src/main/java/me/lethunderhawk/world/entity/player/ServerPlayer.java

+ 2 - 2
core/src/main/java/me/lethunderhawk/camera/CameraController.java

@@ -22,8 +22,8 @@ public class CameraController {
 
         // Get player's bounds and compute player center (player.x/y is bottom-left)
         Rectangle bounds = player.getBounds();
-        float playerCenterX = player.x + bounds.width/2;
-        float playerCenterY = player.y + bounds.height/2;
+        float playerCenterX = player.getPosition().x + bounds.width/2;
+        float playerCenterY = player.getPosition().y + bounds.height/2;
 
         // Convert dead zone from pixels to world units (respect camera zoom)
         float deadZoneWorldX = deadZonePx * camera.zoom;

+ 0 - 4
core/src/main/java/me/lethunderhawk/controller/CameraController.java

@@ -1,4 +0,0 @@
-package me.lethunderhawk.controller;
-
-public class CameraController {
-}

+ 29 - 0
core/src/main/java/me/lethunderhawk/controller/ChatInputProcessor.java

@@ -0,0 +1,29 @@
+package me.lethunderhawk.controller;
+
+import com.badlogic.gdx.InputAdapter;
+import me.lethunderhawk.manager.ChatManager;
+
+public class ChatInputProcessor extends InputAdapter {
+
+    private final ChatManager chatManager;
+
+    public ChatInputProcessor(ChatManager chatManager) {
+        this.chatManager = chatManager;
+    }
+
+    @Override
+    public boolean keyTyped(char character) {
+        chatManager.keyTyped(character);
+        return true;
+    }
+
+    @Override
+    public boolean scrolled(float amountX, float amountY){
+        if(amountY < 0){
+            chatManager.scroll(1);
+        }else if(amountY > 0){
+            chatManager.scroll(-1);
+        }
+        return true;
+    }
+}

+ 33 - 13
core/src/main/java/me/lethunderhawk/controller/PlayerInputController.java

@@ -3,13 +3,22 @@ package me.lethunderhawk.controller;
 import com.badlogic.gdx.Gdx;
 import me.lethunderhawk.event.EventDispatcher;
 import me.lethunderhawk.event.impl.PlayerMoveEvent;
+import me.lethunderhawk.manager.ChatManager;
 import me.lethunderhawk.network.GameClient;
 import me.lethunderhawk.network.packet.impl.PlayerPositionPacket;
+import me.lethunderhawk.screen.GameScreen;
 import me.lethunderhawk.settings.Keybind;
 import me.lethunderhawk.settings.KeybindSettings;
 import me.lethunderhawk.world.entity.player.Player;
 
 public class PlayerInputController {
+    private final GameScreen screen;
+    private final ChatManager chatManager;
+
+    public PlayerInputController(GameScreen screen, ChatManager chatManager) {
+        this.screen = screen;
+        this.chatManager = chatManager;
+    }
 
     public void update(
         float delta,
@@ -20,9 +29,27 @@ public class PlayerInputController {
         float moveX = 0f;
         float moveY = 0f;
 
+        if(Gdx.input.isKeyPressed(
+            KeybindSettings.getKeybind(Keybind.BACK))){
+            screen.exit();
+        }
+
+        if (Gdx.input.isKeyJustPressed(KeybindSettings.getKeybind(Keybind.OPEN_CHAT))) {
+            if(!chatManager.isActive()){
+                chatManager.open();
+                Gdx.input.setInputProcessor(
+                    new ChatInputProcessor(chatManager)
+                );
+            }
+        }
+
+        if (chatManager.isActive()) {
+            return;
+        }
+
+
         if (Gdx.input.isKeyPressed(
             KeybindSettings.getKeybind(Keybind.UP))) {
-
             moveY++;
         }
 
@@ -50,25 +77,18 @@ public class PlayerInputController {
         if (length <= 0f) {
             return;
         }
+        float beforeX = player.getPosition().x;
+        float beforeY = player.getPosition().y;
 
         moveX /= length;
         moveY /= length;
 
-        player.x += moveX * player.getSpeed() * delta;
-        player.y += moveY * player.getSpeed() * delta;
+        player.getPosition().add(moveX * player.getSpeed() * delta, moveY * player.getSpeed() * delta);
 
-        client.send(new PlayerPositionPacket(
-            player.uuid,
-            player.x,
-            player.y
-        ));
+        client.sendUDP(new PlayerPositionPacket(player));
 
         EventDispatcher.getInstance().fire(
-            new PlayerMoveEvent(
-                player,
-                player.x,
-                player.y
-            )
+            new PlayerMoveEvent(player, beforeX, beforeY)
         );
     }
 }

+ 147 - 0
core/src/main/java/me/lethunderhawk/manager/ChatManager.java

@@ -0,0 +1,147 @@
+package me.lethunderhawk.manager;
+
+import com.badlogic.gdx.Gdx;
+import me.lethunderhawk.render.chat.RenderedChatMessage;
+import me.lethunderhawk.settings.Keybind;
+import me.lethunderhawk.settings.KeybindSettings;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ChatManager {
+
+    private final List<RenderedChatMessage> messages = new ArrayList<>();
+
+    private final int maxMessages = 200;
+
+    private boolean active = false;
+
+    private final StringBuilder currentInput = new StringBuilder();
+
+    private int scrollOffset = 0;
+    boolean ignoreNextChar = false;
+
+    public void clearTextBox() {
+        currentInput.setLength(0);
+    }
+
+    public void open() {
+        activate();
+        clearTextBox();
+        ignoreNextChar = true;
+    }
+
+    public interface ChatListener {
+        void onMessageSent(String message);
+    }
+
+    private final ChatListener listener;
+
+    public ChatManager(ChatListener listener) {
+        this.listener = listener;
+    }
+
+    public void addMessage(String message) {
+        addMessage(new RenderedChatMessage(message));
+    }
+
+    public void addMessage(RenderedChatMessage message) {
+        messages.add(message);
+
+        if (messages.size() > maxMessages) {
+            messages.removeFirst();
+        }
+    }
+
+    public void activate() {
+        active = true;
+    }
+
+    public void deactivate() {
+        active = false;
+        currentInput.setLength(0);
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public String getCurrentInput() {
+        return currentInput.toString();
+    }
+
+    public List<RenderedChatMessage> getMessages() {
+        return Collections.unmodifiableList(messages);
+    }
+
+    public int getScrollOffset() {
+        return scrollOffset;
+    }
+
+    public void scroll(int amount) {
+
+        scrollOffset += amount;
+
+        if (scrollOffset < 0) {
+            scrollOffset = 0;
+        }
+
+        int max = Math.max(0, messages.size() - 1);
+
+        if (scrollOffset > max) {
+            scrollOffset = max;
+        }
+    }
+
+    public void keyTyped(char character) {
+
+        if (!active) {
+            return;
+        }
+        if (ignoreNextChar) { ignoreNextChar = false; return; }
+
+        if (character == '\b') {
+
+            if (!currentInput.isEmpty()) {
+                currentInput.deleteCharAt(currentInput.length() - 1);
+            }
+
+            return;
+        }
+
+        if (character == '\r' || character == '\n') {
+
+            String msg = currentInput.toString().trim();
+
+            if (!msg.isEmpty()) {
+
+                addMessage("You: " + msg);
+
+                listener.onMessageSent(msg);
+            }
+
+            clearTextBox();
+
+            deactivate();
+
+            return;
+        }
+
+        if (!Character.isISOControl(character)) {
+            currentInput.append(character);
+        }
+    }
+
+    public void update() {
+
+        if (!active) {
+            return;
+        }
+
+        if (Gdx.input.isKeyJustPressed(KeybindSettings.getKeybind(Keybind.BACK))) {
+            deactivate();
+        }
+    }
+
+}

+ 20 - 11
core/src/main/java/me/lethunderhawk/network/ClientPacketListener.java

@@ -1,5 +1,8 @@
 package me.lethunderhawk.network;
 
+import me.lethunderhawk.render.chat.RenderedChatMessage;
+import me.lethunderhawk.messages.PlayerChatMessage;
+import me.lethunderhawk.messages.ServerChatMessage;
 import me.lethunderhawk.network.packet.impl.*;
 import me.lethunderhawk.world.World;
 import me.lethunderhawk.world.entity.player.Player;
@@ -28,14 +31,24 @@ public class ClientPacketListener implements PacketListener {
         this.world = packet.world;
     }
 
+
+    @PacketHandler
+    private void handleMessage(MessagePacket packet) {
+        if(packet.getMessage() instanceof ServerChatMessage){
+            gameClient.getChatManager().addMessage(new RenderedChatMessage(packet.getMessage()));
+        }else if(packet.getMessage() instanceof PlayerChatMessage){
+            gameClient.getChatManager().addMessage(new RenderedChatMessage(packet.getMessage()));
+        }
+    }
+
     /**
      * Fired whenever a player logs into the server
      * @param packet The Packet sent by the server
      */
     @PacketHandler
     private void playerLogin(PlayerLoginPacket packet) {
-        Player player = packet.player;
-        otherPlayers.put(player.uuid, player);
+        Player player = packet.getPlayer();
+        otherPlayers.put(player.getUuid(), player);
     }
 
 
@@ -45,7 +58,7 @@ public class ClientPacketListener implements PacketListener {
      */
     @PacketHandler
     private void playerLogout(PlayerLogoutPacket packet) {
-        otherPlayers.remove(packet.uuid);
+        otherPlayers.remove(packet.getUuid());
     }
 
     /**
@@ -54,18 +67,14 @@ public class ClientPacketListener implements PacketListener {
      */
     @PacketHandler
     private void handlePlayerPosition(PlayerPositionPacket packet) {
-        if(packet.uuid.equals(gameClient.getPlayer().uuid)){
-            gameClient.getPlayer().x = packet.x;
-            gameClient.getPlayer().y = packet.y;
+        if(packet.uuid.equals(gameClient.getPlayer().getUuid())){
+            gameClient.getPlayer().getPosition().set(packet.getPosition());
             return;
         }
         Player player = otherPlayers.computeIfAbsent(packet.uuid, uuid -> {
-            Player p = new Player();
-            p.uuid = uuid;
-            return p;
+            return new Player("Other Player", uuid);
         });
-        player.x = packet.x;
-        player.y = packet.y;
+        player.getPosition().set(packet.getPosition());
     }
 
     /*@PacketHandler

+ 22 - 6
core/src/main/java/me/lethunderhawk/network/GameClient.java

@@ -3,6 +3,9 @@ package me.lethunderhawk.network;
 import com.esotericsoftware.kryonet.Client;
 import com.esotericsoftware.kryonet.Connection;
 import com.esotericsoftware.kryonet.Listener;
+import me.lethunderhawk.manager.ChatManager;
+import me.lethunderhawk.messages.PlayerChatMessage;
+import me.lethunderhawk.network.packet.impl.MessagePacket;
 import me.lethunderhawk.world.World;
 import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.network.packet.*;
@@ -11,22 +14,25 @@ import me.lethunderhawk.network.packet.impl.PlayerLoginPacket;
 import java.io.IOException;
 import java.util.*;
 
-public class GameClient extends Listener implements EventListener {
+public class GameClient extends Listener implements EventListener, ChatManager.ChatListener {
 
     private final Client client;
 
     private final Player player;
     private final ClientPacketListener clientPacketListener;
+    private final ChatManager chatManager;
 
     public GameClient() {
         this.player = new Player();
         client = new Client(65536, 65536);
 
         client.start();
+        chatManager = new ChatManager(this);
+        registerPackets();
+
         this.clientPacketListener = new ClientPacketListener(this);
         PacketDispatcher.registerListener(clientPacketListener);
 
-        registerPackets();
     }
 
     private void registerPackets() {
@@ -42,12 +48,11 @@ public class GameClient extends Listener implements EventListener {
             54777
         );
         client.addListener(this);
-        PlayerLoginPacket loginPacket = new PlayerLoginPacket();
-        loginPacket.player = player;
-        send(loginPacket);
+        PlayerLoginPacket loginPacket = new PlayerLoginPacket(player);
+        sendUDP(loginPacket);
     }
 
-    public void send(Object packet) {
+    public void sendUDP(Object packet) {
         client.sendUDP(packet);
     }
 
@@ -73,4 +78,15 @@ public class GameClient extends Listener implements EventListener {
     public Map<UUID, Player> getOtherPlayers() {
         return clientPacketListener.getOtherPlayers();
     }
+
+    public ChatManager getChatManager() {
+        return this.chatManager;
+    }
+
+    @Override
+    public void onMessageSent(String message) {
+        Player player = getPlayer();
+        MessagePacket packet = new MessagePacket(new PlayerChatMessage(player, message));
+        sendUDP(packet);
+    }
 }

+ 1 - 1
core/src/main/java/me/lethunderhawk/render/MapManager.java

@@ -35,7 +35,7 @@ public class MapManager {
         renderer =
             new OrthogonalTiledMapRenderer(
                 map,
-                1f / TILE_SIZE
+                2f
             );
     }
 

+ 4 - 4
core/src/main/java/me/lethunderhawk/render/PlayerRenderer.java

@@ -35,7 +35,7 @@ public class PlayerRenderer {
 
     private void renderPlayer(Player player) {
 
-        if (player.avatar == null) {
+        if (player.getAvatar() == null) {
             return;
         }
 
@@ -46,12 +46,12 @@ public class PlayerRenderer {
             for (int y = 0; y < 16; y++) {
 
                 renderer.setColor(
-                    player.avatar.getColor(x, y)
+                    player.getAvatar().getColor(x, y)
                 );
 
                 renderer.rect(
-                    player.x + x * cellSize,
-                    player.y + y * cellSize,
+                    player.getPosition().x + x * cellSize,
+                    player.getPosition().y + y * cellSize,
                     cellSize,
                     cellSize
                 );

+ 207 - 0
core/src/main/java/me/lethunderhawk/render/chat/ChatRenderer.java

@@ -0,0 +1,207 @@
+package me.lethunderhawk.render.chat;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.OrthographicCamera;
+import com.badlogic.gdx.graphics.g2d.BitmapFont;
+import com.badlogic.gdx.graphics.g2d.GlyphLayout;
+import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
+import me.lethunderhawk.manager.ChatManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChatRenderer {
+
+    private final SpriteBatch batch;
+    private final ShapeRenderer shapeRenderer;
+
+    private final BitmapFont font = new BitmapFont();
+
+    private final GlyphLayout layout = new GlyphLayout();
+
+    private final ChatManager chatManager;
+
+    private final int width = 400;
+    private final int height = 220;
+
+    private final int padding = 10;
+
+    public ChatRenderer(
+        SpriteBatch batch,
+        ShapeRenderer shapeRenderer,
+        ChatManager chatManager
+    ) {
+        this.batch = batch;
+        this.shapeRenderer = shapeRenderer;
+        this.chatManager = chatManager;
+    }
+
+    public void render(OrthographicCamera camera) {
+
+        float x = camera.position.x + 5 - camera.viewportWidth / 2f;
+
+        float y = camera.position.y + 5 - camera.viewportHeight / 2f;
+
+        batch.setProjectionMatrix(camera.combined);
+
+        /*
+         * ONLY render background while chat is active
+         */
+        if (chatManager.isActive()) {
+
+            Gdx.gl.glEnable(GL20.GL_BLEND);
+
+            shapeRenderer.setProjectionMatrix(camera.combined);
+
+            shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
+
+            shapeRenderer.setColor(0, 0, 0, 0.6f);
+
+            shapeRenderer.rect(x, y, width, height);
+
+            shapeRenderer.setColor(0.15f, 0.15f, 0.15f, 0.95f);
+
+            shapeRenderer.rect(x, y, width, 30);
+
+            shapeRenderer.end();
+        }
+
+        batch.begin();
+
+        renderMessages(x, y);
+
+        if (chatManager.isActive()) {
+
+            font.draw(
+                batch,
+                "> " + chatManager.getCurrentInput(),
+                x + padding,
+                y + 20
+            );
+        }
+
+        batch.end();
+    }
+
+    private void renderMessages(float x, float y) {
+
+        List<RenderedLine> renderedLines = new ArrayList<>();
+
+        /*
+         * Convert messages -> wrapped lines
+         */
+        for (RenderedChatMessage msg : chatManager.getMessages()) {
+
+            List<String> wrapped =
+                wrap(msg.text, width - padding * 2);
+
+            for (String line : wrapped) {
+                renderedLines.add(
+                    new RenderedLine(line, msg)
+                );
+            }
+        }
+
+        int visibleLines = 10;
+
+        int end = Math.max(
+            0,
+            renderedLines.size() - chatManager.getScrollOffset()
+        );
+
+        int start = Math.max(
+            0,
+            end - visibleLines
+        );
+
+        float drawY;
+
+        if (chatManager.isActive()) {
+            drawY = y + 50;
+        } else {
+            drawY = y + 20;
+        }
+
+        /*
+         * Draw newest -> oldest
+         */
+        for (int i = end - 1; i >= start; i--) {
+
+            RenderedLine line = renderedLines.get(i);
+
+            float alpha =
+                line.message.getAlpha(chatManager.isActive());
+
+            /*
+             * Skip invisible messages
+             */
+            if (alpha <= 0f) {
+                continue;
+            }
+
+            font.setColor(1f, 1f, 1f, alpha);
+
+            font.draw(
+                batch,
+                line.text,
+                x + padding,
+                drawY
+            );
+
+            drawY += 18;
+        }
+
+        font.setColor(Color.WHITE);
+    }
+
+    private List<String> wrap(String text, float maxWidth) {
+
+        List<String> result = new ArrayList<>();
+
+        String[] words = text.split(" ");
+
+        StringBuilder line = new StringBuilder();
+
+        for (String word : words) {
+
+            String test = line + word + " ";
+
+            layout.setText(font, test);
+
+            if (layout.width > maxWidth) {
+
+                result.add(line.toString());
+
+                line = new StringBuilder();
+            }
+
+            line.append(word).append(" ");
+        }
+
+        result.add(line.toString());
+
+        return result;
+    }
+
+    public void dispose() {
+        font.dispose();
+    }
+
+    private static class RenderedLine {
+
+        private final String text;
+
+        private final RenderedChatMessage message;
+
+        public RenderedLine(
+            String text,
+            RenderedChatMessage message
+        ) {
+            this.text = text;
+            this.message = message;
+        }
+    }
+}

+ 55 - 0
core/src/main/java/me/lethunderhawk/render/chat/RenderedChatMessage.java

@@ -0,0 +1,55 @@
+package me.lethunderhawk.render.chat;
+
+import me.lethunderhawk.messages.Message;
+
+public class RenderedChatMessage {
+
+    public final String text;
+
+    public final long timestamp;
+
+    public RenderedChatMessage(String text) {
+        this.text = text;
+        this.timestamp = System.currentTimeMillis();
+    }
+
+    public RenderedChatMessage(Message message) {
+        this.text = message.getFromName() + ": " + message.getMessage();
+        this.timestamp = message.getTimestamp();
+    }
+
+    public float getAlpha(boolean chatOpen) {
+
+        /*
+         * Chat fully visible while opened
+         */
+        if (chatOpen) {
+            return 1f;
+        }
+
+        float lifetime =
+            (System.currentTimeMillis() - timestamp) / 1000f;
+
+        /*
+         * Visible for 5 seconds
+         */
+        float visibleDuration = 5f;
+
+        /*
+         * Fade duration
+         */
+        float fadeDuration = 2f;
+
+        if (lifetime < visibleDuration) {
+            return 1f;
+        }
+
+        float fadeTime = lifetime - visibleDuration;
+
+        if (fadeTime >= fadeDuration) {
+            return 0f;
+        }
+
+        return 1f - (fadeTime / fadeDuration);
+    }
+}

+ 48 - 17
core/src/main/java/me/lethunderhawk/screen/GameScreen.java

@@ -10,6 +10,10 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
 import com.badlogic.gdx.utils.viewport.ScreenViewport;
 import me.lethunderhawk.Main;
 import me.lethunderhawk.controller.PlayerInputController;
+import me.lethunderhawk.render.chat.RenderedChatMessage;
+import me.lethunderhawk.messages.PlayerChatMessage;
+import me.lethunderhawk.network.packet.impl.MessagePacket;
+import me.lethunderhawk.render.chat.ChatRenderer;
 import me.lethunderhawk.render.MapManager;
 import me.lethunderhawk.render.PlayerRenderer;
 import me.lethunderhawk.render.WorldRenderer;
@@ -22,7 +26,7 @@ import java.io.IOException;
 
 public class GameScreen implements Screen {
 
-    private final Main game;
+    private final Main main;
 
     private final ShapeRenderer shapeRenderer;
     private final SpriteBatch spriteBatch;
@@ -39,19 +43,22 @@ public class GameScreen implements Screen {
 
     private WorldRenderer worldRenderer;
     private PlayerRenderer playerRenderer;
+    private ChatRenderer chatRenderer;
 
-    public GameScreen(Main game, String ip) {
+    private boolean singlePlayer = false;
 
-        this.game = game;
+    public GameScreen(Main main, String ip) {
+
+        this.main = main;
 
         this.shapeRenderer = new ShapeRenderer();
         this.spriteBatch = new SpriteBatch();
 
         this.client = new GameClient();
 
-        this.cameraController = new CameraController();
-        this.inputController = new PlayerInputController();
 
+        this.cameraController = new CameraController();
+        this.inputController = new PlayerInputController(this, client.getChatManager());
         connect(ip);
     }
 
@@ -61,7 +68,7 @@ public class GameScreen implements Screen {
             client.connect(ip);
 
         } catch (IOException e) {
-
+            this.singlePlayer = true;
             System.out.println("Couldn't connect to ip: " + ip);
             System.out.println("Starting Singleplayer instead!");
         }
@@ -69,7 +76,6 @@ public class GameScreen implements Screen {
 
     @Override
     public void show() {
-
         setupCamera();
 
         mapManager = new MapManager("level/lobby.tmx");
@@ -81,6 +87,18 @@ public class GameScreen implements Screen {
         );
 
         playerRenderer = new PlayerRenderer(shapeRenderer);
+
+        chatRenderer = new ChatRenderer(
+            spriteBatch,
+            shapeRenderer,
+            client.getChatManager()
+        );
+
+        if(singlePlayer) {
+            client.getChatManager().addMessage(new RenderedChatMessage("Started Singleplayer"));
+        }else{
+            client.getChatManager().addMessage("Connected to Multiplayer");
+        }
     }
 
     private void setupCamera() {
@@ -101,11 +119,18 @@ public class GameScreen implements Screen {
 
         clearScreen();
 
-        mapManager.render(camera);
 
-        worldRenderer.render(camera);
+        if(singlePlayer) {
+            mapManager.render(camera);
+        }else{
+            worldRenderer.render(camera);
+        }
 
         playerRenderer.render(camera, client);
+
+        client.getChatManager().update();
+
+        chatRenderer.render(camera);
     }
 
     private void update(float delta) {
@@ -118,25 +143,22 @@ public class GameScreen implements Screen {
 
         inputController.update(delta, player, client);
 
+
         cameraController.update(delta, camera, player);
     }
 
     private void clearScreen() {
-
         Gdx.gl.glClearColor(0, 0, 0, 1);
-
         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
     }
 
     @Override
     public void resize(int width, int height) {
-
         viewport.update(width, height, true);
     }
 
     @Override
     public void hide() {
-
         logout();
     }
 
@@ -157,11 +179,9 @@ public class GameScreen implements Screen {
 
         if (player != null) {
 
-            PlayerLogoutPacket packet = new PlayerLogoutPacket();
-
-            packet.uuid = player.uuid;
+            PlayerLogoutPacket packet = new PlayerLogoutPacket(player.getUuid());
 
-            client.send(packet);
+            client.sendUDP(packet);
         }
 
         client.disconnect();
@@ -169,4 +189,15 @@ public class GameScreen implements Screen {
 
     @Override public void pause() {}
     @Override public void resume() {}
+
+    public void exit() {
+        logout();
+        main.setScreen(new MenuScreen(main));
+    }
+
+    public void onMessageSent(String message) {
+        Player player = client.getPlayer();
+        MessagePacket packet = new MessagePacket(new PlayerChatMessage(player, message));
+        client.sendUDP(packet);
+    }
 }

+ 13 - 2
core/src/main/java/me/lethunderhawk/screen/SettingsScreen.java

@@ -9,12 +9,16 @@ import com.badlogic.gdx.graphics.GL20;
 import com.badlogic.gdx.scenes.scene2d.InputEvent;
 import com.badlogic.gdx.scenes.scene2d.Stage;
 import com.badlogic.gdx.scenes.scene2d.ui.*;
+import com.badlogic.gdx.scenes.scene2d.ui.List;
 import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
 import com.badlogic.gdx.utils.viewport.ScreenViewport;
 import me.lethunderhawk.Main;
 import me.lethunderhawk.settings.Keybind;
 import me.lethunderhawk.settings.KeybindSettings;
 
+import java.util.*;
+import java.util.stream.Collectors;
+
 public class SettingsScreen implements Screen {
     private final Main game;
     private Stage stage;
@@ -53,8 +57,15 @@ public class SettingsScreen implements Screen {
         table.add(title).colspan(2).row();
 
         // Create one row per keybind
-        KeybindSettings.getKeybindMap().forEach((keybind, keycode) -> {
-
+        Map<Keybind, Integer> keymap = KeybindSettings.getKeybindMap();
+        java.util.List<Map.Entry<Keybind, Integer>> orderedEntries = Arrays.stream(Keybind.values())
+            .map(k -> new AbstractMap.SimpleEntry<>(k, keymap.get(k)))
+            .filter(e -> e.getValue() != null)
+            .collect(Collectors.toList());
+
+        orderedEntries.forEach((entry) -> {
+            Keybind keybind = entry.getKey();
+            Integer keycode = entry.getValue();
             // Action label
             Label actionLabel = new Label(formatKeybindName(keybind), skin);
 

+ 1 - 0
core/src/main/java/me/lethunderhawk/settings/Keybind.java

@@ -6,4 +6,5 @@ public enum Keybind {
     LEFT,
     RIGHT,
     BACK,
+    OPEN_CHAT
 }

+ 2 - 0
core/src/main/java/me/lethunderhawk/settings/KeybindSettings.java

@@ -10,6 +10,7 @@ public class KeybindSettings {
     private static final Map<Keybind, Integer> keymap = new HashMap<>();
 
     public static Map<Keybind, Integer> getKeybindMap() {
+
         return new HashMap<>(keymap);
     }
 
@@ -23,6 +24,7 @@ public class KeybindSettings {
         keymap.put(Keybind.LEFT, Input.Keys.A);
         keymap.put(Keybind.RIGHT, Input.Keys.D);
         keymap.put(Keybind.BACK, Input.Keys.ESCAPE);
+        keymap.put(Keybind.OPEN_CHAT, Input.Keys.T);
     }
 
     public static void setKeybind(Keybind key, int value) {

+ 9 - 0
server/src/main/java/me/lethunderhawk/server/GameServer.java

@@ -3,6 +3,9 @@ package me.lethunderhawk.server;
 import com.esotericsoftware.kryonet.Connection;
 import com.esotericsoftware.kryonet.Listener;
 import com.esotericsoftware.kryonet.Server;
+import me.lethunderhawk.command.CommandRegistry;
+import me.lethunderhawk.command.impl.HelpCommand;
+import me.lethunderhawk.command.impl.TeleportCommand;
 import me.lethunderhawk.network.packet.*;
 import me.lethunderhawk.world.World;
 import me.lethunderhawk.world.blocks.BlockRegistry;
@@ -21,6 +24,7 @@ public class GameServer {
         server = new Server(65536, 65536);
 
         registerBlocks();
+        registerCommands();
 
         this.world = generateNewMap();
 
@@ -40,6 +44,11 @@ public class GameServer {
         });
     }
 
+    private void registerCommands() {
+        CommandRegistry.registerCommand(new HelpCommand(), "help", "h");
+        CommandRegistry.registerCommand(new TeleportCommand(), "teleport", "tp");
+    }
+
     private void registerBlocks() {
         this.registry = new BlockRegistry();
         // In a real scenario, the registry would map IDs to asset paths

+ 51 - 16
server/src/main/java/me/lethunderhawk/server/GameServerPacketListener.java

@@ -2,12 +2,20 @@ package me.lethunderhawk.server;
 
 import com.esotericsoftware.kryonet.Connection;
 import com.esotericsoftware.kryonet.Server;
+import me.lethunderhawk.command.Command;
+import me.lethunderhawk.command.CommandContext;
+import me.lethunderhawk.command.CommandRegistry;
 import me.lethunderhawk.constant.IntProperties;
+import me.lethunderhawk.constant.StringProperties;
+import me.lethunderhawk.messages.Message;
+import me.lethunderhawk.messages.PlayerChatMessage;
 import me.lethunderhawk.network.packet.impl.*;
+import me.lethunderhawk.world.entity.player.ServerPlayer;
 import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.network.packet.PacketHandler;
 import me.lethunderhawk.network.packet.PacketListener;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -15,7 +23,7 @@ public class GameServerPacketListener implements PacketListener {
     /**
      * Connection id --> Player
      */
-    private final Map<Integer, Player> players = new HashMap<>();
+    private final Map<Integer, ServerPlayer> players = new HashMap<>();
     public Server server;
     private final GameServer gameServer;
 
@@ -23,23 +31,53 @@ public class GameServerPacketListener implements PacketListener {
         this.server = server;
         this.gameServer = gameServer;
     }
+    @PacketHandler
+    public void onMessage(MessagePacket packet, Connection connection) {
+        int connectionId = connection.getID();
+        Message playerMessage = packet.getMessage();
+
+        if(!(playerMessage instanceof PlayerChatMessage)){ return; }
+
+        String message = playerMessage.getMessage();
+        ServerPlayer connectedPlayer = players.get(connectionId);
+
+        if(message.startsWith(StringProperties.COMMAND_PREFIX.getValue())){
+
+            Command command = CommandRegistry.get(message.substring(1).split(" ")[0]);
+            if(command != null && connectedPlayer.isOp()){
+                String[] split = message.split(" ");
+                String[] args = Arrays.copyOfRange(split, 1, split.length);
+
+                command.execute(new CommandContext(message, args, connectedPlayer));
+
+                System.out.println(playerMessage.getFromName() + " issued Command: " + message);
+            }else{
+                System.out.println(playerMessage.getFromName() + " tried to issue Command: " + message);
+                connectedPlayer.sendMessage("Unknown Command or insufficient permissions!");
+            }
+
+        }else{
+            server.sendToAllExceptUDP(connectionId, packet);
+            System.out.println("Message from " + playerMessage.getFromName() + ": " + message);
+        }
+    }
+
 
     @PacketHandler
     public void onPlayerPosition(PlayerPositionPacket packet, Connection connection) {
         int connectionId = connection.getID();
         Player player = players.get(connectionId);
-
-        if(packet.x < 0 || packet.x > gameServer.getWorld().getRealWidth() - player.getBounds().width || packet.y < 0 || packet.y > gameServer.getWorld().getRealHeight() - player.getBounds().height) {
-            float realX = Math.clamp(packet.x, 0f, gameServer.getWorld().getRealWidth() - player.getBounds().width);
-            float realY = Math.clamp(packet.y, 0f, gameServer.getWorld().getRealHeight() - player.getBounds().height);
+        float x = packet.getPosition().x;
+        float y = packet.getPosition().y;
+        if(x < 0 || x > gameServer.getWorld().getRealWidth() - player.getBounds().width || y < 0 || y > gameServer.getWorld().getRealHeight() - player.getBounds().height) {
+            float realX = Math.clamp(x, 0f, gameServer.getWorld().getRealWidth() - player.getBounds().width);
+            float realY = Math.clamp(y, 0f, gameServer.getWorld().getRealHeight() - player.getBounds().height);
             PlayerPositionPacket correctionPacket = new PlayerPositionPacket(packet.uuid, realX, realY);
             server.sendToAllUDP(correctionPacket);
             return;
         }
         server.sendToAllExceptUDP(connectionId, packet);
-        Player p = players.get(connectionId);
-        p.x = packet.x;
-        p.y = packet.y;
+        player.getPosition().set(x, y);
     }
 
     @PacketHandler
@@ -50,12 +88,12 @@ public class GameServerPacketListener implements PacketListener {
 
     @PacketHandler
     public void onLogin(PlayerLoginPacket packet, Connection connection){
-        if(!validatePlayer(packet.player)){
+        if(!validatePlayer(packet.getPlayer())){
             connection.close();
             return;
         }
 
-        players.putIfAbsent(connection.getID(), packet.player);
+        players.putIfAbsent(connection.getID(), new ServerPlayer(packet.getPlayer(), connection));
 
         server.sendToAllExceptUDP(connection.getID(), packet);
         server.sendToUDP(connection.getID(), new PlayerLoginSuccessPacket());
@@ -64,12 +102,9 @@ public class GameServerPacketListener implements PacketListener {
         WorldDataPacket worldPacket = new WorldDataPacket(gameServer.getWorld());
         server.sendToTCP(connection.getID(), worldPacket);
 
-        for(Player player : players.values()){
-            if(player.uuid.equals(packet.player.uuid)) return;
-            PlayerLoginPacket loginPacket = new PlayerLoginPacket();
-            loginPacket.player = player;
-            loginPacket.login_x = player.x;
-            loginPacket.login_y = player.y;
+        for(ServerPlayer player : players.values()){
+            if(player.getUuid().equals(packet.getPlayer().getUuid())) return;
+            PlayerLoginPacket loginPacket = new PlayerLoginPacket(new Player(player));
             server.sendToUDP(connection.getID(), loginPacket);
         }
     }

+ 0 - 2
server/src/main/java/me/lethunderhawk/server/ServerLauncher.java

@@ -4,9 +4,7 @@ package me.lethunderhawk.server;
 public class ServerLauncher {
 
     public static void main(String[] args) {
-
         try {
-
             new GameServer();
             System.out.println("Server gestartet.");
         } catch (Exception e) {

+ 16 - 0
shared/src/main/java/me/lethunderhawk/command/Command.java

@@ -0,0 +1,16 @@
+package me.lethunderhawk.command;
+
+public interface Command {
+
+    String name();
+
+    void execute(CommandContext context);
+
+    default boolean isServerOnly() {
+        return true;
+    }
+
+    default boolean isOpOnly() {
+        return true;
+    }
+}

+ 36 - 0
shared/src/main/java/me/lethunderhawk/command/CommandContext.java

@@ -0,0 +1,36 @@
+package me.lethunderhawk.command;
+
+import me.lethunderhawk.world.entity.player.Player;
+import me.lethunderhawk.world.entity.player.ServerPlayer;
+
+public class CommandContext {
+
+    private final String rawMessage;
+
+    private final String[] args;
+
+    private final ServerPlayer player;
+
+
+    public CommandContext(
+        String rawMessage,
+        String[] args,
+        ServerPlayer player
+    ) {
+        this.rawMessage = rawMessage;
+        this.args = args;
+        this.player = player;
+    }
+
+    public String getRawMessage() {
+        return rawMessage;
+    }
+
+    public String[] getArgs() {
+        return args;
+    }
+
+    public ServerPlayer getPlayer() {
+        return player;
+    }
+}

+ 18 - 0
shared/src/main/java/me/lethunderhawk/command/CommandRegistry.java

@@ -0,0 +1,18 @@
+package me.lethunderhawk.command;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CommandRegistry {
+    public static Map<String, Command> commands = new HashMap<>();
+
+    public static void registerCommand(Command command, String... aliases) {
+        for(String alias : aliases){
+            commands.put(alias, command);
+        }
+    }
+
+    public static Command get(String commandName) {
+        return commands.get(commandName);
+    }
+}

+ 32 - 0
shared/src/main/java/me/lethunderhawk/command/impl/HelpCommand.java

@@ -0,0 +1,32 @@
+package me.lethunderhawk.command.impl;
+
+import me.lethunderhawk.command.Command;
+import me.lethunderhawk.command.CommandContext;
+import me.lethunderhawk.command.CommandRegistry;
+import me.lethunderhawk.world.entity.player.ServerPlayer;
+
+public class HelpCommand implements Command {
+    @Override
+    public String name() {
+        return "help";
+    }
+
+    @Override
+    public void execute(CommandContext context) {
+        ServerPlayer player = context.getPlayer();
+        player.sendMessage("Currently there are only so few Commands right now:");
+        for(Command command : CommandRegistry.commands.values()){
+            player.sendMessage(command.name());
+        }
+    }
+
+    @Override
+    public boolean isServerOnly() {
+        return false;
+    }
+
+    @Override
+    public boolean isOpOnly(){
+        return false;
+    }
+}

+ 22 - 0
shared/src/main/java/me/lethunderhawk/command/impl/TeleportCommand.java

@@ -0,0 +1,22 @@
+package me.lethunderhawk.command.impl;
+
+import me.lethunderhawk.command.Command;
+import me.lethunderhawk.command.CommandContext;
+
+public class TeleportCommand implements Command {
+    @Override
+    public String name() {
+        return "teleport";
+    }
+
+    @Override
+    public void execute(CommandContext context) {
+        context.getPlayer().teleport(Float.parseFloat(context.getArgs()[0]), Float.parseFloat(context.getArgs()[1]));
+    }
+
+    @Override
+    public boolean isServerOnly() {
+        return false;
+    }
+
+}

+ 16 - 0
shared/src/main/java/me/lethunderhawk/constant/StringProperties.java

@@ -0,0 +1,16 @@
+package me.lethunderhawk.constant;
+
+public enum StringProperties {
+    COMMAND_PREFIX("/");
+
+    // ----- Instantiator
+
+    private final String value;
+    StringProperties(String value){
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+}

+ 19 - 11
shared/src/main/java/me/lethunderhawk/event/impl/PlayerMoveEvent.java

@@ -6,30 +6,38 @@ import me.lethunderhawk.event.Event;
 public class PlayerMoveEvent extends Event {
 
     private final Player player;
-
-    private final float x;
-    private final float y;
+    private final float fromX, fromY, toX, toY;
 
     public PlayerMoveEvent(
         Player player,
-        float x,
-        float y
+        float fromX, float fromY
     ) {
 
         this.player = player;
-        this.x = x;
-        this.y = y;
+        this.fromX = fromX;
+        this.fromY = fromY;
+        this.toX = player.getPosition().x;
+        this.toY = player.getPosition().y;
     }
 
     public Player getPlayer() {
         return player;
     }
 
-    public float getX() {
-        return x;
+    public float getFromX() {
+        return fromX;
+    }
+
+    public float getFromY() {
+        return fromY;
     }
 
-    public float getY() {
-        return y;
+    public float getToX() {
+        return toX;
     }
+
+    public float getToY() {
+        return toY;
+    }
+
 }

+ 29 - 0
shared/src/main/java/me/lethunderhawk/messages/BaseMessage.java

@@ -0,0 +1,29 @@
+package me.lethunderhawk.messages;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public abstract class BaseMessage implements Message {
+    private String fromName;
+    private UUID fromUuid;
+    private String message;
+    private long timestamp;
+
+    protected BaseMessage(){}
+
+    protected BaseMessage(String fromName, UUID uuid, String message) {
+        this(fromName, uuid, message, System.currentTimeMillis());
+    }
+
+    protected BaseMessage(String fromName, UUID fromUuid, String message, long timestamp) {
+        this.fromName = fromName;
+        this.fromUuid = fromUuid;
+        this.message = message;
+        this.timestamp = timestamp;
+    }
+
+    @Override public String getFromName() { return fromName; }
+    @Override public Optional<UUID> getUuid() { return Optional.ofNullable(fromUuid); }
+    @Override public String getMessage() { return message; }
+    @Override public long getTimestamp() { return timestamp; }
+}

+ 13 - 0
shared/src/main/java/me/lethunderhawk/messages/Message.java

@@ -0,0 +1,13 @@
+package me.lethunderhawk.messages;
+
+import me.lethunderhawk.world.entity.player.Player;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public interface Message {
+    String getFromName();
+    Optional<UUID> getUuid();
+    String getMessage();
+    long getTimestamp();
+}

+ 14 - 0
shared/src/main/java/me/lethunderhawk/messages/PlayerChatMessage.java

@@ -0,0 +1,14 @@
+package me.lethunderhawk.messages;
+
+import me.lethunderhawk.world.entity.player.Player;
+
+import java.util.UUID;
+
+public final class PlayerChatMessage extends BaseMessage {
+    public PlayerChatMessage(Player player, String text) {
+        super(player.getName(), player.getUuid(), text);
+    }
+    private PlayerChatMessage() {
+        super();
+    }
+}

+ 11 - 0
shared/src/main/java/me/lethunderhawk/messages/ServerChatMessage.java

@@ -0,0 +1,11 @@
+package me.lethunderhawk.messages;
+
+public final class ServerChatMessage extends BaseMessage {
+    public ServerChatMessage(String text) {
+        super("Server", null, text, System.currentTimeMillis());
+    }
+
+    private ServerChatMessage(){
+        super();
+    }
+}

+ 0 - 26
shared/src/main/java/me/lethunderhawk/network/NetworkRegister.java

@@ -1,26 +0,0 @@
-package me.lethunderhawk.network;
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-import me.lethunderhawk.world.entity.player.Player;
-
-import java.util.UUID;
-
-public class NetworkRegister {
-
-    public static void register(Kryo kryo) {
-        kryo.register(Player.class);
-        kryo.register(UUID.class, new Serializer<UUID>() {
-            public void write(Kryo kryo, Output out, UUID uuid) {
-                out.writeLong(uuid.getMostSignificantBits());
-                out.writeLong(uuid.getLeastSignificantBits());
-            }
-
-            public UUID read(Kryo kryo, Input in, Class<UUID> type) {
-                return new UUID(in.readLong(), in.readLong());
-            }
-        });
-    }
-}

+ 12 - 10
shared/src/main/java/me/lethunderhawk/network/packet/PacketRegistry.java

@@ -2,16 +2,22 @@ package me.lethunderhawk.network.packet;
 
 import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.math.Rectangle;
+import com.badlogic.gdx.math.Vector2;
 import com.esotericsoftware.kryo.Kryo;
 import com.esotericsoftware.kryo.Serializer;
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
+import me.lethunderhawk.messages.PlayerChatMessage;
+import me.lethunderhawk.messages.ServerChatMessage;
+import me.lethunderhawk.network.packet.serialization.OptionalSerializer;
+import me.lethunderhawk.network.packet.serialization.UUIDSerializer;
 import me.lethunderhawk.world.World;
 import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.world.entity.player.PlayerAvatar;
 import me.lethunderhawk.network.packet.impl.*;
 import me.lethunderhawk.network.packet.serialization.ColorSerializer;
 
+import java.util.Optional;
 import java.util.UUID;
 
 public class PacketRegistry {
@@ -29,18 +35,14 @@ public class PacketRegistry {
         kryo.register(int[][].class);
         kryo.register(World.class);
         kryo.register(WorldDataPacket.class);
-
+        kryo.register(MessagePacket.class);
+        kryo.register(PlayerChatMessage.class);
+        kryo.register(ServerChatMessage.class);
+        kryo.register(Vector2.class);
+        kryo.register(Optional.class, new OptionalSerializer());
         kryo.register(Color.class, new ColorSerializer());
         kryo.register(Color[].class);
         kryo.register(Color[][].class);
-        kryo.register(UUID.class, new Serializer<UUID>() {
-            public void write(Kryo kryo, Output out, UUID uuid) {
-                out.writeLong(uuid.getMostSignificantBits());
-                out.writeLong(uuid.getLeastSignificantBits());
-            }
-            public UUID read(Kryo kryo, Input in, Class<UUID> type) {
-                return new UUID(in.readLong(), in.readLong());
-            }
-        });
+        kryo.register(UUID.class, new UUIDSerializer());
     }
 }

+ 31 - 0
shared/src/main/java/me/lethunderhawk/network/packet/impl/MessagePacket.java

@@ -0,0 +1,31 @@
+package me.lethunderhawk.network.packet.impl;
+
+import me.lethunderhawk.messages.Message;
+import me.lethunderhawk.network.packet.Packet;
+
+public class MessagePacket implements Packet {
+    private Message message;
+    private boolean fromServer;
+    private MessagePacket(){}
+
+    public MessagePacket(Message message, boolean fromServer) {
+        this.message = message;
+        this.fromServer = fromServer;
+    }
+
+    public MessagePacket(Message message) {
+        this(message, false);
+    }
+
+    public Message getMessage(){
+        return message;
+    }
+
+    public boolean isFromServer() {
+        return fromServer;
+    }
+
+    public void setFromServer(boolean b) {
+        fromServer = b;
+    }
+}

+ 26 - 3
shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLoginPacket.java

@@ -1,10 +1,33 @@
 package me.lethunderhawk.network.packet.impl;
 
+import com.badlogic.gdx.math.Vector2;
 import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.network.packet.Packet;
 
 public class PlayerLoginPacket implements Packet {
-    public Player player;
-    public float login_x;
-    public float login_y;
+    private Player player;
+    private Vector2 position;
+    private PlayerLoginPacket(){}
+
+
+    public PlayerLoginPacket(Player player) {
+        this(player, player.getPosition());
+    }
+
+    public PlayerLoginPacket(Player player, Vector2 position) {
+        this.player = player;
+        this.position = position;
+    }
+
+    public Player getPlayer() {
+        return player;
+    }
+
+    public void setPlayer(Player player) {
+        this.player = player;
+    }
+
+    public Vector2 getPosition() {
+        return position;
+    }
 }

+ 10 - 1
shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLogoutPacket.java

@@ -5,5 +5,14 @@ import me.lethunderhawk.network.packet.Packet;
 import java.util.UUID;
 
 public class PlayerLogoutPacket implements Packet {
-    public UUID uuid;
+    private UUID uuid;
+
+    private PlayerLogoutPacket(){}
+    public PlayerLogoutPacket(UUID uuid) {
+        this.uuid = uuid;
+    }
+
+    public UUID getUuid() {
+        return uuid;
+    }
 }

+ 14 - 5
shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerPositionPacket.java

@@ -1,19 +1,28 @@
 package me.lethunderhawk.network.packet.impl;
 
+import com.badlogic.gdx.math.Vector2;
 import me.lethunderhawk.network.packet.Packet;
+import me.lethunderhawk.world.entity.player.Player;
 
 import java.util.UUID;
+import java.util.Vector;
 
 public class PlayerPositionPacket implements Packet {
     public UUID uuid;
-    public float x;
-    public float y;
+    public Vector2 position;
 
-    public PlayerPositionPacket() {}
+    private PlayerPositionPacket() {}
 
     public PlayerPositionPacket(UUID uuid, float x, float y) {
         this.uuid = uuid;
-        this.x = x;
-        this.y = y;
+        this.position = new Vector2(x, y);
+    }
+    public PlayerPositionPacket(Player player) {
+        this.uuid = player.getUuid();
+        this.position = player.getPosition();
+    }
+
+    public Vector2 getPosition() {
+        return position;
     }
 }

+ 21 - 0
shared/src/main/java/me/lethunderhawk/network/packet/serialization/OptionalSerializer.java

@@ -0,0 +1,21 @@
+package me.lethunderhawk.network.packet.serialization;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+import java.util.Optional;
+
+public class OptionalSerializer extends Serializer<Optional<?>> {
+    @Override
+    public void write(Kryo kryo, Output output, Optional<?> optional) {
+        output.writeBoolean(optional.isPresent());
+        optional.ifPresent(o -> kryo.writeClassAndObject(output, o));
+    }
+    @Override
+    public Optional<?> read(Kryo kryo, Input input, Class<Optional<?>> type) {
+        if (!input.readBoolean()) return Optional.empty();
+        return Optional.ofNullable(kryo.readClassAndObject(input));
+    }
+}

+ 18 - 0
shared/src/main/java/me/lethunderhawk/network/packet/serialization/UUIDSerializer.java

@@ -0,0 +1,18 @@
+package me.lethunderhawk.network.packet.serialization;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+import java.util.UUID;
+
+public class UUIDSerializer extends Serializer<UUID> {
+    public void write(Kryo kryo, Output out, UUID uuid) {
+        out.writeLong(uuid.getMostSignificantBits());
+        out.writeLong(uuid.getLeastSignificantBits());
+    }
+    public UUID read(Kryo kryo, Input in, Class<UUID> type) {
+        return new UUID(in.readLong(), in.readLong());
+    }
+}

+ 44 - 10
shared/src/main/java/me/lethunderhawk/world/entity/player/Player.java

@@ -1,33 +1,49 @@
 package me.lethunderhawk.world.entity.player;
 
 import com.badlogic.gdx.math.Rectangle;
+import com.badlogic.gdx.math.Vector2;
 import me.lethunderhawk.world.entity.collision.Collider;
 
 import java.util.UUID;
 
 public class Player implements Collider {
-    public UUID uuid;
-    public PlayerAvatar avatar;
-    public float x;
-    public float y;
+    private final String name;
+    private final UUID uuid;
+    private final PlayerAvatar avatar;
+    private Vector2 position;
     private final Rectangle bounds;
     private final float speed;
 
-    public Player(){
-        this.uuid = UUID.randomUUID();
+    public Player(Player p){
+        this.name = p.getName();
+        this.uuid = p.getUuid();
+        this.avatar = p.getAvatar();
+        this.bounds = p.getBounds();
+        this.speed = p.getSpeed();
+        this.position = p.getPosition();
+    }
+
+    public Player(String name, UUID uuid){
+        this.uuid = uuid;
         this.avatar = new PlayerAvatar();
         this.speed = 300f;
+        this.position = new Vector2();
         avatar.initializeGrid();
         bounds = new Rectangle(
-            x,
-            y,
+            position.x,
+            position.y,
             32,
             32
         );
+        this.name = name;
+    }
+
+    public Player(String name){
+        this(name, UUID.randomUUID());
     }
 
-    public void updateBounds() {
-        bounds.setPosition(x, y);
+    public Player(){
+        this("Unknown Player", UUID.randomUUID());
     }
 
     @Override
@@ -38,4 +54,22 @@ public class Player implements Collider {
     public float getSpeed() {
         return this.speed;
     }
+
+    public PlayerAvatar getAvatar() {
+        return avatar;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public UUID getUuid() {
+        return uuid;
+    }
+
+    public Vector2 getPosition() {
+        return position;
+    }
+
+
 }

+ 40 - 0
shared/src/main/java/me/lethunderhawk/world/entity/player/ServerPlayer.java

@@ -0,0 +1,40 @@
+package me.lethunderhawk.world.entity.player;
+
+import com.esotericsoftware.kryonet.Connection;
+import me.lethunderhawk.messages.ServerChatMessage;import me.lethunderhawk.network.packet.impl.MessagePacket;
+import me.lethunderhawk.network.packet.impl.PlayerPositionPacket;
+
+public class ServerPlayer extends Player {
+    private final Connection connection;
+    private boolean op;
+
+    public ServerPlayer(Player player, Connection connection) {
+        this(player, connection, false);
+    }
+
+    public ServerPlayer(Player p, Connection connection, boolean isOp) {
+        super(p);
+        this.op = isOp;
+        this.connection = connection;
+    }
+
+    public void setOp(boolean opStatus){
+        op = opStatus;
+    }
+
+    public boolean isOp() {
+        return op;
+    }
+
+    public void sendMessage(String message) {
+        MessagePacket packet = new MessagePacket(new ServerChatMessage(message));
+        packet.setFromServer(true);
+        connection.sendUDP(packet);
+    }
+
+    public void teleport(float x, float y) {
+        this.getPosition().set(x, y);
+        PlayerPositionPacket packet = new PlayerPositionPacket(this.getUuid(), x, y);
+        connection.sendUDP(packet);
+    }
+}