Forráskód Böngészése

Start of underlying structures for worlds instead of onscreen coordinates

jan 1 hónapja
szülő
commit
9f9489b6e9
46 módosított fájl, 993 hozzáadás és 114 törlés
  1. 2 1
      README.md
  2. BIN
      assets/TX/Extra/TX Plant with Shadow.png
  3. BIN
      assets/TX/Extra/TX Props with Shadow.png
  4. BIN
      assets/TX/TX Plant.png
  5. BIN
      assets/TX/TX Player.png
  6. BIN
      assets/TX/TX Props.png
  7. BIN
      assets/TX/TX Shadow Plant.png
  8. BIN
      assets/TX/TX Shadow.png
  9. BIN
      assets/TX/TX Struct.png
  10. BIN
      assets/TX/TX Tileset Grass.png
  11. BIN
      assets/TX/TX Tileset Stone Ground.png
  12. BIN
      assets/TX/TX Tileset Wall.png
  13. 15 0
      assets/assets.txt
  14. BIN
      assets/level/TX Tileset Grass.png
  15. 10 0
      assets/level/lobby.json
  16. 131 0
      assets/level/lobby.tmx
  17. BIN
      assets/level/tileset.png
  18. 8 0
      core/src/main/java/me/lethunderhawk/Main.java
  19. 28 0
      core/src/main/java/me/lethunderhawk/camera/CameraController.java
  20. 18 9
      core/src/main/java/me/lethunderhawk/network/ClientPacketListener.java
  21. 6 3
      core/src/main/java/me/lethunderhawk/network/GameClient.java
  22. 182 48
      core/src/main/java/me/lethunderhawk/screen/GameScreen.java
  23. 21 3
      core/src/main/java/me/lethunderhawk/screen/MenuScreen.java
  24. 43 9
      server/src/main/java/me/lethunderhawk/server/GameServer.java
  25. 21 10
      server/src/main/java/me/lethunderhawk/server/GameServerPacketListener.java
  26. 1 0
      shared/build.gradle
  27. 0 13
      shared/src/main/java/me/lethunderhawk/entity/Player.java
  28. 1 1
      shared/src/main/java/me/lethunderhawk/event/impl/PlayerMoveEvent.java
  29. 1 1
      shared/src/main/java/me/lethunderhawk/network/NetworkRegister.java
  30. 14 3
      shared/src/main/java/me/lethunderhawk/network/packet/PacketRegistry.java
  31. 3 2
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLoginPacket.java
  32. 0 3
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLoginSuccessPacket.java
  33. 3 2
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLogoutPacket.java
  34. 3 2
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerMovePacket.java
  35. 5 4
      shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerPositionPacket.java
  36. 14 0
      shared/src/main/java/me/lethunderhawk/network/packet/impl/WorldDataPacket.java
  37. 54 0
      shared/src/main/java/me/lethunderhawk/network/packet/serialization/ColorSerializer.java
  38. 246 0
      shared/src/main/java/me/lethunderhawk/network/packet/serialization/GraphHeader.java
  39. 4 0
      shared/src/main/java/me/lethunderhawk/world/BackGroundTile.java
  40. 26 0
      shared/src/main/java/me/lethunderhawk/world/World.java
  41. 23 0
      shared/src/main/java/me/lethunderhawk/world/blocks/Block.java
  42. 7 0
      shared/src/main/java/me/lethunderhawk/world/blocks/BlockRegistry.java
  43. 8 0
      shared/src/main/java/me/lethunderhawk/world/entity/collision/Collider.java
  44. 29 0
      shared/src/main/java/me/lethunderhawk/world/entity/collision/CollisionSystem.java
  45. 35 0
      shared/src/main/java/me/lethunderhawk/world/entity/player/Player.java
  46. 31 0
      shared/src/main/java/me/lethunderhawk/world/entity/player/PlayerAvatar.java

+ 2 - 1
README.md

