From 45ce4ca9a97246bdf27ede8baa83b108b22ca87d Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 24 Mar 2024 21:51:11 -0400 Subject: [PATCH] rudimentary scene saving --- .../authentication/authentication.md | 5 + docs/src/progress/renderertodo.md | 24 ++- saves/defaultLevel_0/0_0_0t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/0_0_1t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/0_0_2t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/0_1_0t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/0_1_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/0_1_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/0_2_0t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/0_2_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/0_2_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/1_0_0t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/1_0_1t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/1_0_2t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/1_1_0t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/1_1_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/1_1_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/1_2_0t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/1_2_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/1_2_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/2_0_0t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/2_0_1t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/2_0_2t.dat | Bin 0 -> 141 bytes saves/defaultLevel_0/2_1_0t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/2_1_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/2_1_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/2_2_0t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/2_2_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/2_2_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/3_1_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/3_1_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/3_2_1t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/3_2_2t.dat | Bin 0 -> 60 bytes saves/defaultLevel_0/central.db | Bin 0 -> 81920 bytes saves/defaultLevel_0/chunk.map | 1 + saves/defaultLevel_0/content/0_0_0.dat | 1 + saves/defaultLevel_0/content/0_0_1.dat | 1 + saves/defaultLevel_0/content/0_0_2.dat | 1 + saves/defaultLevel_0/content/0_1_0.dat | 1 + saves/defaultLevel_0/content/0_1_1.dat | 1 + saves/defaultLevel_0/content/0_1_2.dat | 1 + saves/defaultLevel_0/content/0_2_0.dat | 1 + saves/defaultLevel_0/content/0_2_1.dat | 1 + saves/defaultLevel_0/content/0_2_2.dat | 1 + saves/defaultLevel_0/content/1_0_0.dat | 1 + saves/defaultLevel_0/content/1_0_1.dat | 1 + saves/defaultLevel_0/content/1_0_2.dat | 1 + saves/defaultLevel_0/content/1_1_0.dat | 1 + saves/defaultLevel_0/content/1_1_1.dat | 1 + saves/defaultLevel_0/content/1_1_2.dat | 1 + saves/defaultLevel_0/content/1_2_0.dat | 1 + saves/defaultLevel_0/content/1_2_1.dat | 1 + saves/defaultLevel_0/content/1_2_2.dat | 1 + saves/defaultLevel_0/content/2_0_0.dat | 1 + saves/defaultLevel_0/content/2_0_1.dat | 1 + saves/defaultLevel_0/content/2_0_2.dat | 1 + saves/defaultLevel_0/content/2_1_0.dat | 1 + saves/defaultLevel_0/content/2_1_1.dat | 1 + saves/defaultLevel_0/content/2_1_2.dat | 1 + saves/defaultLevel_0/content/2_2_0.dat | 1 + saves/defaultLevel_0/content/2_2_1.dat | 1 + saves/defaultLevel_0/content/2_2_2.dat | 1 + saves/defaultLevel_0/world.json | 1 + .../auth/AuthenticationManager.java | 4 + .../engine/loadingthreads/ArenaLoading.java | 2 +- .../loadingthreads/DebugSPWorldLoading.java | 12 +- .../loadingthreads/LevelEditorLoading.java | 102 +++++++++++ .../engine/loadingthreads/LevelLoading.java | 84 +++++++++ .../engine/loadingthreads/LoadingThread.java | 12 ++ .../entity/types/foliage/FoliageUtils.java | 3 + .../entity/types/object/ObjectUtils.java | 26 +++ .../types/structure/StructureUtils.java | 3 + .../menu/ingame/MenuGeneratorsInGame.java | 1 + .../mainmenu/MenuGeneratorsLevelEditor.java | 173 ++++++++++++++++++ .../mainmenu/MenuGeneratorsTitleMenu.java | 17 +- .../server/content/ServerContentManager.java | 84 ++++++++- .../serialization/ContentSerialization.java | 63 +++++++ .../serialization/EntitySerialization.java | 57 ++++++ .../datacell/GriddedDataCellManager.java | 20 +- .../electrosphere/server/datacell/Realm.java | 8 + .../server/datacell/RealmManager.java | 10 + .../datacell/interfaces/DataCellManager.java | 8 + .../electrosphere/server/saves/SaveUtils.java | 30 ++- .../terrain/manager/ServerTerrainManager.java | 38 ++-- .../java/electrosphere/util/FileUtils.java | 11 ++ 85 files changed, 775 insertions(+), 51 deletions(-) create mode 100644 docs/src/architecture/authentication/authentication.md create mode 100644 saves/defaultLevel_0/0_0_0t.dat create mode 100644 saves/defaultLevel_0/0_0_1t.dat create mode 100644 saves/defaultLevel_0/0_0_2t.dat create mode 100644 saves/defaultLevel_0/0_1_0t.dat create mode 100644 saves/defaultLevel_0/0_1_1t.dat create mode 100644 saves/defaultLevel_0/0_1_2t.dat create mode 100644 saves/defaultLevel_0/0_2_0t.dat create mode 100644 saves/defaultLevel_0/0_2_1t.dat create mode 100644 saves/defaultLevel_0/0_2_2t.dat create mode 100644 saves/defaultLevel_0/1_0_0t.dat create mode 100644 saves/defaultLevel_0/1_0_1t.dat create mode 100644 saves/defaultLevel_0/1_0_2t.dat create mode 100644 saves/defaultLevel_0/1_1_0t.dat create mode 100644 saves/defaultLevel_0/1_1_1t.dat create mode 100644 saves/defaultLevel_0/1_1_2t.dat create mode 100644 saves/defaultLevel_0/1_2_0t.dat create mode 100644 saves/defaultLevel_0/1_2_1t.dat create mode 100644 saves/defaultLevel_0/1_2_2t.dat create mode 100644 saves/defaultLevel_0/2_0_0t.dat create mode 100644 saves/defaultLevel_0/2_0_1t.dat create mode 100644 saves/defaultLevel_0/2_0_2t.dat create mode 100644 saves/defaultLevel_0/2_1_0t.dat create mode 100644 saves/defaultLevel_0/2_1_1t.dat create mode 100644 saves/defaultLevel_0/2_1_2t.dat create mode 100644 saves/defaultLevel_0/2_2_0t.dat create mode 100644 saves/defaultLevel_0/2_2_1t.dat create mode 100644 saves/defaultLevel_0/2_2_2t.dat create mode 100644 saves/defaultLevel_0/3_1_1t.dat create mode 100644 saves/defaultLevel_0/3_1_2t.dat create mode 100644 saves/defaultLevel_0/3_2_1t.dat create mode 100644 saves/defaultLevel_0/3_2_2t.dat create mode 100644 saves/defaultLevel_0/central.db create mode 100644 saves/defaultLevel_0/chunk.map create mode 100644 saves/defaultLevel_0/content/0_0_0.dat create mode 100644 saves/defaultLevel_0/content/0_0_1.dat create mode 100644 saves/defaultLevel_0/content/0_0_2.dat create mode 100644 saves/defaultLevel_0/content/0_1_0.dat create mode 100644 saves/defaultLevel_0/content/0_1_1.dat create mode 100644 saves/defaultLevel_0/content/0_1_2.dat create mode 100644 saves/defaultLevel_0/content/0_2_0.dat create mode 100644 saves/defaultLevel_0/content/0_2_1.dat create mode 100644 saves/defaultLevel_0/content/0_2_2.dat create mode 100644 saves/defaultLevel_0/content/1_0_0.dat create mode 100644 saves/defaultLevel_0/content/1_0_1.dat create mode 100644 saves/defaultLevel_0/content/1_0_2.dat create mode 100644 saves/defaultLevel_0/content/1_1_0.dat create mode 100644 saves/defaultLevel_0/content/1_1_1.dat create mode 100644 saves/defaultLevel_0/content/1_1_2.dat create mode 100644 saves/defaultLevel_0/content/1_2_0.dat create mode 100644 saves/defaultLevel_0/content/1_2_1.dat create mode 100644 saves/defaultLevel_0/content/1_2_2.dat create mode 100644 saves/defaultLevel_0/content/2_0_0.dat create mode 100644 saves/defaultLevel_0/content/2_0_1.dat create mode 100644 saves/defaultLevel_0/content/2_0_2.dat create mode 100644 saves/defaultLevel_0/content/2_1_0.dat create mode 100644 saves/defaultLevel_0/content/2_1_1.dat create mode 100644 saves/defaultLevel_0/content/2_1_2.dat create mode 100644 saves/defaultLevel_0/content/2_2_0.dat create mode 100644 saves/defaultLevel_0/content/2_2_1.dat create mode 100644 saves/defaultLevel_0/content/2_2_2.dat create mode 100644 saves/defaultLevel_0/world.json create mode 100644 src/main/java/electrosphere/engine/loadingthreads/LevelEditorLoading.java create mode 100644 src/main/java/electrosphere/engine/loadingthreads/LevelLoading.java create mode 100644 src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsLevelEditor.java create mode 100644 src/main/java/electrosphere/server/content/serialization/ContentSerialization.java create mode 100644 src/main/java/electrosphere/server/content/serialization/EntitySerialization.java diff --git a/docs/src/architecture/authentication/authentication.md b/docs/src/architecture/authentication/authentication.md new file mode 100644 index 00000000..5303915c --- /dev/null +++ b/docs/src/architecture/authentication/authentication.md @@ -0,0 +1,5 @@ +@page authentication Authentication + +TODO: how authentication works + +Look at DebugSPWorldLoading if you want to have a loading thread that preempts the auth system locally \ No newline at end of file diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index a860d6cb..7cdbd82d 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -146,6 +146,8 @@ Overhaul mesh class (03/10/2024) De-dupe render calls via doing mutations in render pipeline status and dont call setting variables to values they are already set to +Render pipeline updates to support multiple pipelines defined in different files + - Grouping meshes together independent of actor so don't have to rebind shader programs or VAOs to redraw (03/12/2024) Foliage Manager upgrades @@ -185,12 +187,23 @@ Physics-controlled objects system Clean up main method/class - Include Remotery library +(03/24/2024) +Bring LWJGL version up to latest -# TODO +Automatic Scene unloading + - Tree structure inside scenes for tearing down groups of entities + - Entity decomposition + - Server handling + - Client handling when scene should be unloaded Level loading/saving + Basic Editor - Spin up voxel level (think arena mode) - Save voxel level + + +# TODO + +Level loading/saving + Basic Editor - Basic editor functionality - Menu of types of entities to spawn - Button to spawn them at cursor @@ -310,8 +323,6 @@ Upgrade terrain editing user experience further - Lock to axis tools - Server validation for client request to change terrain -Bring LWJGL version up to latest - @@ -326,11 +337,6 @@ Upgrade terrain generation algorithms # Eventually -Automatic Scene unloading - - Tree structure inside scenes for tearing down groups of entities - - Entity decomposition - - Server handling - - Client handling when scene should be unloaded Generic collision engine to support different instances of engine (eg hitboxes vs terrain vs liquids, etc) - Major refactoring to happen here Procedural Cliff Texture @@ -338,8 +344,6 @@ Procedural Cliff Texture Terrain Chunks: - Scale textures to be 1 texture per unit of terrain - Texture atlasing (512x512) -Render pipeline updates to support multiple pipelines defined in different files - - Grouping meshes together independent of actor so don't have to rebind shader programs or VAOs to redraw Loot Generator - System that can generate items that would be appropriate reward given some variables - ie you tell it 'this is this character's stats, this is the relative level of loot I want to provide' diff --git a/saves/defaultLevel_0/0_0_0t.dat b/saves/defaultLevel_0/0_0_0t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/0_0_1t.dat b/saves/defaultLevel_0/0_0_1t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/0_0_2t.dat b/saves/defaultLevel_0/0_0_2t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/0_1_0t.dat b/saves/defaultLevel_0/0_1_0t.dat new file mode 100644 index 0000000000000000000000000000000000000000..41183a70f562b5c7b7f3f0025e262ac3271e7736 GIT binary patch literal 60 qcmb=J^Y*YIF9QP)^8&B=o13NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/1_0_1t.dat b/saves/defaultLevel_0/1_0_1t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/1_0_2t.dat b/saves/defaultLevel_0/1_0_2t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/1_1_0t.dat b/saves/defaultLevel_0/1_1_0t.dat new file mode 100644 index 0000000000000000000000000000000000000000..41183a70f562b5c7b7f3f0025e262ac3271e7736 GIT binary patch literal 60 qcmb=J^Y*YIF9QP)^8&B=o13NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/2_0_1t.dat b/saves/defaultLevel_0/2_0_1t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/2_0_2t.dat b/saves/defaultLevel_0/2_0_2t.dat new file mode 100644 index 0000000000000000000000000000000000000000..40d281ea15a37761dddf1e6c798da43df9d0621a GIT binary patch literal 141 zcmb=J^Y*eMZ-W8Dfdh&;_X^q?A9C8f+4^PHoR)K2@9liJ^Ekt~EuZ)Of0*;HUGKm1 z^#2P_|G)6G{>3NzlArRs{_w|Q2>*?ptulA^|D}2VrtkV0|LR%&N+LL2-x%xGh1viB D!1-Hv literal 0 HcmV?d00001 diff --git a/saves/defaultLevel_0/2_1_0t.dat b/saves/defaultLevel_0/2_1_0t.dat new file mode 100644 index 0000000000000000000000000000000000000000..41183a70f562b5c7b7f3f0025e262ac3271e7736 GIT binary patch literal 60 qcmb=J^Y*YIF9QP)^8&B=o1M1NOl_p(hP&&TWFnh1Qt6H4kiqaZ>)FiXZkBD21k z$i$toDNRo%-tw-dZVje2xPL7EY&g?VO%59T9rbynxn0x)n za%I|6=L&hR;@OFiK0mEi8cufQs@zeF-pi=u;k9-ccumHfyC!O|6w&nQY1Nr;B<*#3 z&l6H|UTgn^nmhGE?ooS#mWRGnEr3)yO?ReA1?ViE zipxr8C!l_w1mD?ln$7=IuV7d|4 znOQ7n-7~(wG9X3fHw)-$R>E-X;l|T zMP6$3KSg}8AbUI{r~4m^m)({Bp(D2KmY** z5I_I{1Q0*~0R-N4fm3x@x#z5wtV^q86^eGH`n3A+(%StzWgR^_C|*`;m9KN9dMUeR ze_FNk`}5D9JU-mdWj2$WiO-fxg+gjGcYShJ%oocmhhOAM`IFuH%8r#-dsZ%AS61#n z+`2fw-djtot~Rg#UntQR@45-`A%Fk^2q1s}0tg_000IagfWR9FjH!duVejuCxYz&U zkN&YBfB*srAbn!82q1s}0tg_000IagfB*sr1Ydy9{|A4L$q)eq p5I_I{1Q0*~0R#|000Hj*Qv(PffB*srAb saveNames = SaveUtils.getSaves(); + int i = 0; + for(String saveName : saveNames){ + i++; + + //delete level button + Button deleteButton = new Button(); + Label deleteLabel = new Label(100,screenTop + 225 + 75 * i,1.0f); + deleteLabel.setText("[X]"); + deleteButton.addChild(deleteLabel); + rVal.addChild(deleteButton); + deleteButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + SaveUtils.deleteSave(saveName); + WindowUtils.replaceMainMenuContents(MenuGeneratorsLevelEditor.createLevelEditorTopMenu()); + return false; + }}); + + + //button (launch level) + Button launchButton = new Button(); + Label launchLabel = new Label(200,screenTop + 225 + 75 * i,1.0f); + launchLabel.setText(saveName); + launchButton.addChild(launchLabel); + rVal.addChild(launchButton); + launchButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + //launch level + Globals.currentSaveName = saveName; + LoadingThread loadingThread = new LoadingThread(LoadingThread.LOAD_LEVEL); + Globals.loadingThreadsList.add(loadingThread); + Globals.RUN_CLIENT = true; + Globals.RUN_SERVER = true; + loadingThread.start(); + return false; + }}); + } + + return rVal; + } + + + /** + * Creates the level creation menu + * @return The element containing the level creation menu + */ + public static Element createLevelEditorCreationMenu(){ + FormElement rVal = new FormElement(); + int screenTop = 150; + + //label (address) + Label usernameLabel = new Label(100,screenTop + 50,1.0f); + usernameLabel.setText("Create Level"); + rVal.addChild(usernameLabel); + + //button (back) + { + Button backButton = new Button(); + Label backLabel = new Label(100,screenTop + 150,1.0f); + backLabel.setText("Back"); + backButton.addChild(backLabel); + rVal.addChild(backButton); + backButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + WindowUtils.replaceMainMenuContents(MenuGeneratorsTitleMenu.createTitleMenu()); + return false; + }}); + } + + //label for text input for level name + { + Label levelNameLabel = new Label(100,screenTop + 225,1.0f); + levelNameLabel.setText("Level Name:"); + rVal.addChild(levelNameLabel); + } + + //generate default level name + List saveNames = SaveUtils.getSaves(); + int i = 0; + String defaultSaveName = "defaultLevel_" + i; + while(saveNames.contains(defaultSaveName)){ + i++; + } + + //get level name + TextInput usernameInput = new TextInput(100,screenTop + 300,1.0f); + usernameInput.setText(defaultSaveName); + rVal.addChild(usernameInput); + + //button (create level) + { + Button createLevelButton = new Button(); + Label createLevelLabel = new Label(100,screenTop + 375,1.0f); + createLevelLabel.setText("Create Level"); + createLevelButton.addChild(createLevelLabel); + rVal.addChild(createLevelButton); + createLevelButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + //launch level editor + Globals.currentSaveName = usernameInput.getText(); + LoadingThread loadingThread = new LoadingThread(LoadingThread.LOAD_LEVEL_EDITOR); + Globals.loadingThreadsList.add(loadingThread); + Globals.RUN_CLIENT = true; + Globals.RUN_SERVER = true; + loadingThread.start(); + return false; + }}); + } + + + return rVal; + } + +} diff --git a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsTitleMenu.java b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsTitleMenu.java index 58197d21..a63a9e29 100644 --- a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsTitleMenu.java +++ b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsTitleMenu.java @@ -63,9 +63,20 @@ public class MenuGeneratorsTitleMenu { return false; }}); + //button (static level) + Button staticLevelButton = new Button(); + Label staticLevelLabel = new Label(100,500,1.0f); + staticLevelLabel.setText("Level Editor"); + staticLevelButton.addChild(staticLevelLabel); + rVal.addChild(staticLevelButton); + staticLevelButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + WindowUtils.replaceMainMenuContents(MenuGeneratorsLevelEditor.createLevelEditorTopMenu()); + return false; + }}); + //button (options) Button optionsButton = new Button(); - Label optionsLabel = new Label(100,500,1.0f); + Label optionsLabel = new Label(100,575,1.0f); optionsLabel.setText("Options"); optionsButton.addChild(optionsLabel); rVal.addChild(optionsButton); @@ -76,7 +87,7 @@ public class MenuGeneratorsTitleMenu { //button (sp debug) Button uiDebugSPQuickstartButton = new Button(); - Label uiDebugSPQuickstartLabel = new Label(100,575,1.0f); + Label uiDebugSPQuickstartLabel = new Label(100,650,1.0f); uiDebugSPQuickstartLabel.setText("Debug SP Quickstart"); uiDebugSPQuickstartButton.addChild(uiDebugSPQuickstartLabel); rVal.addChild(uiDebugSPQuickstartButton); @@ -91,7 +102,7 @@ public class MenuGeneratorsTitleMenu { //button (ui testing) Button uiTestingButton = new Button(); - Label uiTestingLabel = new Label(100,650,1.0f); + Label uiTestingLabel = new Label(100,725,1.0f); uiTestingLabel.setText("UI Testing"); uiTestingButton.addChild(uiTestingLabel); rVal.addChild(uiTestingButton); diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index 3ca905f4..2e7aa3b7 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -1,29 +1,66 @@ package electrosphere.server.content; +import java.util.List; + import org.joml.Vector3i; import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.server.content.serialization.ContentSerialization; +import electrosphere.server.content.serialization.EntitySerialization; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.pathfinding.NavMeshUtils; +import electrosphere.server.saves.SaveUtils; +import electrosphere.util.FileUtils; public class ServerContentManager { + //controls whether the manager should generate content on loading a new scene + boolean generateContent = false; - private ServerContentManager(){ - + /** + * Constructor + */ + private ServerContentManager(boolean generateContent){ + this.generateContent = generateContent; } - public static ServerContentManager createServerContentManager(){ - return new ServerContentManager(); + /** + * Creates a server content manager + * @param generateContent if true, will generate content on loading a new scene, otherwise will not + * @return The server content manager + */ + public static ServerContentManager createServerContentManager(boolean generateContent){ + return new ServerContentManager(generateContent); } - - public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell){ - if(!Globals.serverWorldData.isArena()){ //in other words, if not arena mode - //if on disk (has already been generated) - //else create from scratch - EnvironmentGenerator.generateForest(realm, cell, worldPos, 0); + /** + * Generates content for a given data cell + * @param realm The realm + * @param worldPos The world position + * @param cell The cell + */ + public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell, String cellKey){ + String fullPath = "/content/" + cellKey + ".dat"; + if(generateContent){ //in other words, if not arena mode + if(FileUtils.checkSavePathExists(Globals.currentSaveName, fullPath)){ + //if on disk (has already been generated) + ContentSerialization contentRaw = FileUtils.loadObjectFromSavePath(Globals.currentSaveName, fullPath, ContentSerialization.class); + hydrateRawContent(realm,cell,contentRaw); + } else { + //else create from scratch + EnvironmentGenerator.generateForest(realm, cell, worldPos, 0); + } + } else { + //just because content wasn't generated doesn't mean there isn't data saved under that key + if(FileUtils.checkSavePathExists(Globals.currentSaveName, fullPath)){ + //if on disk (has already been generated) + ContentSerialization contentRaw = FileUtils.loadObjectFromSavePath(Globals.currentSaveName, fullPath, ContentSerialization.class); + hydrateRawContent(realm,cell,contentRaw); + } } //TODO: generate navmesh // cell.setNavMesh( @@ -36,6 +73,33 @@ public class ServerContentManager { // ); } + /** + * Saves entity content to disk + * @param locationKey the location key to save under + * @param entities the list of entities to save + */ + public void saveContentToDisk(String locationKey, List entities){ + ContentSerialization serialization = ContentSerialization.constructContentSerialization(entities); + String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSaveName); + String fullPath = dirPath + "/content/" + locationKey + ".dat"; + FileUtils.serializeObjectToFilePath(fullPath, serialization); + } + + /** + * Actually creates the entities from a content serialization + * @param contentRaw The content serialization + */ + public void hydrateRawContent(Realm realm, ServerDataCell serverDataCell, ContentSerialization contentRaw){ + List serializedEntities = contentRaw.getSerializedEntities(); + for(EntitySerialization serializedEntity : serializedEntities){ + switch(serializedEntity.getType()){ + case CreatureUtils.ENTITY_TYPE_CREATURE: { + Entity creature = CreatureUtils.serverSpawnBasicCreature(realm, serializedEntity.getPosition(), serializedEntity.getSubtype(), null); + EntityUtils.getRotation(creature).set(serializedEntity.getRotation()); + } break; + } + } + } } diff --git a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java new file mode 100644 index 00000000..fde49d55 --- /dev/null +++ b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java @@ -0,0 +1,63 @@ +package electrosphere.server.content.serialization; + +import java.util.LinkedList; +import java.util.List; + +import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.entity.types.foliage.FoliageUtils; +import electrosphere.entity.types.item.ItemUtils; +import electrosphere.entity.types.object.ObjectUtils; +import electrosphere.entity.types.structure.StructureUtils; + +/** + * Contains all content for a given cell + */ +public class ContentSerialization { + + + //The entities, serialized + List serializedEntities = new LinkedList(); + + /** + * Constructs a content serialization of a given list of entities + * @param entities The entities + * @return The content serialization + */ + public static ContentSerialization constructContentSerialization(List entities){ + ContentSerialization rVal = new ContentSerialization(); + for(Entity entity : entities){ + if(CreatureUtils.isCreature(entity)){ + EntitySerialization serializedEntity = new EntitySerialization(); + serializedEntity.setPosition(EntityUtils.getPosition(entity)); + serializedEntity.setRotation(EntityUtils.getRotation(entity)); + serializedEntity.setType(CreatureUtils.ENTITY_TYPE_CREATURE); + serializedEntity.setSubtype(CreatureUtils.getType(entity)); + rVal.serializedEntities.add(serializedEntity); + } + if(ItemUtils.isItem(entity)){ + throw new UnsupportedOperationException(); + } + if(ObjectUtils.isObject(entity)){ + throw new UnsupportedOperationException(); + } + if(StructureUtils.isStructure(entity)){ + throw new UnsupportedOperationException(); + } + if(FoliageUtils.isFoliage(entity)){ + throw new UnsupportedOperationException(); + } + } + return rVal; + } + + /** + * Gets all serialized entities + * @return the list of serialized entities + */ + public List getSerializedEntities(){ + return serializedEntities; + } + +} diff --git a/src/main/java/electrosphere/server/content/serialization/EntitySerialization.java b/src/main/java/electrosphere/server/content/serialization/EntitySerialization.java new file mode 100644 index 00000000..cb03cdbd --- /dev/null +++ b/src/main/java/electrosphere/server/content/serialization/EntitySerialization.java @@ -0,0 +1,57 @@ +package electrosphere.server.content.serialization; + +import org.joml.Quaterniond; +import org.joml.Vector3d; + +/** + * A serializaed entity + */ +public class EntitySerialization { + + int type; + String subtype; + Vector3d position; + Quaterniond rotation; + + //type getter + public int getType(){ + return type; + } + + //type setter + public void setType(int type){ + this.type = type; + } + + //subtype getter + public String getSubtype(){ + return subtype; + } + + //subtype setter + public void setSubtype(String subtype){ + this.subtype = subtype; + } + + //position getter + public Vector3d getPosition(){ + return position; + } + + //position setter + public void setPosition(Vector3d position){ + this.position = position; + } + + //rotation getter + public Quaterniond getRotation(){ + return rotation; + } + + //rotation setter + public void setRotation(Quaterniond rotation){ + this.rotation = rotation; + } + + +} diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java index 12a428eb..aed03d9d 100644 --- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java @@ -2,6 +2,7 @@ package electrosphere.server.datacell; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -24,6 +25,7 @@ import electrosphere.server.datacell.interfaces.VoxelCellManager; import electrosphere.server.datacell.physics.PhysicsDataCell; import electrosphere.server.fluid.manager.ServerFluidChunk; import electrosphere.server.fluid.manager.ServerFluidManager; +import electrosphere.server.saves.SaveUtils; import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainChunk; @@ -261,6 +263,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager Vector3i worldPos = getCellWorldPosition(cell); String key = getServerDataCellKey(worldPos); groundDataCells.remove(key); + //offload all entities in cell to chunk file + serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); //clear all entities in cell for(Entity entity : cell.getScene().getEntityList()){ EntityUtils.cleanUpEntity(entity); @@ -418,10 +422,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager */ private ServerDataCell createServerDataCell(Vector3i worldPos){ ServerDataCell rVal = parent.createNewCell(); - groundDataCells.put(getServerDataCellKey(worldPos),rVal); - LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + getServerDataCellKey(worldPos)); + String cellKey = getServerDataCellKey(worldPos); + groundDataCells.put(cellKey,rVal); + LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey); cellPositionMap.put(rVal,new Vector3i(worldPos)); - serverContentManager.generateContentForDataCell(parent, worldPos, rVal); + serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey); return rVal; } @@ -507,4 +512,13 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager ); } + @Override + public void save(String saveName) { + for(ServerDataCell cell : loadedCells){ + String key = this.getServerDataCellKey(this.getCellWorldPosition(cell)); + //offload all entities in cell to chunk file + serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); + } + } + } diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java index 0c76b57b..e259a8bb 100644 --- a/src/main/java/electrosphere/server/datacell/Realm.java +++ b/src/main/java/electrosphere/server/datacell/Realm.java @@ -169,5 +169,13 @@ public class Realm { collisionEngine.clearCollidableImpulseLists(); } + /** + * Saves all server data cells in the realm to a given save + * @param saveName The name of the save + */ + protected void save(String saveName){ + dataCellManager.save(saveName); + } + } diff --git a/src/main/java/electrosphere/server/datacell/RealmManager.java b/src/main/java/electrosphere/server/datacell/RealmManager.java index 13567d65..b571255e 100644 --- a/src/main/java/electrosphere/server/datacell/RealmManager.java +++ b/src/main/java/electrosphere/server/datacell/RealmManager.java @@ -128,5 +128,15 @@ public class RealmManager { return playerToRealmMap.get(player); } + /** + * Saves all cells in all realms in the realm manager + * @param saveName The name of the save + */ + public void save(String saveName){ + for(Realm realm : realms){ + realm.save(saveName); + } + } + } diff --git a/src/main/java/electrosphere/server/datacell/interfaces/DataCellManager.java b/src/main/java/electrosphere/server/datacell/interfaces/DataCellManager.java index b6fa1263..2253cbd6 100644 --- a/src/main/java/electrosphere/server/datacell/interfaces/DataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/interfaces/DataCellManager.java @@ -1,5 +1,7 @@ package electrosphere.server.datacell.interfaces; +import java.util.List; + import org.joml.Vector3d; import org.joml.Vector3i; @@ -68,5 +70,11 @@ public interface DataCellManager { * Unloads playerless chunks. Strategy for doing this is defined per data cell manager. */ public void unloadPlayerlessChunks(); + + /** + * Saves the data cell manager + * @param saveName The name of the save + */ + public void save(String saveName); } diff --git a/src/main/java/electrosphere/server/saves/SaveUtils.java b/src/main/java/electrosphere/server/saves/SaveUtils.java index 0b235881..2f993bd4 100644 --- a/src/main/java/electrosphere/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/server/saves/SaveUtils.java @@ -9,6 +9,7 @@ import electrosphere.server.content.ServerContentManager; import electrosphere.server.db.DatabaseUtils; import electrosphere.server.fluid.generation.ArenaFluidGenerator; import electrosphere.server.fluid.manager.ServerFluidManager; +import electrosphere.server.terrain.generation.ArenaChunkGenerator; import electrosphere.server.terrain.generation.OverworldChunkGenerator; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; @@ -50,6 +51,12 @@ public class SaveUtils { return true; } + + //all directories to create within the save each time it is initialized + private static final String[] directoryStructure = new String[]{ + "/content" + }; + /** * Initializes a save directory, overwrites if one is already there * @param saveName Name of the save @@ -66,12 +73,32 @@ public class SaveUtils { //we for some unknown reason, couldn't make the save dir return false; } + //create full directory structure + for(String subDir : directoryStructure){ + String fullPath = dirPath + subDir; + if(!FileUtils.createDirectory(fullPath)){ + //we for some unknown reason, couldn't make the save dir + return false; + } + } //init db file if(!DatabaseUtils.initCentralDBFile(dirPath)){ return false; } return true; } + + /** + * Deletes a save + * @param saveName The name of the save + */ + public static void deleteSave(String saveName){ + String dirPath = deriveSaveDirectoryPath(saveName); + //check if exists + if(FileUtils.checkFileExists(dirPath)){ + FileUtils.recursivelyDelete(dirPath); + } + } @Deprecated @@ -94,6 +121,7 @@ public class SaveUtils { Globals.dbController.connect(dbFilePath); if(!saveName.equals("arena")){ Globals.serverWorldData = FileUtils.loadObjectFromSavePath(saveName, "world.json", ServerWorldData.class); + Globals.serverTerrainManager = new ServerTerrainManager(Globals.serverWorldData.getWorldSizeDiscrete(), 1, 0, 0, new ArenaChunkGenerator()); Globals.serverTerrainManager.load(saveName); Globals.serverFluidManager = new ServerFluidManager(Globals.serverTerrainManager, 2000, 50, 0, 0, new ArenaFluidGenerator()); } @@ -129,7 +157,7 @@ public class SaveUtils { Globals.serverFluidManager = new ServerFluidManager(Globals.serverTerrainManager, 2000, 50, 0.0f, 0, new ArenaFluidGenerator()); SaveUtils.loadTerrainAndDB(currentSaveName); Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager); - Globals.serverContentManager = ServerContentManager.createServerContentManager(); + Globals.serverContentManager = ServerContentManager.createServerContentManager(true); Globals.realmManager.createGriddedRealm(Globals.serverWorldData); return true; } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java index 157744b9..10b107db 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java @@ -117,14 +117,16 @@ public class ServerTerrainManager { * @param saveName The name of the save */ public void save(String saveName){ - ByteBuffer buffer = ByteBuffer.allocate(model.getElevation().length * model.getElevation()[0].length * 4); - FloatBuffer floatView = buffer.asFloatBuffer(); - for(int x = 0; x < model.getElevation().length; x++){ - floatView.put(model.getElevation()[x]); + if(model != null){ + ByteBuffer buffer = ByteBuffer.allocate(model.getElevation().length * model.getElevation()[0].length * 4); + FloatBuffer floatView = buffer.asFloatBuffer(); + for(int x = 0; x < model.getElevation().length; x++){ + floatView.put(model.getElevation()[x]); + } + floatView.flip(); + FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model); + FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array()); } - floatView.flip(); - FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array()); - FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model); //for each chunk, save via disk map for(String chunkKey : chunkCacheContents){ ServerTerrainChunk chunk = chunkCache.get(chunkKey); @@ -142,18 +144,20 @@ public class ServerTerrainManager { */ public void load(String saveName){ //load terrain model - model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class); - chunkGenerator.setModel(model); - byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat"); - ByteBuffer buffer = ByteBuffer.wrap(data); - FloatBuffer floatView = buffer.asFloatBuffer(); - float[][] elevation = new float[Globals.serverWorldData.getWorldSizeDiscrete()][Globals.serverWorldData.getWorldSizeDiscrete()]; - for(int x = 0; x < Globals.serverWorldData.getWorldSizeDiscrete(); x++){ - for(int y = 0; y < Globals.serverWorldData.getWorldSizeDiscrete(); y++){ - elevation[x][y] = floatView.get(); + if(FileUtils.getSaveFile(saveName, "./terrain.json").exists()){ + model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class); + chunkGenerator.setModel(model); + byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat"); + ByteBuffer buffer = ByteBuffer.wrap(data); + FloatBuffer floatView = buffer.asFloatBuffer(); + float[][] elevation = new float[Globals.serverWorldData.getWorldSizeDiscrete()][Globals.serverWorldData.getWorldSizeDiscrete()]; + for(int x = 0; x < Globals.serverWorldData.getWorldSizeDiscrete(); x++){ + for(int y = 0; y < Globals.serverWorldData.getWorldSizeDiscrete(); y++){ + elevation[x][y] = floatView.get(); + } } + model.setElevationArray(elevation); } - model.setElevationArray(elevation); //load chunk disk map chunkDiskMap = new ChunkDiskMap(); chunkDiskMap.init(saveName); diff --git a/src/main/java/electrosphere/util/FileUtils.java b/src/main/java/electrosphere/util/FileUtils.java index c352a490..11239b34 100644 --- a/src/main/java/electrosphere/util/FileUtils.java +++ b/src/main/java/electrosphere/util/FileUtils.java @@ -264,6 +264,17 @@ public class FileUtils { ex.printStackTrace(); } } + + /** + * Checks if a given file exists in a given save + * @param saveName the name of the save + * @param filePath The path to the file RELATIVE TO THE SAVE DIRECTORY + * @return true if it exists, false otherwise + */ + public static boolean checkSavePathExists(String saveName, String filePath){ + String sanitizedFilePath = sanitizeFilePath(filePath); + return getSaveFile(saveName,sanitizedFilePath).exists(); + } /** * Checks if a directory exists