Compare commits
No commits in common. "f2877edf376354ab7bb9adc498b9abb59730724d" and "14f4607c47b4504efcd7bd5e82bb392f7ca4fa84" have entirely different histories.
f2877edf37
...
14f4607c47
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"entities" : [],
|
|
||||||
"scriptPaths" : [],
|
|
||||||
"initScriptPath" : "/Scenes/defaultLevel_2/scene.ts",
|
|
||||||
"realmDescriptor" : {
|
|
||||||
"type" : "gridded",
|
|
||||||
"griddedRealmSize" : 2
|
|
||||||
},
|
|
||||||
"createSaveInstance" : true
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import { TrackedScene } from "/Scripts/engine/scene/scene-loader";
|
|
||||||
import { Scene } from "/Scripts/types/scene";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main scene interface
|
|
||||||
*/
|
|
||||||
const TestScene1: Scene = {
|
|
||||||
|
|
||||||
onCreate: (instanceId: number) => {
|
|
||||||
console.log('Hello from the scene! My ID is ' + instanceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The scene to export
|
|
||||||
*/
|
|
||||||
export default TestScene1
|
|
||||||
@ -1,479 +1,119 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description The compiler object
|
* The map of all source files to their content and compiled value
|
||||||
*/
|
*/
|
||||||
let COMPILER = {
|
let COMPILER_fileMap = { }
|
||||||
|
|
||||||
//
|
/**
|
||||||
//
|
* The compiled program
|
||||||
// VIRTUAL FILE SYSTEM
|
*/
|
||||||
//
|
let COMPILER_emitted_value = ''
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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: {
|
|
||||||
//as required by our framework
|
|
||||||
Scripts: {
|
|
||||||
compiler: {
|
|
||||||
"host_access.js": {
|
|
||||||
content: "",
|
|
||||||
version: 0,
|
|
||||||
},
|
|
||||||
version: 0,
|
|
||||||
isDir: true,
|
|
||||||
},
|
|
||||||
version: 0,
|
|
||||||
isDir: true,
|
|
||||||
},
|
|
||||||
//as required by language service
|
|
||||||
node_modules: {
|
|
||||||
"@types": {
|
|
||||||
"lib.d.ts": {
|
|
||||||
content: "",
|
|
||||||
version: 0,
|
|
||||||
isDir: false,
|
|
||||||
},
|
|
||||||
version: 0,
|
|
||||||
isDir: true,
|
|
||||||
},
|
|
||||||
version: 0,
|
|
||||||
isDir: true,
|
|
||||||
},
|
|
||||||
version: 0,
|
|
||||||
isDir: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current directory, "/"
|
|
||||||
*/
|
|
||||||
currentDirectory : { },
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a file with the compiler
|
|
||||||
* @param {string} fileName The file's name
|
|
||||||
* @param {string} content The content of the file
|
|
||||||
* @returns {string[]} The list of all files that still need to be registered by the host
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
//If the compiler has already run once, run the language service against only this file
|
|
||||||
if(!!COMPILER.compilerHasRun){
|
|
||||||
COMPILER.emitFile(fileName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dependentFiles;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a file object for a given path
|
|
||||||
* @param string} fileName The name of the file
|
|
||||||
* @param {string} content The content of the file
|
|
||||||
* @returns The file object
|
|
||||||
*/
|
|
||||||
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,
|
|
||||||
version: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
//return the file
|
|
||||||
return currentFolder[mutableArray[0]]
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the path for the file
|
|
||||||
* @param {string} fullyQualifiedFilePath The fully qualified file path
|
|
||||||
* @returns {string[]} The array of directories ending with the name of the file
|
|
||||||
*/
|
|
||||||
getPath: (fullyQualifiedFilePath) => {
|
|
||||||
let modifiedFileName = fullyQualifiedFilePath
|
|
||||||
//remove leading "/"
|
|
||||||
if(modifiedFileName.startsWith("/")){
|
|
||||||
modifiedFileName = modifiedFileName.substring(1)
|
|
||||||
}
|
|
||||||
//split
|
|
||||||
return modifiedFileName.split("/")
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the path for the file
|
|
||||||
* @param {stringp[]} filePathArray The fully qualified file path
|
|
||||||
* @returns The array of directories ending with the name of the file
|
|
||||||
*/
|
|
||||||
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 {string[]} filePathArray The file path array
|
|
||||||
* @returns true if it exists, false otherwise
|
|
||||||
*/
|
|
||||||
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]]
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The callback invoked when the compiler host tries to read a file
|
|
||||||
* @param {string} fileName The name of the file
|
|
||||||
* @param {*} languageVersion The language version
|
|
||||||
* @returns The file if it exists, null otherwise
|
|
||||||
*/
|
|
||||||
getSourceFile: (fileName, languageVersion) => {
|
|
||||||
if(!!COMPILER.fileMap[fileName]){
|
|
||||||
return COMPILER.fileMap[fileName].tsSourceFile
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
//
|
/**
|
||||||
//
|
* Registers a file with the compiler
|
||||||
// COMPILATION
|
* @param {*} fileName The file's name
|
||||||
//
|
* @param {*} content The content of the file
|
||||||
//
|
*/
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The compiler options
|
|
||||||
*/
|
|
||||||
compilerOptions: { },
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks whether the compiler has run or not
|
* The callback invoked when the compiler host tries to read a file
|
||||||
*/
|
* @param {*} fileName The name of the file
|
||||||
compilerHasRun: false,
|
* @param {*} languageVersion The language version
|
||||||
|
* @returns The file if it exists, null otherwise
|
||||||
/**
|
*/
|
||||||
* The typescript compiler host definition
|
const COMPILER_getSourceFile = (fileName, languageVersion) => {
|
||||||
*/
|
if(!!COMPILER_fileMap[fileName]){
|
||||||
customCompilerHost: null,
|
return COMPILER_fileMap[fileName].compiled
|
||||||
|
} else {
|
||||||
/**
|
return null
|
||||||
* The typescript program
|
}
|
||||||
*/
|
|
||||||
program: null,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits a file
|
|
||||||
* @param {string} fileName The name of the file
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
emitFile: (fileName) => {
|
|
||||||
loggerScripts.DEBUG('Compiler evaluating source path ' + fileName)
|
|
||||||
/**
|
|
||||||
* {
|
|
||||||
* outputFiles: [ ],
|
|
||||||
* emitSkipped: boolean,
|
|
||||||
* diagnostics: { },
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const output = COMPILER.program.getEmitOutput(fileName)
|
|
||||||
if (!output.emitSkipped) {
|
|
||||||
output.outputFiles.forEach(outputFile => {
|
|
||||||
loggerScripts.DEBUG(`[ts] Emitting ${outputFile}`);
|
|
||||||
COMPILER.customCompilerHost.writeFile(outputFile.name, outputFile.text)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
loggerScripts.DEBUG(`[ts] Emitting ${fileName} failed`);
|
|
||||||
COMPILER.logEmitError(fileName);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs errors raised during emission of files
|
|
||||||
* @param {string} fileName The name of the file to log errors about
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
logEmitError: (fileName) => {
|
|
||||||
loggerScripts.DEBUG('[ts] logErrors ' + fileName)
|
|
||||||
let allDiagnostics = services
|
|
||||||
.getCompilerOptionsDiagnostics()
|
|
||||||
.concat(services.getSyntacticDiagnostics(fileName))
|
|
||||||
.concat(services.getSemanticDiagnostics(fileName));
|
|
||||||
|
|
||||||
allDiagnostics.forEach(diagnostic => {
|
|
||||||
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
||||||
if (diagnostic.file) {
|
|
||||||
let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
|
|
||||||
diagnostic.start
|
|
||||||
);
|
|
||||||
loggerScripts.DEBUG(`[ts] Error ${diagnostic.file.fileName} (${line + 1},${character +1}): ${message}`);
|
|
||||||
} else {
|
|
||||||
loggerScripts.DEBUG(`[ts] Error: ${message}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs Typescript to emit the final compiled value
|
|
||||||
*/
|
|
||||||
run: () => {
|
|
||||||
loggerScripts.INFO('COMPILE ALL REGISTERED FILES')
|
|
||||||
|
|
||||||
if(!COMPILER.program){
|
|
||||||
COMPILER.program = ts.createLanguageService(COMPILER.customCompilerHost, ts.createDocumentRegistry());
|
|
||||||
}
|
|
||||||
|
|
||||||
//Emit all currently known files
|
|
||||||
COMPILER.sourceFiles.forEach(sourcePath => {
|
|
||||||
COMPILER.emitFile(sourcePath)
|
|
||||||
})
|
|
||||||
|
|
||||||
//flag that the compiler has run (ie only incrementally compile when new files are added, now)
|
|
||||||
COMPILER.compilerHasRun = true
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a file
|
|
||||||
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
|
|
||||||
*/
|
|
||||||
runFile: (fileName) => {
|
|
||||||
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
|
|
||||||
if(!!COMPILER.fileMap[normalizedFilePath]){
|
|
||||||
loggerScripts.INFO('RUN FILE ' + normalizedFilePath)
|
|
||||||
eval(COMPILER.fileMap[normalizedFilePath].content)
|
|
||||||
} else {
|
|
||||||
const message = 'FAILED TO RESOLVE FILE ' + normalizedFilePath
|
|
||||||
loggerScripts.WARNING(message)
|
|
||||||
throw new Error(message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a file
|
|
||||||
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
|
|
||||||
*/
|
|
||||||
printSource: (fileName) => {
|
|
||||||
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
|
|
||||||
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) + "")
|
|
||||||
throw new Error(message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the compiler host
|
* Constructs the compiler host
|
||||||
* https://www.typescriptlang.org/tsconfig/#compilerOptions
|
* https://www.typescriptlang.org/tsconfig/#compilerOptions
|
||||||
*
|
|
||||||
* Examples:
|
|
||||||
* https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
COMPILER.customCompilerHost = {
|
const COMPILER_customCompilerHost = {
|
||||||
getSourceFile: COMPILER.getSourceFile,
|
getSourceFile: COMPILER_getSourceFile,
|
||||||
writeFile: (fileName, data) => {
|
writeFile: (fileName, data) => {
|
||||||
loggerScripts.INFO("EMIT FILE " + fileName)
|
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
|
||||||
//wrap in require logic
|
loggerScripts.INFO("EMIT FILE " + normalizedFilePath)
|
||||||
let finalData =
|
let finalData =
|
||||||
"let exports = { }\n" +
|
"let exports = { }\n" +
|
||||||
data + "\n" +
|
data + "\n" +
|
||||||
"return exports"
|
"return exports"
|
||||||
|
// COMPILER_emitted_value = data
|
||||||
//create file
|
COMPILER_fileMap[normalizedFilePath] = {
|
||||||
COMPILER.createFile(fileName,finalData)
|
|
||||||
|
|
||||||
//register in file map
|
|
||||||
COMPILER.fileMap[fileName] = {
|
|
||||||
content: data, //to be eval'd from top level
|
content: data, //to be eval'd from top level
|
||||||
moduleContent: finalData, //to be eval'd from require()
|
moduleContent: finalData, //to be eval'd from require()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDefaultLibFileName: ts.getDefaultLibFileName,
|
getDefaultLibFileName: () => "lib.d.ts",
|
||||||
useCaseSensitiveFileNames: () => false,
|
useCaseSensitiveFileNames: () => false,
|
||||||
getCanonicalFileName: filename => filename,
|
getCanonicalFileName: filename => filename,
|
||||||
getCurrentDirectory: () => "/",
|
getCurrentDirectory: () => "",
|
||||||
getNewLine: () => "\n",
|
getNewLine: () => "\n",
|
||||||
getDirectories: (path) => {
|
getDirectories: () => [],
|
||||||
loggerScripts.DEBUG('[ts] getDirectories ' + path)
|
fileExists: () => true,
|
||||||
const dirs = Object.keys(COMPILER.getFileByPath(COMPILER.getPath(path)))
|
readFile: () => ""
|
||||||
loggerScripts.DEBUG('[ts] dirs: ' + dirs)
|
|
||||||
return dirs
|
|
||||||
},
|
|
||||||
directoryExists: (path) => {
|
|
||||||
let exists = COMPILER.fileExists(COMPILER.getPath(path))
|
|
||||||
if(exists){
|
|
||||||
exists = COMPILER.getFileByPath(COMPILER.getPath(path))?.isDir
|
|
||||||
}
|
|
||||||
loggerScripts.DEBUG('[ts] directoryExists ' + path + " - " + exists)
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
fileExists: (path) => {
|
|
||||||
const exists = COMPILER.fileExists(COMPILER.getPath(path))
|
|
||||||
loggerScripts.DEBUG('[ts] fileExists ' + path + " - " + exists)
|
|
||||||
return exists
|
|
||||||
},
|
|
||||||
readFile: (path) => {
|
|
||||||
loggerScripts.DEBUG('[ts] readFile ' + path)
|
|
||||||
const file = COMPILER.getFileByPath(COMPILER.getPath(path))
|
|
||||||
loggerScripts.DEBUG('[ts] readFile (content): ' + file.content)
|
|
||||||
return file.content
|
|
||||||
},
|
|
||||||
getScriptFileNames: () => {
|
|
||||||
loggerScripts.DEBUG('[ts] getScriptFileNames')
|
|
||||||
return COMPILER.sourceFiles
|
|
||||||
},
|
|
||||||
getScriptVersion: (fileName) => {
|
|
||||||
loggerScripts.DEBUG('[ts] getScriptVersion: ' + fileName)
|
|
||||||
const file = COMPILER.getFileByPath(COMPILER.getPath(fileName))
|
|
||||||
return file?.version
|
|
||||||
},
|
|
||||||
//https://github.com/microsoft/TypeScript/wiki/Using-the-Language-Service-API#scriptsnapshot
|
|
||||||
getScriptSnapshot: (fileName) => {
|
|
||||||
loggerScripts.DEBUG('[ts] getScriptSnapshot: ' + fileName)
|
|
||||||
const file = COMPILER.getFileByPath(COMPILER.getPath(fileName))
|
|
||||||
if(file){
|
|
||||||
return ts.ScriptSnapshot.fromString(file.content)
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getCompilationSettings: () => COMPILER.compilerOptions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//initialized CWD
|
/**
|
||||||
COMPILER.currentDirectory = COMPILER.topLevelDirectory
|
* Instructs Typescript to emit the final compiled value
|
||||||
|
*/
|
||||||
|
const COMPILER_run = () => {
|
||||||
|
loggerScripts.INFO('COMPILE ALL REGISTERED FILES')
|
||||||
|
|
||||||
|
const compilerOptions = { }
|
||||||
|
|
||||||
|
const COMPILER_program = ts.createProgram(
|
||||||
|
Object.keys(COMPILER_fileMap), compilerOptions, COMPILER_customCompilerHost
|
||||||
|
)
|
||||||
|
COMPILER_program.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a file
|
||||||
|
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
|
||||||
|
*/
|
||||||
|
const COMPILER_runFile = (fileName) => {
|
||||||
|
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
|
||||||
|
if(!!COMPILER_fileMap[normalizedFilePath]){
|
||||||
|
loggerScripts.INFO('RUN FILE ' + normalizedFilePath)
|
||||||
|
eval(COMPILER_fileMap[normalizedFilePath].content)
|
||||||
|
} else {
|
||||||
|
const message = 'FAILED TO RESOLVE FILE ' + normalizedFilePath
|
||||||
|
loggerScripts.WARNING(message)
|
||||||
|
throw new Error(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a file
|
||||||
|
* @param {*} fileName The name of the file to load (preferably already has .ts at the end)
|
||||||
|
*/
|
||||||
|
const COMPILER_printSource = (fileName) => {
|
||||||
|
let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
|
||||||
|
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) + "")
|
||||||
|
throw new Error(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -10,10 +10,10 @@ const FILE_RESOLUTION_getFilePath = (rawFilePath, isJavascript = true) => {
|
|||||||
fileName = fileName.replace('.ts','.js')
|
fileName = fileName.replace('.ts','.js')
|
||||||
}
|
}
|
||||||
if(fileName.startsWith('/Scripts')){
|
if(fileName.startsWith('/Scripts')){
|
||||||
// fileName = fileName.replace('/Scripts','')
|
fileName = fileName.replace('/Scripts','')
|
||||||
}
|
}
|
||||||
if(fileName.startsWith('Scripts')){
|
if(fileName.startsWith('Scripts/')){
|
||||||
fileName = fileName.replace('Scripts','/Scripts')
|
fileName = fileName.replace('Scripts/','/')
|
||||||
}
|
}
|
||||||
if(isJavascript && !fileName.endsWith(".js")){
|
if(isJavascript && !fileName.endsWith(".js")){
|
||||||
fileName = fileName + ".js"
|
fileName = fileName + ".js"
|
||||||
|
|||||||
@ -8,14 +8,14 @@ let HOST_ACCESS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//fake require
|
//fake require
|
||||||
REQUIRE_CACHE["/Scripts/compiler/host_access.js"] = {
|
REQUIRE_CACHE["/compiler/host_access.js"] = {
|
||||||
exports: {
|
exports: {
|
||||||
'HOST_ACCESS': HOST_ACCESS,
|
'HOST_ACCESS': HOST_ACCESS,
|
||||||
'loggerScripts': loggerScripts,
|
'loggerScripts': loggerScripts,
|
||||||
},
|
},
|
||||||
exportedValues: [
|
exportedValues: [
|
||||||
'HOST_ACCESS',
|
'HOST_ACCESS',
|
||||||
'loggerScripts',
|
'loggerScripts'
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,24 +16,13 @@ let exports = { }
|
|||||||
* @param {*} cwd The current working directory
|
* @param {*} cwd The current working directory
|
||||||
*/
|
*/
|
||||||
const require = (path) => {
|
const require = (path) => {
|
||||||
//get the path
|
|
||||||
loggerScripts.DEBUG('Require path ' + path)
|
loggerScripts.DEBUG('Require path ' + path)
|
||||||
let normalizedFilePath = FILE_RESOLUTION_getFilePath(path)
|
let normalizedFilePath = FILE_RESOLUTION_getFilePath(path)
|
||||||
|
|
||||||
//actually require
|
|
||||||
if(!!REQUIRE_CACHE[normalizedFilePath]){
|
if(!!REQUIRE_CACHE[normalizedFilePath]){
|
||||||
//return if has already been required
|
|
||||||
return REQUIRE_CACHE[normalizedFilePath].exports
|
return REQUIRE_CACHE[normalizedFilePath].exports
|
||||||
} else if(!!COMPILER.fileMap[normalizedFilePath]?.content) {
|
} else if(!!COMPILER_fileMap[normalizedFilePath]?.content) {
|
||||||
//require if it is already registered
|
const code = COMPILER_fileMap[normalizedFilePath].moduleContent
|
||||||
const code = COMPILER.fileMap[normalizedFilePath].moduleContent
|
|
||||||
loggerScripts.DEBUG('Module code ' + JSON.stringify(code))
|
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)()
|
let exports = new Function(code)()
|
||||||
//create module object
|
//create module object
|
||||||
const module = {
|
const module = {
|
||||||
@ -44,7 +33,6 @@ const require = (path) => {
|
|||||||
loggerScripts.INFO("[require] CREATE MODULE " + normalizedFilePath)
|
loggerScripts.INFO("[require] CREATE MODULE " + normalizedFilePath)
|
||||||
return module.exports
|
return module.exports
|
||||||
} else {
|
} else {
|
||||||
//fail if it doesn't exist from host's view
|
|
||||||
const errorMsg = "FAILED TO REQUIRE FILE " + normalizedFilePath
|
const errorMsg = "FAILED TO REQUIRE FILE " + normalizedFilePath
|
||||||
loggerScripts.WARNING(errorMsg)
|
loggerScripts.WARNING(errorMsg)
|
||||||
loggerScripts.WARNING('Module value:')
|
loggerScripts.WARNING('Module value:')
|
||||||
@ -53,19 +41,7 @@ const require = (path) => {
|
|||||||
loggerScripts.WARNING('Require cache contents:')
|
loggerScripts.WARNING('Require cache contents:')
|
||||||
loggerScripts.WARNING(Object.keys(REQUIRE_CACHE) + '')
|
loggerScripts.WARNING(Object.keys(REQUIRE_CACHE) + '')
|
||||||
loggerScripts.WARNING('File cache contents:')
|
loggerScripts.WARNING('File cache contents:')
|
||||||
loggerScripts.WARNING(Object.keys(COMPILER.fileMap) + '')
|
loggerScripts.WARNING(Object.keys(COMPILER_fileMap) + '')
|
||||||
throw new Error(errorMsg)
|
throw new Error(errorMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add require to its own cache
|
|
||||||
REQUIRE_CACHE["/Scripts/compiler/require_polyfill.js"] = {
|
|
||||||
exports: {
|
|
||||||
'require': require,
|
|
||||||
'exports': exports,
|
|
||||||
},
|
|
||||||
exportedValues: [
|
|
||||||
'require',
|
|
||||||
'exports',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { Engine } from '/Scripts/engine/engine-interface'
|
import { Engine } from '/Scripts/engine/engine-interface'
|
||||||
import { loggerScripts } from '/Scripts/compiler/host_access'
|
import { loggerScripts } from '/Scripts/compiler/host_access'
|
||||||
import { Client, NamespaceClient } from '/Scripts/client/client'
|
import { Client, NamespaceClient } from '/Scripts/client/client'
|
||||||
import { HookManager } from '/Scripts/engine/hooks/hook-manager'
|
import { HookManager } from '/Scripts/engine/hook-manager'
|
||||||
import { SceneLoader } from '/Scripts/engine/scene/scene-loader'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The core engine values
|
* The core engine values
|
||||||
@ -11,7 +10,6 @@ export const engine: Engine = {
|
|||||||
classes: [],
|
classes: [],
|
||||||
singletons: [],
|
singletons: [],
|
||||||
hookManager: new HookManager(),
|
hookManager: new HookManager(),
|
||||||
sceneLoader: new SceneLoader(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,7 +20,6 @@ export const ENGINE_onInit = () => {
|
|||||||
|
|
||||||
//load namespaces
|
//load namespaces
|
||||||
let client: NamespaceClient = Client
|
let client: NamespaceClient = Client
|
||||||
engine.sceneLoader.hookManager = engine.hookManager
|
|
||||||
|
|
||||||
loggerScripts.INFO('Script Engine Initialized')
|
loggerScripts.INFO('Script Engine Initialized')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { HookManager } from "/Scripts/engine/hooks/hook-manager";
|
import { HookManager } from "/Scripts/engine/hook-manager";
|
||||||
import { SceneLoader } from "/Scripts/engine/scene/scene-loader";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,9 +21,4 @@ export interface Engine {
|
|||||||
*/
|
*/
|
||||||
hookManager: HookManager,
|
hookManager: HookManager,
|
||||||
|
|
||||||
/**
|
|
||||||
* Tracks and loads scenes
|
|
||||||
*/
|
|
||||||
sceneLoader: SceneLoader,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { TrackedScene } from "/Scripts/engine/scene/scene-loader"
|
|
||||||
import { Hook } from "/Scripts/types/hook"
|
import { Hook } from "/Scripts/types/hook"
|
||||||
import { Scene } from "/Scripts/types/scene"
|
import { Scene } from "/Scripts/types/scene"
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ export enum HookScope {
|
|||||||
/**
|
/**
|
||||||
* A hook that is tracked by the manager
|
* A hook that is tracked by the manager
|
||||||
*/
|
*/
|
||||||
export interface TrackedHook extends Hook {
|
interface TrackedHook extends Hook {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scope that this hook was defined at
|
* The scope that this hook was defined at
|
||||||
@ -30,6 +29,23 @@ export 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
|
* Manages hooks for the engine
|
||||||
*/
|
*/
|
||||||
@ -40,6 +56,11 @@ export class HookManager {
|
|||||||
*/
|
*/
|
||||||
hooks: Array<TrackedHook> = []
|
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
|
* A map of engine signal to the list of hooks that should be called when that signal fires
|
||||||
*/
|
*/
|
||||||
@ -48,7 +69,57 @@ export class HookManager {
|
|||||||
/**
|
/**
|
||||||
* The list of all scenes tracked by the manager
|
* The list of all scenes tracked by the manager
|
||||||
*/
|
*/
|
||||||
trackedScenes: Array<TrackedScene> = []
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a hook
|
* Registers a hook
|
||||||
@ -68,19 +139,10 @@ export class HookManager {
|
|||||||
const signalArray: Array<TrackedHook> = this.signalHookMap?.[hookSignal] ? this.signalHookMap?.[hookSignal] : []
|
const signalArray: Array<TrackedHook> = this.signalHookMap?.[hookSignal] ? this.signalHookMap?.[hookSignal] : []
|
||||||
signalArray.push(trackedHook)
|
signalArray.push(trackedHook)
|
||||||
this.signalHookMap[hookSignal] = signalArray
|
this.signalHookMap[hookSignal] = signalArray
|
||||||
//
|
//add to scene array
|
||||||
//Scene related structures
|
const sceneArray: Array<TrackedHook> = this.sceneHookMap?.[scene.sceneId] ? this.sceneHookMap?.[scene.sceneId] : []
|
||||||
//
|
sceneArray.push(trackedHook)
|
||||||
//track scene if it isn't already being tracked
|
this.sceneHookMap[scene.sceneId] = sceneArray
|
||||||
let filteredArr = this.trackedScenes.filter(trackedScene => trackedScene.instanceId === scene.instanceId)
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +159,7 @@ export class HookManager {
|
|||||||
* @param signal The signal
|
* @param signal The signal
|
||||||
* @param value The value associated with the signal
|
* @param value The value associated with the signal
|
||||||
*/
|
*/
|
||||||
fireSignal(instanceId: number, signal: string, value: any){
|
fireSignal(signal: string, value: any){
|
||||||
const hooks: Array<TrackedHook> = this.signalHookMap[signal]
|
const hooks: Array<TrackedHook> = this.signalHookMap[signal]
|
||||||
hooks.forEach(trackedHook => {
|
hooks.forEach(trackedHook => {
|
||||||
trackedHook.callback(value)
|
trackedHook.callback(value)
|
||||||
@ -1,137 +0,0 @@
|
|||||||
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
|
|
||||||
*/
|
|
||||||
instanceId: 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a scene
|
|
||||||
* @param sceneName The scene to load
|
|
||||||
* @returns The id assigned to the instance of the scene
|
|
||||||
*/
|
|
||||||
loadScene(sceneName: string): number {
|
|
||||||
//@ts-ignore
|
|
||||||
const scene: Scene = require(sceneName).default
|
|
||||||
|
|
||||||
//creates an instance of the scene
|
|
||||||
let sceneInstanceId: number = this.createInstance(scene,true)
|
|
||||||
|
|
||||||
//call on init for scene
|
|
||||||
if(scene.onCreate){
|
|
||||||
scene.onCreate(sceneInstanceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sceneInstanceId
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers all hooks in a scene to the hook manager
|
|
||||||
* @param scene The scene
|
|
||||||
* @returns The id assigned to the instance of the scene
|
|
||||||
*/
|
|
||||||
createInstance(scene: Scene, isServerScene: boolean): number{
|
|
||||||
//add to the list of tracked scenes
|
|
||||||
const trackedScene: TrackedScene = {
|
|
||||||
instanceId: this.sceneIdIncrementer++,
|
|
||||||
scene: scene,
|
|
||||||
sceneHooks: [],
|
|
||||||
signalHookMap: { },
|
|
||||||
}
|
|
||||||
this.loadedScenes.push(trackedScene)
|
|
||||||
this.sceneIdMap[trackedScene.instanceId] = trackedScene
|
|
||||||
|
|
||||||
//load all hooks from the scene
|
|
||||||
scene?.hooks?.forEach((hook: Hook) => {
|
|
||||||
engine.hookManager.registerHook(trackedScene,hook,isServerScene)
|
|
||||||
})
|
|
||||||
|
|
||||||
return trackedScene.instanceId
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { TrackedScene } from "/Scripts/engine/scene/scene-loader";
|
|
||||||
import { Hook } from "/Scripts/types/hook";
|
import { Hook } from "/Scripts/types/hook";
|
||||||
import { Namespace } from "/Scripts/types/namespace";
|
import { Namespace } from "/Scripts/types/namespace";
|
||||||
|
|
||||||
@ -27,9 +26,4 @@ export interface Scene extends Namespace {
|
|||||||
*/
|
*/
|
||||||
hooks?: Array<Hook>
|
hooks?: Array<Hook>
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when the scene is created
|
|
||||||
*/
|
|
||||||
onCreate?: (instanceId: number) => void
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
@page dungeonsindex Dungeons
|
@page dungeonsindex Dungeons
|
||||||
|
|
||||||
[TOC]
|
|
||||||
- @subpage dungeonslayout
|
|
||||||
|
|
||||||
Enforce slower walking speed in dungeons (to give movement a more deliberate and weighty feel)
|
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
|
- 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
|
Have the engine generate different types of shortcuts to allow you to quickly navigate up/down to the uncleared floors
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
@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.
|
|
||||||
@ -33,7 +33,7 @@ public class DebugSPWorldLoading {
|
|||||||
SaveUtils.createOrOverwriteSave(saveName, SceneGenerator.createProceduralSceneFile(saveName));
|
SaveUtils.createOrOverwriteSave(saveName, SceneGenerator.createProceduralSceneFile(saveName));
|
||||||
}
|
}
|
||||||
//load just-created save
|
//load just-created save
|
||||||
SaveUtils.loadSave(saveName, false);
|
SaveUtils.loadSave(saveName);
|
||||||
//initialize the "virtual" objects simulation
|
//initialize the "virtual" objects simulation
|
||||||
LoadingUtils.initMacroSimulation();
|
LoadingUtils.initMacroSimulation();
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,7 @@ public class LevelEditorLoading {
|
|||||||
SaveUtils.createOrOverwriteSave(saveName,sceneFile);
|
SaveUtils.createOrOverwriteSave(saveName,sceneFile);
|
||||||
}
|
}
|
||||||
//load just-created save
|
//load just-created save
|
||||||
SaveUtils.loadSave(saveName, true);
|
SaveUtils.loadSave(saveName);
|
||||||
|
|
||||||
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
|
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
|
||||||
//init authentication
|
//init authentication
|
||||||
|
|||||||
@ -34,7 +34,7 @@ public class LevelLoading {
|
|||||||
loadingWindow.setVisible(true);
|
loadingWindow.setVisible(true);
|
||||||
|
|
||||||
//load save
|
//load save
|
||||||
SaveUtils.loadSave(saveName, false);
|
SaveUtils.loadSave(saveName);
|
||||||
|
|
||||||
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
|
LoggerInterface.loggerEngine.INFO("run server: " + Globals.RUN_SERVER + " run client: " + Globals.RUN_CLIENT);
|
||||||
//init authentication
|
//init authentication
|
||||||
|
|||||||
@ -95,12 +95,4 @@ public class SceneFile {
|
|||||||
return createSaveInstance;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,23 +23,23 @@ public class SceneLoader {
|
|||||||
* Loads a scene file on the server
|
* Loads a scene file on the server
|
||||||
* @param path The name of the save to look for a scene file within
|
* @param path The name of the save to look for a scene file within
|
||||||
*/
|
*/
|
||||||
public static void serverInstantiateSaveSceneFile(String saveName, ServerWorldData serverWorldData, boolean isLevelEditor){
|
public static void serverInstantiateSaveSceneFile(String saveName, ServerWorldData serverWorldData){
|
||||||
//load scene file
|
//load scene file
|
||||||
SceneFile file = FileUtils.loadObjectFromSavePath(saveName, "/scene.json", SceneFile.class);
|
SceneFile file = FileUtils.loadObjectFromSavePath(saveName, "/scene.json", SceneFile.class);
|
||||||
//instantiate scene data
|
//instantiate scene data
|
||||||
serverInstantiateSceneFile(file,serverWorldData,isLevelEditor);
|
serverInstantiateSceneFile(file,serverWorldData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a scene file on the server
|
* Loads a scene file on the server
|
||||||
* @param path The name of the scene in the assets/scenes folder
|
* @param path The name of the scene in the assets/scenes folder
|
||||||
*/
|
*/
|
||||||
public static void serverInstantiateAssetSceneFile(String sceneName, ServerWorldData serverWorldData, boolean isLevelEditor){
|
public static void serverInstantiateAssetSceneFile(String sceneName, ServerWorldData serverWorldData){
|
||||||
//load scene file
|
//load scene file
|
||||||
String sanitizedPath = FileUtils.sanitizeFilePath("/Scenes/" + sceneName + "/scene.json");
|
String sanitizedPath = FileUtils.sanitizeFilePath("/Scenes/" + sceneName + "/scene.json");
|
||||||
SceneFile file = FileUtils.loadObjectFromAssetPath(sanitizedPath, SceneFile.class);
|
SceneFile file = FileUtils.loadObjectFromAssetPath(sanitizedPath, SceneFile.class);
|
||||||
//instantiate scene data
|
//instantiate scene data
|
||||||
serverInstantiateSceneFile(file,serverWorldData,isLevelEditor);
|
serverInstantiateSceneFile(file,serverWorldData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +47,7 @@ public class SceneLoader {
|
|||||||
* @param path The path in the assets directory to a scene file
|
* @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
|
* @param isSave if true, will try to load scene from save file instead of asset file
|
||||||
*/
|
*/
|
||||||
private static void serverInstantiateSceneFile(SceneFile file, ServerWorldData serverWorldData, boolean isLevelEditor){
|
private static void serverInstantiateSceneFile(SceneFile file, ServerWorldData serverWorldData){
|
||||||
|
|
||||||
//
|
//
|
||||||
//Content manager
|
//Content manager
|
||||||
@ -103,9 +103,6 @@ public class SceneLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//load scripts
|
//load scripts
|
||||||
if(!isLevelEditor && file.getInitScriptPath() != null){
|
|
||||||
Globals.scriptEngine.initScene(file.getInitScriptPath());
|
|
||||||
}
|
|
||||||
//TODO: integrate scripts for client side of scenes
|
//TODO: integrate scripts for client side of scenes
|
||||||
// for(String scriptPath : file.getScriptPaths()){
|
// for(String scriptPath : file.getScriptPaths()){
|
||||||
// Globals.scriptEngine.loadScript(scriptPath);
|
// Globals.scriptEngine.loadScript(scriptPath);
|
||||||
|
|||||||
@ -59,7 +59,7 @@ public class MenuGenerators {
|
|||||||
serverThread.start();
|
serverThread.start();
|
||||||
clientThread.start();
|
clientThread.start();
|
||||||
} else {
|
} else {
|
||||||
SaveUtils.loadSave(saveName.toLowerCase(), false);
|
SaveUtils.loadSave(saveName.toLowerCase());
|
||||||
WindowUtils.replaceMainMenuContents(MenuGenerators.createSaveCreationMenu());
|
WindowUtils.replaceMainMenuContents(MenuGenerators.createSaveCreationMenu());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -179,11 +179,6 @@ public class MenuGeneratorsLevelEditor {
|
|||||||
}, DEFAULT_GRID_SIZE / (float)GriddedDataCellManager.MAX_GRID_SIZE)
|
}, DEFAULT_GRID_SIZE / (float)GriddedDataCellManager.MAX_GRID_SIZE)
|
||||||
);
|
);
|
||||||
sceneFile.getRealmDescriptor().setGriddedRealmSize(DEFAULT_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);
|
rVal.addChild(griddedRealmControls);
|
||||||
|
|
||||||
|
|||||||
@ -191,7 +191,7 @@ public class ToggleInput extends StandardDrawableElement implements ClickableEle
|
|||||||
} else {
|
} else {
|
||||||
Globals.elementManager.focusElement(this);
|
Globals.elementManager.focusElement(this);
|
||||||
this.value = !this.value;
|
this.value = !this.value;
|
||||||
Globals.elementManager.fireEventNoPosition(new ValueChangeEvent(this.value), this);
|
Globals.elementManager.fireEventNoPosition(new ValueChangeEvent(absoluteX), this);
|
||||||
propagate = false;
|
propagate = false;
|
||||||
}
|
}
|
||||||
} else if(event instanceof ValueChangeEvent){
|
} else if(event instanceof ValueChangeEvent){
|
||||||
|
|||||||
@ -119,9 +119,6 @@ public class InputMacros {
|
|||||||
//the actual input
|
//the actual input
|
||||||
ToggleInput toggleInput = ToggleInput.createToggleInput();
|
ToggleInput toggleInput = ToggleInput.createToggleInput();
|
||||||
toggleInput.setValue(defaultValue);
|
toggleInput.setValue(defaultValue);
|
||||||
toggleInput.setOnValueChangeCallback(new ValueChangeEventCallback() {public void execute(ValueChangeEvent event) {
|
|
||||||
onChange.accept(event);
|
|
||||||
}});
|
|
||||||
rVal.addChild(toggleInput);
|
rVal.addChild(toggleInput);
|
||||||
|
|
||||||
return rVal;
|
return rVal;
|
||||||
|
|||||||
@ -42,9 +42,6 @@ public class ScriptEngine {
|
|||||||
//the object that contains all host values accessible to javascript land
|
//the object that contains all host values accessible to javascript land
|
||||||
Value hostObject;
|
Value hostObject;
|
||||||
|
|
||||||
//the engine object
|
|
||||||
Value engineObject;
|
|
||||||
|
|
||||||
//The files that are loaded on init to bootstrap the script engine
|
//The files that are loaded on init to bootstrap the script engine
|
||||||
static final String[] filesToLoadOnInit = new String[]{
|
static final String[] filesToLoadOnInit = new String[]{
|
||||||
//polyfills
|
//polyfills
|
||||||
@ -59,6 +56,12 @@ public class ScriptEngine {
|
|||||||
"Scripts/compiler/host_access.js",
|
"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
|
//The classes that will be provided to the scripting engine
|
||||||
//https://stackoverflow.com/a/65942034
|
//https://stackoverflow.com/a/65942034
|
||||||
static final Object[][] staticClasses = new Object[][]{
|
static final Object[][] staticClasses = new Object[][]{
|
||||||
@ -100,15 +103,15 @@ public class ScriptEngine {
|
|||||||
defineHostMembers();
|
defineHostMembers();
|
||||||
|
|
||||||
//register engine files
|
//register engine files
|
||||||
registerFile("/Scripts/engine/engine-init.ts");
|
for(String folderToRegister : foldersToLoadOnInit){
|
||||||
|
registerScriptDirectory(folderToRegister,FileUtils.getAssetFile(folderToRegister));
|
||||||
|
}
|
||||||
|
|
||||||
//compile
|
//compile
|
||||||
compile();
|
compile();
|
||||||
|
|
||||||
//run script for engine init
|
//run script for engine init
|
||||||
requireModule("/Scripts/engine/engine-init.ts");
|
requireModule("/Scripts/engine/engine-init.ts");
|
||||||
//get the engine object
|
|
||||||
engineObject = topLevelValue.getMember("REQUIRE_CACHE").getMember("/Scripts/engine/engine-init.js").getMember("exports").getMember("engine");
|
|
||||||
invokeModuleFunction("/Scripts/engine/engine-init.ts","ENGINE_onInit");
|
invokeModuleFunction("/Scripts/engine/engine-init.ts","ENGINE_onInit");
|
||||||
|
|
||||||
|
|
||||||
@ -157,7 +160,6 @@ public class ScriptEngine {
|
|||||||
return topLevelValue.removeMember(valueName);
|
return topLevelValue.removeMember(valueName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
/**
|
/**
|
||||||
* Reads the scripts directory
|
* Reads the scripts directory
|
||||||
* @param path The
|
* @param path The
|
||||||
@ -184,7 +186,7 @@ public class ScriptEngine {
|
|||||||
* Loads a script from disk
|
* Loads a script from disk
|
||||||
* @param path The path to the script file
|
* @param path The path to the script file
|
||||||
*/
|
*/
|
||||||
void loadDependency(String path){
|
public void loadDependency(String path){
|
||||||
String content;
|
String content;
|
||||||
Source source = null;
|
Source source = null;
|
||||||
try {
|
try {
|
||||||
@ -203,59 +205,44 @@ 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
|
* Prints the content of a file
|
||||||
* @param path The filepath of the script
|
* @param path The filepath of the script
|
||||||
*/
|
*/
|
||||||
public void printScriptSource(String path){
|
public void printScriptSource(String path){
|
||||||
invokeMemberFunction("COMPILER", "printSource", path);
|
invokeFunction("COMPILER_printSource", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a file with the scripting engine to be compiled into the full binary
|
* Registers a file with the scripting engine to be compiled into the full binary
|
||||||
* @param path The path to the script file
|
* @param path The path to the script file
|
||||||
*/
|
*/
|
||||||
private boolean registerFile(String path){
|
private void registerFile(String path){
|
||||||
String content;
|
String content;
|
||||||
try {
|
try {
|
||||||
content = FileUtils.getAssetFileAsString(path);
|
content = FileUtils.getAssetFileAsString(path);
|
||||||
sourceMap.put(path,Source.create("js",content));
|
sourceMap.put(path,Source.create("js",content));
|
||||||
Value dependentFilesValue = invokeMemberFunction("COMPILER", "registerFile",path,content);
|
invokeFunction("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) {
|
} catch (IOException e) {
|
||||||
LoggerInterface.loggerScripts.ERROR("FAILED TO LOAD SCRIPT", e);
|
LoggerInterface.loggerScripts.ERROR("FAILED TO LOAD SCRIPT", e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles the project
|
* Compiles the project
|
||||||
*/
|
*/
|
||||||
private void compile(){
|
private void compile(){
|
||||||
invokeMemberFunction("COMPILER", "run");
|
invokeFunction("COMPILER_run");
|
||||||
}
|
Value compiledCode = topLevelValue.getMember("COMPILER_emitted_value");
|
||||||
|
context.eval("js",compiledCode.asString());
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
//load scene from javascript side
|
|
||||||
Value sceneLoader = this.engineObject.getMember("sceneLoader");
|
|
||||||
Value loadFunc = sceneLoader.getMember("loadScene");
|
|
||||||
loadFunc.execute(scenePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -265,7 +252,6 @@ public class ScriptEngine {
|
|||||||
* @param args The arguments
|
* @param args The arguments
|
||||||
*/
|
*/
|
||||||
public Value invokeFunction(String functionName, Object... args){
|
public Value invokeFunction(String functionName, Object... args){
|
||||||
LoggerInterface.loggerScripts.DEBUG("Host execute: " + functionName);
|
|
||||||
Value function = topLevelValue.getMember(functionName);
|
Value function = topLevelValue.getMember(functionName);
|
||||||
if(function != null){
|
if(function != null){
|
||||||
return function.execute(args);
|
return function.execute(args);
|
||||||
@ -275,25 +261,6 @@ public class ScriptEngine {
|
|||||||
return null;
|
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
|
* Invokes a function defined in a file
|
||||||
* @param filePath The file the function is defined in
|
* @param filePath The file the function is defined in
|
||||||
@ -331,8 +298,6 @@ public class ScriptEngine {
|
|||||||
for(Object[] currentClass : staticClasses){
|
for(Object[] currentClass : staticClasses){
|
||||||
classes.putMember((String)currentClass[0], currentClass[1]);
|
classes.putMember((String)currentClass[0], currentClass[1]);
|
||||||
}
|
}
|
||||||
//give access to script engine instance
|
|
||||||
hostObject.putMember("scriptEngine", this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -174,7 +174,7 @@ public class SaveUtils {
|
|||||||
* @param saveName The name of the save
|
* @param saveName The name of the save
|
||||||
* @return true always
|
* @return true always
|
||||||
*/
|
*/
|
||||||
public static boolean loadSave(String saveName, boolean isLevelEditor){
|
public static boolean loadSave(String saveName){
|
||||||
String dirPath = deriveSaveDirectoryPath(saveName);
|
String dirPath = deriveSaveDirectoryPath(saveName);
|
||||||
|
|
||||||
//load save file
|
//load save file
|
||||||
@ -200,11 +200,11 @@ public class SaveUtils {
|
|||||||
if(FileUtils.checkSavePathExists(saveName, "/scene.json")){
|
if(FileUtils.checkSavePathExists(saveName, "/scene.json")){
|
||||||
//load from save itself
|
//load from save itself
|
||||||
LoggerInterface.loggerEngine.INFO("Load scene data from save " + saveName);
|
LoggerInterface.loggerEngine.INFO("Load scene data from save " + saveName);
|
||||||
SceneLoader.serverInstantiateSaveSceneFile(saveName, serverWorldData, isLevelEditor);
|
SceneLoader.serverInstantiateSaveSceneFile(saveName, serverWorldData);
|
||||||
} else if(FileUtils.getAssetFile("/Scenes/" + saveName + "/scene.json").exists()){
|
} else if(FileUtils.checkFileExists("/Scenes/" + saveName)){
|
||||||
//load from defined scene
|
//load from defined scene
|
||||||
LoggerInterface.loggerEngine.INFO("Load scene data from scene " + saveName);
|
LoggerInterface.loggerEngine.INFO("Load scene data from scene " + saveName);
|
||||||
SceneLoader.serverInstantiateAssetSceneFile(saveName, serverWorldData, isLevelEditor);
|
SceneLoader.serverInstantiateAssetSceneFile(saveName, serverWorldData);
|
||||||
} else {
|
} else {
|
||||||
//The scene is neither defined in the save itself nor in the assets scene files
|
//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!");
|
throw new IllegalStateException("Trying to load a save that does not contain a scene!");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user