From 09245b67cdf3b75519885a2c25f0baae8ed0ca2b Mon Sep 17 00:00:00 2001
From: jez04 <david.jezek@post.cz>
Date: Tue, 15 Apr 2025 23:39:37 +0200
Subject: [PATCH] feat: :tada: solution

---
 src/main/java/lab/game/Level.java         | 93 ++++++++++++++++++++---
 src/main/java/lab/game/Monster.java       |  6 +-
 src/main/java/lab/game/MyDimension.java   |  6 +-
 src/main/java/lab/game/MyPoint.java       |  8 +-
 src/main/java/lab/game/NicerObstacle.java | 11 ++-
 src/main/java/lab/game/Obstacle.java      |  6 +-
 src/main/java/lab/game/Player.java        |  4 +
 src/main/java/lab/game/WorldEntity.java   | 12 ++-
 8 files changed, 124 insertions(+), 22 deletions(-)

diff --git a/src/main/java/lab/game/Level.java b/src/main/java/lab/game/Level.java
index 234c52e..d2c640c 100644
--- a/src/main/java/lab/game/Level.java
+++ b/src/main/java/lab/game/Level.java
@@ -1,5 +1,12 @@
 package lab.game;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -14,6 +21,8 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class Level {
 
+	private Object entitiesLock = new Object();
+
 	private boolean viewMode;
 
 	private double width;
@@ -51,11 +60,67 @@ public class Level {
 	}
 
 	public void startServer() {
-		// TODO:
+		new Thread(() -> {
+			try (ServerSocket listenSocket = new ServerSocket(4600)) {
+				while (!Thread.currentThread().isInterrupted()) {
+					try {
+						Socket client = listenSocket.accept();
+						new Thread(() -> handleViewer(client), "Viewer handler").start();
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+
+		}).start();
+	}
+
+	public void handleViewer(Socket client) {
+		try (OutputStream outputStream = client.getOutputStream();
+				ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
+			while (!Thread.currentThread().isInterrupted()) {
+				synchronized (entitiesLock) {
+					objectOutputStream.reset();
+					synchronized (entitiesLock) {
+						objectOutputStream.writeObject(entities);
+					}
+					objectOutputStream.flush();
+				}
+				sleepForWhile();
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void sleepForWhile() {
+		try {
+			Thread.sleep(20);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
 	}
 
 	public void connectToServer() {
-		// TODO:
+		new Thread(() -> {
+			try (Socket socketToServer = new Socket("localhost", 4600);
+					InputStream inputStream = socketToServer.getInputStream();
+					ObjectInputStream objectInputStream = new ObjectInputStream(inputStream)) {
+				while (!Thread.currentThread().isInterrupted()) {
+					Object o = objectInputStream.readObject();
+					if (o instanceof List l) {
+						synchronized (entitiesLock) {
+							entities = l;
+						}
+					}
+				}
+			} catch (IOException | ClassNotFoundException e) {
+				e.printStackTrace();
+			}
+
+		}).start();
 	}
 
 	record MonsterDeadLog(LocalDateTime time, MyPoint position) {
@@ -70,8 +135,10 @@ public class Level {
 	public void draw(GraphicsContext gc) {
 		gc.setFill(Color.WHITE);
 		gc.clearRect(0, 0, width, height);
-		for (DrawableSimulable entity : entities) {
-			entity.draw(gc);
+		synchronized (entitiesLock) {
+			for (DrawableSimulable entity : entities) {
+				entity.draw(gc);
+			}
 		}
 	}
 
@@ -79,15 +146,17 @@ public class Level {
 		if (viewMode) {
 			return;
 		}
-		for (DrawableSimulable entity : entities) {
-			entity.simulate(delay);
+		synchronized (entitiesLock) {
+			for (DrawableSimulable entity : entities) {
+				entity.simulate(delay);
+			}
+			detectCollisions();
+			entities.removeAll(entitiesToRemove);
+			entities.addAll(entitiesToAdd);
+			entitiesToAdd.clear();
+			entitiesToRemove.clear();
+			entities.sort(Comparator.comparing(DrawableSimulable::getZIndex));
 		}
-		detectCollisions();
-		entities.removeAll(entitiesToRemove);
-		entities.addAll(entitiesToAdd);
-		entitiesToAdd.clear();
-		entitiesToRemove.clear();
-		entities.sort(Comparator.comparing(DrawableSimulable::getZIndex));
 	}
 
 	private void detectCollisions() {
diff --git a/src/main/java/lab/game/Monster.java b/src/main/java/lab/game/Monster.java
index d9c2788..2877a56 100644
--- a/src/main/java/lab/game/Monster.java
+++ b/src/main/java/lab/game/Monster.java
@@ -1,5 +1,6 @@
 package lab.game;
 
+import java.io.Serial;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
@@ -14,13 +15,16 @@ import lab.Config;
 
 public class Monster extends WorldEntity implements Collisionable {
 
+	@Serial
+	private static final long serialVersionUID = 4227585479714088964L;
+
 	private static Logger log = LogManager.getLogger(Monster.class);
 
 	private static final Random RANDOM = new Random();
 
 	private static Image image;
 	private MyPoint speed;
-	private List<DeadListener> deadListeners = new ArrayList<>();
+	private transient List<DeadListener> deadListeners = new ArrayList<>();
 
 	public Monster(Level level) {
 		super(level, new MyPoint(0, 0), 100);
diff --git a/src/main/java/lab/game/MyDimension.java b/src/main/java/lab/game/MyDimension.java
index d76dfb2..20cb459 100644
--- a/src/main/java/lab/game/MyDimension.java
+++ b/src/main/java/lab/game/MyDimension.java
@@ -1,5 +1,6 @@
 package lab.game;
 
+import java.io.Serial;
 import java.io.Serializable;
 
 import lombok.AllArgsConstructor;
@@ -7,7 +8,10 @@ import lombok.Getter;
 
 @AllArgsConstructor
 @Getter
-public class MyDimension {
+public class MyDimension implements Serializable {
+
+	@Serial
+	private static final long serialVersionUID = -4053578370437363200L;
 
 	private double width;
 
diff --git a/src/main/java/lab/game/MyPoint.java b/src/main/java/lab/game/MyPoint.java
index 721a676..265ed63 100644
--- a/src/main/java/lab/game/MyPoint.java
+++ b/src/main/java/lab/game/MyPoint.java
@@ -1,6 +1,12 @@
 package lab.game;
 
-public class MyPoint {
+import java.io.Serial;
+import java.io.Serializable;
+
+public class MyPoint implements Serializable {
+
+	@Serial
+	private static final long serialVersionUID = 5131948780897974323L;
 
 	public double x;
 	public double y;
diff --git a/src/main/java/lab/game/NicerObstacle.java b/src/main/java/lab/game/NicerObstacle.java
index 140412c..dc8138a 100644
--- a/src/main/java/lab/game/NicerObstacle.java
+++ b/src/main/java/lab/game/NicerObstacle.java
@@ -1,10 +1,15 @@
 package lab.game;
 
+import java.io.Serial;
+
 import javafx.scene.canvas.GraphicsContext;
 import javafx.scene.image.Image;
 
 public class NicerObstacle extends WorldEntity {
 
+	@Serial
+	private static final long serialVersionUID = -6623353377339746275L;
+	
 	private static Image image;
 
 	public NicerObstacle(Level level, MyPoint position) {
@@ -12,18 +17,18 @@ public class NicerObstacle extends WorldEntity {
 	}
 
 	private static Image getImage() {
-		if (image == null) {
+		if(image == null){
 			image = new Image(NicerObstacle.class.getResourceAsStream("spike.gif"));
 		}
 		return image;
 	}
-
+	
 	public void drawInternal(GraphicsContext gc) {
 		gc.drawImage(getImage(), position.getX(), position.getY());
 	}
 
 	public void simulate(double delay) {
-		/* nothing to do */
+		/*nothing to do*/
 	}
 
 }
diff --git a/src/main/java/lab/game/Obstacle.java b/src/main/java/lab/game/Obstacle.java
index 4bc5a98..285fdb7 100644
--- a/src/main/java/lab/game/Obstacle.java
+++ b/src/main/java/lab/game/Obstacle.java
@@ -1,11 +1,15 @@
 package lab.game;
 
+import java.io.Serial;
+
 import javafx.scene.canvas.GraphicsContext;
 import javafx.scene.paint.Color;
 import lab.Config;
 
 public class Obstacle extends WorldEntity {
 
+	@Serial
+	private static final long serialVersionUID = 3430702588557969248L;
 	private MyDimension size;
 
 	public Obstacle(Level level) {
@@ -27,7 +31,7 @@ public class Obstacle extends WorldEntity {
 	}
 
 	public void simulate(double delay) {
-		/* nothing to do */
+		/*nothing to do*/
 	}
 
 }
diff --git a/src/main/java/lab/game/Player.java b/src/main/java/lab/game/Player.java
index 1c84de4..8ad0eda 100644
--- a/src/main/java/lab/game/Player.java
+++ b/src/main/java/lab/game/Player.java
@@ -1,5 +1,6 @@
 package lab.game;
 
+import java.io.Serial;
 import java.util.Random;
 
 import javafx.geometry.Rectangle2D;
@@ -12,6 +13,9 @@ import lab.Config;
 
 public class Player extends WorldEntity implements Collisionable {
 
+	@Serial
+	private static final long serialVersionUID = -4487836387706082936L;
+
 	private static final Random RANDOM = new Random();
 
 	private MyPoint speed;
diff --git a/src/main/java/lab/game/WorldEntity.java b/src/main/java/lab/game/WorldEntity.java
index 965ef45..067e052 100644
--- a/src/main/java/lab/game/WorldEntity.java
+++ b/src/main/java/lab/game/WorldEntity.java
@@ -1,10 +1,16 @@
 package lab.game;
 
+import java.io.Serial;
+import java.io.Serializable;
+
 import javafx.scene.canvas.GraphicsContext;
 
-public abstract class WorldEntity implements DrawableSimulable {
+public abstract  class WorldEntity implements DrawableSimulable, Serializable {
 
-	protected final Level level;
+	@Serial
+	private static final long serialVersionUID = 2626363250832813659L;
+	
+	protected transient final Level level;
 	protected MyPoint position;
 	private int zIndex;
 
@@ -36,5 +42,5 @@ public abstract class WorldEntity implements DrawableSimulable {
 	public int getZIndex() {
 		return zIndex;
 	}
-
+	
 }
-- 
GitLab