@@ -1,4 +1,5 @@
-# Smog
+# Smog 
+A very experimental game, many changes to come 
 
 A [libGDX](https://libgdx.com/) project generated with [gdx-liftoff](https://github.com/libgdx/gdx-liftoff).
 

BIN
assets/TX/Extra/TX Plant with Shadow.png


BIN
assets/TX/Extra/TX Props with Shadow.png


BIN
assets/TX/TX Plant.png


BIN
assets/TX/TX Player.png


BIN
assets/TX/TX Props.png


BIN
assets/TX/TX Shadow Plant.png


BIN
assets/TX/TX Shadow.png


BIN
assets/TX/TX Struct.png


BIN
assets/TX/TX Tileset Grass.png


BIN
assets/TX/TX Tileset Stone Ground.png


BIN
assets/TX/TX Tileset Wall.png


+ 15 - 0
assets/assets.txt

@@ -1,4 +1,19 @@
 .gitkeep
+TX/Extra/TX Plant with Shadow.png
+TX/Extra/TX Props with Shadow.png
+TX/TX Plant.png
+TX/TX Player.png
+TX/TX Props.png
+TX/TX Shadow Plant.png
+TX/TX Shadow.png
+TX/TX Struct.png
+TX/TX Tileset Grass.png
+TX/TX Tileset Stone Ground.png
+TX/TX Tileset Wall.png
+level/TX Tileset Grass.png
+level/lobby.json
+level/lobby.tmx
+level/tileset.png
 ui/font-list.fnt
 ui/font-subtitle.fnt
 ui/font-window.fnt

BIN
assets/level/TX Tileset Grass.png


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 10 - 0
assets/level/lobby.json


+ 131 - 0
assets/level/lobby.tmx

@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by TileMap Studio on 2026-05-20T13:53:19.706Z -->
+<map version="1.0" tiledversion="1.0" orientation="orthogonal" renderorder="right-down"
+     width="52" height="52"
+     tilewidth="16" tileheight="16"
+     backgroundcolor="#2a2a2a" infinite="0">
+  <tileset firstgid="1" name="default"
+           tilewidth="16" tileheight="16"
+           tilecount="256" columns="16">
+    <image source="tileset.png" width="256" height="256"/>
+  </tileset>
+  <tileset firstgid="1001" name="TX Tileset Grass"
+           tilewidth="16" tileheight="16"
+           tilecount="256" columns="16">
+    <image source="TX Tileset Grass.png" width="256" height="256"/>
+  </tileset>
+  <layer id="0" name="Background" width="52" height="52"
+         visible="1" locked="0" opacity="100">
+    <data encoding="csv">
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+    </data>
+  </layer>
+  <layer id="1" name="Foreground" width="52" height="52"
+         visible="1" locked="0" opacity="100">
+    <data encoding="csv">
+      1071,1071,1071,1071,1071,1071,1071,1071,1091,1071,1071,1071,1135,1061,1018,1018,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1091,1091,
+      1071,1071,1091,1071,1071,1053,1071,1061,1071,1071,1071,1071,1135,1135,1018,1091,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1061,1018,1050,1050,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1091,1018,1018,1018,1018,1091,1018,1018,1091,1018,1018,1018,
+      1071,1053,1071,1071,1071,1053,1071,1071,1071,1071,1050,1071,1071,1135,1018,1061,1018,1018,1061,1050,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1018,1018,1091,1018,1091,1018,1091,1018,1091,1018,1018,1018,1018,
+      1053,1053,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1053,1135,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1089,1050,1018,1018,1018,1061,1018,1018,1018,1018,1018,1018,1091,1091,1091,1091,1091,1018,1018,1018,1018,1018,1018,1091,1018,
+      1071,1071,1071,1050,1071,1091,1091,1061,1061,1071,1071,1071,1071,1135,1018,1018,1018,1018,1018,1018,1018,1050,1018,1053,1018,1018,1018,1018,1050,1082,1018,1050,1018,1018,1018,1089,1018,1053,1018,1091,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1071,1053,1071,1061,1071,1071,1071,1091,1071,1071,1071,1071,1071,1135,1050,1018,1061,1053,1053,1089,1050,1091,1082,1018,1018,1050,1018,1018,1018,1018,1050,1018,1018,1018,1082,1018,1018,1018,1053,1018,1018,1091,1018,1018,1018,1018,1091,1018,1082,1018,1091,1135,
+      1071,1061,1071,1050,1071,1071,1071,1071,1061,1071,1071,1071,1091,1135,1018,1018,1018,1018,1061,1053,1018,1018,1018,1018,1050,1018,1018,1061,1018,1018,1018,1018,1018,1018,1018,1050,1061,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1018,1091,1135,1135,1135,
+      1071,1061,1061,1050,1071,1071,1071,1071,1071,1071,1071,1061,1071,1071,1135,1018,1061,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1053,1018,1018,1050,1050,1018,1018,1018,1018,1082,1018,1018,1018,1018,1018,1091,1082,1091,1018,1018,1018,1135,1135,1018,1091,
+      1071,1071,1071,1071,1061,1089,1071,1061,1071,1071,1050,1071,1071,1071,1135,1053,1018,1018,1018,1053,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1053,1050,1018,1018,1018,1018,1018,1050,1018,1053,1050,1050,1050,1018,1091,1018,1018,1091,1135,1018,1018,1018,
+      1071,1071,1071,1071,1071,1071,1050,1071,1061,1071,1050,1071,1061,1071,1135,1135,1053,1082,1018,1091,1053,1050,1018,1050,1018,1018,1018,1018,1018,1018,1089,1018,1018,1018,1018,1018,1053,1018,1091,1018,1053,1018,1018,1091,1018,1018,1018,1135,1018,1061,1018,1061,
+      1061,1071,1071,1071,1071,1061,1071,1071,1071,1071,1071,1061,1071,1071,1091,1135,1018,1018,1018,1018,1053,1018,1018,1091,1018,1091,1018,1018,1082,1091,1053,1018,1018,1018,1018,1050,1050,1061,1082,1018,1091,1050,1018,1018,1018,1018,1135,1135,1018,1018,1018,1018,
+      1071,1071,1061,1089,1050,1071,1071,1071,1071,1053,1071,1071,1071,1050,1071,1135,1135,1018,1018,1018,1018,1082,1061,1053,1018,1018,1050,1018,1050,1018,1018,1053,1018,1018,1018,1018,1018,1053,1061,1018,1018,1018,1018,1018,1018,1018,1135,1091,1018,1018,1018,1091,
+      1053,1071,1071,1050,1071,1071,1071,1050,1050,1091,1071,1053,1071,1071,1071,1071,1135,1050,1018,1018,1050,1053,1053,1018,1018,1018,1091,1018,1018,1018,1018,1061,1018,1018,1053,1018,1050,1018,1050,1018,1061,1018,1018,1082,1018,1135,1135,1061,1018,1018,1018,1018,
+      1071,1071,1071,1091,1061,1071,1071,1071,1071,1053,1071,1071,1053,1082,1071,1071,1050,1135,1018,1018,1018,1061,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1018,1018,1089,1082,1018,1018,1018,1091,1053,1018,1018,1135,1135,1018,1018,1018,1089,1018,1018,
+      1071,1071,1071,1071,1071,1050,1053,1071,1071,1071,1061,1071,1071,1071,1071,1071,1071,1135,1135,1018,1018,1018,1018,1050,1018,1053,1018,1018,1091,1018,1018,1050,1018,1018,1018,1018,1018,1053,1018,1053,1082,1091,1061,1135,1135,1018,1018,1061,1018,1018,1018,1018,
+      1071,1071,1071,1071,1050,1091,1071,1091,1071,1071,1050,1071,1071,1071,1053,1053,1082,1053,1135,1135,1018,1061,1018,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1135,1135,1018,1082,1018,1018,1018,1018,1018,1018,
+      1071,1071,1071,1071,1071,1061,1071,1071,1071,1050,1053,1071,1071,1071,1071,1071,1071,1071,1071,1135,1018,1018,1018,1061,1018,1018,1018,1091,1018,1018,1018,1018,1050,1050,1018,1091,1050,1050,1018,1018,1018,1135,1135,1061,1018,1018,1018,1018,1018,1018,1018,1018,
+      1071,1071,1071,1071,1089,1053,1071,1091,1071,1071,1071,1050,1089,1082,1050,1071,1071,1071,1071,1235,1135,1018,1053,1091,1050,1018,1018,1018,1053,1061,1018,1018,1018,1018,1018,1053,1091,1053,1018,1135,1135,1050,1018,1091,1018,1018,1082,1018,1053,1053,1050,1082,
+      1061,1071,1071,1071,1071,1071,1071,1053,1061,1053,1091,1071,1061,1071,1071,1082,1091,1071,1071,1071,1135,1018,1018,1018,1050,1018,1018,1091,1018,1061,1091,1018,1018,1018,1018,1018,1018,1135,1135,1135,1050,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1050,
+      1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1050,1071,1071,1071,1071,1071,1071,1071,1135,1018,1018,1053,1018,1018,1018,1018,1018,1050,1018,1018,1091,1018,1053,1135,1135,1135,1053,1018,1082,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1018,
+      1071,1071,1071,1071,1071,1071,1071,1071,1053,1071,1053,1091,1071,1071,1071,1071,1071,1071,1071,1050,1071,1135,1050,1018,1018,1018,1091,1018,1018,1018,1053,1018,1135,1135,1135,1135,1053,1018,1018,1018,1061,1018,1018,1018,1018,1018,1018,1089,1018,1018,1018,1018,
+      1071,1071,1071,1071,1071,1053,1071,1050,1071,1071,1071,1091,1050,1071,1071,1082,1071,1091,1071,1050,1071,1135,1091,1018,1061,1018,1018,1050,1018,1018,1135,1135,1183,1018,1018,1018,1018,1018,1018,1018,1018,1061,1018,1050,1018,1082,1018,1018,1018,1091,1018,1018,
+      1071,1071,1071,1071,1071,1071,1071,1071,1071,1053,1061,1071,1091,1050,1071,1071,1082,1071,1071,1071,1071,1135,1135,1018,1018,1018,1018,1091,1135,1135,1183,1018,1018,1091,1061,1091,1018,1018,1018,1018,1018,1018,1018,1018,1082,1018,1018,1018,1018,1018,1018,1018,
+      1071,1071,1071,1071,1071,1071,1053,1071,1071,1071,1071,1071,1050,1071,1071,1061,1053,1061,1061,1071,1071,1135,1135,1018,1018,1018,1018,1135,1135,1053,1053,1091,1018,1018,1018,1018,1050,1050,1061,1050,1018,1018,1082,1082,1018,1018,1018,1018,1018,1018,1018,1018,
+      1053,1071,1071,1050,1089,1071,1071,1071,1050,1071,1071,1071,1071,1071,1071,1071,1071,1050,1018,1235,1135,1135,1235,1135,1135,1135,1135,1018,1053,1050,1050,1018,1082,1091,1018,1018,1018,1061,1050,1018,1018,1050,1082,1018,1018,1018,1018,1050,1018,1018,1018,1018,
+      1071,1071,1071,1071,1071,1053,1071,1071,1091,1071,1071,1091,1071,1091,1135,1135,1135,1135,1135,1135,1018,1053,1018,1018,1061,1135,1135,1135,1135,1135,1091,1053,1018,1018,1018,1018,1053,1053,1018,1050,1018,1082,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1071,1050,1071,1050,1071,1071,1071,1071,1071,1053,1071,1135,1135,1135,1135,1018,1091,1018,1091,1082,1050,1091,1050,1050,1018,1050,1091,1018,1018,1135,1135,1135,1018,1018,1018,1018,1061,1018,1018,1018,1018,1018,1018,1018,1089,1018,1018,1018,1018,1018,1018,1018,
+      1071,1071,1071,1091,1071,1071,1071,1071,1071,1135,1135,1135,1018,1018,1018,1018,1050,1053,1018,1018,1018,1018,1082,1018,1082,1061,1018,1018,1082,1018,1018,1135,1135,1135,1018,1061,1091,1018,1018,1082,1018,1050,1018,1018,1018,1018,1018,1018,1018,1018,1018,1082,
+      1071,1071,1071,1071,1071,1071,1071,1135,1135,1135,1018,1018,1053,1018,1018,1050,1018,1050,1018,1091,1018,1018,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1135,1135,1135,1135,1050,1091,1091,1018,1018,1053,1018,1018,1082,1018,1018,1018,1018,1018,1018,
+      1071,1071,1071,1071,1071,1053,1135,1135,1018,1091,1018,1018,1050,1018,1018,1053,1050,1018,1018,1018,1018,1053,1018,1018,1091,1018,1018,1018,1018,1018,1053,1091,1018,1018,1018,1135,1135,1135,1135,1018,1091,1018,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,
+      1071,1071,1071,1053,1135,1135,1135,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1091,1018,1018,1053,1091,1018,1018,1018,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1135,1018,1082,1082,1135,1135,1135,1135,1018,1018,1018,1082,1050,1050,1018,1018,1018,
+      1071,1135,1135,1135,1135,1018,1018,1091,1018,1018,1050,1050,1018,1089,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1091,1018,1018,1091,1091,1018,1018,1018,1135,1183,1018,1018,1018,1018,1091,1091,1135,1135,1135,1135,1135,1018,1091,1018,1061,1018,
+      1135,1018,1018,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1050,1053,1018,1018,1018,1018,1018,1091,1018,1050,1018,1089,1018,1018,1018,1018,1050,1018,1082,1018,1135,1053,1018,1091,1018,1091,1018,1018,1018,1018,1018,1018,1135,1135,1135,1135,1018,1018,
+      1018,1018,1018,1018,1053,1018,1018,1018,1091,1050,1018,1018,1050,1018,1050,1018,1018,1018,1018,1053,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1050,1018,1053,1135,1183,1091,1018,1018,1018,1082,1018,1050,1018,1018,1018,1018,1018,1018,1018,1135,1135,1135,
+      1018,1053,1018,1018,1018,1018,1018,1018,1050,1053,1018,1018,1018,1050,1018,1018,1089,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1050,1018,1053,1018,1091,1018,1135,1091,1018,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1053,1018,1050,1050,1053,1018,1050,1018,1018,1018,1050,1053,1018,1053,1018,1018,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1135,1018,1018,1018,1018,1053,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1053,
+      1018,1018,1018,1018,1018,1089,1018,1053,1018,1018,1018,1091,1053,1018,1018,1018,1018,1018,1018,1091,1091,1053,1018,1018,1018,1018,1018,1018,1018,1018,1018,1091,1135,1050,1018,1053,1018,1018,1018,1091,1091,1018,1091,1018,1018,1050,1018,1018,1018,1018,1018,1091,
+      1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1089,1018,1091,1018,1018,1091,1018,1018,1091,1050,1018,1091,1050,1018,1018,1091,1053,1018,1053,1183,1135,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1050,1018,1018,1091,1018,1018,1018,1018,1091,
+      1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1091,1091,1018,1018,1050,1018,1053,1018,1018,1053,1018,1018,1018,1091,1135,1050,1018,1018,1018,1018,1050,1018,1018,1018,1018,1053,1018,1018,1091,1018,1018,1091,1018,1018,1018,
+      1018,1018,1018,1018,1091,1018,1018,1053,1018,1018,1018,1018,1018,1053,1018,1018,1091,1091,1053,1053,1018,1018,1018,1053,1091,1089,1018,1018,1018,1018,1135,1183,1018,1018,1018,1018,1050,1018,1018,1018,1091,1018,1018,1050,1018,1018,1018,1018,1053,1018,1018,1018,
+      1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1053,1018,1053,1018,1018,1018,1053,1018,1018,1018,1018,1053,1018,1018,1018,1053,1018,1018,1053,1235,1135,1018,1018,1091,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,
+      1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1053,1091,1018,1018,1053,1018,1018,1018,1053,1018,1018,1018,1018,1091,1050,1018,1018,1135,1018,1053,1018,1018,1050,1053,1018,1018,1018,1091,1018,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1018,1018,1089,1091,1091,1053,1018,1091,1053,1018,1018,1018,1053,1018,1018,1018,1018,1091,1018,1018,1018,1053,1018,1053,1135,1135,1018,1091,1053,1018,1018,1091,1018,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1018,1091,1018,1050,1018,1053,1018,1018,1018,1018,1018,1091,1018,1018,1053,1018,1018,1018,1053,1018,1053,1091,1018,1135,1135,1091,1018,1018,1053,1091,1053,1053,1018,1050,1091,1018,1018,1050,1091,1018,1091,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1018,1050,1018,1053,1018,1018,1050,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1135,1183,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1050,1050,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1089,1091,1053,1018,1050,1050,1018,1018,1091,1135,1183,1091,1018,1018,1018,1018,1018,1018,1091,1018,1018,1050,1018,1018,1018,1091,1018,1050,1091,1050,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1053,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1018,1091,1091,1018,1050,1018,1091,1018,1135,1135,1018,1018,1053,1091,1050,1050,1018,1018,1053,1018,1050,1091,1018,1018,1018,1018,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1091,1018,1018,1053,1018,1053,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1091,1018,1018,1018,1018,1053,1018,1135,1235,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1050,1091,1018,1018,1091,1018,1018,1018,1018,1018,1018,1050,1018,1018,
+      1018,1018,1018,1018,1091,1018,1018,1018,1018,1050,1091,1018,1018,1018,1091,1018,1018,1018,1018,1018,1050,1050,1135,1235,1018,1050,1050,1018,1018,1089,1089,1053,1018,1018,1018,1091,1050,1018,1091,1018,1018,1091,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1050,1018,1018,1050,1018,1018,1018,1018,1135,1053,1050,1018,1018,1018,1018,1089,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,
+      1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1018,1053,1018,1053,1018,1018,1018,1135,1135,1050,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,
+      1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1018,1018,1018,1018,1053,1018,1018,1018,1135,1235,1135,1018,1018,1018,1018,1050,1053,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1050,1018,1018,1018,1018,1018,1018,1018
+    </data>
+  </layer>
+</map>

BIN
assets/level/tileset.png


+ 8 - 0
core/src/main/java/me/lethunderhawk/Main.java

@@ -1,16 +1,24 @@
 package me.lethunderhawk;
 
 import com.badlogic.gdx.Game;
+import com.badlogic.gdx.assets.AssetManager;
 import me.lethunderhawk.event.EventDispatcher;
 import me.lethunderhawk.event.ExampleEventListener;
 import me.lethunderhawk.network.packet.PacketRegistry;
 import me.lethunderhawk.screen.MenuScreen;
 
 public class Main extends Game {
+    public AssetManager assetManager = new AssetManager();
+
     @Override
     public void create() {
         setScreen(new MenuScreen(this));
         EventDispatcher.addListener(new ExampleEventListener());
     }
 
+    @Override
+    public void dispose() {
+        assetManager.dispose();
+        super.dispose();
+    }
 }

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

@@ -0,0 +1,28 @@
+package me.lethunderhawk.camera;
+
+import com.badlogic.gdx.math.Vector2;
+import com.badlogic.gdx.graphics.OrthographicCamera;
+import me.lethunderhawk.world.entity.player.Player;
+
+public class CameraController {
+    private final Vector2 targetPosition = new Vector2();
+    private final float followSpeed = 8f;      // Higher = faster follow
+    private final float deadZone = 100f;       // Pixels before camera moves
+
+    public void update(float delta, OrthographicCamera camera, Player player) {
+        targetPosition.set(player.x, player.y);
+
+        float dx = targetPosition.x - camera.position.x;
+        float dy = targetPosition.y - camera.position.y;
+
+        // If player is within dead zone, keep camera centered on its last position
+        if (Math.abs(dx) < deadZone && Math.abs(dy) < deadZone) {
+            return;
+        }
+
+        // Smoothly interpolate camera position
+        camera.position.x += dx * followSpeed * delta;
+        camera.position.y += dy * followSpeed * delta;
+        camera.update();
+    }
+}

+ 18 - 9
core/src/main/java/me/lethunderhawk/network/ClientPacketListener.java

@@ -1,12 +1,10 @@
 package me.lethunderhawk.network;
 
-import me.lethunderhawk.entity.Player;
+import me.lethunderhawk.network.packet.impl.*;
+import me.lethunderhawk.world.World;
+import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.network.packet.PacketHandler;
 import me.lethunderhawk.network.packet.PacketListener;
-import me.lethunderhawk.network.packet.impl.PlayerLoginPacket;
-import me.lethunderhawk.network.packet.impl.PlayerLogoutPacket;
-import me.lethunderhawk.network.packet.impl.PlayerMovePacket;
-import me.lethunderhawk.network.packet.impl.PlayerPositionPacket;
 
 import java.util.Map;
 import java.util.UUID;
@@ -14,20 +12,27 @@ import java.util.concurrent.ConcurrentHashMap;
 
 public class ClientPacketListener implements PacketListener {
     private final Map<UUID, Player> otherPlayers = new ConcurrentHashMap<>();
+    private World world;
+
+    @PacketHandler
+    private void handleWorldData(WorldDataPacket packet) {
+        this.world = packet.world;
+    }
 
     @PacketHandler
     private void playerLogin(PlayerLoginPacket packet) {
-        otherPlayers.put(packet.player.uuid, packet.player);
+        Player player = packet.player;
+        otherPlayers.put(player.uuid, player);
     }
 
     @PacketHandler
     private void playerLogout(PlayerLogoutPacket packet) {
-        otherPlayers.remove(packet.player.uuid);
+        otherPlayers.remove(packet.uuid);
     }
 
     @PacketHandler
     private void handlePlayerPosition(PlayerPositionPacket packet) {
-        Player player = otherPlayers.computeIfAbsent(packet.player.uuid, uuid -> {
+        Player player = otherPlayers.computeIfAbsent(packet.uuid, uuid -> {
             Player p = new Player();
             p.uuid = uuid;
             return p;
@@ -38,7 +43,7 @@ public class ClientPacketListener implements PacketListener {
 
     @PacketHandler
     private void handlePlayerMove(PlayerMovePacket packet) {
-        Player player = otherPlayers.computeIfAbsent(packet.player.uuid, uuid -> {
+        Player player = otherPlayers.computeIfAbsent(packet.uuid, uuid -> {
             Player p = new Player();
             p.uuid = uuid;
             return p;
@@ -50,4 +55,8 @@ public class ClientPacketListener implements PacketListener {
     public Map<UUID, Player> getOtherPlayers() {
         return otherPlayers;
     }
+
+    public World getWorld() {
+        return world;
+    }
 }

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

@@ -3,14 +3,13 @@ package me.lethunderhawk.network;
 import com.esotericsoftware.kryonet.Client;
 import com.esotericsoftware.kryonet.Connection;
 import com.esotericsoftware.kryonet.Listener;
-import me.lethunderhawk.entity.Player;
+import me.lethunderhawk.world.World;
+import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.network.packet.*;
 import me.lethunderhawk.network.packet.impl.PlayerLoginPacket;
-import me.lethunderhawk.network.packet.impl.PlayerMovePacket;
 
 import java.io.IOException;
 import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
 
 public class GameClient extends Listener implements EventListener {
 
@@ -65,6 +64,10 @@ public class GameClient extends Listener implements EventListener {
         client.stop();
     }
 
+    public World getWorld() {
+        return clientPacketListener.getWorld();
+    }
+
     public Player getPlayer() {
         return player;
     }

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

@@ -1,36 +1,57 @@
+
 package me.lethunderhawk.screen;
 
 import com.badlogic.gdx.Gdx;
 import com.badlogic.gdx.Input;
 import com.badlogic.gdx.Screen;
+import com.badlogic.gdx.assets.AssetDescriptor;
+import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.OrthographicCamera;
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
 import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
+import com.badlogic.gdx.maps.tiled.TiledMap;
+import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
+import com.badlogic.gdx.maps.tiled.TmxMapLoader;
+import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
+import com.badlogic.gdx.maps.tiled.tiles.StaticTiledMapTile;
+import com.badlogic.gdx.utils.viewport.ScreenViewport;
 import me.lethunderhawk.Main;
-import me.lethunderhawk.entity.Player;
+import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.event.EventDispatcher;
-import me.lethunderhawk.event.EventListener;
 import me.lethunderhawk.event.impl.PlayerMoveEvent;
 import me.lethunderhawk.network.GameClient;
-import me.lethunderhawk.network.packet.PacketRegistry;
 import me.lethunderhawk.network.packet.impl.PlayerLogoutPacket;
 import me.lethunderhawk.network.packet.impl.PlayerPositionPacket;
+import me.lethunderhawk.world.World;
+import me.lethunderhawk.camera.CameraController;
 
 import java.io.IOException;
 import java.util.Map;
 import java.util.UUID;
 
-public class GameScreen implements Screen, EventListener {
+public class GameScreen implements Screen {
 
     private final ShapeRenderer shapeRenderer;
+    private final SpriteBatch spriteBatch;
     private final GameClient client;
     private final Main mainGame;
+    private OrthographicCamera camera;
+    private ScreenViewport viewport;
+    private CameraController cameraController;
+    private World world;
+    private TiledMap tiledMap;
+    private OrthogonalTiledMapRenderer tiledMapRenderer;
+    private Texture tilesetTexture;
 
     public GameScreen(Main game, String ip) {
         this.mainGame = game;
-        shapeRenderer = new ShapeRenderer();
-
-        client = new GameClient();
-
+        this.shapeRenderer = new ShapeRenderer();
+        this.spriteBatch = new SpriteBatch();
+        this.client = new GameClient();
+        this.cameraController = new CameraController();
         try {
             client.connect(ip);
         } catch (IOException e) {
@@ -39,86 +60,199 @@ public class GameScreen implements Screen, EventListener {
     }
 
     @Override
-    public void render(float delta) {
+    public void show() {
+        // --- Load tileset texture and split into 16x16 tiles ---
+        Texture tilesetTexture = new Texture(Gdx.files.internal("TX/TX Tileset Grass.png")); // ensure file has extension
+        final int TILE_PX = 16;
+        TextureRegion[][] regions = TextureRegion.split(tilesetTexture, TILE_PX, TILE_PX);
+
+        // Create a TiledMapTileSet and put each tile in it (IDs start at 1)
+        TiledMapTileSet tileset = new TiledMapTileSet();
+        int id = 1;
+        for (int row = 0; row < regions.length; row++) {
+            for (int col = 0; col < regions[row].length; col++) {
+                TextureRegion region = regions[row][col];
+                if (region == null) continue;
+                StaticTiledMapTile tile = new StaticTiledMapTile(region);
+                tile.setId(id);
+                tileset.putTile(id, tile);
+                id++;
+            }
+        }
+
+        // --- Load other textures if you still need them via AssetManager (optional) ---
+        // Example shown in your snippet (keep or remove as needed)
+        /*mainGame.assetManager.load("tiles/grass.png", Texture.class);
+        mainGame.assetManager.load("tiles/flowering_grass.png", Texture.class);
+        mainGame.assetManager.load("tiles/stone_path.png", Texture.class);
+        mainGame.assetManager.load("tiles/broken_stone_path.png", Texture.class);
+        mainGame.assetManager.load("tiles/wall.png", Texture.class);*/
+        mainGame.assetManager.finishLoading();
+
+        // --- Load the Tiled map ---
+        TiledMap map = new TmxMapLoader().load("level/lobby.tmx");
+
+        // Add the runtime tileset to the map so its tiles can be referenced by layers/objects if needed
+        map.getTileSets().addTileSet(tileset);
+
+        // --- Setup renderer and camera ---
+        final float UNIT_SCALE = 1f / TILE_PX; // map tiles are 16px so 1/16f unit scale
+        OrthogonalTiledMapRenderer renderer = new OrthogonalTiledMapRenderer(map, UNIT_SCALE);
+
+        // Determine map world size (in world units) and center camera on it
+        int mapTilesX = map.getProperties().get("width", Integer.class);
+        int mapTilesY = map.getProperties().get("height", Integer.class);
+        int mapTilePixelWidth = map.getProperties().get("tilewidth", Integer.class);
+        int mapTilePixelHeight = map.getProperties().get("tileheight", Integer.class);
+
+        float worldWidth = mapTilesX * mapTilePixelWidth * UNIT_SCALE;
+        float worldHeight = mapTilesY * mapTilePixelHeight * UNIT_SCALE;
+
+        camera = new OrthographicCamera();
+        viewport = new ScreenViewport(camera);
 
+        // Set a reasonable viewport size: keep the screen viewport but ensure camera centers on map
+        camera.setToOrtho(false, viewport.getWorldWidth(), viewport.getWorldHeight());
+        camera.position.set(worldWidth / 2f, worldHeight / 2f, 0f);
+        camera.update();
+
+        renderer.setView(camera);
+
+        // Store renderer / map / tileset as fields if you need to render/dispose later:
+        this.tiledMap = map;
+        this.tiledMapRenderer = renderer;
+        this.tilesetTexture = tilesetTexture; // keep a ref for disposal
+
+    }
+
+    @Override
+    public void render(float delta) {
         update(delta);
 
         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
 
+        // Render World with SpriteBatch
+        spriteBatch.setProjectionMatrix(camera.combined);
+        spriteBatch.begin();
+        renderWorld();
+        spriteBatch.end();
+
+        // Render Players with ShapeRenderer
+        shapeRenderer.setProjectionMatrix(camera.combined);
         shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
-        Player player = client.getPlayer();
-        shapeRenderer.rect(player.x, player.y, 50, 50);
 
-        for(Map.Entry<UUID, Player> entry : client.getOtherPlayers().entrySet()) {
-            Player otherPlayer = entry.getValue();
-            shapeRenderer.rect(otherPlayer.x, otherPlayer.y, 50, 50);
+        if (client.getPlayer() != null) {
+            renderPlayerGrid(client.getPlayer());
+        }
+
+        for (Map.Entry<UUID, Player> entry : client.getOtherPlayers().entrySet()) {
+            renderPlayerGrid(entry.getValue());
         }
 
         shapeRenderer.end();
     }
 
-    private void update(float delta) {
+    private void renderWorld() {
+        World world = client.getWorld();
+        if (world == null) return;
+        float scale = world.getBlockScale() * 32f; // Assuming 32px tiles
 
-        float speed = 300f;
+        for (int x = 0; x < world.getWidth(); x++) {
+            for (int y = 0; y < world.getHeight(); y++) {
+                int blockId = world.getBlock(x, y);
+                if (blockId == 0) continue;
 
-        boolean moved = false;
-        Player currentPlayer = client.getPlayer();
-        if(Gdx.input.isKeyPressed(Input.Keys.ESCAPE)) {
-            mainGame.setScreen(new MenuScreen(mainGame));
-        }
-        if(Gdx.input.isKeyPressed(Input.Keys.W)) {
-            currentPlayer.y += speed * delta;
-            moved = true;
+                Texture tex = getTextureForBlock(blockId);
+                if (tex != null) {
+                    spriteBatch.draw(tex, x * scale, y * scale, scale, scale);
+                }
+            }
         }
+    }
 
-        if(Gdx.input.isKeyPressed(Input.Keys.S)) {
-            currentPlayer.y -= speed * delta;
-            moved = true;
-        }
+    private Texture getTextureForBlock(int blockId) {
+        return switch (blockId) {
+            case 1 -> mainGame.assetManager.get("tiles/grass.png", Texture.class);
+            case 2 -> mainGame.assetManager.get("tiles/flowering_grass.png", Texture.class);
+            case 3 -> mainGame.assetManager.get("tiles/stone_path.png", Texture.class);
+            case 4 -> mainGame.assetManager.get("tiles/broken_stone_path.png", Texture.class);
+            case 10 -> mainGame.assetManager.get("tiles/wall.png", Texture.class);
+            default -> null;
+        };
+    }
 
-        if(Gdx.input.isKeyPressed(Input.Keys.A)) {
-            currentPlayer.x -= speed * delta;
-            moved = true;
+    private void renderPlayerGrid(Player player) {
+        if (player.avatar == null) return;
+        float cellSize = 50f / 16f;
+        for (int x = 0; x < 16; x++) {
+            for (int y = 0; y < 16; y++) {
+                Color color = player.avatar.getColor(x, y);
+                shapeRenderer.setColor(color);
+                shapeRenderer.rect(player.x + x * cellSize, player.y + y * cellSize, cellSize, cellSize);
+            }
         }
+    }
 
-        if(Gdx.input.isKeyPressed(Input.Keys.D)) {
-            currentPlayer.x += speed * delta;
-            moved = true;
+    private void update(float delta) {
+        float speed = 300f;
+        boolean moved = false;
+        Player currentPlayer = client.getPlayer();
+
+        if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE)) {
+            mainGame.setScreen(new MenuScreen(mainGame));
+            return;
         }
 
-        if(moved) {
+        if (currentPlayer != null) {
+            if (Gdx.input.isKeyPressed(Input.Keys.W)) { currentPlayer.y += speed * delta; moved = true; }
+            if (Gdx.input.isKeyPressed(Input.Keys.S)) { currentPlayer.y -= speed * delta; moved = true; }
+            if (Gdx.input.isKeyPressed(Input.Keys.A)) { currentPlayer.x -= speed * delta; moved = true; }
+            if (Gdx.input.isKeyPressed(Input.Keys.D)) { currentPlayer.x += speed * delta; moved = true; }
 
-            PlayerPositionPacket packet =
-                new PlayerPositionPacket(currentPlayer, currentPlayer.x, currentPlayer.y);
-            PlayerMoveEvent event = new PlayerMoveEvent(currentPlayer, currentPlayer.x, currentPlayer.y);
-            EventDispatcher.getInstance().fire(event);
+            if (moved) {
+                PlayerPositionPacket packet = new PlayerPositionPacket(currentPlayer.uuid, currentPlayer.x, currentPlayer.y);
+                EventDispatcher.getInstance().fire(new PlayerMoveEvent(currentPlayer, currentPlayer.x, currentPlayer.y));
+                client.send(packet);
+            }
+        }
 
-            client.send(packet);
+        // Update camera dead-zone follow logic
+        if (currentPlayer != null) {
+            cameraController.update(delta, camera, currentPlayer);
         }
     }
 
-
     @Override
-    public void show() {}
-    @Override
-    public void resize(int width, int height) {}
+    public void resize(int width, int height) {
+        if (viewport != null) {
+            viewport.update(width, height, true);
+            camera.update();
+        }
+    }
+
     @Override
     public void pause() {}
+
     @Override
     public void resume() {}
+
     @Override
     public void hide() {
         logout();
     }
+
     @Override
     public void dispose() {
         logout();
+        shapeRenderer.dispose();
     }
 
-    private void logout(){
-        PlayerLogoutPacket packet = new PlayerLogoutPacket();
-        packet.player = client.getPlayer();
-        client.send(packet);
+    private void logout() {
+        if (client.getPlayer() != null) {
+            PlayerLogoutPacket packet = new PlayerLogoutPacket();
+            packet.uuid = client.getPlayer().uuid;
+            client.send(packet);
+        }
         client.disconnect();
     }
 }

+ 21 - 3
core/src/main/java/me/lethunderhawk/screen/MenuScreen.java

@@ -60,7 +60,7 @@ public class MenuScreen implements Screen {
         ipField = new TextField("localhost", skin);
 
         // Start Button
-        TextButton startButton = new TextButton("Start Game", skin);
+        TextButton startButton = new TextButton("Multiplayer", skin);
         startButton.addListener(new ClickListener() {
             @Override
             public void clicked(InputEvent event, float x, float y) {
@@ -70,14 +70,32 @@ public class MenuScreen implements Screen {
                 }
                 GameScreen gameScreen = new GameScreen(game, ip);
                 game.setScreen(gameScreen);
-                EventDispatcher.addListener(gameScreen);
             }
         });
+        TextButton singlePlayerButton = new TextButton("Single Player", skin);
+        /*
+        singlePlayerButton.addListener(new ClickListener() {
+            @Override
+            public void clicked(InputEvent event, float x, float y) {
+                String ip = ipField.getText();
+                if (ip == null || ip.trim().isEmpty()) {
+                    ip = "localhost";
+                }
+                GameScreen gameScreen = new GameScreen(game, ip);
+                game.setScreen(gameScreen);
+            }
+        });
+        */
 
         // Layout components
+
         table.add(label).padBottom(10).row();
         table.add(ipField).width(300).padBottom(20).row();
-        table.add(startButton).width(200);
+        table.add(startButton).width(200).padBottom(20).row();
+/*
+        table.add(singlePlayerButton).width(200).row();
+*/
+
 
         // Set input processor
         Gdx.input.setInputProcessor(stage);

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

@@ -3,29 +3,29 @@ package me.lethunderhawk.server;
 import com.esotericsoftware.kryonet.Connection;
 import com.esotericsoftware.kryonet.Listener;
 import com.esotericsoftware.kryonet.Server;
-import me.lethunderhawk.entity.Player;
-import me.lethunderhawk.network.NetworkRegister;
 import me.lethunderhawk.network.packet.*;
-import me.lethunderhawk.network.packet.impl.PlayerLoginPacket;
-import me.lethunderhawk.network.packet.impl.PlayerLoginSuccessPacket;
-import me.lethunderhawk.network.packet.impl.PlayerLogoutPacket;
-import me.lethunderhawk.network.packet.impl.PlayerPositionPacket;
+import me.lethunderhawk.world.World;
+import me.lethunderhawk.world.blocks.BlockRegistry;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
 
 public class GameServer {
 
     private final Server server;
+    private final World world;
+    private BlockRegistry registry;
 
 
     public GameServer() throws IOException {
 
         server = new Server();
 
+        registerBlocks();
+
+        this.world = generateNewMap();
+
         registerPackets();
-        PacketDispatcher.registerListener(new GameListener(server));
+        PacketDispatcher.registerListener(new GameServerPacketListener(server, this));
 
         server.start();
 
@@ -40,6 +40,40 @@ public class GameServer {
         });
     }
 
+    private void registerBlocks() {
+        this.registry = new BlockRegistry();
+        // In a real scenario, the registry would map IDs to asset paths
+    }
+
+    private World generateNewMap() {
+        int width = 500;
+        int height = 500;
+        World world = new World(width, height, 1.0f);
+
+        java.util.Random random = new java.util.Random();
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                // Simple random generation for demonstration
+                float noise = random.nextFloat();
+                if (noise < 0.6f) {
+                    world.setBlock(x, y, 1); // Grass
+                } else if (noise < 0.7f) {
+                    world.setBlock(x, y, 2); // Flowering Grass
+                } else if (noise < 0.8f) {
+                    world.setBlock(x, y, 3); // Stone Path
+                } else if (noise < 0.9f) {
+                    world.setBlock(x, y, 4); // Broken Stone Path
+                } else {
+                    world.setBlock(x, y, 10); // Solid Block (Wall)
+                }
+            }
+        }
+        return world;
+    }
+
+    public World getWorld() {
+        return world;
+    }
 
     private void registerPackets() {
         PacketRegistry.register(server.getKryo());

+ 21 - 10
server/src/main/java/me/lethunderhawk/server/GameListener.java → server/src/main/java/me/lethunderhawk/server/GameServerPacketListener.java

@@ -2,24 +2,27 @@ package me.lethunderhawk.server;
 
 import com.esotericsoftware.kryonet.Connection;
 import com.esotericsoftware.kryonet.Server;
-import me.lethunderhawk.entity.Player;
+import me.lethunderhawk.network.packet.impl.*;
+import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.network.packet.PacketHandler;
 import me.lethunderhawk.network.packet.PacketListener;
-import me.lethunderhawk.network.packet.impl.PlayerLoginPacket;
-import me.lethunderhawk.network.packet.impl.PlayerLoginSuccessPacket;
-import me.lethunderhawk.network.packet.impl.PlayerLogoutPacket;
-import me.lethunderhawk.network.packet.impl.PlayerPositionPacket;
 
 import java.util.HashMap;
 import java.util.Map;
 
-public class GameListener implements PacketListener {
+public class GameServerPacketListener implements PacketListener {
+    /**
+     * Connection id --> Player
+     */
     private final Map<Integer, Player> players = new HashMap<>();
     public Server server;
+    private final GameServer gameServer;
 
-    public GameListener(Server server) {
+    public GameServerPacketListener(Server server, GameServer gameServer) {
         this.server = server;
+        this.gameServer = gameServer;
     }
+
     @PacketHandler
     public void onPlayerPosition(PlayerPositionPacket packet, Connection connection) {
         server.sendToAllExceptUDP(connection.getID(), packet);
@@ -36,14 +39,22 @@ public class GameListener implements PacketListener {
 
     @PacketHandler
     public void onLogin(PlayerLoginPacket packet, Connection connection){
-        players.putIfAbsent(connection.getID(),  packet.player);
+        players.putIfAbsent(connection.getID(), packet.player);
 
         server.sendToAllExceptUDP(connection.getID(), packet);
         server.sendToUDP(connection.getID(), new PlayerLoginSuccessPacket());
 
+        // Send the world data to the player upon login
+        WorldDataPacket worldPacket = new WorldDataPacket(gameServer.getWorld());
+        server.sendToUDP(connection.getID(), worldPacket);
+
         for(Player player : players.values()){
-            if(player.equals(packet.player)) return;
-            server.sendToUDP(connection.getID(), new PlayerPositionPacket(player, player.x, player.y));
+            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;
+            server.sendToUDP(connection.getID(), loginPacket);
         }
     }
 }

+ 1 - 0
shared/build.gradle

@@ -3,4 +3,5 @@ eclipse.project.name = appName + '-shared'
 dependencies {
   implementation "com.esotericsoftware:kryonet:2.22.0-RC1"
   api "com.esotericsoftware:kryonet:2.22.0-RC1"
+  implementation "com.badlogicgames.gdx:gdx:1.11.0"
 }

+ 0 - 13
shared/src/main/java/me/lethunderhawk/entity/Player.java

@@ -1,13 +0,0 @@
-package me.lethunderhawk.entity;
-
-import java.util.UUID;
-
-public class Player {
-    public UUID uuid;
-    public float x;
-    public float y;
-
-    public Player(){
-        this.uuid = UUID.randomUUID();
-    }
-}

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

@@ -1,6 +1,6 @@
 package me.lethunderhawk.event.impl;
 
-import me.lethunderhawk.entity.Player;
+import me.lethunderhawk.world.entity.player.Player;
 import me.lethunderhawk.event.Event;
 
 public class PlayerMoveEvent extends Event {

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

@@ -4,7 +4,7 @@ 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.entity.Player;
+import me.lethunderhawk.world.entity.player.Player;
 
 import java.util.UUID;
 

+ 14 - 3
shared/src/main/java/me/lethunderhawk/network/packet/PacketRegistry.java

@@ -1,12 +1,16 @@
 package me.lethunderhawk.network.packet;
 
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.math.Rectangle;
 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.entity.Player;
-import me.lethunderhawk.event.impl.PlayerMoveEvent;
+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.UUID;
 
@@ -19,12 +23,19 @@ public class PacketRegistry {
         kryo.register(PlayerLogoutPacket.class);
         kryo.register(PlayerMovePacket.class);
         kryo.register(PlayerPositionPacket.class);
+        kryo.register(PlayerAvatar.class);
+        kryo.register(Rectangle.class);
+        kryo.register(World.class);
+        kryo.register(WorldDataPacket.class);
+
+        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());
             }

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

@@ -1,9 +1,10 @@
 package me.lethunderhawk.network.packet.impl;
 
-import me.lethunderhawk.entity.Player;
+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;
 }

+ 0 - 3
shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLoginSuccessPacket.java

@@ -1,9 +1,6 @@
 package me.lethunderhawk.network.packet.impl;
 
-import me.lethunderhawk.entity.Player;
 import me.lethunderhawk.network.packet.Packet;
 
 public class PlayerLoginSuccessPacket implements Packet {
-    public Player player;
-
 }

+ 3 - 2
shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerLogoutPacket.java

@@ -1,8 +1,9 @@
 package me.lethunderhawk.network.packet.impl;
 
-import me.lethunderhawk.entity.Player;
 import me.lethunderhawk.network.packet.Packet;
 
+import java.util.UUID;
+
 public class PlayerLogoutPacket implements Packet {
-    public Player player;
+    public UUID uuid;
 }

+ 3 - 2
shared/src/main/java/me/lethunderhawk/network/packet/impl/PlayerMovePacket.java

@@ -1,10 +1,11 @@
 package me.lethunderhawk.network.packet.impl;
 
-import me.lethunderhawk.entity.Player;
 import me.lethunderhawk.network.packet.Packet;
 
+import java.util.UUID;
+
 public class PlayerMovePacket implements Packet {
-    public Player player;
+    public UUID uuid;
     public float x;
     public float y;
 }

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

@@ -1,17 +1,18 @@
 package me.lethunderhawk.network.packet.impl;
 
-import me.lethunderhawk.entity.Player;
 import me.lethunderhawk.network.packet.Packet;
 
+import java.util.UUID;
+
 public class PlayerPositionPacket implements Packet {
-    public Player player;
+    public UUID uuid;
     public float x;
     public float y;
 
     public PlayerPositionPacket() {}
 
-    public PlayerPositionPacket(Player player, float x, float y) {
-        this.player = player;
+    public PlayerPositionPacket(UUID uuid, float x, float y) {
+        this.uuid = uuid;
         this.x = x;
         this.y = y;
     }

+ 14 - 0
shared/src/main/java/me/lethunderhawk/network/packet/impl/WorldDataPacket.java

@@ -0,0 +1,14 @@
+package me.lethunderhawk.network.packet.impl;
+
+import me.lethunderhawk.network.packet.Packet;
+import me.lethunderhawk.world.World;
+
+public class WorldDataPacket implements Packet {
+    public World world;
+
+    public WorldDataPacket() {}
+
+    public WorldDataPacket(World world) {
+        this.world = world;
+    }
+}

+ 54 - 0
shared/src/main/java/me/lethunderhawk/network/packet/serialization/ColorSerializer.java

@@ -0,0 +1,54 @@
+package me.lethunderhawk.network.packet.serialization;
+
+import com.badlogic.gdx.graphics.Color;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+public class ColorSerializer extends Serializer<Color> {
+
+    private boolean isCompactDefault = true;
+
+    /** Sets whether compact encoding should be used or not in the absence of a {@link GraphHeader}. True by default.*/
+    public void setCompactDefault(boolean compactDefault) {
+        isCompactDefault = compactDefault;
+    }
+
+    @Override
+    public void write(Kryo kryo, Output output, Color color) {
+        Boolean compact = GraphHeader.isUseCompactColor(kryo);
+        if (compact == null) compact = isCompactDefault;
+        if (compact){
+            output.writeInt(Color.rgba8888(color));
+        } else {
+            output.writeFloat(color.r);
+            output.writeFloat(color.g);
+            output.writeFloat(color.b);
+            output.writeFloat(color.a);
+        }
+    }
+
+    @Override
+    public Color read(Kryo kryo, Input input, Class<Color> type) {
+        Boolean compact = GraphHeader.isUseCompactColor(kryo);
+        if (compact == null) compact = isCompactDefault;
+        if (compact){
+            Color color = new Color();
+            Color.rgba8888ToColor(color, input.readInt());
+            return color;
+        } else {
+            float r = input.readFloat();
+            float g = input.readFloat();
+            float b = input.readFloat();
+            float a = input.readFloat();
+            return new Color(r, g, b, a);
+        }
+
+    }
+
+    @Override
+    public Color copy (Kryo kryo, Color original) {
+        return new Color(original);
+    }
+}

+ 246 - 0
shared/src/main/java/me/lethunderhawk/network/packet/serialization/GraphHeader.java

@@ -0,0 +1,246 @@
+package me.lethunderhawk.network.packet.serialization;
+
+import com.badlogic.gdx.Version;
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.Pixmap;
+import com.badlogic.gdx.utils.Array;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.KryoSerializable;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer;
+import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer;
+
+/**
+ * An instance of GraphHeader should be used to wrap a data graph if backward compatibility is desired. Backward
+ * compatibility is the ability for data written in a previous version of an application to be read by a newer version
+ * of the application even if the fields, constructors, and methods of classes in the graph have changed.
+ * <p>
+ * In the case of the GdxToKryo serializers, the information in the header is used to determine which version of LibGDX
+ * was used when the data was written, so it can be correctly deserialized in applications with later versions of LibGDX.
+ * <p>
+ * When GraphHeader is used, changes to LibGDX classes are handled automatically, so they are backward compatible. For
+ * other classes, {@link TaggedFieldSerializer} or {@link } may be used to manage a class's backward
+ * compatibility. But for custom serializers, the value of {@link #currentReadWriteVersion} can be used to keep track of changes
+ * affecting compatibility between releases of an application. Custom serializers' <code>read</code> methods may call
+ * {@link #getWrittenVersion(Kryo)} to retrieve the value of {@link #currentReadWriteVersion} that was used when the
+ * graph was written.
+ * <p>
+ * The serializers in GdxToKryo are not necessarily forward compatible with all future versions of LibGDX. This is also
+ * likely the case for other classes whose serializers do not use chunked encoding, such as {@link CompatibleFieldSerializer}.
+ * When it is known that something in the object graph has changed such that it can no longer be read in a previous release
+ * version of the application, not only should the {@link #currentReadWriteVersion} should be incremented, but also
+ * {@link #minimumReadVersion} should be updated to match it. This will cause the GraphHeader serializer to skip reading
+ * of the {@link #data}, leaving it null. After reading, an application can check if {@link #data} is null, and if so,
+ * use the values of {@link #minimumReadVersion} and {@link #minimumReadVersionString} to react accordingly, such as
+ * showing the user a prompt message to update the application. Since the data is not read, subsequent data in the
+ * input stream cannot be read either.
+ * <p>
+ * Subclasses of GraphHeader may use {@link #writeExtra(Kryo, Output)} and {@link #readExtra(Kryo, Input)} to handle writing
+ * and reading of extra information in the subclass. These are called immediately before writing or reading the {@link #data}.
+ *
+ * @param <T> The type of the data to be written, referenced in {@link #data}.
+ */
+public class GraphHeader<T> implements KryoSerializable{
+
+    /** The current version of the GraphHeader class. */
+    private static final int GRAPH_HEADER_VERSION = 0;
+    private static final Object GRAPH_HEADER_KEY = new Object();
+
+    private static void pushHeader (Kryo kryo, GraphHeader graphHeader){
+        Array<GraphHeader> graphHeaders;
+        if (kryo.getGraphContext().containsKey(GRAPH_HEADER_KEY))
+            graphHeaders = (Array<GraphHeader>)kryo.getGraphContext().get(GRAPH_HEADER_KEY);
+        else
+            graphHeaders = new Array<GraphHeader>();
+        graphHeaders.add(graphHeader);
+        kryo.getGraphContext().put(GRAPH_HEADER_KEY, graphHeaders);
+    }
+
+    private static GraphHeader peekHeader (Kryo kryo){
+        if (kryo.getGraphContext().containsKey(GRAPH_HEADER_KEY)) {
+            Array<GraphHeader> graphHeaders = (Array<GraphHeader>)kryo.getGraphContext().get(GRAPH_HEADER_KEY);
+            if (graphHeaders.size > 0)
+                return graphHeaders.peek();
+        }
+        return null;
+    }
+
+    private static GraphHeader popHeader (Kryo kryo){
+        if (kryo.getGraphContext().containsKey(GRAPH_HEADER_KEY)) {
+            Array<GraphHeader> graphHeaders = (Array<GraphHeader>)kryo.getGraphContext().get(GRAPH_HEADER_KEY);
+            if (graphHeaders.size > 0)
+                return graphHeaders.pop();
+        }
+        return null;
+    }
+
+    /**
+     * If reading an object graph wrapped in an instance of GraphHeader, serializers can call this to obtain the
+     * value of {@link #writtenVersion} in that GraphHeader in their <code>read</code> methods.
+     * @param kryo The Kryo instance currently reading data.
+     * @return The value of {@link #writtenVersion} in the GraphHeader that wrapped the data being read in the graph. If
+     * a GraphHeader did not wrap the data being read, 0 is returned.
+     */
+    public static int getWrittenVersion(Kryo kryo){
+        GraphHeader graphHeader = peekHeader(kryo);
+        if (graphHeader != null)
+            return graphHeader.writtenVersion;
+        return currentReadWriteVersion;
+    }
+
+    /**
+     * If reading an object graph that started with an instance of GraphHeader, serializers can call this in their
+     * <code>read</code> methods to determine if the LibGDX version of the data being read is from a version of LibGDX
+     * higher or equal to a given specific revision.
+     * @param kryo The Kryo instance currently reading data.
+     * @return Whether the LibGDX version used to write the data being read is higher than or equal to the given version.
+     * If a GraphHeader did not precede the data being read, the currently used LibGDX version is assumed as the version
+     * that wrote the data.
+     */
+    public static boolean isWrittenGdxVersionAtLeast (Kryo kryo, int major, int minor, int revision) {
+        GraphHeader graphHeader = peekHeader(kryo);
+        if (graphHeader != null){
+            if (graphHeader.gdxMajorVersion != major)
+                return graphHeader.gdxMajorVersion > major;
+            if (graphHeader.gdxMinorVersion != minor)
+                return graphHeader.gdxMinorVersion > minor;
+            return graphHeader.gdxRevisionVersion >= revision;
+        }
+        return Version.isHigherEqual(major, minor, revision);
+    }
+
+    /**
+     * Used by {@link ColorSerializer} to determine whether the
+     * data in the graph should use compact color (32-bit int) encoding. See {@link #useCompactColor}.
+     * @param kryo The Kryo instance currently reading data.
+     * @return The value of {@link #useCompactColor} in the GraphHeader that preceded the data being read in the graph.
+     * If a GraphHeader did not precede the data being written or read, null is returned.
+     */
+    public static Boolean isUseCompactColor (Kryo kryo){
+        GraphHeader graphHeader = peekHeader(kryo);
+        if (graphHeader != null)
+            return graphHeader.useCompactColor;
+        return null;
+    }
+
+    /**
+     * Used by {@link} to determine whether the
+     * data in the graph should include pixmap drawing params. See {@link #includePixmapDrawingParams}.
+     * @param kryo The Kryo instance currently reading data.
+     * @return The value of {@link #includePixmapDrawingParams} in the GraphHeader that preceded the data being read in
+     * the graph. If a GraphHeader did not precede the data being written or read, null is returned.
+     */
+    public static Boolean isIncludePixmapDrawingParams (Kryo kryo){
+        GraphHeader graphHeader = peekHeader(kryo);
+        if (graphHeader != null)
+            return graphHeader.includePixmapDrawingParams;
+        return null;
+    }
+
+    /**
+     * A representative value that can be used by custom serializers to support backward compatibility. If any of
+     * the classes in an object graph change as compared to a previously released version, this value should be incremented
+     * so custom serializers can use it to read data accordingly.
+     * <p>
+     * If there are multiple types of object graphs in this application that share some serializers, the data version used
+     * is shared by all, so it should be incremented if any serializers across all of possible graphs is changed.
+     * <p>
+     * Default is -1. Must be &gt;= 0. The correct value must be set before writing any objects. When the object graph is
+     * being read, custom serializers can access the data version that was used to write the data using
+     * {@link GraphHeader#getWrittenVersion(Kryo)} in their <code>read</code> methods.
+     */
+    public static int currentReadWriteVersion = -1;
+
+    // Gdx version fields are only relevant during reading of the graph.
+    int gdxMajorVersion, gdxMinorVersion, gdxRevisionVersion;
+
+    /** The value of {@link #currentReadWriteVersion} when the graph was written. This value is changed to {@link #currentReadWriteVersion}
+     * when the graph is written. Serializers may access it during reading using {@link GraphHeader#getWrittenVersion(Kryo)}
+     * in their <code>read</code> methods. */
+    protected int writtenVersion;
+
+    /** Represents the oldest version of the application whose serializers are still capable of deserializing data
+     * written in this version. Corresponds to the {@link #currentReadWriteVersion} that was used with that release of
+     * the application.
+     * <p>
+     * If a version of the application that uses a lower {@link #currentReadWriteVersion} attempts to read the graph,
+     * the data will not be read, but instead {@link #data} will remain null. At that point {@link #minimumReadVersionString}
+     * may be used to show the user an appropriate message about what version of the application is required to read
+     * the data. Subsequent data in the input stream cannot be read because there will be an unknown number of bytes to
+     * skip.
+     */
+    public int minimumReadVersion;
+
+    /** A value intended for use when a version of the application attempts to read data from a future version of the
+     * application that it is not able to, as determined by {@link #minimumReadVersion}.
+     */
+    public String minimumReadVersionString;
+
+    /**Whether the written data should use 32-bit integers to represent {@link Color} objects, rather than maintaining
+     * complete equivalence by using four floats. 32-bit integers are adequate for most uses and satisfy
+     * {@link Color#equals(Object)}. The default is true.
+     */
+    public boolean useCompactColor = true;
+
+    /**Whether Pixmap drawing parameters (i.e. {@link Pixmap#setFilter(Pixmap.Filter)}, {@link Pixmap#getBlending()}) should be
+     * written and restored when read or copied. */
+    public boolean includePixmapDrawingParams = false;
+
+    /** The top level object of the object graph. */
+    public T data;
+
+    /** Called immediately before writing the {@link #data} and after writing all other fields of GraphHeader. */
+    protected void writeExtra (Kryo kryo, Output output) {}
+
+    /** Called immediately before reading the {@link #data} and after reading all other fields of GraphHeader. */
+    protected void readExtra (Kryo kryo, Input input) {}
+
+    @Override
+    public final void write (Kryo kryo, Output output) {
+        if (currentReadWriteVersion == -1)
+            throw new RuntimeException("currentReadWriteVersion must be set before writing.");
+        if (currentReadWriteVersion < 0 || minimumReadVersion < 0)
+            throw new RuntimeException("currentReadWriteVersion and minimumReadVersion must not be less than 0.");
+        if (currentReadWriteVersion < minimumReadVersion)
+            throw new RuntimeException("currentReadWriteVersion cannot be lower than minimumReadVersion");
+
+        Class type = data == null ? null : data.getClass();
+        pushHeader(kryo, this);
+        output.writeInt(GRAPH_HEADER_VERSION, true);
+        kryo.writeClass(output, type);
+        output.writeInt(Version.MAJOR, true);
+        output.writeInt(Version.MINOR, true);
+        output.writeInt(Version.REVISION, true);
+        output.writeInt(currentReadWriteVersion, true);
+        output.writeInt(minimumReadVersion, true);
+        output.writeString(minimumReadVersionString);
+        output.writeBoolean(useCompactColor);
+        output.writeBoolean(includePixmapDrawingParams);
+        writeExtra(kryo, output);
+        if (data != null)
+            kryo.writeObject(output, data);
+        popHeader(kryo);
+    }
+
+    @Override
+    public final void read (Kryo kryo, Input input) {
+        pushHeader(kryo, this);
+        input.readInt(true); //if this class ever evolves, version can be used for backward compatibility
+        Class dataType = kryo.readClass(input).getType();
+        gdxMajorVersion = input.readInt(true);
+        gdxMinorVersion = input.readInt(true);
+        gdxRevisionVersion = input.readInt(true);
+        writtenVersion = input.readInt(true);
+        minimumReadVersion = input.readInt(true);
+        minimumReadVersionString = input.readString();
+        useCompactColor = input.readBoolean();
+        includePixmapDrawingParams = input.readBoolean();
+        readExtra(kryo, input);
+        if (dataType != null && minimumReadVersion <= currentReadWriteVersion){
+            data = (T)kryo.readObject(input, dataType);
+        }
+        popHeader(kryo);
+    }
+
+}

+ 4 - 0
shared/src/main/java/me/lethunderhawk/world/BackGroundTile.java

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

+ 26 - 0
shared/src/main/java/me/lethunderhawk/world/World.java

@@ -0,0 +1,26 @@
+package me.lethunderhawk.world;
+
+public class World {
+    private final int width;
+    private final int height;
+    private final int[][] blocks;
+    private final float blockScale;
+
+    public World(int width, int height, float blockScale) {
+        this.width = width;
+        this.height = height;
+        this.blockScale = blockScale;
+        this.blocks = new int[width][height];
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                blocks[x][y] = 0; // 0 = empty/air
+            }
+        }
+    }
+
+    public int getWidth() { return width; }
+    public int getHeight() { return height; }
+    public float getBlockScale() { return blockScale; }
+    public int getBlock(int x, int y) { return blocks[x][y]; }
+    public void setBlock(int x, int y, int blockId) { blocks[x][y] = blockId; }
+}

+ 23 - 0
shared/src/main/java/me/lethunderhawk/world/blocks/Block.java

@@ -0,0 +1,23 @@
+package me.lethunderhawk.world.blocks;
+
+import com.badlogic.gdx.math.Rectangle;
+import me.lethunderhawk.world.entity.collision.Collider;
+
+public class Block implements Collider {
+
+    private final Rectangle bounds;
+
+    public Block(float x, float y, float width, float height) {
+        bounds = new Rectangle(
+            x,
+            y,
+            width,
+            height
+        );
+    }
+
+    @Override
+    public Rectangle getBounds() {
+        return bounds;
+    }
+}

+ 7 - 0
shared/src/main/java/me/lethunderhawk/world/blocks/BlockRegistry.java

@@ -0,0 +1,7 @@
+package me.lethunderhawk.world.blocks;
+
+public class BlockRegistry {
+    public void add(int i, String path) {
+
+    }
+}

+ 8 - 0
shared/src/main/java/me/lethunderhawk/world/entity/collision/Collider.java

@@ -0,0 +1,8 @@
+package me.lethunderhawk.world.entity.collision;
+
+import com.badlogic.gdx.math.Rectangle;
+
+public interface Collider {
+
+    Rectangle getBounds();
+}

+ 29 - 0
shared/src/main/java/me/lethunderhawk/world/entity/collision/CollisionSystem.java

@@ -0,0 +1,29 @@
+package me.lethunderhawk.world.entity.collision;
+
+import com.badlogic.gdx.math.Rectangle;
+
+import java.util.List;
+
+public class CollisionSystem {
+
+    public static boolean collides(
+        Collider collider,
+        List<? extends Collider> others
+    ) {
+
+        Rectangle bounds = collider.getBounds();
+
+        for(Collider other : others) {
+
+            if(other == collider)
+                continue;
+
+            if(bounds.overlaps(other.getBounds())) {
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 35 - 0
shared/src/main/java/me/lethunderhawk/world/entity/player/Player.java

@@ -0,0 +1,35 @@
+package me.lethunderhawk.world.entity.player;
+
+import com.badlogic.gdx.math.Rectangle;
+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 Rectangle bounds;
+
+    public Player(){
+        this.uuid = UUID.randomUUID();
+        this.avatar = new PlayerAvatar();
+        avatar.initializeGrid();
+        bounds = new Rectangle(
+            x,
+            y,
+            32,
+            32
+        );
+    }
+
+    public void updateBounds() {
+        bounds.setPosition(x, y);
+    }
+
+    @Override
+    public Rectangle getBounds() {
+        return bounds;
+    }
+}

+ 31 - 0
shared/src/main/java/me/lethunderhawk/world/entity/player/PlayerAvatar.java

@@ -0,0 +1,31 @@
+package me.lethunderhawk.world.entity.player;
+
+import com.badlogic.gdx.graphics.Color;
+
+import java.util.Random;
+
+public class PlayerAvatar {
+    private static final int GRID_SIZE = 16;
+    private final Color[][] grid;
+    private transient final Random random = new Random();
+
+    public PlayerAvatar() {
+        grid = new Color[GRID_SIZE][GRID_SIZE];
+    }
+
+    public void initializeGrid() {
+        for (int x = 0; x < GRID_SIZE; x++) {
+            for (int y = 0; y < GRID_SIZE; y++) {
+                // Randomly assign gray or white
+                grid[x][y] = random.nextBoolean() ? Color.GRAY : Color.WHITE;
+            }
+        }
+    }
+
+    public Color getColor(int x, int y) {
+        if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) {
+            return grid[x][y];
+        }
+        return Color.WHITE;
+    }
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott