script loading work

This commit is contained in:
austin 2024-07-14 16:00:35 -04:00
parent 14f4607c47
commit da28d13d61
24 changed files with 645 additions and 164 deletions

View File

@ -0,0 +1,10 @@
{
"entities" : [],
"scriptPaths" : [],
"initScriptPath" : "/Scenes/defaultLevel_2/scene.ts",
"realmDescriptor" : {
"type" : "gridded",
"griddedRealmSize" : 2
},
"createSaveInstance" : true
}

View File

@ -0,0 +1,13 @@
import { Scene } from "/Scripts/types/scene";
/**
* The main scene interface
*/
const TestScene1: Scene = {
}
/**
* The scene to export
*/
export default TestScene1

View File

@ -1,29 +1,312 @@
/**
* The map of all source files to their content and compiled value
* The compiler object
*/
let COMPILER_fileMap = { }
let COMPILER = {
//
//
// VIRTUAL FILE SYSTEM
//
//
/**
* The map of all source files to their content and compiled value
*/
fileMap: { },
/**
* The list of all source files to compile
*/
sourceFiles: [ ],
/**
* The top level directory, "/"
*/
topLevelDirectory: {
Scripts: {
compiler: {
"host_access.js": {
content: "",
}
}
}
},
/**
* The current directory, "/"
*/
currentDirectory : { },
/**
* Registers a file with the compiler
* @param {*} fileName The file's name
* @param {*} content The content of the file
* @returns The list of all files that still need to be registered by the host
*/
registerFile: (fileName, content) => [],
/**
* Creates a file object for a given path
* @param {*} fileName The name of the file
* @param {*} content The content of the file
* @returns The file object
*/
createFile: (fileName, content) => null,
/**
* Gets the path for the file
* @param {*} fullyQualifiedFilePath The fully qualified file path
* @returns The array of directories ending with the name of the file
*/
getPath: (fullyQualifiedFilePath) => null,
/**
* Gets the path for the file
* @param {*} fullyQualifiedFilePath The fully qualified file path
* @returns The array of directories ending with the name of the file
*/
getFileByPath: (filePathArray) => null,
/**
* Checks if a file exists
* @param {*} filePathArray The file path array
* @returns true if it exists, false otherwise
*/
fileExists: (filePathArray) => null,
/**
* The callback invoked when the compiler host tries to read a file
* @param {*} fileName The name of the file
* @param {*} languageVersion The language version
* @returns The file if it exists, null otherwise
*/
getSourceFile: (fileName, languageVersion) => null,
//
//
// COMPILATION
//
//
/**
* The compiler options
*/
compilerOptions: { },
/**
* Instructs Typescript to emit the final compiled value
*/
run: () => null,
/**
* Loads a file
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
*/
runFile: (fileName) => null,
/**
* Loads a file
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
*/
printSource: (fileName) => null,
/**
* The typescript compiler host definition
*/
customCompilerHost: null,
/**
* The typescript program
*/
program: null,
}
/**
* The compiled program
* Gets the path for the file
* @param {*} fullyQualifiedFilePath The fully qualified file path
* @returns The array of directories ending with the name of the file
*/
let COMPILER_emitted_value = ''
COMPILER.getPath = (fullyQualifiedFilePath) => {
let modifiedFileName = fullyQualifiedFilePath
//remove leading "/"
if(modifiedFileName.startsWith("/")){
modifiedFileName = modifiedFileName.substring(1)
}
//split
return modifiedFileName.split("/")
}
/**
* Gets a file based on its path array
* @param {*} filePathArray The file path array
* @returns The file
*/
COMPILER.getFileByPath = (filePathArray) => {
let currentFolder = COMPILER.topLevelDirectory
let mutableArray = filePathArray
//illegal state
if(mutableArray?.length < 1){
throw new Error("Trying to get a file with a path array of length 0!")
}
while(mutableArray.length > 1){
let nextDirName = mutableArray.shift()
currentFolder = currentFolder?.[nextDirName]
if(!currentFolder){
let errorMessage = "Trying to get file in directory that doesn't exist! \n" +
nextDirName
throw new Error(errorMessage)
}
}
return currentFolder[mutableArray[0]]
}
/**
* Checks if a file exists
* @param {*} filePathArray The file path array
* @returns true if it exists, false otherwise
*/
COMPILER.fileExists = (filePathArray) => {
let currentFolder = COMPILER.topLevelDirectory
let mutableArray = filePathArray
//illegal state
if(mutableArray?.length < 1){
throw new Error("Trying to get a file with a path array of length 0!")
}
while(mutableArray.length > 1){
let nextDirName = mutableArray.shift()
currentFolder = currentFolder?.[nextDirName]
if(!currentFolder){
return false
}
}
return !!currentFolder?.[mutableArray[0]]
}
/**
* Creates a file object for a given path
* @param {*} fileName The name of the file
* @param {*} content The content of the file
* @returns The file object
*/
COMPILER.createFile = (fileName, content) => {
//get the file path array
const filePathArray = COMPILER.getPath(fileName)
let mutableArray = filePathArray
//the current folder as we recursively create folders to populate this file
let currentFolder = COMPILER.topLevelDirectory
//recursively create directories until our file is written
while(mutableArray.length > 1){
let nextDirName = mutableArray.shift()
if(!currentFolder?.[nextDirName]){
//create directory
currentFolder[nextDirName] = {
isDir: true,
"..": currentFolder,
}
}
currentFolder = currentFolder?.[nextDirName]
}
//create the actual file
currentFolder[mutableArray[0]] = {
isDir: false,
dir: currentFolder,
content: content,
}
//return the file
return currentFolder[mutableArray[0]]
}
/**
* Registers a file with the compiler
* @param {*} fileName The file's name
* @param {*} content The content of the file
* @returns The list of all files that still need to be registered by the host
*/
const COMPILER_registerFile = (fileName, content) => {
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName,false)
loggerScripts.INFO('REGISTER FILE ' + normalizedFilePath)
COMPILER_fileMap[normalizedFilePath] = {
content: content,
compiled: ts.createSourceFile(
normalizedFilePath, content, ts.ScriptTarget.Latest
COMPILER.registerFile = (fileName, content) => {
//the list of files that are imported by this file
let dependentFiles = []
loggerScripts.INFO('REGISTER FILE ' + fileName)
if(!COMPILER.fileMap[fileName]){
//create the virtual file
COMPILER.fileMap[fileName] = COMPILER.createFile(fileName,content)
//register the file itself
COMPILER.fileMap[fileName].tsSourceFile = ts.createSourceFile(
fileName,
content,
ts.ScriptTarget.Latest,
)
COMPILER.sourceFiles.push(fileName)
/**
* The preprocessed info about the file
* {
* referencedFiles: ?,
* typeReferenceDirectives: ?,
* libReferenceDirectives: ?,
* importedFiles: Array<{
* fileName: string, //the path (without file ending) of the file that is imported by this file
* pos: ?,
* end: ?,
* }>,
* isLibFile: boolean,
* ambientExternalModules: ?,
* }
*/
const fileInfo = ts.preProcessFile(content)
loggerScripts.INFO('==========================')
loggerScripts.INFO(fileName)
loggerScripts.INFO('Registered file depends on:')
fileInfo.importedFiles.forEach(module => {
let extension = ".ts"
/**
* {
* resolvedModule: ?,
* failedLookupLocations: Array<string>,
* affectingLocations: ?,
* resolutionDiagnostics: ?,
* alternateResult: ?,
* }
*/
const resolvedImport = ts.resolveModuleName(module.fileName,fileName,COMPILER.compilerOptions,COMPILER.customCompilerHost)
if(resolvedImport?.resolvedModule){
/**
* undefined
* OR
* {
* resolvedFileName: ?,
* originalPath: ?,
* extension: string, (ie ".js", ".ts", etc)
* isExternalLibraryImport: boolean,
* packageId: ?,
* resolvedUsingTsExtension: boolean,
* }
*/
const module = resolvedImport.resolvedModule
extension = module.extension
}
//am assuming we're always importing typescript for the time being
const dependentFile = module.fileName + extension
const normalizedDependentFilePath = FILE_RESOLUTION_getFilePath(dependentFile,false)
if(!COMPILER.fileMap[normalizedDependentFilePath]){
dependentFiles.push(normalizedDependentFilePath)
loggerScripts.INFO(" - " + normalizedDependentFilePath)
}
})
}
return dependentFiles;
}
@ -33,9 +316,9 @@ const COMPILER_registerFile = (fileName, content) => {
* @param {*} languageVersion The language version
* @returns The file if it exists, null otherwise
*/
const COMPILER_getSourceFile = (fileName, languageVersion) => {
if(!!COMPILER_fileMap[fileName]){
return COMPILER_fileMap[fileName].compiled
COMPILER.getSourceFile = (fileName, languageVersion) => {
if(!!COMPILER.fileMap[fileName]){
return COMPILER.fileMap[fileName].tsSourceFile
} else {
return null
}
@ -45,18 +328,26 @@ const COMPILER_getSourceFile = (fileName, languageVersion) => {
/**
* Constructs the compiler host
* https://www.typescriptlang.org/tsconfig/#compilerOptions
*
* Examples:
* https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
*
*/
const COMPILER_customCompilerHost = {
getSourceFile: COMPILER_getSourceFile,
COMPILER.customCompilerHost = {
getSourceFile: COMPILER.getSourceFile,
writeFile: (fileName, data) => {
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
loggerScripts.INFO("EMIT FILE " + normalizedFilePath)
loggerScripts.INFO("EMIT FILE " + fileName)
//wrap in require logic
let finalData =
"let exports = { }\n" +
data + "\n" +
"return exports"
// COMPILER_emitted_value = data
COMPILER_fileMap[normalizedFilePath] = {
//create file
COMPILER.createFile(fileName,finalData)
//register in file map
COMPILER.fileMap[fileName] = {
content: data, //to be eval'd from top level
moduleContent: finalData, //to be eval'd from require()
}
@ -64,36 +355,59 @@ const COMPILER_customCompilerHost = {
getDefaultLibFileName: () => "lib.d.ts",
useCaseSensitiveFileNames: () => false,
getCanonicalFileName: filename => filename,
getCurrentDirectory: () => "",
getCurrentDirectory: () => "/",
getNewLine: () => "\n",
getDirectories: () => [],
fileExists: () => true,
readFile: () => ""
getDirectories: (path) => {
loggerScripts.DEBUG('[ts] get dirs ' + path)
const dirs = Object.keys(COMPILER.getFileByPath(COMPILER.getPath(path)))
loggerScripts.DEBUG('[ts] dirs: ' + dirs)
return dirs
},
directoryExists: (path) => {
const exists = COMPILER.fileExists(COMPILER.getPath(path))
loggerScripts.DEBUG('[ts] directory exists? ' + path + " - " + exists)
return exists
},
fileExists: (path) => {
const exists = COMPILER.fileExists(COMPILER.getPath(path))
loggerScripts.DEBUG('[ts] file exists? ' + path + " - " + exists)
return exists
},
readFile: (path) => {
loggerScripts.DEBUG('[ts] read file ' + path)
const file = COMPILER.getFileByPath(COMPILER.getPath(path))
loggerScripts.DEBUG('[ts] content: ' + file.content)
return file.content
},
}
/**
* Instructs Typescript to emit the final compiled value
*/
const COMPILER_run = () => {
COMPILER.run = () => {
loggerScripts.INFO('COMPILE ALL REGISTERED FILES')
const compilerOptions = { }
const COMPILER_program = ts.createProgram(
Object.keys(COMPILER_fileMap), compilerOptions, COMPILER_customCompilerHost
COMPILER.program = ts.createProgram(
COMPILER.sourceFiles, COMPILER.compilerOptions, COMPILER.customCompilerHost
)
COMPILER_program.emit()
console.log('program')
console.log(COMPILER.program)
console.log(Object.keys(COMPILER.program))
const emitResult = COMPILER.program.emit()
console.log("Emit result")
console.log(emitResult)
console.log(Object.keys(emitResult))
}
/**
* Loads a file
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
*/
const COMPILER_runFile = (fileName) => {
COMPILER.runFile = (fileName) => {
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
if(!!COMPILER_fileMap[normalizedFilePath]){
if(!!COMPILER.fileMap[normalizedFilePath]){
loggerScripts.INFO('RUN FILE ' + normalizedFilePath)
eval(COMPILER_fileMap[normalizedFilePath].content)
eval(COMPILER.fileMap[normalizedFilePath].content)
} else {
const message = 'FAILED TO RESOLVE FILE ' + normalizedFilePath
loggerScripts.WARNING(message)
@ -105,15 +419,18 @@ const COMPILER_runFile = (fileName) => {
* Loads a file
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
*/
const COMPILER_printSource = (fileName) => {
COMPILER.printSource = (fileName) => {
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
if(!!COMPILER_fileMap[normalizedFilePath]){
if(!!COMPILER.fileMap[normalizedFilePath]){
loggerScripts.INFO('FILE CONTENT ' + normalizedFilePath)
} else {
const message = 'FAILED TO RESOLVE FILE ' + normalizedFilePath
loggerScripts.WARNING(message)
loggerScripts.WARNING('file map content:')
loggerScripts.WARNING(OBject.keys(COMPILER_fileMap) + "")
loggerScripts.WARNING(OBject.keys(COMPILER.fileMap) + "")
throw new Error(message)
}
}
//initialized CWD
COMPILER.currentDirectory = COMPILER.topLevelDirectory

View File

@ -10,10 +10,10 @@ const FILE_RESOLUTION_getFilePath = (rawFilePath, isJavascript = true) => {
fileName = fileName.replace('.ts','.js')
}
if(fileName.startsWith('/Scripts')){
fileName = fileName.replace('/Scripts','')
// fileName = fileName.replace('/Scripts','')
}
if(fileName.startsWith('Scripts/')){
fileName = fileName.replace('Scripts/','/')
if(fileName.startsWith('Scripts')){
fileName = fileName.replace('Scripts','/Scripts')
}
if(isJavascript && !fileName.endsWith(".js")){
fileName = fileName + ".js"

View File

@ -8,14 +8,14 @@ let HOST_ACCESS = {
}
//fake require
REQUIRE_CACHE["/compiler/host_access.js"] = {
REQUIRE_CACHE["/Scripts/compiler/host_access.js"] = {
exports: {
'HOST_ACCESS': HOST_ACCESS,
'loggerScripts': loggerScripts,
},
exportedValues: [
'HOST_ACCESS',
'loggerScripts'
'loggerScripts',
],
}

View File

@ -16,13 +16,24 @@ let exports = { }
* @param {*} cwd The current working directory
*/
const require = (path) => {
//get the path
loggerScripts.DEBUG('Require path ' + path)
let normalizedFilePath = FILE_RESOLUTION_getFilePath(path)
//actually require
if(!!REQUIRE_CACHE[normalizedFilePath]){
//return if has already been required
return REQUIRE_CACHE[normalizedFilePath].exports
} else if(!!COMPILER_fileMap[normalizedFilePath]?.content) {
const code = COMPILER_fileMap[normalizedFilePath].moduleContent
} else if(!!COMPILER.fileMap[normalizedFilePath]?.content) {
//require if it is already registered
const code = COMPILER.fileMap[normalizedFilePath].moduleContent
loggerScripts.DEBUG('Module code ' + JSON.stringify(code))
//create dummy prior to fully evaluating code so that we don't recurse infinitely
REQUIRE_CACHE[normalizedFilePath] = {
exports: {},
exportedValues: [],
}
//evaluate script for exports
let exports = new Function(code)()
//create module object
const module = {
@ -33,6 +44,7 @@ const require = (path) => {
loggerScripts.INFO("[require] CREATE MODULE " + normalizedFilePath)
return module.exports
} else {
//fail if it doesn't exist from host's view
const errorMsg = "FAILED TO REQUIRE FILE " + normalizedFilePath
loggerScripts.WARNING(errorMsg)
loggerScripts.WARNING('Module value:')
@ -41,7 +53,7 @@ const require = (path) => {
loggerScripts.WARNING('Require cache contents:')
loggerScripts.WARNING(Object.keys(REQUIRE_CACHE) + '')
loggerScripts.WARNING('File cache contents:')
loggerScripts.WARNING(Object.keys(COMPILER_fileMap) + '')
loggerScripts.WARNING(Object.keys(COMPILER.fileMap) + '')
throw new Error(errorMsg)
}
}

View File

@ -1,7 +1,8 @@
import { Engine } from '/Scripts/engine/engine-interface'
import { loggerScripts } from '/Scripts/compiler/host_access'
import { Client, NamespaceClient } from '/Scripts/client/client'
import { HookManager } from '/Scripts/engine/hook-manager'
import { HookManager } from '/Scripts/engine/hooks/hook-manager'
import { SceneLoader } from '/Scripts/engine/scene/scene-loader'
/**
* The core engine values
@ -10,6 +11,7 @@ export const engine: Engine = {
classes: [],
singletons: [],
hookManager: new HookManager(),
sceneLoader: new SceneLoader(),
}
/**

View File

@ -1,4 +1,5 @@
import { HookManager } from "/Scripts/engine/hook-manager";
import { HookManager } from "/Scripts/engine/hooks/hook-manager";
import { SceneLoader } from "/Scripts/engine/scene/scene-loader";
/**
@ -21,4 +22,9 @@ export interface Engine {
*/
hookManager: HookManager,
/**
* Tracks and loads scenes
*/
sceneLoader: SceneLoader,
}

View File

@ -1,3 +1,4 @@
import { TrackedScene } from "/Scripts/engine/scene/scene-loader"
import { Hook } from "/Scripts/types/hook"
import { Scene } from "/Scripts/types/scene"
@ -15,7 +16,7 @@ export enum HookScope {
/**
* A hook that is tracked by the manager
*/
interface TrackedHook extends Hook {
export interface TrackedHook extends Hook {
/**
* The scope that this hook was defined at
@ -29,23 +30,6 @@ interface TrackedHook extends Hook {
}
/**
* A scene being tracked by the manager
*/
interface TrackedScene {
/**
* The id assigned to the scene
*/
sceneId: number,
/**
* The scene itself
*/
scene: Scene,
}
/**
* Manages hooks for the engine
*/
@ -56,11 +40,6 @@ export class HookManager {
*/
hooks: Array<TrackedHook> = []
/**
* A map of a scene to all hooks related to the scene
*/
sceneHookMap: Record<number,Array<TrackedHook>> = { }
/**
* A map of engine signal to the list of hooks that should be called when that signal fires
*/
@ -69,57 +48,7 @@ export class HookManager {
/**
* The list of all scenes tracked by the manager
*/
trackedScenes: Array<{ sceneId: number, scene: Scene }> = []
/**
* A scene
*/
sceneIdIncrementer: number = 0
/**
* Registers all hooks in a scene to the hook manager
* @param scene The scene
*/
registerScene(scene: Scene, isServerScene: boolean){
const shouldRegister: boolean = !this.containsScene(scene)
if(shouldRegister){
//add to the list of tracked scenes
const trackedScene: TrackedScene = {
sceneId: this.sceneIdIncrementer++,
scene: scene,
}
this.trackedScenes.push(trackedScene)
//load all hooks from the scene
scene.hooks.forEach((hook: Hook) => {
this.registerHook(trackedScene,hook,isServerScene)
})
}
}
/**
* Deregisters all hooks in a scene from the hook manager
* @param scene The scene
*/
deregisterScene(scene: Scene){
throw new Error("Unsupported Operation!")
}
/**
* Checks if the manager is tracking a given scene
* @param scene The scene
* @returns true if it is being tracked already, false otherwise
*/
containsScene(scene: Scene): boolean {
this.trackedScenes.forEach(trackedScene => {
if(trackedScene.scene === scene){
return true
}
})
return false
}
trackedScenes: Array<TrackedScene> = []
/**
* Registers a hook
@ -139,10 +68,19 @@ export class HookManager {
const signalArray: Array<TrackedHook> = this.signalHookMap?.[hookSignal] ? this.signalHookMap?.[hookSignal] : []
signalArray.push(trackedHook)
this.signalHookMap[hookSignal] = signalArray
//add to scene array
const sceneArray: Array<TrackedHook> = this.sceneHookMap?.[scene.sceneId] ? this.sceneHookMap?.[scene.sceneId] : []
sceneArray.push(trackedHook)
this.sceneHookMap[scene.sceneId] = sceneArray
//
//Scene related structures
//
//track scene if it isn't already being tracked
let filteredArr = this.trackedScenes.filter(trackedScene => trackedScene.sceneId === scene.sceneId)
if(filteredArr.length < 1){
this.trackedScenes.push(scene)
}
//add to scene tracking structures
scene.sceneHooks.push(trackedHook)
const sceneSignalArray: Array<TrackedHook> = scene.signalHookMap?.[hookSignal] ? scene.signalHookMap?.[hookSignal] : []
sceneSignalArray.push(trackedHook)
scene.signalHookMap[hookSignal] = sceneSignalArray
}
/**
@ -159,7 +97,7 @@ export class HookManager {
* @param signal The signal
* @param value The value associated with the signal
*/
fireSignal(signal: string, value: any){
fireSignal(sceneId: number, signal: string, value: any){
const hooks: Array<TrackedHook> = this.signalHookMap[signal]
hooks.forEach(trackedHook => {
trackedHook.callback(value)

View File

@ -0,0 +1,119 @@
import { engine } from "/Scripts/engine/engine-init";
import { HookManager, TrackedHook } from "/Scripts/engine/hooks/hook-manager";
import { Hook } from "/Scripts/types/hook";
import { Scene } from "/Scripts/types/scene";
/**
* A scene being tracked by the manager
*/
export interface TrackedScene {
/**
* The id assigned to the scene
*/
sceneId: number,
/**
* The scene itself
*/
scene: Scene,
/**
* A map of a scene to all hooks related to the scene
*/
sceneHooks: Array<TrackedHook>
/**
* A map of engine signal to the list of hooks that should be called when that signal fires
*/
signalHookMap: Record<string,Array<TrackedHook>>
}
/**
* Loads scenes
*/
export class SceneLoader {
/**
* The hook manager
*/
hookManager: HookManager
/**
* The list of loaded scenes
*/
loadedScenes: TrackedScene[]
/**
* A record of tracked scene id to tracked scene object
*/
sceneIdMap: Record<number,TrackedScene>
/**
* A scene
*/
sceneIdIncrementer: number = 0
/**
* Registers all hooks in a scene to the hook manager
* @param scene The scene
*/
registerScene(scene: Scene, isServerScene: boolean){
const shouldRegister: boolean = !this.containsScene(scene)
if(shouldRegister){
//add to the list of tracked scenes
const trackedScene: TrackedScene = {
sceneId: this.sceneIdIncrementer++,
scene: scene,
sceneHooks: [],
signalHookMap: { },
}
this.loadedScenes.push(trackedScene)
this.sceneIdMap[trackedScene.sceneId] = trackedScene
//load all hooks from the scene
scene.hooks.forEach((hook: Hook) => {
engine.hookManager.registerHook(trackedScene,hook,isServerScene)
})
}
}
/**
* Deregisters all hooks in a scene from the hook manager
* @param scene The scene
*/
deregisterScene(scene: Scene){
throw new Error("Unsupported Operation!")
}
/**
* Checks if the manager is tracking a given scene
* @param scene The scene
* @returns true if it is being tracked already, false otherwise
*/
containsScene(scene: Scene): boolean {
this.loadedScenes.forEach(trackedScene => {
if(trackedScene.scene === scene){
return true
}
})
return false
}
/**
* Gets a tracked scene by its id
* @param sceneId The tracked scene
* @returns The tracked scene if it exists, null otherwise
*/
getScene(sceneId: number){
return this.sceneIdMap[sceneId]
}
}

View File

@ -1,5 +1,8 @@
@page dungeonsindex Dungeons
[TOC]
- @subpage dungeonslayout
Enforce slower walking speed in dungeons (to give movement a more deliberate and weighty feel)
- Slower movement will also help with weapons feel more impactful
Have the engine generate different types of shortcuts to allow you to quickly navigate up/down to the uncleared floors

View File

@ -0,0 +1,11 @@
@page dungeonslayout Dungeon Layout
Procedural generation works better the more levers you have to tweak the generation with.
Ideas for levers with respect to layout of dungeon templates:
- "Themes" of floors. IE having a spider themed floor, a magma/fire themed floor, etc
- "Navigation Lines". Having specific templates that are used to make easily identifiable highways through the dungeon that guide the player between highest level points of interest
- Color coordinating within floors
- Puzzle subsections of floors
- Working off of puzzles, have a central mechanism with explicit rooms around the mechanism (think a pillar that raises/lowers, a water level, etc)
- Generate rooms connecting all of these explicit rooms
- Don't necessarily want to connect them optimally. Players enjoy having to remember the location of other rooms earlier in the dungeon metroidvania style.

View File

@ -33,7 +33,7 @@ public class DebugSPWorldLoading {
SaveUtils.createOrOverwriteSave(saveName, SceneGenerator.createProceduralSceneFile(saveName));
}
//load just-created save
SaveUtils.loadSave(saveName);
SaveUtils.loadSave(saveName, false);
//initialize the "virtual" objects simulation
LoadingUtils.initMacroSimulation();

View File

@ -53,7 +53,7 @@ public class LevelEditorLoading {
SaveUtils.createOrOverwriteSave(saveName,sceneFile);
}
//load just-created save
SaveUtils.loadSave(saveName);
SaveUtils.loadSave(saveName, true);
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
//init authentication

View File

@ -34,7 +34,7 @@ public class LevelLoading {
loadingWindow.setVisible(true);
//load save
SaveUtils.loadSave(saveName);
SaveUtils.loadSave(saveName, false);
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
//init authentication

View File

@ -95,4 +95,12 @@ public class SceneFile {
return createSaveInstance;
}
/**
* Sets whether the save utils will store a copy of the scene file in the save or not
* @param createSaveInstancetrue if should create instance of scene file in save, false otherwise
*/
public void setCreateSaveInstance(boolean createSaveInstance){
this.createSaveInstance = createSaveInstance;
}
}

View File

@ -23,23 +23,23 @@ public class SceneLoader {
* Loads a scene file on the server
* @param path The name of the save to look for a scene file within
*/
public static void serverInstantiateSaveSceneFile(String saveName, ServerWorldData serverWorldData){
public static void serverInstantiateSaveSceneFile(String saveName, ServerWorldData serverWorldData, boolean isLevelEditor){
//load scene file
SceneFile file = FileUtils.loadObjectFromSavePath(saveName, "/scene.json", SceneFile.class);
//instantiate scene data
serverInstantiateSceneFile(file,serverWorldData);
serverInstantiateSceneFile(file,serverWorldData,isLevelEditor);
}
/**
* Loads a scene file on the server
* @param path The name of the scene in the assets/scenes folder
*/
public static void serverInstantiateAssetSceneFile(String sceneName, ServerWorldData serverWorldData){
public static void serverInstantiateAssetSceneFile(String sceneName, ServerWorldData serverWorldData, boolean isLevelEditor){
//load scene file
String sanitizedPath = FileUtils.sanitizeFilePath("/Scenes/" + sceneName + "/scene.json");
SceneFile file = FileUtils.loadObjectFromAssetPath(sanitizedPath, SceneFile.class);
//instantiate scene data
serverInstantiateSceneFile(file,serverWorldData);
serverInstantiateSceneFile(file,serverWorldData,isLevelEditor);
}
/**
@ -47,7 +47,7 @@ public class SceneLoader {
* @param path The path in the assets directory to a scene file
* @param isSave if true, will try to load scene from save file instead of asset file
*/
private static void serverInstantiateSceneFile(SceneFile file, ServerWorldData serverWorldData){
private static void serverInstantiateSceneFile(SceneFile file, ServerWorldData serverWorldData, boolean isLevelEditor){
//
//Content manager
@ -103,6 +103,9 @@ public class SceneLoader {
}
}
//load scripts
if(!isLevelEditor && file.getInitScriptPath() != null){
Globals.scriptEngine.initScene(file.getInitScriptPath());
}
//TODO: integrate scripts for client side of scenes
// for(String scriptPath : file.getScriptPaths()){
// Globals.scriptEngine.loadScript(scriptPath);

View File

@ -35,7 +35,7 @@ public class LoggerInterface {
loggerDB = new Logger(LogLevel.WARNING);
loggerAudio = new Logger(LogLevel.WARNING);
loggerUI = new Logger(LogLevel.WARNING);
loggerScripts = new Logger(LogLevel.WARNING);
loggerScripts = new Logger(LogLevel.DEBUG);
loggerStartup.INFO("Initialized loggers");
}
}

View File

@ -59,7 +59,7 @@ public class MenuGenerators {
serverThread.start();
clientThread.start();
} else {
SaveUtils.loadSave(saveName.toLowerCase());
SaveUtils.loadSave(saveName.toLowerCase(), false);
WindowUtils.replaceMainMenuContents(MenuGenerators.createSaveCreationMenu());
}
return false;

View File

@ -179,6 +179,11 @@ public class MenuGeneratorsLevelEditor {
}, DEFAULT_GRID_SIZE / (float)GriddedDataCellManager.MAX_GRID_SIZE)
);
sceneFile.getRealmDescriptor().setGriddedRealmSize(DEFAULT_GRID_SIZE);
griddedRealmControls.addChild(InputMacros.createToggle("Create Scene File", false, (ValueChangeEvent event) -> {
sceneFile.setCreateSaveInstance(event.getAsBoolean());
System.out.println(sceneFile.getCreateSaveInstance());
}));
}
rVal.addChild(griddedRealmControls);

View File

@ -191,7 +191,7 @@ public class ToggleInput extends StandardDrawableElement implements ClickableEle
} else {
Globals.elementManager.focusElement(this);
this.value = !this.value;
Globals.elementManager.fireEventNoPosition(new ValueChangeEvent(absoluteX), this);
Globals.elementManager.fireEventNoPosition(new ValueChangeEvent(this.value), this);
propagate = false;
}
} else if(event instanceof ValueChangeEvent){

View File

@ -119,6 +119,9 @@ public class InputMacros {
//the actual input
ToggleInput toggleInput = ToggleInput.createToggleInput();
toggleInput.setValue(defaultValue);
toggleInput.setOnValueChangeCallback(new ValueChangeEventCallback() {public void execute(ValueChangeEvent event) {
onChange.accept(event);
}});
rVal.addChild(toggleInput);
return rVal;

View File

@ -11,6 +11,7 @@ import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Source.Builder;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.HostAccess.Export;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
@ -56,12 +57,6 @@ public class ScriptEngine {
"Scripts/compiler/host_access.js",
};
//folders that should be loaded once we have bootstrapped the compiler
static final String[] foldersToLoadOnInit = new String[]{
"Scripts/engine",
"Scripts/client",
};
//The classes that will be provided to the scripting engine
//https://stackoverflow.com/a/65942034
static final Object[][] staticClasses = new Object[][]{
@ -103,9 +98,7 @@ public class ScriptEngine {
defineHostMembers();
//register engine files
for(String folderToRegister : foldersToLoadOnInit){
registerScriptDirectory(folderToRegister,FileUtils.getAssetFile(folderToRegister));
}
registerFile("/Scripts/engine/engine-init.ts");
//compile
compile();
@ -160,6 +153,7 @@ public class ScriptEngine {
return topLevelValue.removeMember(valueName);
}
@Deprecated
/**
* Reads the scripts directory
* @param path The
@ -186,7 +180,7 @@ public class ScriptEngine {
* Loads a script from disk
* @param path The path to the script file
*/
public void loadDependency(String path){
void loadDependency(String path){
String content;
Source source = null;
try {
@ -205,44 +199,59 @@ public class ScriptEngine {
}
}
/**
* Runs a script
* @param path The filepath of the script
*/
public void runScript(String path){
invokeFunction("COMPILER_runFile", path);
}
/**
* Prints the content of a file
* @param path The filepath of the script
*/
public void printScriptSource(String path){
invokeFunction("COMPILER_printSource", path);
invokeMemberFunction("COMPILER", "printSource", path);
}
/**
* Registers a file with the scripting engine to be compiled into the full binary
* @param path The path to the script file
*/
private void registerFile(String path){
private boolean registerFile(String path){
String content;
try {
content = FileUtils.getAssetFileAsString(path);
sourceMap.put(path,Source.create("js",content));
invokeFunction("COMPILER_registerFile",path,content);
Value dependentFilesValue = invokeMemberFunction("COMPILER", "registerFile",path,content);
//
//register dependent files if necessary
long dependentFilesCount = dependentFilesValue.getArraySize();
if(dependentFilesCount > 0){
for(int i = 0; i < dependentFilesCount; i++){
String dependentFilePath = dependentFilesValue.getArrayElement(i).asString();
LoggerInterface.loggerScripts.INFO("[HOST - Script Engine] Should register file " + dependentFilePath);
registerFile(dependentFilePath);
}
}
} catch (IOException e) {
LoggerInterface.loggerScripts.ERROR("FAILED TO LOAD SCRIPT", e);
return false;
}
return true;
}
/**
* Compiles the project
*/
private void compile(){
invokeFunction("COMPILER_run");
Value compiledCode = topLevelValue.getMember("COMPILER_emitted_value");
context.eval("js",compiledCode.asString());
invokeMemberFunction("COMPILER", "run");
}
/**
* Initializes a scene script
* @param scenePath The scene's init script path
*/
public void initScene(String scenePath){
//add files to virtual filesystem in script engine
registerFile(scenePath);
//compile with new files added
compile();
//require the module
requireModule(scenePath);
}
@ -252,6 +261,7 @@ public class ScriptEngine {
* @param args The arguments
*/
public Value invokeFunction(String functionName, Object... args){
LoggerInterface.loggerScripts.DEBUG("Host execute: " + functionName);
Value function = topLevelValue.getMember(functionName);
if(function != null){
return function.execute(args);
@ -261,6 +271,25 @@ public class ScriptEngine {
return null;
}
/**
* Calls a function on a child of the top level member
* @param memberName The name of the child
* @param functionName The name of the function
* @param args The arguments for the function
* @return The value from the function call
*/
public Value invokeMemberFunction(String memberName, String functionName, Object ... args){
LoggerInterface.loggerScripts.DEBUG("Host execute: " + functionName);
Value childMember = topLevelValue.getMember(memberName);
Value function = childMember.getMember(functionName);
if(function != null){
return function.execute(args);
} else {
LoggerInterface.loggerScripts.WARNING("Failed to invoke function " + functionName);
}
return null;
}
/**
* Invokes a function defined in a file
* @param filePath The file the function is defined in
@ -298,6 +327,8 @@ public class ScriptEngine {
for(Object[] currentClass : staticClasses){
classes.putMember((String)currentClass[0], currentClass[1]);
}
//give access to script engine instance
hostObject.putMember("scriptEngine", this);
}

View File

@ -174,7 +174,7 @@ public class SaveUtils {
* @param saveName The name of the save
* @return true always
*/
public static boolean loadSave(String saveName){
public static boolean loadSave(String saveName, boolean isLevelEditor){
String dirPath = deriveSaveDirectoryPath(saveName);
//load save file
@ -200,11 +200,11 @@ public class SaveUtils {
if(FileUtils.checkSavePathExists(saveName, "/scene.json")){
//load from save itself
LoggerInterface.loggerEngine.INFO("Load scene data from save " + saveName);
SceneLoader.serverInstantiateSaveSceneFile(saveName, serverWorldData);
} else if(FileUtils.checkFileExists("/Scenes/" + saveName)){
SceneLoader.serverInstantiateSaveSceneFile(saveName, serverWorldData, isLevelEditor);
} else if(FileUtils.getAssetFile("/Scenes/" + saveName + "/scene.json").exists()){
//load from defined scene
LoggerInterface.loggerEngine.INFO("Load scene data from scene " + saveName);
SceneLoader.serverInstantiateAssetSceneFile(saveName, serverWorldData);
SceneLoader.serverInstantiateAssetSceneFile(saveName, serverWorldData, isLevelEditor);
} else {
//The scene is neither defined in the save itself nor in the assets scene files
throw new IllegalStateException("Trying to load a save that does not contain a scene!");