1
0
forked from jzitnik/twodcraft

feat: Implemented saving of world

This commit is contained in:
Jakub Žitník 2025-03-08 17:36:59 +01:00
parent 90408a723f
commit 32fba49587
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
18 changed files with 233 additions and 33 deletions

@ -128,6 +128,12 @@
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.15.0</version> <!-- Use the latest version -->
</dependency>
</dependencies>
</project>

@ -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 };

@ -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<Block>[][] world = (List<Block>[][]) 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);

@ -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);
}
}
}

@ -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<Enum> spriteState = Optional.empty();
private MyOptional<Enum> spriteState = MyOptional.empty();
private boolean ghost = false;
private boolean isMineable = true;
private int hardness = 1;
private Optional<ItemType> tool = Optional.empty();
private MyOptional<ItemType> tool = MyOptional.empty();
private List<ToolVariant> toolVariants = new ArrayList<>();
private List<Item> 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) {

@ -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();
}

@ -0,0 +1,115 @@
package cz.jzitnik.game.entities;
import java.io.*;
import java.util.NoSuchElementException;
import java.util.function.*;
public class MyOptional<T> 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 <U> MyOptional<U> 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 <U> MyOptional<U> flatMap(Function<? super T, ? extends MyOptional<U>> 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<T> 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 <T> MyOptional<T> empty() {
return new MyOptional<>();
}
// Static factory method for a present value
public static <T> MyOptional<T> of(T value) {
return new MyOptional<>(value);
}
// Static factory method for a present value or null
public static <T> MyOptional<T> ofNullable(T value) {
return value == null ? empty() : new MyOptional<>(value);
}
public boolean isEmpty() {
return !isPresent;
}
}

@ -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;

@ -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> item;

@ -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> toolVariant = Optional.empty();
private MyOptional<ToolVariant> toolVariant = MyOptional.empty();
private SpriteLoader.SPRITES sprite;
private Optional<Enum> spriteState = Optional.empty();
private MyOptional<Enum> 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> block = Optional.empty();
private MyOptional<Block> 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);
}
}

@ -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;

@ -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;
}

@ -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() {

@ -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;

@ -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;

@ -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;

@ -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 -> {
}

@ -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;