diff --git a/assets/Scripts/engine/engine-init.ts b/assets/Scripts/engine/engine-init.ts index f7a4b4ca..a9e8bd9d 100644 --- a/assets/Scripts/engine/engine-init.ts +++ b/assets/Scripts/engine/engine-init.ts @@ -4,6 +4,7 @@ import { HookManager } from '/Scripts/engine/hooks/hook-manager' import { SceneLoader } from '/Scripts/engine/scene/scene-loader' import { Engine } from '/Scripts/types/engine' import { clientHooks } from '/Scripts/client/clienthooks' +import { ChunkGeneratorManager } from '/Scripts/server/chunk/chunkgeneratormanager' /** * The core engine values @@ -13,6 +14,7 @@ export const engine: Engine = { singletons: {}, hookManager: new HookManager(), sceneLoader: new SceneLoader(), + chunkGeneratorManager: new ChunkGeneratorManager(), } /** @@ -25,6 +27,7 @@ export const ENGINE_onInit = () => { let client: NamespaceClient = Client engine.sceneLoader.engine = engine engine.hookManager.engine = engine + engine.chunkGeneratorManager.engine = engine //load global hooks clientHooks.forEach(hook => { diff --git a/assets/Scripts/server/chunk/chunkgeneratormanager.ts b/assets/Scripts/server/chunk/chunkgeneratormanager.ts new file mode 100644 index 00000000..c5dad04e --- /dev/null +++ b/assets/Scripts/server/chunk/chunkgeneratormanager.ts @@ -0,0 +1,39 @@ +import { TestGen } from "/Scripts/server/chunk/generators/testgen" +import { Engine } from "/Scripts/types/engine" +import { ChunkGenerator, VoxelFunction } from "/Scripts/types/host/server/chunk/chunkgenerator" + + + +/** + * Manages all the chunk generators defined script-side + */ +export class ChunkGeneratorManager { + + /** + * The parent engine object + */ + engine: Engine + + /** + * The list of registered chunk generators + */ + readonly registeredGenerators: ChunkGenerator[] = [ + TestGen, + ] + + /** + * Gets the voxel function for the tag + * @param tag The tag + * @returns The voxel function if it exists, null otherwise + */ + readonly getVoxelFunction = (tag: string): VoxelFunction => { + let rVal: VoxelFunction = null + this.registeredGenerators.forEach(generator => { + if(generator.getTag() === tag){ + rVal = generator.getVoxelFunction(this.engine) + } + }) + return rVal + } + +} diff --git a/assets/Scripts/server/chunk/generators/testgen.ts b/assets/Scripts/server/chunk/generators/testgen.ts new file mode 100644 index 00000000..89bbac55 --- /dev/null +++ b/assets/Scripts/server/chunk/generators/testgen.ts @@ -0,0 +1,159 @@ +import { Engine } from "/Scripts/types/engine"; +import { CHUNK_WIDTH, ChunkGenerator, GeneratedVoxel, VoxelFunction } from "/Scripts/types/host/server/chunk/chunkgenerator"; +import { BiomeData } from "/Scripts/types/host/server/data/biomedata"; + + +/** + * Converts a chunk coordinate to a real coordinate + * @param chunk The chunk pos + * @param world The world pos + * @returns The real pos + */ +const voxelToReal = (chunk: number, world: number) => { + return world * CHUNK_WIDTH + chunk +} + +/** + * Gets the voxel on the surface + * @return The voxel + */ +const getSurfaceVoxel = ( + voxel: GeneratedVoxel, + worldX: number, worldY: number, worldZ: number, + chunkX: number, chunkY: number, chunkZ: number, + realX: number, realY: number, realZ: number, + surfacePercent: number, + surfaceHeight: number, + surfaceBiome: BiomeData +) => { + voxel.weight = surfacePercent * 2 - 1; + voxel.type = 2; + return voxel; +} + +/** + * Gets the voxel below the surface + * @return The voxel + */ +const getSubsurfaceVoxel = ( + voxel: GeneratedVoxel, + worldX: number, worldY: number, worldZ: number, + chunkX: number, chunkY: number, chunkZ: number, + realX: number, realY: number, realZ: number, + surfacePercent: number, + surfaceHeight: number, + surfaceBiome: BiomeData +) => { + if(realY < surfaceHeight - 5){ + voxel.weight = 1; + voxel.type = 6; + } else { + voxel.weight = 1; + voxel.type = 1; + } + return voxel; +} + +/** + * Gets the voxel above the service + * @return The voxel + */ +const getOverSurfaceVoxel = ( + voxel: GeneratedVoxel, + worldX: number, worldY: number, worldZ: number, + chunkX: number, chunkY: number, chunkZ: number, + realX: number, realY: number, realZ: number, + surfacePercent: number, + surfaceHeight: number, + surfaceBiome: BiomeData +) => { + voxel.weight = -1; + voxel.type = 0; + return voxel; +} + +/** + * A test generator + */ +export const TestGen: ChunkGenerator = { + + /** + * Gets the tag for this generator + * @returns The tag + */ + getTag: () => "test", + + /** + * The elevation function + * @param worldX The world x coordinate + * @param worldZ The world z coordinate + * @param chunkX The chunk x coordinate + * @param chunkZ The chunk z coordinate + */ + getElevation: (worldX: number, worldZ: number, chunkX: number, chunkZ: number): number => { + return 1 + }, + + /** + * Gets the function to actually get voxels + * @param engine The engine reference + */ + getVoxelFunction: ( + engine: Engine + ): VoxelFunction => { + const rVal = ( + worldX: number, worldY: number, worldZ: number, + chunkX: number, chunkY: number, chunkZ: number, + stride: number, + surfaceHeight: number, + surfaceBiome: BiomeData + ): GeneratedVoxel => { + let rVal: GeneratedVoxel = { + type: 0, + weight: -1, + } + + const realX = voxelToReal(chunkX,worldX) + const realY = voxelToReal(chunkY,worldY) + const realZ = voxelToReal(chunkZ,worldZ) + const strideMultiplier = engine.classes.math.static.pow(2,stride) + // const strideMultiplier = 1 + const heightDiff = realY - surfaceHeight + const surfacePercent = (surfaceHeight - realY) / strideMultiplier + + if(heightDiff < -strideMultiplier){ + return getSubsurfaceVoxel( + rVal, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ, + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } else if(heightDiff > 0) { + return getOverSurfaceVoxel( + rVal, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ,- + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } else { + return getSurfaceVoxel( + rVal, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ, + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } + } + return rVal + }, + +} \ No newline at end of file diff --git a/assets/Scripts/types/engine.ts b/assets/Scripts/types/engine.ts index d8362323..628e60f1 100644 --- a/assets/Scripts/types/engine.ts +++ b/assets/Scripts/types/engine.ts @@ -1,5 +1,6 @@ import { HookManager } from "/Scripts/engine/hooks/hook-manager"; import { SceneLoader } from "/Scripts/engine/scene/scene-loader"; +import { ChunkGeneratorManager } from "/Scripts/server/chunk/chunkgeneratormanager"; import { SingletonsMap } from "/Scripts/types/host/singletons"; import { StaticClasses } from "/Scripts/types/host/static-classes"; @@ -29,5 +30,10 @@ export interface Engine { */ readonly sceneLoader: SceneLoader, + /** + * The chunk generator manager + */ + readonly chunkGeneratorManager: ChunkGeneratorManager, + } diff --git a/assets/Scripts/types/host/server/chunk/chunkgenerator.ts b/assets/Scripts/types/host/server/chunk/chunkgenerator.ts new file mode 100644 index 00000000..81753229 --- /dev/null +++ b/assets/Scripts/types/host/server/chunk/chunkgenerator.ts @@ -0,0 +1,95 @@ +import { Engine } from "/Scripts/types/engine"; +import { BiomeData } from "/Scripts/types/host/server/data/biomedata"; + + +/** + * The width of a chunk + */ +export const CHUNK_WIDTH = 16; + +/** + * A voxel generated by a ChunkGenerator + */ +export interface GeneratedVoxel { + + /** + * The id of the voxel type + */ + type: number, + + /** + * The weight of the voxel + */ + weight: number, +} + +/** +* Gets a voxel for a given position +* @param worldX The world x coordinate of the chunk +* @param worldY The world y coordinate of the chunk +* @param worldZ The world z coordinate of the chunk +* @param chunkX The chunk x coordinate +* @param chunkY The chunk y coordinate +* @param chunkZ The chunk z coordinate +* @param stride The stride of the data (this is used for LOD, it ranges 0->4 with 0 being the highest level of detail and 4 being the lowest) +* @param surfaceHeight The height of the surface at the given x,z coordinate +* @param terrainModel The terrain model +* @param surfaceBiome The surface biome +* @returns The voxel at the provided position +*/ +export type VoxelFunction = ( + worldX: number, worldY: number, worldZ: number, + chunkX: number, chunkY: number, chunkZ: number, + stride: number, + surfaceHeight: number, + surfaceBiome: BiomeData +) => GeneratedVoxel; + + + +/** +* Gets a chunk for a given world position +* @param worldX The world x coordinate of the chunk +* @param worldY The world y coordinate of the chunk +* @param worldZ The world z coordinate of the chunk +* @param stride The stride of the data (this is used for LOD, it ranges 0->4 with 0 being the highest level of detail and 4 being the lowest) +* @param surfaceHeight The height of the surface at the given x,z coordinate +* @param terrainModel The terrain model +* @param surfaceBiome The surface biome +* @returns The voxel at the provided position +*/ +export type ChunkFunction = ( + worldX: number, worldY: number, worldZ: number, + chunkX: number, chunkY: number, chunkZ: number, + stride: number, + surfaceHeight: number, + surfaceBiome: BiomeData +) => number[][][]; + +/** + * A chunk generator + */ +export interface ChunkGenerator { + + /** + * Gets the tag for this generator + * @returns The tag + */ + getTag: () => string + + /** + * Retrieves the elevation for the world at a given x,z coordinate + * @param worldX The world x coordinate + * @param worldZ The world z coordinate + * @param chunkX The x coordinate of the chunk within the specified world coordinate + * @param chunkZ The z coordinate of the chunk within the specified world coordinate + * @returns The elevation at that specific position + */ + getElevation: (worldX: number, worldZ: number, chunkX: number, chunkZ: number) => number + + /** + * The function to get a voxel for a given position + */ + getVoxelFunction: (engine: Engine) => VoxelFunction + +} \ No newline at end of file diff --git a/assets/Scripts/types/host/server/data/biomedata.ts b/assets/Scripts/types/host/server/data/biomedata.ts new file mode 100644 index 00000000..d0d11abd --- /dev/null +++ b/assets/Scripts/types/host/server/data/biomedata.ts @@ -0,0 +1,95 @@ + + +/** + * Describes a type of foliage that can be generated in a biome + */ +export interface BiomeFoliageDescription { + + /** + * The list of entity IDs in the type + */ + entityIDs: string[], + + /** + * How regular the placement of the foliage is. + * Low values make the placement more random. + * High values make the placement more orderly (aligned with a grid). + */ + regularity: number, + + /** + * The percentage of the ground to cover with foliage + */ + threshold: number, + + /** + * The priority of this type in particular + */ + priority: number, + + /** + * The scale of the noise used to place foliage + */ + scale: number, + +} + +/** + * The parameters for generating the surface of a biome + */ +export interface BiomeSurfaceGenerationParams { + + /** + * The tag for the generation algorithm to generate the surface of the biome + */ + surfaceGenTag: string, + + /** + * The offset added to the generated heightfield to get the final height of the surface at a given position. + * This is most useful for cases like plateaus where you really want to put the surface noise higher than its surroundings. + */ + heightOffset: number, + + /** + * The different foliage types that can be generated on this surface + */ + foliageDescriptions: BiomeFoliageDescription[], + +} + +/** + * Biome data + */ +export interface BiomeData { + + /** + * The id of the biome type + */ + id: string, + + /** + * The display name of the biome + */ + displayName: string, + + /** + * True if this is an aerial biome + */ + isAerial: boolean, + + /** + * True if this is a surface biome + */ + isSurface: boolean, + + /** + * True if this is a subterranean biome + */ + isSubterranean: boolean, + + /** + * The parameters for generating the surface of this biome + */ + surfaceGenerationParams: BiomeSurfaceGenerationParams, + +} \ No newline at end of file diff --git a/assets/Scripts/types/host/static-classes.ts b/assets/Scripts/types/host/static-classes.ts index 0b927875..6541ddad 100644 --- a/assets/Scripts/types/host/static-classes.ts +++ b/assets/Scripts/types/host/static-classes.ts @@ -46,6 +46,11 @@ export interface StaticClasses { */ readonly levelEditorUtils?: Class, + /** + * Math functions + */ + readonly math?: Class + } /** diff --git a/assets/Scripts/types/host/util/mathutils.ts b/assets/Scripts/types/host/util/mathutils.ts new file mode 100644 index 00000000..24a4cef0 --- /dev/null +++ b/assets/Scripts/types/host/util/mathutils.ts @@ -0,0 +1,14 @@ +/** + * Math functions + */ +export interface Math { + + /** + * Computes an arbitrary power of a number + * @param num1 The number + * @param num2 The power + * @returns The result of the power + */ + readonly pow: (num1: number, num2: number) => number + +} \ No newline at end of file diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 9de885c5..c91408e2 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -963,6 +963,15 @@ Fix foliage rendering Fix async physics gen on client Convert volumetric + shadow pass to entity tags +(11/09/2024) +Fix eighth res chunk radius +Fix chunk gen debug ui regenerate button +Script-defined chunk generators +Script engine synchronization utility +Convert ScriptEngine to service +ScriptEngine full re-initialization signal +Add surface width to test generator + # TODO diff --git a/src/main/java/electrosphere/client/script/ClientScriptUtils.java b/src/main/java/electrosphere/client/script/ClientScriptUtils.java index 55d53ab1..2a90e880 100644 --- a/src/main/java/electrosphere/client/script/ClientScriptUtils.java +++ b/src/main/java/electrosphere/client/script/ClientScriptUtils.java @@ -14,9 +14,11 @@ public class ClientScriptUtils { * @param args The arguments provided alongside the signal */ public static void fireSignal(String signalName, Object ... args){ - if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){ - Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args); - } + Globals.scriptEngine.executeSynchronously(() -> { + if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){ + Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args); + } + }); } } diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index 589f0952..bc817d90 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -97,6 +97,9 @@ public class ClientTerrainCache { this.cacheList.clear(); this.cacheMapFullRes.clear(); this.cacheMapHalfRes.clear(); + this.cacheMapQuarterRes.clear(); + this.cacheMapEighthRes.clear(); + this.cacheMapSixteenthRes.clear(); this.chunkPositionMap.clear(); lock.release(); } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 603f748b..3ac46e7e 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -98,6 +98,11 @@ public class ClientDrawCellManager { */ Map,Boolean> evaluationMap = new HashMap,Boolean>(); + /** + * All draw cells currently tracked + */ + List activeCells = new LinkedList(); + /** * Tracks whether the cell manager updated last frame or not */ @@ -227,6 +232,7 @@ public class ClientDrawCellManager { child.getMinBound().z ); DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos); + activeCells.add(drawCell); child.convertToLeaf(drawCell); evaluationMap.put(child,true); }); @@ -246,6 +252,7 @@ public class ClientDrawCellManager { //do creations DrawCell drawCell = DrawCell.generateTerrainCell(node.getMinBound()); + activeCells.add(drawCell); newLeaf.convertToLeaf(drawCell); //update neighbors @@ -544,7 +551,7 @@ public class ClientDrawCellManager { ( node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && - this.getMinDistance(pos, node) <= EIGHTH_RES_DIST + this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST ) || ( @@ -591,7 +598,7 @@ public class ClientDrawCellManager { ( node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD && - this.getMinDistance(pos, node) <= EIGHTH_RES_DIST + this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST ) || ( @@ -631,6 +638,7 @@ public class ClientDrawCellManager { node.getChildren().forEach(child -> recursivelyDestroy(child)); } if(node.getData() != null){ + activeCells.remove(node.getData()); node.getData().destroy(); } } @@ -659,6 +667,7 @@ public class ClientDrawCellManager { * Evicts all cells */ public void evictAll(){ + this.recursivelyDestroy(this.chunkTree.getRoot()); this.chunkTree.clear(); } diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiTestGen.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiTestGen.java index 52400574..0c0157a8 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiTestGen.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiTestGen.java @@ -1,6 +1,7 @@ package electrosphere.client.ui.menu.debug; import electrosphere.engine.Globals; +import electrosphere.engine.signal.Signal.SignalType; import electrosphere.renderer.ui.imgui.ImGuiWindow; import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; import electrosphere.server.datacell.GriddedDataCellManager; @@ -45,10 +46,19 @@ public class ImGuiTestGen { //regenerate the test area if(ImGui.button("Regenerate")){ - GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager(); - gridManager.evictAll(); - Globals.clientDrawCellManager.evictAll(); - Globals.clientTerrainManager.evictAll(); + //recompile script engine + Globals.signalSystem.post(SignalType.SCRIPT_RECOMPILE, () -> { + + //run once script recompilation has completed + + //clear server + GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager(); + gridManager.evictAll(); + + //clear client + Globals.clientDrawCellManager.evictAll(); + Globals.clientTerrainManager.evictAll(); + }); } //set macro data scale in terrain model @@ -82,7 +92,7 @@ public class ImGuiTestGen { } //Toggles whether the client draws cell manager should update or not - if(ImGui.button("Toggle ClientDrawCellManager updates")){ + if(ImGui.button("Toggle ClientDrawCellManager updates " + (Globals.clientDrawCellManager.getShouldUpdate() ? "off" : "on"))){ Globals.clientDrawCellManager.setShouldUpdate(!Globals.clientDrawCellManager.getShouldUpdate()); } diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index f6d1618e..28d200d1 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -481,8 +481,6 @@ public class Globals { skyboxColors = new ArrayList(); //load asset manager assetManager = new AssetManager(); - //script engine - scriptEngine = new ScriptEngine(); //ai manager aiManager = new AIManager(0); //realm & data cell manager @@ -526,6 +524,7 @@ public class Globals { Globals.signalSystem = (SignalSystem)serviceManager.registerService(new SignalSystem()); Globals.elementService = (ElementService)serviceManager.registerService(new ElementService()); Globals.particleService = (ParticleService)serviceManager.registerService(new ParticleService()); + Globals.scriptEngine = (ScriptEngine)serviceManager.registerService(new ScriptEngine()); serviceManager.instantiate(); // //End service manager @@ -537,6 +536,7 @@ public class Globals { Globals.signalSystem.registerService(SignalType.YOGA_DESTROY, Globals.elementService); Globals.signalSystem.registerService(SignalType.UI_MODIFICATION, Globals.elementService); Globals.signalSystem.registerService(SignalType.RENDERING_ENGINE_READY, Globals.particleService); + Globals.signalSystem.registerService(SignalType.SCRIPT_RECOMPILE, Globals.scriptEngine); } diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index 8cca6c93..35b4461e 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -291,7 +291,13 @@ public class Main { RenderingEngine.recaptureIfNecessary(); Globals.profiler.endCpuSample(); } - + + + /// + /// S Y N C H R O N O U S S I G N A L H A N D L I N G + /// + Globals.scriptEngine.handleAllSignals(); + /// /// diff --git a/src/main/java/electrosphere/engine/loadingthreads/ChunkGenerationTestLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ChunkGenerationTestLoading.java index 760d330c..a34f2f70 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ChunkGenerationTestLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ChunkGenerationTestLoading.java @@ -14,6 +14,7 @@ import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.server.ServerConnectionHandler; import electrosphere.renderer.ui.elements.Window; import electrosphere.server.saves.SaveUtils; +import electrosphere.server.terrain.generation.TestGenerationChunkGenerator; /** * Loads the chunk generation testing realm @@ -42,6 +43,17 @@ public class ChunkGenerationTestLoading { loadingWindow.setVisible(true); }); + //wait on script engine to load + if(TestGenerationChunkGenerator.DEFAULT_USE_JAVASCRIPT){ + WindowUtils.updateLoadingWindow("Waiting on scripting engine"); + while(!Globals.scriptEngine.isInitialized()){ + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException ex) {} + } + WindowUtils.updateLoadingWindow("LOADING"); + } + String saveName = "generation_testing"; diff --git a/src/main/java/electrosphere/engine/loadingthreads/EngineInitLoading.java b/src/main/java/electrosphere/engine/loadingthreads/EngineInitLoading.java index 8cb25256..021bac32 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/EngineInitLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/EngineInitLoading.java @@ -11,7 +11,7 @@ public class EngineInitLoading { * Loads the core assets of the scripting engine from disk and initializes the engine */ protected static void loadScriptingEngine(Object[] params){ - Globals.scriptEngine.init(); + Globals.scriptEngine.initScripts(); } } diff --git a/src/main/java/electrosphere/engine/signal/Signal.java b/src/main/java/electrosphere/engine/signal/Signal.java index fcabe215..c9593d60 100644 --- a/src/main/java/electrosphere/engine/signal/Signal.java +++ b/src/main/java/electrosphere/engine/signal/Signal.java @@ -37,6 +37,11 @@ public class Signal { REQUEST_CHUNK_EDIT, CHUNK_EDITED, + // + //Script + // + SCRIPT_RECOMPILE, + } /** diff --git a/src/main/java/electrosphere/entity/scene/SceneLoader.java b/src/main/java/electrosphere/entity/scene/SceneLoader.java index f113f173..442a67c4 100644 --- a/src/main/java/electrosphere/entity/scene/SceneLoader.java +++ b/src/main/java/electrosphere/entity/scene/SceneLoader.java @@ -124,8 +124,11 @@ public class SceneLoader { } //load scripts if(!isLevelEditor && file.getInitScriptPath() != null){ - int sceneInstanceId = Globals.scriptEngine.initScene(file.getInitScriptPath()); - realm.setSceneInstanceId(sceneInstanceId); + Realm finalRealm = realm; + Globals.scriptEngine.executeSynchronously(() -> { + int sceneInstanceId = Globals.scriptEngine.initScene(file.getInitScriptPath()); + finalRealm.setSceneInstanceId(sceneInstanceId); + }); } //TODO: integrate scripts for client side of scenes // for(String scriptPath : file.getScriptPaths()){ diff --git a/src/main/java/electrosphere/game/data/biome/BiomeData.java b/src/main/java/electrosphere/game/data/biome/BiomeData.java index 47fad22c..bac641fe 100644 --- a/src/main/java/electrosphere/game/data/biome/BiomeData.java +++ b/src/main/java/electrosphere/game/data/biome/BiomeData.java @@ -2,6 +2,8 @@ package electrosphere.game.data.biome; import java.util.List; +import org.graalvm.polyglot.HostAccess.Export; + /** * Data about a given biome */ @@ -10,11 +12,13 @@ public class BiomeData { /** * The id of the biome */ + @Export String id; /** * The display name of the biome */ + @Export String displayName; /** @@ -25,21 +29,25 @@ public class BiomeData { /** * True if this region applies above the surface */ + @Export Boolean isAerial; /** * True if this region applies to the surface */ + @Export Boolean isSurface; /** * True if this region applies below the surface */ + @Export Boolean isSubterranean; /** * The surface generation params */ + @Export BiomeSurfaceGenerationParams surfaceGenerationParams; diff --git a/src/main/java/electrosphere/game/data/biome/BiomeFoliageDescription.java b/src/main/java/electrosphere/game/data/biome/BiomeFoliageDescription.java index 58936a26..779ba782 100644 --- a/src/main/java/electrosphere/game/data/biome/BiomeFoliageDescription.java +++ b/src/main/java/electrosphere/game/data/biome/BiomeFoliageDescription.java @@ -2,34 +2,41 @@ package electrosphere.game.data.biome; import java.util.List; +import org.graalvm.polyglot.HostAccess.Export; + /** * Describes behavior for spawning a specific type of foliage in the biome */ public class BiomeFoliageDescription { /** - * The liust of entity IDs of this foliage type in particular + * The list of entity IDs of this foliage type in particular */ + @Export List entityIDs; /** * How regular the placement of foliage is. Low values will create very uneven foliage, while high values will place them along a grid. */ + @Export Double regularity; /** * The percentage of the ground to cover with foliage */ + @Export Double threshold; /** * The priority of this floor element in particular */ + @Export Double priority; /** * The scale of the noise used to place foliage */ + @Export Double scale; /** diff --git a/src/main/java/electrosphere/game/data/biome/BiomeSurfaceGenerationParams.java b/src/main/java/electrosphere/game/data/biome/BiomeSurfaceGenerationParams.java index 9c7e0a75..d497da92 100644 --- a/src/main/java/electrosphere/game/data/biome/BiomeSurfaceGenerationParams.java +++ b/src/main/java/electrosphere/game/data/biome/BiomeSurfaceGenerationParams.java @@ -2,6 +2,8 @@ package electrosphere.game.data.biome; import java.util.List; +import org.graalvm.polyglot.HostAccess.Export; + /** * Params for the surface generation algorithm */ @@ -10,11 +12,13 @@ public class BiomeSurfaceGenerationParams { /** * The tag for the generation algorithm for generating the surface */ + @Export String surfaceGenTag; /** * The offset from baseline for height generation with this biome */ + @Export Float heightOffset; /** @@ -25,6 +29,7 @@ public class BiomeSurfaceGenerationParams { /** * The list of foliage descriptions available to this biome type */ + @Export List foliageDescriptions; /** diff --git a/src/main/java/electrosphere/game/server/world/ServerWorldData.java b/src/main/java/electrosphere/game/server/world/ServerWorldData.java index 192058df..187568c6 100644 --- a/src/main/java/electrosphere/game/server/world/ServerWorldData.java +++ b/src/main/java/electrosphere/game/server/world/ServerWorldData.java @@ -153,7 +153,7 @@ public class ServerWorldData { //test terrain gen { - TestGenerationChunkGenerator chunkGen = new TestGenerationChunkGenerator(serverWorldData); + TestGenerationChunkGenerator chunkGen = new TestGenerationChunkGenerator(serverWorldData, TestGenerationChunkGenerator.DEFAULT_USE_JAVASCRIPT); serverTerrainManager = new ServerTerrainManager(serverWorldData, 0, chunkGen); serverTerrainManager.genTestData(chunkGen); } diff --git a/src/main/java/electrosphere/script/ScriptEngine.java b/src/main/java/electrosphere/script/ScriptEngine.java index 472455ca..1ce9beb6 100644 --- a/src/main/java/electrosphere/script/ScriptEngine.java +++ b/src/main/java/electrosphere/script/ScriptEngine.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Engine; @@ -19,15 +20,18 @@ import electrosphere.client.ui.menu.script.ScriptMenuUtils; import electrosphere.client.ui.menu.tutorial.TutorialMenus; import electrosphere.engine.Globals; import electrosphere.engine.Main; +import electrosphere.engine.signal.Signal; +import electrosphere.engine.signal.SignalServiceImpl; import electrosphere.logger.LoggerInterface; import electrosphere.script.translation.JSServerUtils; +import electrosphere.script.utils.ScriptMathInterface; import electrosphere.util.FileUtils; import electrosphere.util.math.SpatialMathUtils; /** * Interface for executing scripts in the game engine */ -public class ScriptEngine { +public class ScriptEngine extends SignalServiceImpl { //the default namespaces for public static String SCRIPT_NAMESPACE_ENGINE = "engine"; //namespace for the engine functions exposed to the script engine @@ -67,6 +71,11 @@ public class ScriptEngine { */ boolean initialized = false; + /** + * Locks the script engine to enforce synchronization + */ + ReentrantLock lock = new ReentrantLock(); + //The files that are loaded on init to bootstrap the script engine static final String[] filesToLoadOnInit = new String[]{ //polyfills @@ -98,6 +107,7 @@ public class ScriptEngine { {"menuUtils",ScriptMenuUtils.class}, {"voxelUtils",ScriptClientVoxelUtils.class}, {"levelEditorUtils",ScriptLevelEditorUtils.class}, + {"math",ScriptMathInterface.class}, }; //singletons from the host that are provided to the javascript context @@ -107,10 +117,18 @@ public class ScriptEngine { {"loggerScripts",LoggerInterface.loggerScripts}, }; + + /** + * Constructor + */ + public ScriptEngine(){ + super("ScriptEngine"); + } + /** * Initializes the engine */ - public void init(){ + public void initScripts(){ //init datastructures sourceMap = new HashMap(); initialized = false; @@ -320,6 +338,22 @@ public class ScriptEngine { invokeMemberFunction("COMPILER", "run"); } + /** + * Recompiles the scripting engine + */ + private void recompile(Runnable onCompletion){ + Thread recompileThread = new Thread(() -> { + Globals.scriptEngine.executeSynchronously(() -> { + this.initScripts(); + }); + if(onCompletion != null){ + onCompletion.run(); + } + }); + recompileThread.setName("Recompile Script Engine"); + recompileThread.start(); + } + /** * Initializes a scene script * @param scenePath The scene's init script path @@ -398,6 +432,40 @@ public class ScriptEngine { invokeFunction("require", filePath); } + /** + * Invokes a function on a member of arbitrary depth on the engine object + * @param memberName The member name + * @param functionName The function's name + * @param className The class of the expected return value + * @param args The args to pass to the function + * @return The results of the invocation or null if there was no result + */ + public Value invokeEngineMember(String memberName, String functionName, Object ... args){ + Value member = this.engineObject.getMember(memberName); + if(member == null){ + throw new Error("Member is null!"); + } + Value function = member.getMember(functionName); + if(function == null || !function.canExecute()){ + throw new Error("Function is not executable! " + function); + } + Value executionResult = function.execute(args); + if(executionResult == null){ + return null; + } + return executionResult; + } + + /** + * Executes some code synchronously that requires script engine access + * @param function The function + */ + public void executeSynchronously(Runnable function){ + lock.lock(); + function.run(); + lock.unlock(); + } + /** * Defines host members within javascript context */ @@ -431,5 +499,23 @@ public class ScriptEngine { return this.initialized; } + + @Override + public boolean handle(Signal signal){ + boolean rVal = false; + switch(signal.getType()){ + case SCRIPT_RECOMPILE: { + if(signal.getData() != null && signal.getData() instanceof Runnable){ + this.recompile((Runnable)signal.getData()); + } else { + this.recompile(null); + } + rVal = true; + } break; + default: { + } break; + } + return rVal; + } } diff --git a/src/main/java/electrosphere/script/utils/ScriptMathInterface.java b/src/main/java/electrosphere/script/utils/ScriptMathInterface.java new file mode 100644 index 00000000..58a2362c --- /dev/null +++ b/src/main/java/electrosphere/script/utils/ScriptMathInterface.java @@ -0,0 +1,21 @@ +package electrosphere.script.utils; + +import org.graalvm.polyglot.HostAccess.Export; + +/** + * Script access to specific math functions + */ +public class ScriptMathInterface { + + /** + * Power function + * @param val1 The number + * @param val2 The exponent + * @return The power + */ + @Export + public static double pow(double val1, double val2){ + return Math.pow(val1,val2); + } + +} diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java index 1801eaee..32dddab7 100644 --- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java @@ -421,9 +421,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager /** * Evicts all loaded chunks. - *

- * Note: Does not save to disk. - *

*/ public void evictAll(){ //TODO: improve to make have less performance impact diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java index bfb1258c..fbc9219f 100644 --- a/src/main/java/electrosphere/server/datacell/Realm.java +++ b/src/main/java/electrosphere/server/datacell/Realm.java @@ -274,11 +274,13 @@ public class Realm { */ public void fireSignal(String signalName, Object ... args){ if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){ - if(this.sceneInstanceId != NO_SCENE_INSTANCE){ - Globals.scriptEngine.fireSignal(signalName, sceneInstanceId, args); - } else { - Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args); - } + Globals.scriptEngine.executeSynchronously(() -> { + if(this.sceneInstanceId != NO_SCENE_INSTANCE){ + Globals.scriptEngine.fireSignal(signalName, sceneInstanceId, args); + } else { + Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args); + } + }); } } diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index dada56cd..aef44752 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -3,6 +3,8 @@ package electrosphere.server.terrain.generation; import java.util.HashMap; import java.util.Map; +import org.graalvm.polyglot.Value; + import electrosphere.engine.Globals; import electrosphere.game.data.biome.BiomeData; import electrosphere.game.data.biome.BiomeSurfaceGenerationParams; @@ -12,6 +14,7 @@ import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator; import electrosphere.server.terrain.generation.heightmap.HillsGen; import electrosphere.server.terrain.generation.heightmap.PlainsGen; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; +import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.models.TerrainModel; @@ -29,6 +32,21 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { * The default biome index */ public static final int DEFAULT_BIOME_INDEX = 1; + + /** + * The width of the surface in number of voxels + */ + public static final int SURFACE_VOXEL_WIDTH = 2; + + /** + * Tag for the test generator + */ + public static final String SCRIPT_GEN_TEST_TAG = "test"; + + /** + * Controls the default setting for whether to use javascript or not + */ + public static final boolean DEFAULT_USE_JAVASCRIPT = false; /** * The terreain model for the generator @@ -45,14 +63,20 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { */ Map tagGeneratorMap = new HashMap(); + /** + * Tracks whether to use javascript generation or not + */ + boolean useJavascript = false; + /** * Constructor */ - public TestGenerationChunkGenerator(ServerWorldData serverWorldData){ + public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){ this.serverWorldData = serverWorldData; registerGenerator(new EmptySkyGen()); registerGenerator(new HillsGen()); registerGenerator(new PlainsGen()); + this.useJavascript = useJavascript; } /** @@ -101,22 +125,61 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { } } - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); - for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ - for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome); - weights[x][y][z] = voxel.weight; - values[x][y][z] = voxel.type; + if(this.useJavascript){ + Globals.scriptEngine.executeSynchronously(() -> { + Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG); + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); + for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + Value result = getVoxelFunc.execute( + finalWorldX, finalWorldY, finalWorldZ, + finalChunkX, finalChunkY, finalChunkZ, + stride, + heightfield[x][z], + surfaceBiome + ); + if(result != null){ + weights[x][y][z] = result.getMember("weight").asFloat(); + values[x][y][z] = result.getMember("type").asInt(); + } + } + } + Globals.profiler.endCpuSample(); } + }); + } else { + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); + for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + GeneratedVoxel voxel = this.getVoxel( + finalWorldX, finalWorldY, finalWorldZ, + finalChunkX, finalChunkY, finalChunkZ, + stride, + heightfield[x][z], + surfaceBiome + ); + if(voxel != null){ + weights[x][y][z] = voxel.weight; + values[x][y][z] = voxel.type; + } + } + } + Globals.profiler.endCpuSample(); } - Globals.profiler.endCpuSample(); } } catch(Exception ex){ ex.printStackTrace(); @@ -156,16 +219,16 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { * @param chunkX The chunk x pos * @param chunkY The chunk y pos * @param chunkZ The chunk z pos + * @param stride The stride of the data * @param surfaceHeight The height of the surface at x,z - * @param terrainModel The terrain model * @param surfaceBiome The surface biome of the chunk * @return The value of the chunk */ private GeneratedVoxel getVoxel( int worldX, int worldY, int worldZ, int chunkX, int chunkY, int chunkZ, + int stride, float surfaceHeight, - TerrainModel terrainModel, BiomeData surfaceBiome ){ Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.getChunkValue"); @@ -179,24 +242,26 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY); double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ); - double flooredSurfaceHeight = Math.floor(surfaceHeight); + double strideMultiplier = Math.pow(2,stride); + double heightDiff = realY - surfaceHeight; + double surfacePercent = TestGenerationChunkGenerator.getSurfaceWeight(surfaceHeight,realY,strideMultiplier); Globals.profiler.endCpuSample(); - if(realY < surfaceHeight - 1){ + if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){ return getSubsurfaceVoxel( worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, - surfaceHeight, flooredSurfaceHeight, - terrainModel, + surfacePercent, + surfaceHeight, surfaceBiome ); - } else if(realY > flooredSurfaceHeight) { + } else if(heightDiff > 0) { return getOverSurfaceVoxel( worldX, worldY, worldZ, chunkX, chunkY, chunkZ, - realX, realY, realZ, - surfaceHeight, flooredSurfaceHeight, - terrainModel, + realX, realY, realZ,- + surfacePercent, + surfaceHeight, surfaceBiome ); } else { @@ -204,8 +269,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, - surfaceHeight, flooredSurfaceHeight, - terrainModel, + surfacePercent, + surfaceHeight, surfaceBiome ); } @@ -219,12 +284,12 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { int worldX, int worldY, int worldZ, int chunkX, int chunkY, int chunkZ, double realX, double realY, double realZ, - float surfaceHeight, double flooredSurfaceHeight, - TerrainModel terrainModel, + double surfacePercent, + float surfaceHeight, BiomeData surfaceBiome ){ GeneratedVoxel voxel = new GeneratedVoxel(); - voxel.weight = (float)(surfaceHeight - flooredSurfaceHeight) * 2 - 1; + voxel.weight = (float)surfacePercent * 2 - 1; voxel.type = 2; return voxel; } @@ -237,8 +302,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { int worldX, int worldY, int worldZ, int chunkX, int chunkY, int chunkZ, double realX, double realY, double realZ, - float surfaceHeight, double flooredSurfaceHeight, - TerrainModel terrainModel, + double surfacePercent, + float surfaceHeight, BiomeData surfaceBiome ){ GeneratedVoxel voxel = new GeneratedVoxel(); @@ -260,8 +325,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { int worldX, int worldY, int worldZ, int chunkX, int chunkY, int chunkZ, double realX, double realY, double realZ, - float surfaceHeight, double flooredSurfaceHeight, - TerrainModel terrainModel, + double surfacePercent, + float surfaceHeight, BiomeData surfaceBiome ){ GeneratedVoxel voxel = new GeneratedVoxel(); @@ -271,17 +336,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { } /** - * A voxel that was generated + * Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier + * @param surfaceHeight The surface height + * @param realPosY The position of the voxel + * @param strideMultiplier The stride multiplier + * @return The weight of the voxel */ - static class GeneratedVoxel { - /** - * The type of the voxel - */ - int type; - /** - * The weight of the voxel - */ - float weight; + protected static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){ + return ((surfaceHeight - realPosY) / strideMultiplier); } } diff --git a/src/main/java/electrosphere/server/terrain/generation/interfaces/GeneratedVoxel.java b/src/main/java/electrosphere/server/terrain/generation/interfaces/GeneratedVoxel.java new file mode 100644 index 00000000..d43d4e7e --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/interfaces/GeneratedVoxel.java @@ -0,0 +1,22 @@ +package electrosphere.server.terrain.generation.interfaces; + +import org.graalvm.polyglot.HostAccess.Export; + +/** + * A voxel generated by a chunk generator + */ +public class GeneratedVoxel { + + /** + * The type of the voxel + */ + @Export + public int type; + + /** + * The weight of the voxel + */ + @Export + public float weight; + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/interfaces/JSChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/interfaces/JSChunkGenerator.java new file mode 100644 index 00000000..fee6ad19 --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/interfaces/JSChunkGenerator.java @@ -0,0 +1,37 @@ +package electrosphere.server.terrain.generation.interfaces; + +import electrosphere.game.data.biome.BiomeData; + +/** + * A script-defined chunk generator + */ +public interface JSChunkGenerator { + + /** + * Gets the tag for this generator + * @returns The tag + */ + public String getTag(); + + /** + * Retrieves the elevation for the world at a given x,z coordinate + * @param worldX The world x coordinate + * @param worldZ The world z coordinate + * @param chunkX The x coordinate of the chunk within the specified world coordinate + * @param chunkZ The z coordinate of the chunk within the specified world coordinate + * @returns The elevation at that specific position + */ + public float getElevation(int worldX, int worldZ, int chunkX, int chunkZ); + + /** + * The function to get a voxel for a given position + */ + public GeneratedVoxel getVoxel( + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + int stride, + double surfaceHeight, + BiomeData surfaceBiome + ); + +} diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index da685ea8..4467cd7c 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -84,6 +84,9 @@ public class ServerChunkCache { lock.acquireUninterruptibly(); cacheMapFullRes.clear(); cacheMapHalfRes.clear(); + cacheMapQuarterRes.clear(); + cacheMapEighthRes.clear(); + cacheMapSixteenthRes.clear(); lock.release(); } diff --git a/src/test/java/electrosphere/server/terrain/generation/TestGenerationChunkGeneratorTests.java b/src/test/java/electrosphere/server/terrain/generation/TestGenerationChunkGeneratorTests.java new file mode 100644 index 00000000..e0e5e542 --- /dev/null +++ b/src/test/java/electrosphere/server/terrain/generation/TestGenerationChunkGeneratorTests.java @@ -0,0 +1,22 @@ +package electrosphere.server.terrain.generation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import electrosphere.test.annotations.FastTest; +import electrosphere.test.annotations.UnitTest; + +/** + * Unit tests for the test generation chunk generator + */ +public class TestGenerationChunkGeneratorTests { + + @UnitTest + @FastTest + public void getSurfaceWeight_ValueTests(){ + assertEquals(0.5,TestGenerationChunkGenerator.getSurfaceWeight(0.5, 0, 1)); + assertEquals(0.1,TestGenerationChunkGenerator.getSurfaceWeight(0.1, 0, 1)); + assertEquals(0.9,TestGenerationChunkGenerator.getSurfaceWeight(0.9, 0, 1)); + assertEquals(0.95,TestGenerationChunkGenerator.getSurfaceWeight(1.9, 0, 2)); + } + +}