Pārlūkot izejas kodu

World loading from server and out of bounds checks implemented (serverside)

jan 1 mēnesi atpakaļ
vecāks
revīzija
97615f3a8e

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

@@ -1,28 +1,63 @@
 package me.lethunderhawk.camera;
 
+import com.badlogic.gdx.math.MathUtils;
+import com.badlogic.gdx.math.Rectangle;
 import com.badlogic.gdx.math.Vector2;
 import com.badlogic.gdx.graphics.OrthographicCamera;
 import me.lethunderhawk.world.entity.player.Player;
+import me.lethunderhawk.world.entity.player.PlayerAvatar;
 
 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
+    private final float followSpeed = 15f;      // Higher = faster follow
+    private final float deadZonePx = 100f;      // Pixels (screen space)
+
+    // Exponential smoothing helper: produces a time-consistent lerp factor
+    private float expSmoothing(float delta, float speed) {
+        return 1f - (float)Math.exp(-speed * delta);
+    }
 
     public void update(float delta, OrthographicCamera camera, Player player) {
-        targetPosition.set(player.x, player.y);
+        if (player == null) return;
+
+        // 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;
+
+        // Convert dead zone from pixels to world units (respect camera zoom)
+        float deadZoneWorldX = deadZonePx * camera.zoom;
+        float deadZoneWorldY = deadZonePx * camera.zoom * (camera.viewportHeight / camera.viewportWidth); // optional adjust for aspect, or use same value for both
+        //System.out.println("Normal Dead Zone: " + deadZoneWorldX + ", " + deadZoneWorldY);
+
+        // Use player center as the target position so the dead zone is effectively shifted by player size
+        targetPosition.set(playerCenterX, playerCenterY);
 
         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) {
+        // If player center inside dead zone, do nothing
+        if (Math.abs(dx) <= deadZoneWorldX && Math.abs(dy) <= deadZoneWorldY) {
+            camera.update();
             return;
         }
 
-        // Smoothly interpolate camera position
-        camera.position.x += dx * followSpeed * delta;
-        camera.position.y += dy * followSpeed * delta;
+        // Compute desired camera position so player center sits on the dead zone edge (prevents instant snapping)
+        float desiredX = camera.position.x;
+        float desiredY = camera.position.y;
+
+        if (Math.abs(dx) > deadZoneWorldX) {
+            desiredX = targetPosition.x - Math.signum(dx) * deadZoneWorldX;
+        }
+        if (Math.abs(dy) > deadZoneWorldY) {
+            desiredY = targetPosition.y - Math.signum(dy) * deadZoneWorldY;
+        }
+
+        // Smoothly move camera toward desired position using exponential smoothing (frame-rate independent)
+        float t = expSmoothing(delta, followSpeed);
+        camera.position.x = MathUtils.lerp(camera.position.x, desiredX, t);
+        camera.position.y = MathUtils.lerp(camera.position.y, desiredY, t);
+
         camera.update();
     }
 }

+ 29 - 2
core/src/main/java/me/lethunderhawk/network/ClientPacketListener.java

