Debug
For an easier debugging during development, Daneel provides extensive, flexible and easy to setup error reporting, and a stack trace.
Debugging is turned off by default, so be sure to enable it via Daneel's user config :
function Daneel.UserConfig()
return {
debug = {
enableDebug = true
}
}
end
Be advised that having the debug enabled seriously lowers the performances, so be sure to disable it before exporting your game.
Error reporting
In every functions introduced or modified by Daneel, every arguments are checked for type and value and a comprehensive error message is thrown if needed.
For instance, passing a Vector2
instead of a Vector3
to transform:SetPosition()
would trigger the following error :
Transform.SetPosition( transform, position ) : Argument 'position' is of type 'Vector2' with value 'Vector2: { x=1, y=2 }' instead of 'Vector3'.
Setting up error reporting for your functions
Be sure to check the function reference to learn about all the functions of the Daneel.Debug
object you can use to setup error reporting in your scripts.
The simplest way to setup error reporting is to set information about the function's arguments in the Daneel.Debug.functionArgumentsInfo
table.
Ie:
function Scene.Append( sceneNameOrAsset, parentNameOrInstance )
local scene = Asset.Get( sceneNameOrAsset, "Scene", true )
local parent = nil
if parentNameOrInstance ~= nil then
parent = GameObject.Get( parentNameOrInstance, true )
end
return CraftStudio.AppendScene( scene, parent )
end
Daneel.Debug.functionArgumentsInfo["Scene.Append"] = {
{ "sceneNameOrAsset", {"string", "Scene"} },
{ "parentNameOrInstance", {"string", "GameObject"}, isOptional = true },
}
The keys are the full, global function's names (including the object(s) the functions are nested in).
Which means that you can not set error reporting for local (or anonymous) functions. They must be globally accessible.
The values are the argument's lists as a table. Arguments must be formatted as follow :
- The first entry in the table must be the argument's name.
- The second entry may be the argument's type(s) as a string or a table of strings (when the argument's type may be one of several). Omit this entry when the argument can be of any type.
- The
isOptional
property must betrue
when the argument is optional.
Ie:
{ "value", "number" }
{ "alignment", {"string", "userdata", "number"} }
{ "decimal", "number", isOptional = true }
{ "modelNameOrAsset", { "string", "Model" }, isOptional = true }
This works for public behavior functions too, provided that you set the script asset as the value of the script
property in the argument's list
-- in a script named "Folder/MyScript"
function Behavior:ReceiveDamage( damageCount )
...
end
Daneel.Debug.functionArgumentsInfo["ReceiveDamage"] = { { "damageCount", "number" }, script = Behavior }
An error in the argument's type (a string instead of a number is passed for instance) would trigger the following error:
Folder/MyScript.ReceiveDamage( self, damageCount ) : Argument 'damageCount' is of type 'string' with value '"10"' instead of 'number'.
Note that the first argument of a behavior function is always the scripted behavior instance, but that argument is automatically added to the argument's list if it's not found.
Stack Trace
If you enabled it in the config (set the debug.enableStackTrace
property to true
as you did for debug.enableDebug
), Daneel prints a "stack trace" in the Runtime Report when an error is triggered.
The stack trace nicely shows the history of function calls within the framework and your code that lead to the error and display the values received as argument.
It reads from top to bottom, the last function called -where the error occurred a priori- at the bottom.
For instance, when trying to set the model of a ModelRenderer
to a Model
that does not exists via gameObject:Set()
:
~~~~~ Daneel.Debug.StackTrace ~~~~~
#01 GameObject.Set( GameObject: 14476932: 'Object1', table: 04DAC148 )
#02 Component.Set( ModelRenderer: 31780825, table: 04DAC238 )
#03 ModelRenderer.SetModel( ModelRenderer: 31780825, "UnknowModel" )
[string "Behavior Daneel/Daneel (0)"]:1302: ModelRenderer.SetModel( modelRenderer, modelNameOrAsset ) : Argument 'modelNameOrAsset' : model with name 'UnknowModel' was not found.
When the stack trace is enabled, the location of the error shown in the Runtime Report will always be in the Daneel
script, so pay attention to the stack trace and the error message to locate the actual source of the error.
When error reporting is setup via the Daneel.Debug.functionArgumentsInfo
table, the functions are added to the stacktrace by default.
You can prevent this by setting an includeInStackTrace
property to false
in the argument's list :
Daneel.Debug.functionArgumentsInfo["Scene.Append"] = {
includeInStackTrace = false,
{ "sceneNameOrAsset", {"string", "Scene"} },
...
}
This also works for any behavior functions you set in the functionArgumentsInfo
table.
If you want all your functions script's to be easily included in the stack trace, you can just pass the script asset to the Daneel.Debug.RegisterScript(scriptAsset)
function at the end of the script.
Note that because of CrafStudio's inner working, the Awake()
functions can't be included automatically in the stacktrace.
You can however include it manually in two ways :
With Daneel.Debug.StackTrace.BeginFunction(functionName)
and EndFunction()
,
function Behavior:Awake()
Daneel.Debug.StackTrace.BeginFunction("Folder/MyScript.Awake")
...
Daneel.Debug.StackTrace.EndFunction()
end
Or like this (provided that the script has been registered via Daneel.Debug.RegisterScript(scriptAsset)
) :
function Behavior:Awake( calledBySelf )
if calledBySelf ~= true then
self:Awake( true )
return
end
...
end
You can also include quickly many functions in the stack trace by passing the object they are nested in to the Daneel.Debug.RegisterObject(object)
function.
Data types
The function Daneel.Debug.GetType(value)
may returns any of the built-in Lua types or the name of any of the objects introduced by CraftStudio or Daneel : GameObject
, ModelRenderer
, RaycastHit
, ...
GetType()
actually returns the name as a string of the provided table's metatable (or the Lua type in every other cases), instead of returning "table"
.
This automatically works when the metatable is a first-level global variable but it can work for nested objects too as you define them in the objects
table in Daneel's or any module's config.
Ie : the function will return "ObjectName"
when a table with MyObject
as a metatable is provided
function Daneel.UserConfig()
return {
debug = { ... },
objects = {
ObjectName = MyObject
}
}
end
MyObject = {}
function MyObject.New()
return setmetatable( {}, MyObject )
end
local instance = MyObject.New()
Daneel.Debug.GetType( instance ) -- returns "ObjectName"