diff --git a/src/projectzombie/Main.java b/src/projectzombie/Main.java index 0446c03..1bbb671 100755 --- a/src/projectzombie/Main.java +++ b/src/projectzombie/Main.java @@ -34,6 +34,7 @@ import projectzombie.settings.Settings; import projectzombie.time.GameTimer; import projectzombie.time.NoSleep; import projectzombie.worker.WorkerChunks; +import projectzombie.worker.WorkerFile; import projectzombie.worker.WorkerLighting; import projectzombie.world.World; import projectzombie.world.chunk.ChunkEventHandler; @@ -48,9 +49,11 @@ public class Main public static AudioEngine audio; public static Random rand = new Random(); public static Menu menu; + public static WorkerFile workerFile; public static WorkerLighting workerLighting; - public static WorkerChunks workerChunks; + public static WorkerChunks[] workerChunks; public static boolean game_paused = false; + public static boolean running = true; public static int tickrate = 10; public static void main(String[] args) throws IOException @@ -59,12 +62,19 @@ public class Main { clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - workerChunks = new WorkerChunks(); - workerLighting = new WorkerLighting(); + workerFile = new WorkerFile(); + workerFile.start(); - workerChunks.start(); + workerLighting = new WorkerLighting(); workerLighting.start(); + workerChunks = new WorkerChunks[4]; + + for(int i=0;i lighting = new AtomicReference(); public static void setDirty() { @@ -49,7 +50,7 @@ public class DisplayLighting private static void setLighting(Lighting lighting) { DisplayLighting.lighting.set(lighting); - lighting_new = true; + lighting_new.set(true); } public DisplayLighting() { @@ -230,23 +231,31 @@ public class DisplayLighting Lighting lighting = getLighting(); Layer layer = Main.world.getLayer(); + if(lighting_new.getAndSet(false)) + { + for(int i=0;i 1; + return millis > 10; } @Override @@ -58,6 +59,15 @@ public class MainloopEventHandler implements IMainloopEvent, IMainloopTask { // Stop the mainloop if the window should close if(Main.window.shouldClose()) Main.mainloop.stop(); + + // Autosave every minute + autosave_interval += 1; + if(autosave_interval > 6000) + { + autosave_interval -= 6000; + + Main.world.save(); + } } } diff --git a/src/projectzombie/menu/MenuGamePause.java b/src/projectzombie/menu/MenuGamePause.java index 01b65c9..ae9c43e 100755 --- a/src/projectzombie/menu/MenuGamePause.java +++ b/src/projectzombie/menu/MenuGamePause.java @@ -41,6 +41,6 @@ public class MenuGamePause extends Menu public void saveAndQuit() { Main.world.save(); - Main.menu = new MenuMain(); + Main.menu = new MenuSavingWorld(); } } diff --git a/src/projectzombie/menu/MenuSavingWorld.java b/src/projectzombie/menu/MenuSavingWorld.java new file mode 100644 index 0000000..cf6d2fc --- /dev/null +++ b/src/projectzombie/menu/MenuSavingWorld.java @@ -0,0 +1,63 @@ +package projectzombie.menu; + +import gl_engine.matrix.Matrix4; +import gl_engine.vec.Vec3d; +import projectzombie.Main; +import projectzombie.init.Models; +import projectzombie.input.types.InputGUI; +import projectzombie.menu.gui.GUI; +import projectzombie.menu.gui.GUIAlignment; +import projectzombie.menu.gui.GUILabel; +import projectzombie.util.gl.GlHelpers; +import projectzombie.worker.WorkerChunks; +import projectzombie.world.chunk.ChunkEventHandler; + +public class MenuSavingWorld extends Menu +{ + private GUI gui; + + public MenuSavingWorld() + { + doGameloop = true; + doGameRender = true; + keepMouse = false; + showIngameGUI = false; + + GUILabel label = new GUILabel(); + label.setText("Saving world..."); + label.setAlign(GUIAlignment.CENTRE); + + gui = new GUI(); + input = new InputGUI(gui); + + gui.add(label); + } + + @Override + public void render() + { + double a = GlHelpers.getAspectRatio(); + + for(int x = 0; x < 16; x ++) { + for(int y = 0; y < 16; y ++) + { + Models.UI_STONE_BG.setModel(Matrix4.multiply( + Matrix4.scale(new Vec3d(1.25 * a, 1.25 * a, 1)), + Matrix4.translate(new Vec3d(-10 * a + 1.25 * a * x, -10 * a + 1.25 * a * y, 0)))); + Models.UI_STONE_BG.render(); + } + } + + gui.render(); + } + + @Override + public void update() + { + super.update(); + + if(WorkerChunks.isDone()) { + Main.menu = new MenuMain(); + } + } +} diff --git a/src/projectzombie/menu/MenuWorldDelete.java b/src/projectzombie/menu/MenuWorldDelete.java index fe9bfaf..dfc7c18 100644 --- a/src/projectzombie/menu/MenuWorldDelete.java +++ b/src/projectzombie/menu/MenuWorldDelete.java @@ -1,6 +1,13 @@ package projectzombie.menu; import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; import bdf.file.BdfFileManager; import bdf.types.BdfArray; @@ -22,7 +29,6 @@ public class MenuWorldDelete extends Menu { private MenuSaves parent; - private String name; private String path; private GUI gui; @@ -30,7 +36,6 @@ public class MenuWorldDelete extends Menu public MenuWorldDelete(MenuSaves parent, String name, String path) { this.parent = parent; - this.name = name; this.path = path; keepMouse = false; @@ -85,8 +90,37 @@ public class MenuWorldDelete extends Menu BdfNamedList nl = bdf.getNamedList(); BdfArray array = nl.get("saves").getArray(); - File save_file = new File("./saves/" + path + ".bdf"); - save_file.delete(); + try + { + Files.walkFileTree(Paths.get("./saves/" + path), new FileVisitor() + { + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + } + + catch(IOException e) { + e.printStackTrace(); + } for(int i=0;i chunks_in = new Vector(); - private AtomicReference> chunks_out = new AtomicReference>(); + private static Vector chunks_in = new Vector(); + private static AtomicReference> chunks_out = new AtomicReference>(); - public void generateChunk(LayerGen layergen, Layer layer, Chunk chunk, Random rand, Vec2i pos) { - chunks_in.add(new WorkerChunkTask(layergen, layer, chunk, rand, pos)); + public static void generateChunk(LayerGen layergen, Layer layer, Chunk chunk, Random rand, Vec2i pos) { + chunks_in.add(new WorkerChunkTaskGenerate(layergen, layer, chunk, rand, pos.copy())); } - public Vector getChunks() { + public static void loadChunk(String path, Layer layer, Vec2i pos) { + chunks_in.add(new WorkerChunkTaskLoad(path, layer, pos.copy())); + } + + public static void saveChunk(String path, Vec2i pos, Chunk chunk, int id) { + chunks_in.add(new WorkerChunkTaskSave(path, pos.copy(), chunk, id)); + } + + public static Vector getChunks() { return chunks_out.getAndSet(new Vector()); } - public boolean hasChunk(Vec2i cpos) + public static boolean hasChunk(Vec2i cpos) { boolean has[] = {false}; - chunks_in.forEach(c -> { - if(c.pos.equal(cpos)) { + chunks_in.forEach(c -> + { + if( + (c instanceof WorkerChunkTaskGenerate || + c instanceof WorkerChunkTaskLoad) && + c.pos.equal(cpos)) + { has[0] = true; } }); @@ -41,31 +55,80 @@ public class WorkerChunks extends Thread chunks_out.set(new Vector()); } + private synchronized WorkerChunkTask getNextIfAvailable() + { + if(chunks_in.size() == 0) { + return null; + } else { + return chunks_in.remove(0); + } + } + @Override public void run() { - running = true; - try { - while(running) + while(Main.running) { - if(chunks_in.size() == 0) { - Thread.sleep(1); + WorkerChunkTask ct = getNextIfAvailable(); + + if(ct == null) { + Thread.sleep(10); continue; } - WorkerChunkTask ct = chunks_in.remove(0); + // Generate a new chunk + if(ct instanceof WorkerChunkTaskGenerate) + { + WorkerChunkTaskGenerate ct_g = (WorkerChunkTaskGenerate)ct; + + ct_g.layergen.generateChunk(ct_g.chunk, ct_g.layer, ct_g.rand, ct_g.pos); + + chunks_out.get().add(ct_g.chunk); + } - Chunk chunk = ct.chunk; - LayerGen layergen = ct.layergen; - Layer layer = ct.layer; - Random rand = ct.rand; - Vec2i pos = ct.pos; + // Load an existing chunk + if(ct instanceof WorkerChunkTaskLoad) + { + WorkerChunkTaskLoad ct_l = (WorkerChunkTaskLoad)ct; + + if(ct_l.path == null) { + System.out.println("ChunkLoad: Path is NULL"); + continue; + } + + String path = ( + "./saves/" + ct_l.path + "/c_" + ct_l.layer.id + + "_" + ct_l.pos.x + "_" + ct_l.pos.y + ".bdf"); + + BdfFileManager reader = new BdfFileManager(path, true); + + Chunk chunk = new Chunk(ct_l.layer, ct_l.pos, reader.getObject()); + + chunks_out.get().add(chunk); + } - layergen.generateChunk(chunk, layer, rand, pos); - - chunks_out.get().add(chunk); + // Save a chunk + if(ct instanceof WorkerChunkTaskSave) + { + WorkerChunkTaskSave ct_s = (WorkerChunkTaskSave)ct; + + if(ct_s.path == null) { + System.out.println("ChunkSave: Path is NULL"); + continue; + } + + String path = ( + "./saves/" + ct_s.path + "/c_" + ct_s.id + + "_" + ct_s.pos.x + "_" + ct_s.pos.y + ".bdf"); + + BdfFileManager reader = new BdfFileManager(path, true); + + ct_s.chunk.BdfClassSave(reader.getObject()); + + reader.saveDatabase(); + } } } @@ -74,7 +137,7 @@ public class WorkerChunks extends Thread } } - public void kill() { - running = false; + public static boolean isDone() { + return chunks_in.size() == 0; } } diff --git a/src/projectzombie/worker/WorkerFile.java b/src/projectzombie/worker/WorkerFile.java new file mode 100644 index 0000000..fae9386 --- /dev/null +++ b/src/projectzombie/worker/WorkerFile.java @@ -0,0 +1,63 @@ +package projectzombie.worker; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Vector; +import java.util.zip.GZIPOutputStream; + +import bdf.types.BdfReader; +import projectzombie.Main; + +public class WorkerFile extends Thread +{ + private static Vector tasks = new Vector(); + + public static void addTask(String path, BdfReader reader) { + tasks.add(new WorkerFileTask(path, reader)); + } + + private synchronized WorkerFileTask getNextIfAvailable() + { + if(tasks.size() == 0) { + return null; + } else { + return tasks.remove(0); + } + } + + @Override + public void run() + { + try + { + while(Main.running) + { + WorkerFileTask task = getNextIfAvailable(); + + if(task == null) { + Thread.sleep(10); + continue; + } + + try + { + OutputStream out = new FileOutputStream(task.path); + out = new GZIPOutputStream(out); + + task.reader.serialize().writeToStream(out); + + out.close(); + } + + catch(IOException e) { + e.printStackTrace(); + } + } + } + + catch(InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/src/projectzombie/worker/WorkerFileTask.java b/src/projectzombie/worker/WorkerFileTask.java new file mode 100644 index 0000000..c2471a7 --- /dev/null +++ b/src/projectzombie/worker/WorkerFileTask.java @@ -0,0 +1,14 @@ +package projectzombie.worker; + +import bdf.types.BdfReader; + +class WorkerFileTask +{ + String path; + BdfReader reader; + + public WorkerFileTask(String path, BdfReader reader) { + this.reader = reader; + this.path = path; + } +} diff --git a/src/projectzombie/worker/WorkerLighting.java b/src/projectzombie/worker/WorkerLighting.java index 17b2c53..d54aa87 100644 --- a/src/projectzombie/worker/WorkerLighting.java +++ b/src/projectzombie/worker/WorkerLighting.java @@ -2,6 +2,7 @@ package projectzombie.worker; import java.util.concurrent.atomic.AtomicReference; +import projectzombie.Main; import projectzombie.display.DisplayLighting; import projectzombie.task.Task; @@ -15,7 +16,6 @@ public class WorkerLighting extends Thread int y; } - private boolean running; private AtomicReference task = new AtomicReference(); public void processLighting(float[] lights, int width, int height, int x, int y) @@ -71,16 +71,13 @@ public class WorkerLighting extends Thread @Override - public void run() { - super.run(); - - running = true; - + public void run() + { try { - while(running) + while(Main.running) { - LightingTask task = this.task.get(); + LightingTask task = this.task.getAndSet(null); if(task != null) { @@ -101,7 +98,7 @@ public class WorkerLighting extends Thread } else { - Thread.sleep(1); + Thread.sleep(10); } } } @@ -110,8 +107,4 @@ public class WorkerLighting extends Thread e.printStackTrace(); } } - - public void kill() { - running = false; - } } diff --git a/src/projectzombie/world/World.java b/src/projectzombie/world/World.java index 3d6fb46..e1639e8 100755 --- a/src/projectzombie/world/World.java +++ b/src/projectzombie/world/World.java @@ -13,6 +13,7 @@ import bdf.file.BdfFileManager; import bdf.types.BdfArray; import bdf.types.BdfNamedList; import bdf.types.BdfObject; +import bdf.types.BdfReader; import gl_engine.matrix.Matrix4; import gl_engine.vec.Vec3d; import projectzombie.Main; @@ -25,6 +26,7 @@ import projectzombie.init.Layers; import projectzombie.menu.MenuLoadingWorld; import projectzombie.model.Model; import projectzombie.time.GameTimer; +import projectzombie.worker.WorkerFile; import projectzombie.world.chunk.ChunkEventHandler; import projectzombie.world.layer.Layer; @@ -38,7 +40,6 @@ public class World implements IBdfClassManager private Layer loaded; private ArrayList layers = new ArrayList(); - private BdfFileManager file_manager; private String path; private int pool_vao, pool_vbo, pool_ibo; @@ -51,22 +52,18 @@ public class World implements IBdfClassManager if(path != null) { - path = "./saves/" + path + ".bdf"; - File save_file = new File(path); - File save_dir = save_file.getParentFile(); + path = "./saves/" + path + "/"; + File save_dir = new File(path); + File file_dir = new File(path + "world.bdf"); if(!save_dir.exists()) { save_dir.mkdirs(); } - if(save_file.exists()) { - file_manager = new BdfFileManager(path); + if(file_dir.exists()) { + BdfFileManager file_manager = new BdfFileManager(path + "world.bdf", true); BdfClassLoad(file_manager.getObject()); } - - else { - file_manager = new BdfFileManager(path); - } } } @@ -265,12 +262,17 @@ public class World implements IBdfClassManager public void save() { - if(file_manager != null) + if(path != null) { - BdfObject bdf = file_manager.resetObject(); + for(Layer layer : layers) { + layer.save(); + } + + BdfReader reader = new BdfReader(); + BdfObject bdf = reader.getObject(); BdfClassSave(bdf); - file_manager.saveDatabase(); + WorkerFile.addTask("./saves/" + path + "/world.bdf", reader); } } diff --git a/src/projectzombie/world/chunk/Chunk.java b/src/projectzombie/world/chunk/Chunk.java index 63f0948..d4a3647 100755 --- a/src/projectzombie/world/chunk/Chunk.java +++ b/src/projectzombie/world/chunk/Chunk.java @@ -54,7 +54,7 @@ public class Chunk implements IBdfClassManager private byte tiles_temperature[] = new byte[CHUNK_INDEX]; private byte tiles_humidity[] = new byte[CHUNK_INDEX]; public ArrayList entities = new ArrayList(); - private Layer layer; + public Layer layer; public Vec2i c_pos; private boolean dirty; private boolean light_dirty; diff --git a/src/projectzombie/world/chunk/ChunkEventHandler.java b/src/projectzombie/world/chunk/ChunkEventHandler.java index ca1ca18..dda2edf 100755 --- a/src/projectzombie/world/chunk/ChunkEventHandler.java +++ b/src/projectzombie/world/chunk/ChunkEventHandler.java @@ -7,6 +7,7 @@ import mainloop.task.IMainloopTask; import projectzombie.Main; import projectzombie.display.DisplayLighting; import projectzombie.util.math.map.Map2DElement; +import projectzombie.worker.WorkerChunks; import projectzombie.world.layer.Layer; public class ChunkEventHandler implements IMainloopTask @@ -73,7 +74,7 @@ public class ChunkEventHandler implements IMainloopTask Vec2i c_pos = new Vec2i(cx, cy); // Is this chunk not loaded - if(!layer.chunkLoaded(c_pos) && !Main.workerChunks.hasChunk(c_pos)) + if(!layer.chunkLoaded(c_pos) && !WorkerChunks.hasChunk(c_pos)) { // Load the chunk layer.loadChunk(c_pos); diff --git a/src/projectzombie/world/layer/Layer.java b/src/projectzombie/world/layer/Layer.java index 15144de..d34d2bc 100755 --- a/src/projectzombie/world/layer/Layer.java +++ b/src/projectzombie/world/layer/Layer.java @@ -21,14 +21,15 @@ import projectzombie.util.math.TileState; import projectzombie.util.math.map.Map2D; import projectzombie.util.math.map.Map2DElement; import projectzombie.util.math.random.NoiseGenerator; +import projectzombie.worker.WorkerChunks; import projectzombie.world.chunk.Chunk; import projectzombie.world.layer.layergen.LayerGen; public class Layer implements IBdfClassManager { public Map2D chunks; - private Map2D dirty_chunks; public NoiseGenerator[] noise_gens; + public ArrayList chunks_saved; public LayerGen layergen; private Random rand; public long lseed; @@ -43,7 +44,7 @@ public class Layer implements IBdfClassManager this.seed = rand.nextLong(); this.lseed = this.rand.nextLong(); this.chunks = new Map2D(layergen); - this.dirty_chunks = new Map2D(layergen); + this.chunks_saved = new ArrayList(); this.layergen.init(this); } @@ -268,19 +269,34 @@ public class Layer implements IBdfClassManager public void loadChunks() { - for(Chunk chunk : Main.workerChunks.getChunks()) + WorkerChunks.getChunks().forEach(chunk -> { + if(chunk.layer != this) { + return; + } + Vec2i pos = chunk.c_pos; chunks.set(pos, chunk); chunk.clearDirty(); + }); + } + + private boolean hasSavedChunk(Vec2i check) + { + for(Vec2i pos : chunks_saved) { + if(pos.equal(check)) { + return true; + } } + + return false; } public void loadChunk(Vec2i pos) { // Has the chunk been saved for later - if(dirty_chunks.contains(pos)) { - chunks.set(pos, dirty_chunks.get(pos)); + if(hasSavedChunk(pos)) { + WorkerChunks.loadChunk(Main.world.getSavePath(), this, pos); } else @@ -290,7 +306,7 @@ public class Layer implements IBdfClassManager // Create and generate the chunk Chunk chunk = new Chunk(this, pos, rand); - Main.workerChunks.generateChunk(layergen, this, chunk, new Random(cseed), pos); + WorkerChunks.generateChunk(layergen, this, chunk, new Random(cseed), pos); } } @@ -298,8 +314,13 @@ public class Layer implements IBdfClassManager { // Store the chunk if its a dirty chunk Chunk chunk = chunks.get(pos); - if(chunk.isDirty()) { - dirty_chunks.set(pos, chunk); + if(chunk.isDirty()) + { + WorkerChunks.saveChunk(Main.world.getSavePath(), pos, chunk, id); + + if(!hasSavedChunk(pos)) { + chunks_saved.add(pos); + } } // Remove the chunk @@ -341,21 +362,19 @@ public class Layer implements IBdfClassManager { BdfNamedList nl = bdf.getNamedList(); - this.layergen = LayerGenerators.loadFromID(nl.get("lgen").getInteger()); - this.dirty_chunks = new Map2D(layergen); + BdfArray bdf_chunks = nl.get("chunks").getArray(); + + this.layergen = LayerGenerators.loadFromID((int)nl.get("lgen").getAutoInt()); + this.chunks_saved = new ArrayList(bdf_chunks.size()); this.chunks = new Map2D(layergen); this.seed = nl.get("seed").getLong(); this.lseed = nl.get("lseed").getLong(); - this.id = nl.get("id").getInteger(); + this.id = (int)nl.get("id").getAutoInt(); this.rand = new Random(); - BdfArray bdf_chunks = nl.get("chunks").getArray(); - for(BdfObject bdf_chunk : bdf_chunks) { - BdfNamedList chunk_nl = bdf_chunk.getNamedList(); - Vec2i cpos = new Vec2i(chunk_nl.get("pos")); - Chunk chunk = new Chunk(this, cpos, chunk_nl.get("chunk")); - dirty_chunks.set(cpos, chunk); + Vec2i c = new Vec2i(bdf_chunk); + chunks_saved.add(c); } layergen.init(this); @@ -368,28 +387,29 @@ public class Layer implements IBdfClassManager nl.set("lseed", bdf.newObject().setLong(lseed)); nl.set("seed", bdf.newObject().setLong(seed)); - nl.set("id", bdf.newObject().setInteger(id)); - nl.set("lgen", bdf.newObject().setInteger(LayerGenerators.getLGID(this.layergen))); + nl.set("id", bdf.newObject().setAutoInt(id)); + nl.set("lgen", bdf.newObject().setAutoInt(LayerGenerators.getLGID(this.layergen))); - BdfArray bdf_chunks = nl.get("chunks").getArray(); + BdfArray bdf_chunks = bdf.newArray(chunks_saved.size()); + nl.set("chunks", bdf.newObject().setArray(bdf_chunks)); - for(Map2DElement chunk : dirty_chunks) { - BdfNamedList chunk_nl = bdf.newNamedList(); - chunk.pos.BdfClassSave(chunk_nl.get("pos")); - chunk.o.BdfClassSave(chunk_nl.get("chunk")); - bdf_chunks.add(bdf.newObject().setNamedList(chunk_nl)); + for(int i=0;i chunk : chunks) { - if(dirty_chunks.contains(chunk.pos)) - continue; - if(!chunk.o.isDirty()) - continue; - - BdfNamedList chunk_nl = bdf.newNamedList(); - chunk.pos.BdfClassSave(chunk_nl.get("pos")); - chunk.o.BdfClassSave(chunk_nl.get("chunk")); - bdf_chunks.add(bdf.newObject().setNamedList(chunk_nl)); + } + + public void save() + { + for(Map2DElement chunk : chunks) + { + if(chunk.o.isDirty()) + { + WorkerChunks.saveChunk(Main.world.getSavePath(), chunk.pos, chunk.o, id); + + if(!hasSavedChunk(chunk.pos)) { + chunks_saved.add(chunk.pos); + } + } } }