@@ -12,26 +12,53 @@ import java.util.concurrent.ConcurrentHashMap;
 
 public class ClientPacketListener implements PacketListener {
     private final Map<UUID, Player> otherPlayers = new ConcurrentHashMap<>();
+    private final GameClient gameClient;
     private World world;
 
+    public ClientPacketListener(GameClient gameClient) {
+        this.gameClient = gameClient;
+    }
+
+    /**
+     * Fired whenever a changing world packet is sent by the server (e.g. on log in)
+     * @param packet The Packet sent by the server
+     */
     @PacketHandler
     private void handleWorldData(WorldDataPacket packet) {
         this.world = packet.world;
     }
 
+    /**
+     * 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);
     }
 
+
+    /**
+     * Fired whenever a player leaves the server
+     * @param packet The Packet sent by the server
+     */
     @PacketHandler
     private void playerLogout(PlayerLogoutPacket packet) {
         otherPlayers.remove(packet.uuid);
     }
 
+    /**
+     * Fired whenever a player changes position or position gets updated by the server
+     * @param packet The Packet sent by the server
+     */
     @PacketHandler
     private void handlePlayerPosition(PlayerPositionPacket packet) {
+        if(packet.uuid.equals(gameClient.getPlayer().uuid)){
+            gameClient.getPlayer().x = packet.x;
+            gameClient.getPlayer().y = packet.y;
+            return;
+        }
         Player player = otherPlayers.computeIfAbsent(packet.uuid, uuid -> {
             Player p = new Player();
             p.uuid = uuid;
@@ -41,7 +68,7 @@ public class ClientPacketListener implements PacketListener {
         player.y = packet.y;
     }
 
-    @PacketHandler
+    /*@PacketHandler
     private void handlePlayerMove(PlayerMovePacket packet) {
         Player player = otherPlayers.computeIfAbsent(packet.uuid, uuid -> {
             Player p = new Player();
@@ -50,7 +77,7 @@ public class ClientPacketListener implements PacketListener {
         });
         player.x = packet.x;
         player.y = packet.y;
-    }
+    }*/
 
     public Map<UUID, Player> getOtherPlayers() {
         return otherPlayers;

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

@@ -20,10 +20,10 @@ public class GameClient extends Listener implements EventListener {
 
     public GameClient() {
         this.player = new Player();
-        client = new Client();
+        client = new Client(65536, 65536);
 
         client.start();
-        this.clientPacketListener = new ClientPacketListener();
+        this.clientPacketListener = new ClientPacketListener(this);
         PacketDispatcher.registerListener(clientPacketListener);
 
         registerPackets();

+ 38 - 27
core/src/main/java/me/lethunderhawk/screen/GameScreen.java

@@ -13,6 +13,7 @@ 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.TiledMapTile;
 import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
 import com.badlogic.gdx.maps.tiled.TmxMapLoader;
 import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
@@ -45,6 +46,7 @@ public class GameScreen implements Screen {
     private TiledMap tiledMap;
     private OrthogonalTiledMapRenderer tiledMapRenderer;
     private Texture tilesetTexture;
+    private TiledMapTileSet runtimeTileset; // will hold the runtime tileset you added
 
     public GameScreen(Main game, String ip) {
         this.mainGame = game;
@@ -79,22 +81,25 @@ public class GameScreen implements Screen {
                 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
+        tileset.setName("runtime_grass");
         map.getTileSets().addTileSet(tileset);
 
+        this.tiledMap = map;
+        this.runtimeTileset = map.getTileSets().getTileSet(tileset.getName() /* tileset.getName() may be null */);
+
+        // fallback: if getTileSet by name fails, use first non-empty tileset:
+        if (this.runtimeTileset == null) {
+            for (TiledMapTileSet ts : map.getTileSets()) {
+                if (ts.size() > 0) { this.runtimeTileset = ts; break; }
+            }
+        }
+
         // --- 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);
@@ -141,49 +146,55 @@ public class GameScreen implements Screen {
         shapeRenderer.setProjectionMatrix(camera.combined);
         shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
 
-        if (client.getPlayer() != null) {
-            renderPlayerGrid(client.getPlayer());
-        }
-
         for (Map.Entry<UUID, Player> entry : client.getOtherPlayers().entrySet()) {
             renderPlayerGrid(entry.getValue());
         }
+        if (client.getPlayer() != null) {
+            renderPlayerGrid(client.getPlayer());
+        }
 
         shapeRenderer.end();
     }
 
     private void renderWorld() {
         World world = client.getWorld();
-        if (world == null) return;
-        float scale = world.getBlockScale() * 32f; // Assuming 32px tiles
+        if (world == null || runtimeTileset == null) return;
+        float scale = world.getBlockScale(); // world units per tile
 
         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;
 
-                Texture tex = getTextureForBlock(blockId);
-                if (tex != null) {
-                    spriteBatch.draw(tex, x * scale, y * scale, scale, scale);
+                TextureRegion region = getRegionForBlock(blockId);
+                if (region != null) {
+                    spriteBatch.draw(region, x * scale, y * scale, scale, scale);
                 }
             }
         }
     }
 
-    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;
-        };
+    private TextureRegion getRegionForBlock(int blockId) {
+        // If your block IDs match the runtime tile IDs you assigned when creating the tileset:
+        TiledMapTile tile = runtimeTileset.getTile(blockId);
+        if (tile == null) return null;
+
+        // Most runtime tiles are StaticTiledMapTile; get the region:
+        if (tile.getTextureRegion() != null) {
+            return tile.getTextureRegion();
+        }
+
+        // fallback for tiled tiles that wrap an atlas region:
+        if (tile instanceof StaticTiledMapTile) {
+            return ((StaticTiledMapTile) tile).getTextureRegion();
+        }
+
+        return null;
     }
 
     private void renderPlayerGrid(Player player) {
         if (player.avatar == null) return;
-        float cellSize = 50f / 16f;
+        float cellSize = 2f;
         for (int x = 0; x < 16; x++) {
             for (int y = 0; y < 16; y++) {
                 Color color = player.avatar.getColor(x, y);

+ 8 - 8
server/src/main/java/me/lethunderhawk/server/GameServer.java

@@ -18,7 +18,7 @@ public class GameServer {
 
     public GameServer() throws IOException {
 
-        server = new Server();
+        server = new Server(65536, 65536);
 
         registerBlocks();
 
@@ -46,9 +46,9 @@ public class GameServer {
     }
 
     private World generateNewMap() {
-        int width = 500;
-        int height = 500;
-        World world = new World(width, height, 1.0f);
+        int width = 50;
+        int height = 50;
+        World world = new World(width, height, 32.0f);
 
         java.util.Random random = new java.util.Random();
         for (int x = 0; x < width; x++) {
@@ -58,13 +58,13 @@ public class GameServer {
                 if (noise < 0.6f) {
                     world.setBlock(x, y, 1); // Grass
                 } else if (noise < 0.7f) {
-                    world.setBlock(x, y, 2); // Flowering Grass
+                    world.setBlock(x, y, 61); // Flowering Grass
                 } else if (noise < 0.8f) {
-                    world.setBlock(x, y, 3); // Stone Path
+                    world.setBlock(x, y, 133); // Stone Path
                 } else if (noise < 0.9f) {
-                    world.setBlock(x, y, 4); // Broken Stone Path
+                    world.setBlock(x, y, 158); // Broken Stone Path
                 } else {
-                    world.setBlock(x, y, 10); // Solid Block (Wall)
+                    world.setBlock(x, y, 249); // Solid Block (Wall)
                 }
             }
         }

+ 13 - 3
server/src/main/java/me/lethunderhawk/server/GameServerPacketListener.java

@@ -25,8 +25,18 @@ public class GameServerPacketListener implements PacketListener {
 
     @PacketHandler
     public void onPlayerPosition(PlayerPositionPacket packet, Connection connection) {
-        server.sendToAllExceptUDP(connection.getID(), packet);
-        Player p = players.get(connection.getID());
+        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);
+            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;
     }
@@ -46,7 +56,7 @@ public class GameServerPacketListener implements PacketListener {
 
         // Send the world data to the player upon login
         WorldDataPacket worldPacket = new WorldDataPacket(gameServer.getWorld());
-        server.sendToUDP(connection.getID(), worldPacket);
+        server.sendToTCP(connection.getID(), worldPacket);
 
         for(Player player : players.values()){
             if(player.uuid.equals(packet.player.uuid)) return;

+ 1 - 0
shared/build.gradle

@@ -4,4 +4,5 @@ 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"
+  api "com.badlogicgames.gdx:gdx:1.11.0"
 }

+ 2 - 0
shared/src/main/java/me/lethunderhawk/network/packet/PacketRegistry.java

@@ -25,6 +25,8 @@ public class PacketRegistry {
         kryo.register(PlayerPositionPacket.class);
         kryo.register(PlayerAvatar.class);
         kryo.register(Rectangle.class);
+        kryo.register(int[].class);
+        kryo.register(int[][].class);
         kryo.register(World.class);
         kryo.register(WorldDataPacket.class);
 

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

@@ -6,6 +6,5 @@ import java.util.UUID;
 
 public class PlayerMovePacket implements Packet {
     public UUID uuid;
-    public float x;
-    public float y;
+    public float fromX, fromY, toX, toY;
 }

+ 16 - 1
shared/src/main/java/me/lethunderhawk/world/World.java

@@ -6,6 +6,7 @@ public class World {
     private final int[][] blocks;
     private final float blockScale;
 
+
     public World(int width, int height, float blockScale) {
         this.width = width;
         this.height = height;
@@ -13,13 +14,27 @@ public class World {
         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
+                blocks[x][y] = 0;
+            }
+        }
+    }
+
+    public World(){
+        this.width = 20;
+        this.height = 20;
+        this.blockScale = 1.0f;
+        this.blocks = new int[width][height];
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                blocks[x][y] = 0;
             }
         }
     }
 
     public int getWidth() { return width; }
+    public float getRealWidth(){ return width * blockScale; }
     public int getHeight() { return height; }
+    public float getRealHeight(){ return height * blockScale; }
     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; }