forked from jzitnik/twodcraft
refactor(transient): Rewritten transient handling
Rewritten transient handling using @AutoTransient annotation. Now we don't have to override readObject method to automatically initilize transient properties in serilizable classes.
This commit is contained in:
parent
c558b756de
commit
ae5d34b41a
@ -10,9 +10,13 @@ import cz.jzitnik.game.handlers.place.CustomPlaceHandler;
|
||||
import cz.jzitnik.game.mobs.EntitySpawnProvider;
|
||||
import cz.jzitnik.game.sprites.Breaking;
|
||||
import cz.jzitnik.game.sprites.Steve;
|
||||
import cz.jzitnik.game.annotations.AutoTransient;
|
||||
import cz.jzitnik.game.annotations.BreaksByPlace;
|
||||
import cz.jzitnik.game.blocks.Chest;
|
||||
import cz.jzitnik.game.blocks.Furnace;
|
||||
import cz.jzitnik.game.core.autotransient.AutoTransientSupport;
|
||||
import cz.jzitnik.game.core.autotransient.initilizers.GameMiningInitializer;
|
||||
import cz.jzitnik.game.core.autotransient.initilizers.GameWindowInitializer;
|
||||
import cz.jzitnik.game.ui.Window;
|
||||
import cz.jzitnik.game.ui.Inventory;
|
||||
import cz.jzitnik.game.handlers.rightclick.RightClickHandlerProvider;
|
||||
@ -22,35 +26,26 @@ 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 implements Serializable {
|
||||
public class Game extends AutoTransientSupport {
|
||||
@SuppressWarnings("unchecked")
|
||||
private final List<Block>[][] world = (List<Block>[][]) new CopyOnWriteArrayList[256][512];
|
||||
private final Player player = new Player();
|
||||
@AutoTransient(initializer = GameMiningInitializer.class)
|
||||
private transient boolean mining = false;
|
||||
@Setter
|
||||
private Window window = Window.WORLD;
|
||||
@AutoTransient(initializer = GameWindowInitializer.class)
|
||||
private transient Window window = Window.WORLD;
|
||||
private final Inventory inventory = new Inventory();
|
||||
|
||||
@AutoTransient
|
||||
private transient EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
|
||||
|
||||
@AutoTransient
|
||||
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);
|
||||
mining = false;
|
||||
}
|
||||
|
||||
public Game() {
|
||||
Generation.generateWorld(this);
|
||||
}
|
||||
|
16
src/main/java/cz/jzitnik/game/annotations/AutoTransient.java
Normal file
16
src/main/java/cz/jzitnik/game/annotations/AutoTransient.java
Normal file
@ -0,0 +1,16 @@
|
||||
package cz.jzitnik.game.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import cz.jzitnik.game.core.autotransient.AutoTransientInitializer;
|
||||
import cz.jzitnik.game.core.autotransient.DefaultInitializer;
|
||||
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AutoTransient {
|
||||
Class<? extends AutoTransientInitializer<?>> initializer() default DefaultInitializer.class;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package cz.jzitnik.game.core.autotransient;
|
||||
|
||||
public interface AutoTransientInitializer<T> {
|
||||
T initialize(Object parent);
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package cz.jzitnik.game.core.autotransient;
|
||||
|
||||
import cz.jzitnik.game.annotations.AutoTransient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public abstract class AutoTransientSupport implements Serializable {
|
||||
|
||||
@Serial
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
reinitializeAutoTransients();
|
||||
}
|
||||
|
||||
protected void reinitializeAutoTransients() {
|
||||
for (Field field : this.getClass().getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(AutoTransient.class)) {
|
||||
field.setAccessible(true);
|
||||
AutoTransient annotation = field.getAnnotation(AutoTransient.class);
|
||||
try {
|
||||
Object value;
|
||||
|
||||
// Use initializer if provided
|
||||
Class<? extends AutoTransientInitializer<?>> initializerClass = annotation.initializer();
|
||||
if (!initializerClass.equals(DefaultInitializer.class)) {
|
||||
AutoTransientInitializer<?> initializer = initializerClass.getDeclaredConstructor().newInstance();
|
||||
value = initializer.initialize(this);
|
||||
} else {
|
||||
// Fallback to default instantiation
|
||||
value = instantiateField(field.getType());
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
field.set(this, value);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to reinitialize @AutoTransient field: " + field.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Object instantiateField(Class<?> clazz) {
|
||||
try {
|
||||
// Try constructor with (this) reference first
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor(this.getClass());
|
||||
return constructor.newInstance(this);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Fallback to default constructor
|
||||
try {
|
||||
return clazz.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cz.jzitnik.game.core.autotransient;
|
||||
|
||||
public class DefaultInitializer implements AutoTransientInitializer<Object> {
|
||||
@Override
|
||||
public Object initialize(Object parent) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package cz.jzitnik.game.core.autotransient.initilizers;
|
||||
|
||||
import cz.jzitnik.game.core.autotransient.AutoTransientInitializer;
|
||||
|
||||
public class GameMiningInitializer implements AutoTransientInitializer<Boolean> {
|
||||
@Override
|
||||
public Boolean initialize(Object parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package cz.jzitnik.game.core.autotransient.initilizers;
|
||||
|
||||
import cz.jzitnik.game.core.autotransient.AutoTransientInitializer;
|
||||
import cz.jzitnik.game.ui.Window;
|
||||
|
||||
public class GameWindowInitializer implements AutoTransientInitializer<Window> {
|
||||
@Override
|
||||
public Window initialize(Object parent) {
|
||||
return Window.WORLD;
|
||||
}
|
||||
}
|
@ -91,10 +91,10 @@ public class InputHandlerThread extends Thread {
|
||||
game.setWindow(Window.ESC);
|
||||
}
|
||||
screenRenderer.render(game);
|
||||
// System.out.println("Exiting game...");
|
||||
// isRunning[0] = false;
|
||||
// game.getGameStates().dependencies.gameSaver.save(game);
|
||||
// System.exit(0);
|
||||
System.out.println("Exiting game...");
|
||||
isRunning[0] = false;
|
||||
game.getGameStates().dependencies.gameSaver.save(game);
|
||||
System.exit(0);
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cz.jzitnik.game.ui;
|
||||
|
||||
import cz.jzitnik.game.annotations.AutoTransient;
|
||||
import cz.jzitnik.game.core.autotransient.AutoTransientSupport;
|
||||
import cz.jzitnik.game.entities.items.InventoryItem;
|
||||
import cz.jzitnik.game.entities.items.Item;
|
||||
import cz.jzitnik.tui.utils.SpriteCombiner;
|
||||
@ -9,29 +11,22 @@ 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 implements Serializable {
|
||||
public class Inventory extends AutoTransientSupport {
|
||||
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];
|
||||
|
||||
@AutoTransient
|
||||
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;
|
||||
@Setter
|
||||
|
Loading…
x
Reference in New Issue
Block a user