I'm close to finalize the design of the scripting engine, so I'm posting some preliminary pseudo code.
The general flow for script execution is: Load, Execute on Update, CleanUp
First, the function that loads a script from a URI (Uniform Resource Identifier)
Function LoadScript(URI As String) As Error
Dim Script As ScriptObject = Nothing
If IsLegalPath(URI) Then
Script = LoadScriptFromFile(URI)
ElseIf IsLegalURL(URI) Then
Script = LoadScriptFromURL(URI)
Else
Return Error.UnrecognizedURIFormat
End If
If Script Is Nothing Then
Return Error.LoadingScript
End If
Dim ErrCode As Error = PreParseScript(Script)
If ErrCode <> Error.none Then
Return ErrCode
End If
End Function
Execute on Update is executed just before each frame is called. Execution of scripts takes place in a single function, and the function returns when the update is complete and the graphics engine can begin rendering objects to the screen. The first time this function is called for a script, it begins execution of the script at the top of the main() function. On subsequent calls, it resumes execution where it left off before.
Function ExecuteScript(Script As ScriptObject) As Error
Dim ErrCode As Error
If Script.PC = -1 Then
ErrCode = CallScriptFunction(Script, "main")
If ErrCode <> Error.none Then
Return ErrCode
End If
End If
ErrCode = RunCode(Script)
If ErrCode <> Error.none
Return ErrCode
End If
Return Error.none
End If
There are structure and enums that hold information about identified functions, variables, structs, enums, and the locations in the script code they are found. These structures hold information such as data type expected, memory contents of variables, and a tree-like structure for tracking structs and arrays. The idea is that the PreParse function can identify all this info before execution begins, thereby speeding execution of the code.
The function RunCode is where all the action is at. It gets symbols from the script one at a time, and interprets what they mean. The whole function is inside a big loop, and repeatedly gets symbols and interprets them, until a command is encountered that flags the script's readiness to update the display screen. Once the script flags that it is done running for this frame, the function returns an Error code (Error.none for success), and the graphics engine begins to update the world with data processed and returned by the script.
All script state information about nesting, function calls, variable contents and scope, and instruction pointer, are all contained in the ScriptObject. This allows the script to trigger an update at just about any point and safely expect execution to resume exactly as it was upon next update cycle.
When the script is finished up (the game is over) the main() function ends, and memory cleanup is initiated. After this cleanup process, the game engine is notified to shut down, and objects are disposed, such as sounds, images, models, network connections, etc. Then the engine exist.
There is one big part missing from this script engine so far: expression evaluation. This topic is so long and complex, it deserves its own separate post. Expression evaluation is likely to be the most complex part of the entire game engine. I envision a recursive token parsing evaluating system, and as soon as I figure out how to implement this, there will be a lengthy post about it.
More updates to come as design continues.
No comments:
Post a Comment