diff --git a/pom.xml b/pom.xml
index 0882215..2a9bf26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -128,6 +128,12 @@
slf4j-simple
2.0.17
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+ 2.15.0
+
diff --git a/src/main/java/cz/jzitnik/Main.java b/src/main/java/cz/jzitnik/Main.java
index 1478a16..2aa20ba 100644
--- a/src/main/java/cz/jzitnik/Main.java
+++ b/src/main/java/cz/jzitnik/Main.java
@@ -1,6 +1,6 @@
package cz.jzitnik;
-import cz.jzitnik.game.Game;
+import cz.jzitnik.game.GameSaver;
import cz.jzitnik.game.logic.CustomLogicProvider;
import cz.jzitnik.game.mobs.EntityLogicProvider;
import cz.jzitnik.game.threads.HealthRegenerationThread;
@@ -28,7 +28,7 @@ public class Main {
var spriteList = SpriteLoader.load();
var screenRenderer = new ScreenRenderer(spriteList, terminal);
- var game = new Game();
+ var game = GameSaver.load();
final boolean[] isRunning = { true };
diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java
index 3a2c290..dc3600c 100644
--- a/src/main/java/cz/jzitnik/game/Game.java
+++ b/src/main/java/cz/jzitnik/game/Game.java
@@ -22,12 +22,14 @@ import lombok.Getter;
import lombok.Setter;
import org.jline.terminal.Terminal;
+import java.io.Serial;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@Getter
-public class Game {
+public class Game implements Serializable {
@SuppressWarnings("unchecked")
private final List[][] world = (List[][]) new CopyOnWriteArrayList[256][512];
private final Player player = new Player();
@@ -36,11 +38,17 @@ public class Game {
private Window window = Window.WORLD;
private final Inventory inventory = new Inventory();
- @JsonIgnore
- private final EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
+ private transient EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
- @JsonIgnore
- private final GameStates gameStates = new GameStates(this);
+ private transient GameStates gameStates = new GameStates(this);
+
+ @Serial
+ private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ entitySpawnProvider = new EntitySpawnProvider();
+ gameStates = new GameStates(this);
+ }
public Game() {
Generation.generateWorld(this);
diff --git a/src/main/java/cz/jzitnik/game/GameSaver.java b/src/main/java/cz/jzitnik/game/GameSaver.java
new file mode 100644
index 0000000..80e6945
--- /dev/null
+++ b/src/main/java/cz/jzitnik/game/GameSaver.java
@@ -0,0 +1,41 @@
+package cz.jzitnik.game;
+
+import java.io.*;
+
+public class GameSaver {
+ public void save(Game game) {
+ try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("world.ser"))) {
+ out.writeObject(game);
+ System.out.println("Inner class saved!");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static Game load() {
+ File file = new File("world.ser");
+
+ if (!file.isFile()) {
+ return new Game();
+ }
+
+ try {
+ FileInputStream fileIn = new FileInputStream("world.ser");
+
+ ObjectInputStream in = new ObjectInputStream(fileIn);
+
+ // Read the object from the file
+ Object object = in.readObject();
+
+ Game game = (Game) object;
+
+ in.close();
+ fileIn.close();
+
+ return game;
+ } catch (IOException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+}
diff --git a/src/main/java/cz/jzitnik/game/entities/Block.java b/src/main/java/cz/jzitnik/game/entities/Block.java
index f90b3b0..a5f9046 100644
--- a/src/main/java/cz/jzitnik/game/entities/Block.java
+++ b/src/main/java/cz/jzitnik/game/entities/Block.java
@@ -8,20 +8,21 @@ import cz.jzitnik.game.ui.Inventory;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Getter
@Setter
-public class Block {
+public class Block implements Serializable {
private String blockId;
private SpriteLoader.SPRITES sprite;
- private Optional spriteState = Optional.empty();
+ private MyOptional spriteState = MyOptional.empty();
private boolean ghost = false;
private boolean isMineable = true;
private int hardness = 1;
- private Optional tool = Optional.empty();
+ private MyOptional tool = MyOptional.empty();
private List toolVariants = new ArrayList<>();
private List- drops = new ArrayList<>();
private Object data = null;
@@ -52,7 +53,7 @@ public class Block {
this.blockId = blockId;
this.sprite = sprite;
this.hardness = hardness;
- this.tool = Optional.of(tool);
+ this.tool = MyOptional.of(tool);
this.toolVariants = toolVariants;
}
@@ -61,13 +62,13 @@ public class Block {
this.blockId = blockId;
this.sprite = sprite;
this.hardness = hardness;
- this.tool = Optional.of(tool);
+ this.tool = MyOptional.of(tool);
this.toolVariants = toolVariants;
this.data = data;
}
public void setSpriteState(Enum spriteState) {
- this.spriteState = Optional.of(spriteState);
+ this.spriteState = MyOptional.of(spriteState);
}
public double calculateHardness(Inventory inventory) {
diff --git a/src/main/java/cz/jzitnik/game/entities/Dependencies.java b/src/main/java/cz/jzitnik/game/entities/Dependencies.java
index 896063c..8839f1a 100644
--- a/src/main/java/cz/jzitnik/game/entities/Dependencies.java
+++ b/src/main/java/cz/jzitnik/game/entities/Dependencies.java
@@ -1,5 +1,6 @@
package cz.jzitnik.game.entities;
+import cz.jzitnik.game.GameSaver;
import cz.jzitnik.game.handlers.pickup.PickupHandlerProvider;
import cz.jzitnik.game.handlers.place.PlaceHandler;
import cz.jzitnik.game.mobs.EntityHurtAnimation;
@@ -10,4 +11,5 @@ public class Dependencies {
public EntityHurtAnimation entityHurtAnimation = new EntityHurtAnimation();
public EntityKill entityKill = new EntityKill();
public PickupHandlerProvider pickupHandlerProvider = new PickupHandlerProvider();
+ public GameSaver gameSaver = new GameSaver();
}
diff --git a/src/main/java/cz/jzitnik/game/entities/MyOptional.java b/src/main/java/cz/jzitnik/game/entities/MyOptional.java
new file mode 100644
index 0000000..3f74c8b
--- /dev/null
+++ b/src/main/java/cz/jzitnik/game/entities/MyOptional.java
@@ -0,0 +1,115 @@
+package cz.jzitnik.game.entities;
+
+import java.io.*;
+import java.util.NoSuchElementException;
+import java.util.function.*;
+
+public class MyOptional implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private T value;
+ private boolean isPresent;
+
+ // Constructor
+ public MyOptional() {
+ this.isPresent = false;
+ }
+
+ public MyOptional(T value) {
+ this.value = value;
+ this.isPresent = true;
+ }
+
+ // Check if value is present
+ public boolean isPresent() {
+ return isPresent;
+ }
+
+ // Get the value (or throw exception if not present)
+ public T get() {
+ if (!isPresent) {
+ throw new NoSuchElementException("No value present");
+ }
+ return value;
+ }
+
+ // Get the value or return default
+ public T orElse(T other) {
+ return isPresent ? value : other;
+ }
+
+ // If value is present, apply function, else return another Optional
+ public MyOptional map(Function super T, ? extends U> mapper) {
+ if (!isPresent) {
+ return new MyOptional<>();
+ }
+ return new MyOptional<>(mapper.apply(value));
+ }
+
+ // If value is present, apply function and return a new Optional
+ public MyOptional flatMap(Function super T, ? extends MyOptional> mapper) {
+ if (!isPresent) {
+ return new MyOptional<>();
+ }
+ return mapper.apply(value);
+ }
+
+ // Perform an action if value is present
+ public void ifPresent(Consumer super T> action) {
+ if (isPresent) {
+ action.accept(value);
+ }
+ }
+
+ // Return the value inside the Optional or default if not present
+ public T orElseGet(Supplier extends T> other) {
+ return isPresent ? value : other.get();
+ }
+
+ // Filter the value based on a condition
+ public MyOptional filter(Predicate super T> predicate) {
+ if (!isPresent || !predicate.test(value)) {
+ return new MyOptional<>();
+ }
+ return this;
+ }
+
+ // Serialize the value
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.writeBoolean(isPresent);
+ if (isPresent) {
+ out.writeObject(value);
+ }
+ }
+
+ // Deserialize the value
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ isPresent = in.readBoolean();
+ if (isPresent) {
+ value = (T) in.readObject();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return isPresent ? "SerializableOptional[" + value + "]" : "SerializableOptional.empty";
+ }
+
+ // Static factory method for an empty SerializableOptional
+ public static MyOptional empty() {
+ return new MyOptional<>();
+ }
+
+ // Static factory method for a present value
+ public static MyOptional of(T value) {
+ return new MyOptional<>(value);
+ }
+
+ // Static factory method for a present value or null
+ public static MyOptional ofNullable(T value) {
+ return value == null ? empty() : new MyOptional<>(value);
+ }
+
+ public boolean isEmpty() {
+ return !isPresent;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cz/jzitnik/game/entities/Player.java b/src/main/java/cz/jzitnik/game/entities/Player.java
index 5a61710..c24d5b9 100644
--- a/src/main/java/cz/jzitnik/game/entities/Player.java
+++ b/src/main/java/cz/jzitnik/game/entities/Player.java
@@ -3,9 +3,11 @@ package cz.jzitnik.game.entities;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
@Getter
@Setter
-public class Player {
+public class Player implements Serializable {
private int health = 10;
private int hunger = 10;
private int fallDistance = 0;
diff --git a/src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java b/src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java
index 44d59fa..7198ecc 100644
--- a/src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java
+++ b/src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java
@@ -3,12 +3,14 @@ package cz.jzitnik.game.entities.items;
import lombok.AllArgsConstructor;
import lombok.Getter;
+import java.io.Serializable;
+import java.lang.foreign.SegmentAllocator;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
-public class InventoryItem {
+public class InventoryItem implements Serializable {
private int amount;
private final List
- item;
diff --git a/src/main/java/cz/jzitnik/game/entities/items/Item.java b/src/main/java/cz/jzitnik/game/entities/items/Item.java
index 514067c..d8c9ec9 100644
--- a/src/main/java/cz/jzitnik/game/entities/items/Item.java
+++ b/src/main/java/cz/jzitnik/game/entities/items/Item.java
@@ -2,29 +2,31 @@ package cz.jzitnik.game.entities.items;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.SpriteLoader;
+import cz.jzitnik.game.entities.MyOptional;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
import java.util.Optional;
@Getter
@Setter
@AllArgsConstructor
-public class Item {
+public class Item implements Serializable {
private String id;
private String name;
private ItemType type;
- private Optional toolVariant = Optional.empty();
+ private MyOptional toolVariant = MyOptional.empty();
private SpriteLoader.SPRITES sprite;
- private Optional spriteState = Optional.empty();
+ private MyOptional spriteState = MyOptional.empty();
private boolean stackable = true;
private int durability;
private double miningDecrease = 0;
private int stackAmount = 64;
private int addHunger = 0;
private int dealDamage = 1;
- private Optional block = Optional.empty();
+ private MyOptional block = MyOptional.empty();
public Item(String id, String name, ItemType type, SpriteLoader.SPRITES sprite, ToolVariant toolVariant,
int durability, boolean stackable, int dealDamage) {
@@ -32,7 +34,7 @@ public class Item {
this.name = name;
this.type = type;
this.sprite = sprite;
- this.toolVariant = Optional.of(toolVariant);
+ this.toolVariant = MyOptional.of(toolVariant);
this.durability = durability;
this.stackable = stackable;
this.dealDamage = dealDamage;
@@ -44,7 +46,7 @@ public class Item {
this.name = name;
this.type = type;
this.sprite = sprite;
- this.toolVariant = Optional.of(toolVariant);
+ this.toolVariant = MyOptional.of(toolVariant);
this.miningDecrease = miningDecrease;
this.durability = durability;
this.stackable = stackable;
@@ -55,7 +57,7 @@ public class Item {
this.name = name;
this.type = type;
this.sprite = sprite;
- this.block = Optional.of(block);
+ this.block = MyOptional.of(block);
}
public Item(String id, String name, ItemType type, SpriteLoader.SPRITES sprite) {
@@ -89,6 +91,6 @@ public class Item {
}
public void setSpriteState(Enum spriteState) {
- this.spriteState = Optional.of(spriteState);
+ this.spriteState = MyOptional.of(spriteState);
}
}
diff --git a/src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java b/src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java
index 54214b7..b20fbe7 100644
--- a/src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java
+++ b/src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java
@@ -4,6 +4,7 @@ import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.EntityRegistry;
import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.Block;
+import cz.jzitnik.game.entities.MyOptional;
import org.reflections.Reflections;
import java.lang.reflect.Constructor;
@@ -107,7 +108,7 @@ public class ItemBlockSupplier {
try {
Item item = registeredItems.get(key).newInstance();
if (registeredBlocks.containsKey(blockList.get(key))) {
- item.setBlock(Optional.of(getBlock(blockList.get(key), item)));
+ item.setBlock(MyOptional.of(getBlock(blockList.get(key), item)));
}
return item;
@@ -121,9 +122,9 @@ public class ItemBlockSupplier {
Item item = registeredItems.get(key).newInstance();
if (blockList.get(key).equals(block.getBlockId())) {
- item.setBlock(Optional.of(block));
+ item.setBlock(MyOptional.of(block));
} else {
- item.setBlock(Optional.of(getBlock(blockList.get(key), item)));
+ item.setBlock(MyOptional.of(getBlock(blockList.get(key), item)));
}
return item;
diff --git a/src/main/java/cz/jzitnik/game/logic/services/flowing/FlowingData.java b/src/main/java/cz/jzitnik/game/logic/services/flowing/FlowingData.java
index 4056f4a..ef5af50 100644
--- a/src/main/java/cz/jzitnik/game/logic/services/flowing/FlowingData.java
+++ b/src/main/java/cz/jzitnik/game/logic/services/flowing/FlowingData.java
@@ -3,8 +3,10 @@ package cz.jzitnik.game.logic.services.flowing;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
@Getter
@Setter
-public class FlowingData {
+public class FlowingData implements Serializable {
protected boolean isSource = true;
}
diff --git a/src/main/java/cz/jzitnik/game/logic/services/saplings/SaplingData.java b/src/main/java/cz/jzitnik/game/logic/services/saplings/SaplingData.java
index 31ab442..871ad51 100644
--- a/src/main/java/cz/jzitnik/game/logic/services/saplings/SaplingData.java
+++ b/src/main/java/cz/jzitnik/game/logic/services/saplings/SaplingData.java
@@ -3,11 +3,12 @@ package cz.jzitnik.game.logic.services.saplings;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
import java.util.Random;
@Getter
@Setter
-public class SaplingData {
+public class SaplingData implements Serializable {
private int growWait;
public SaplingData() {
diff --git a/src/main/java/cz/jzitnik/game/mobs/services/cow/CowData.java b/src/main/java/cz/jzitnik/game/mobs/services/cow/CowData.java
index 37db14f..4a41d8b 100644
--- a/src/main/java/cz/jzitnik/game/mobs/services/cow/CowData.java
+++ b/src/main/java/cz/jzitnik/game/mobs/services/cow/CowData.java
@@ -3,9 +3,11 @@ package cz.jzitnik.game.mobs.services.cow;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
@Getter
@Setter
-public class CowData {
+public class CowData implements Serializable {
private int lastDirection = 1; // 1 = right, -1 = left
private int movementCooldown = 0;
private int jumpAttempts = 0;
diff --git a/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java
index 8f95ab7..e611fa6 100644
--- a/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java
+++ b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java
@@ -3,9 +3,11 @@ package cz.jzitnik.game.mobs.services.pig;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
@Getter
@Setter
-public class PigData {
+public class PigData implements Serializable {
private int lastDirection = 1; // 1 = right, -1 = left
private int movementCooldown = 0;
private int jumpAttempts = 0;
diff --git a/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepData.java b/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepData.java
index ab3e4bf..4caa046 100644
--- a/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepData.java
+++ b/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepData.java
@@ -3,9 +3,11 @@ package cz.jzitnik.game.mobs.services.sheep;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
@Getter
@Setter
-public class SheepData {
+public class SheepData implements Serializable {
private int lastDirection = 1; // 1 = right, -1 = left
private int movementCooldown = 0;
private int jumpAttempts = 0;
diff --git a/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java b/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java
index d757012..6647246 100644
--- a/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java
+++ b/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java
@@ -84,6 +84,8 @@ public class InputHandlerThread extends Thread {
case 'q' -> {
System.out.println("Exiting game...");
isRunning[0] = false;
+ game.getGameStates().dependencies.gameSaver.save(game);
+ System.exit(0);
}
default -> {
}
diff --git a/src/main/java/cz/jzitnik/game/ui/Inventory.java b/src/main/java/cz/jzitnik/game/ui/Inventory.java
index 7679c43..f59a599 100644
--- a/src/main/java/cz/jzitnik/game/ui/Inventory.java
+++ b/src/main/java/cz/jzitnik/game/ui/Inventory.java
@@ -9,19 +9,28 @@ import lombok.Getter;
import lombok.Setter;
import org.jline.terminal.Terminal;
+import java.io.Serial;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Getter
-public class Inventory {
+public class Inventory implements Serializable {
public static final int INVENTORY_SIZE_PX = 470;
public static final int COLUMN_AMOUNT = 5;
public static final int ROW_AMOUNT = 4;
private final InventoryItem[] items = new InventoryItem[20];
private final InventoryItem[] hotbar = new InventoryItem[9];
- private final SmallCraftingTable smallCraftingTable = new SmallCraftingTable(this);
+ private transient SmallCraftingTable smallCraftingTable = new SmallCraftingTable(this);
+
+ @Serial
+ private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ smallCraftingTable = new SmallCraftingTable(this);
+ }
@Setter
private int itemInhHandIndex = 0;