commit x64 compilation from lulu cause the other branch dont seems to compile properly at home

This commit is contained in:
2026-07-17 16:08:20 +02:00
parent c0f3eeb00d
commit 0efa4ee6f7
625 changed files with 117283 additions and 4426 deletions

View File

@ -0,0 +1,159 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_debugger.h"
#include "metafile/nml.h"
using namespace GS;
using namespace GS::Script;
//------------------------------------------------------------------------------
void IDebugger::DereferenceVarTree(const List <DebuggerVariable *> &tree)
{
ListForeachPtr(DebuggerVariable *, v, tree)
{
DereferenceVarTree(v->member_list);
v->referenced = false;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
SourceBreakpoint *IDebugger::FindSourceBreakpoint(const char *source, int line)
{
ListForeachPtr(SourceBreakpoint *, bp, source_breakpoint_list)
if ((bp->source == source) && (bp->line == line))
return bp;
return NULL;
}
SourceBreakpoint *IDebugger::AddSourceBreakpoint(const char *source, int line)
{
SourceBreakpoint *bp = FindSourceBreakpoint(source, line);
if (!bp)
if ((bp = new SourceBreakpoint(source, line)) != NULL)
source_breakpoint_list.Add(bp);
return bp;
}
bool IDebugger::RemoveSourceBreakpoint(SourceBreakpoint *breakpoint)
{ return source_breakpoint_list.Remove(breakpoint); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool IDebugger::SetBreakpoints(const NML::Tag &tag)
{
if (tag.name != "SetBreakpoints")
return false;
source_breakpoint_list.Clear();
NMLTagForeach(bp, tag)
if (bp->name == "Breakpoint")
{
NML::Tag *ts = bp->GetTypedTag("Source;", GS::Variant::VariantString),
*tl = bp->GetTypedTag("Line", GS::Variant::VariantInteger);
if (ts && tl)
AddSourceBreakpoint(ts->GetString(), tl->GetInteger());
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
String IDebugger::GetStackFrameLocals()
{
String data;
data << "<SetStackFrameLocals=\n";
ListForeachPtr(DebuggerVariable *, v, local_var_tree)
VariableToMetatagString(v, data);
data << ">";
return data;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IDebugger::SetDebugStackFrame(int level)
{
if (level == -1)
level = GetStackFrameIndex();
if (level != debug_stack_frame)
{
debug_stack_frame = level;
local_var_tree.Clear();
RefreshStackFrameLocalsCache();
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IDebugger::Suspend()
{
is_suspended = true;
}
void IDebugger::Resume()
{
is_suspended = false;
if (!IsStepping())
local_var_tree.Clear();
}
void IDebugger::StepInto()
{
is_stepping = true;
step_to_callstack_depth = -1;
Resume();
}
void IDebugger::StepOut()
{
is_stepping = true;
step_to_callstack_depth = GetCallstackDepth() > 0 ? GetCallstackDepth() - 1 : 0;
Resume();
}
void IDebugger::Step()
{
is_stepping = true;
step_to_callstack_depth = GetCallstackDepth();
Resume();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IDebugger::CheckSuspendOnBreakpoint(const char *source, int line)
{
if (SourceBreakpoint *bp = FindSourceBreakpoint(source, line))
Suspend();
}
void IDebugger::CheckSuspendOnStep()
{
if ((GetCallstackDepth() <= step_to_callstack_depth) || (step_to_callstack_depth == -1))
{
Suspend();
is_stepping = false;
}
if (IsSuspended())
RefreshStackFrameLocalsCache();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IDebugger::Reset()
{
is_suspended = false;
is_stepping = false;
step_to_callstack_depth = -1;
}
//------------------------------------------------------------------------------
IDebugger::IDebugger()
{
Reset();
}
IDebugger::~IDebugger()
{}

View File

@ -0,0 +1,89 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_engine_types.h"
//------------------------------------------------------------------------------
const char *GS::Script::CObjectTypeToString(GS::Script::CObjectType type)
{
static const char *types[typetag_End] =
{
"Undefined",
"Deleted",
"Engine",
"ScriptVM",
"ResourceSet",
"Project",
"Renderer",
"Raytracer",
"Mixer",
"Scene3d",
"Clock",
"Font",
"RasterFont",
"Scene2d",
"UICursor",
"Window",
"Item2d",
"Sprite2d",
"Widget",
"SizerWidget",
"ContainerWidget",
"SpacerWidget",
"CanvasWidget",
"TextWidget",
"BitmapWidget",
"CheckWidget",
"Picture",
"Group",
"Item",
"Camera",
"Object",
"Light",
"Instance",
"Emitter",
"ParticleModel",
"Motion",
"Trigger",
"Path",
"Sound",
"Shader",
"Texture",
"Geometry",
"GeometryTemplate",
"Material",
"MaterialShader",
"ColShape",
"Constraint",
"Metafile",
"Metatag",
"FileHandle",
"InputDevice",
"EditorPlugin",
"ProjectScene",
"ProjectLayer",
"AutomationSource",
"AutomationSourceGroup",
"ResourceFactories",
"PeerInterface",
"Peer",
};
return types[type];
}
//------------------------------------------------------------------------------

View File

@ -85,8 +85,6 @@ enum CObjectType
typetag_PeerController,
typetag_Peer,
typetag_WebSocketManager,
typetag_End
};

View File

@ -0,0 +1,136 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_profiler.h"
#include "platform.h"
#include "log/log.h"
using namespace GS::Script;
//------------------------------------------------------------------------------
Profiler::FunctionProfile *Profiler::GetFunctionProfile(const char *source, const char *func, int line, FunctionProfile *caller)
{
// Check for profiled function.
FunctionProfile *profile = NULL;
ListForeachPtr(FunctionProfile *, f_profile, function_map)
if (
(f_profile->func == func) &&
(f_profile->caller_function == caller)
)
{
profile = f_profile;
break;
}
// Create a new function profile.
if (!profile)
{
profile = new FunctionProfile(source, func, line);
if (!profile)
__LOG_E__ << "Failed to allocate a new function profile.\n";
// Register caller.
profile->caller_function = caller;
// Add the newly created function as a child of the caller function profile.
if (caller)
caller->callee_function.Add(profile);
function_map.Add(profile);
}
return profile;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Profiler::Start(const char *session_id)
{
id = session_id;
function_map.Clear();
function_list.Clear();
current_function_profile = NULL;
profiler_clock = Platform::Get().GetClock();
total_clock = 0;
profiling = true;
}
int Profiler::GetChildTotalClock(FunctionProfile *profile)
{
int child_total_clock = 0;
ListForeachPtr(FunctionProfile *, c_profile, profile->callee_function)
child_total_clock += GetChildTotalClock(c_profile);
return profile->self_clock + child_total_clock;
}
void Profiler::End()
{
ListForeachPtr(FunctionProfile *, f_profile, function_map)
f_profile->total_clock = GetChildTotalClock(f_profile);
// Build the function list from the call graph function map.
ListForeachPtr(FunctionProfile *, f_profile, function_map)
{
// Check list if function is already present.
FunctionProfile *profile = NULL;
ListForeachPtr(FunctionProfile *, l_profile, function_list)
if (l_profile->func == f_profile->func)
{
profile = l_profile;
break;
}
// If not, create a new list entry.
if (!profile)
{
profile = new FunctionProfile(f_profile->source.c_str(), f_profile->func.c_str(), f_profile->line);
function_list.Add(profile);
}
// Merge this function contribution.
profile->hit_count += f_profile->hit_count;
profile->self_clock += f_profile->self_clock;
profile->total_clock += f_profile->total_clock;
}
profiling = false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Profiler::Update(int type, FunctionProfile *profile)
{
// Update clock.
int current_clock = Platform::Get().GetClock(),
dt_clock = current_clock - profiler_clock;
profiler_clock = current_clock;
// Update current profile.
if (current_function_profile)
{
current_function_profile->Update(dt_clock, 0);
total_clock += dt_clock;
}
// Get profile.
switch (type)
{
case 'c':
current_function_profile = profile;
current_function_profile->Hit();
break;
case 'r':
current_function_profile = NULL;
break;
case 'l':
break;
}
}
//------------------------------------------------------------------------------
Profiler::Profiler() : profiling(false), current_function_profile(NULL) {}

View File

@ -0,0 +1,79 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_unit.h"
#include "script/script_object.h"
#include "script/script_variant.h"
#include "log/log.h"
using namespace GS::Script;
//------------------------------------------------------------------------------
bool Unit::SetupFunctionCall(const char *func, const Object *func_object)
{
if (!vm || !self || !vm->SetupFunctionCall(func, func_object, self))
return false;
return vm->SetFunctionCallContext(Variant(iface_object, iface_type));
}
bool Unit::PushUserObjectFunctionCallArgument(void *p, uint type, bool managed)
{ return vm->PushArgument(Variant(p, type)); }
bool Unit::PushFunctionCallArgument(const Variant &arg)
{ return vm->PushArgument(arg); }
bool Unit::DoFunctionCall(Variant *return_value)
{ return vm->DoFunctionCall(return_value); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Unit::Open()
{
if (!vm)
return false;
if (self)
return true;
if (script_file.IsEmpty() || script_class.IsEmpty())
return true;
// Instantiate class.
if (!vm->CompileFile(script_file) || !vm->SetupFunctionCall(script_class))
return false;
Variant rv;
if (!vm->DoFunctionCall(&rv) || (rv.type != Variant::Type_ScriptObject))
{
vm->Kill(String::Format("Function call to '%s' failed.", script_class.c_str()));
return false;
}
self = rv.object;
rv.object = NULL; // Detach object from the variant so that it does not get deleted.
// Send parameters to the instance.
ListForeachPtr(GS::Variant *, v, parm_list)
vm->Set(v->id, Variant(*v), self);
return true;
}
void Unit::Close()
{
// Note: Do not delete the parameter list from here.
_safe_delete(self);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Unit::Unit(IVM *_vm) : vm(_vm)
{
iface_object = NULL;
iface_type = (uint)~0;
self = NULL;
}
Unit::~Unit()
{
ListDeleteAllPtr(GS::Variant *, parm_list)
Close();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,131 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_unit.h"
#include "metafile/nml.h"
#include "log/log.h"
using namespace GS::Script;
using namespace GS::NML;
//------------------------------------------------------------------------------
bool Unit::FromMetaTag(Tag &tag)
{
if (tag.name != "ScriptUnit")
__ERR__(__LOG_E__ << "Could not parse script unit, incorrect root tag (" << tag.name << ").\n", false)
script_class.Clear();
script_file.Clear();
// Parse root tags.
NMLTagForeach(pt, tag)
{
if (pt->name == "ScriptPath")
script_file = pt->GetString();
else if (pt->name == "Script") // legacy path (convert to explicit @core)
{
script_file = pt->GetString();
if (script_file.StartsWith("builtin/"))
script_file = String("@core/") + script_file;
}
else if (pt->name == "Class")
script_class = pt->GetString();
// Parse instance parameters.
else if (pt->name == "ParmList")
{
NMLTagForeach(c, *pt)
{
Tag *id = c->GetTag("Id"), *vl = c->GetTag("Value");
if (id && vl)
{
GS::Variant *parm = NULL;
switch (vl->GetType())
{
case GS::Variant::VariantBool:
parm = new GS::Variant(id->GetString(), vl->GetBool());
break;
case GS::Variant::VariantInteger:
parm = new GS::Variant(id->GetString(), vl->GetInteger());
break;
case GS::Variant::VariantFloat:
parm = new GS::Variant(id->GetString(), vl->GetReal());
break;
case GS::Variant::VariantString:
parm = new GS::Variant(id->GetString(), vl->GetString());
break;
default:
__LOG_W__ << "parameter '" << id->GetString() << "' type is invalid.\n";
break;
}
if (parm)
parm_list.Add(parm);
}
else __LOG_W__ << "Incomplete script parameter declaration ignored.\n";
}
}
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <ScriptUnit>.\n";
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tag *Unit::AsMetaTag() const
{
Tag *root = new Tag("ScriptUnit");
if (!root)
__ERR__(__LOG_E__ << "Could not serialize script unit. Failed to create root tag.\n", NULL)
// Store script.
if (!script_file.IsEmpty())
root->AddChild("ScriptPath", script_file.c_str());
if (!script_class.IsEmpty())
root->AddChild("Class", script_class.c_str());
// Store instance parameters.
if (parm_list.GetCount())
{
if (Tag *parmlist = root->AddChild("ParmList"))
{
ListForeachPtr(GS::Variant *, v, parm_list)
{
if (Tag *parm = parmlist->AddChild("Parm"))
{
parm->AddChild("Id", v->id);
switch (v->GetType())
{
case GS::Variant::VariantBool:
parm->AddChild("Value", v->b_value);
break;
case GS::Variant::VariantInteger:
parm->AddChild("Value", v->i_value);
break;
case GS::Variant::VariantFloat:
parm->AddChild("Value", v->f_value);
break;
case GS::Variant::VariantString:
parm->AddChild("Value", v->s_value);
break;
}
}
else __LOG_W__ << "Failed to serialize instance parameter '" << v->id << "'.\n";
}
}
else __LOG_W__ << "Failed to serialize instance parameter list.\n";
}
if (!root->GetChildCount())
_safe_delete(root);
return root;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,164 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_variant.h"
#include "script/script_object.h"
#include "script/script_vm.h"
using namespace GS::Script;
//------------------------------------------------------------------------------
bool Variant::operator == (const Variant &b) const
{
return
(
(type == b.type) &&
(variant == b.variant) &&
(object_owner == b.object_owner) &&
(object == b.object) &&
(ptr == b.ptr) &&
(typetag == b.typetag)
);
}
bool Variant::operator != (const Variant &b) const
{ return !(*this == b); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Variant::Set()
{
type = Type_None;
if (object_owner)
_safe_delete(object);
object = NULL;
}
void Variant::Set(bool v)
{
Set();
type = Type_Variant;
variant = v;
}
void Variant::Set(int v)
{
Set();
type = Type_Variant;
variant = v;
}
void Variant::Set(uint v)
{
Set();
type = Type_Variant;
variant = (int)v;
}
void Variant::Set(float v)
{
Set();
type = Type_Variant;
variant = v;
}
void Variant::Set(const char *v)
{
Set();
type = Type_Variant;
variant = v;
}
void Variant::Set(const GS::Variant &v)
{
Set();
type = Type_Variant;
variant = v;
}
void Variant::Set(Object *o, bool own)
{
Set();
type = Type_ScriptObject;
object_owner = own;
object = o;
}
void Variant::Set(const void *p, size_t size)
{
Set();
type = Type_Variant;
variant.SetBinary(p, size);
}
void Variant::Set(void *_ptr, uint _ptr_id)
{
Set();
type = Type_UserObject;
ptr = _ptr;
typetag = _ptr_id;
object = NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant::Variant()
{
type = Type_None;
object = NULL;
}
Variant::Variant(const GS::Variant &v)
{
type = Type_Variant;
variant = v;
object = NULL;
}
Variant::Variant(bool v)
{
type = Type_Variant;
variant = v;
object = NULL;
}
Variant::Variant(int v)
{
type = Type_Variant;
variant = v;
object = NULL;
}
Variant::Variant(uint v)
{
type = Type_Variant;
variant = (int)v;
object = NULL;
}
Variant::Variant(float v)
{
type = Type_Variant;
variant = v;
object = NULL;
}
Variant::Variant(const char *v)
{
type = Type_Variant;
variant = v;
object = NULL;
}
Variant::Variant(Object *o, bool own)
{
type = Type_ScriptObject;
object_owner = own;
object = o;
}
Variant::Variant(const void *_ptr, size_t size)
{
type = Type_Variant;
variant.SetBinary(_ptr, size);
object = NULL;
}
Variant::Variant(void *_ptr, uint _ptr_id)
{
type = Type_UserObject;
ptr = _ptr;
typetag = _ptr_id;
object = NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant::~Variant()
{ Set(); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,42 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_vm.h"
#include "filesystem/filesystem.h"
#include "platform.h"
using namespace GS;
using namespace GS::Script;
//------------------------------------------------------------------------------
bool IVM::CompileFile(const char *uri, const Object *context)
{
Array <char> data;
if (!Platform::Get().io->FileLoad(uri, data))
return false;
return Compile(data, data.GetCount(), context, uri);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IVM::SetDebugInterface(IDebug *i, bool)
{
debug_interface = i;
}
void IVM::Kill(const char *reason)
{
if (debug_interface)
debug_interface->OnFatalError(reason);
state = StateDead;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
IVM::IVM() : state(StateOk) {}
IVM::~IVM() {}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,107 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/script_vm_debug_profile_base.h"
using namespace GS;
using namespace GS::Script;
//------------------------------------------------------------------------------
String IDebuggerProfiler::ConvertCallStackToMetaString(const AutoList <IVM::CallStackEntry *> &callstack, int current_stack_frame)
{
String data = "<SetCallStack=\n";
int level = 0;
ListForeachPtr(IVM::CallStackEntry *, frame, callstack)
{
data += "<Frame=";
data += String::Format("<Source=\"%s\"><Function=\"%s\"><Line=%d>", frame->source.c_str(), frame->function.c_str(), frame->line);
if (level == current_stack_frame)
data += "<IsCurrent>";
data += ">\n";
++level;
}
data += ">";
return data;
}
String IDebuggerProfiler::GetCallstack()
{
AutoList <IVM::CallStackEntry *> callstack;
vm->GetCallStack(callstack);
return ConvertCallStackToMetaString(callstack, debugger->GetDebugStackFrame());
}
String IDebuggerProfiler::GetDebugStackFrameLocals()
{
debugger->RefreshStackFrameLocalsCache();
return debugger->GetStackFrameLocals();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IDebuggerProfiler::Kill()
{
/*
// Kill the VM in order to escape infinite loops.
SquirrelVM &squirrel_vm = (SquirrelVM &)vm;
if (squirrel_vm.VM())
sq_setalive(squirrel_vm.VM(), false);
*/
// FIXME
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void IDebuggerProfiler::OnStep(char type, const char *source, int line, const char *func)
{
// FIXME the stack querying system should be abstracted and go through VM interface calls.
/*
if (profiler.IsValid())
{
HSQUIRRELVM hvm = ((SquirrelVM &)vm).VM();
// Update profiler if currently profiling.
if (profiler.IsProfiling())
{
Profiler::FunctionProfile *profile = 0;
SQStackInfos si;
int depth = 0;
while (SQ_SUCCEEDED(sq_stackinfos(hvm, depth++, &si)))
;
while (depth > 0)
if (SQ_SUCCEEDED(sq_stackinfos(hvm, --depth, &si)))
profile = profiler.GetFunctionProfile(si.source, si.funcname, (int)si.line, profile);
if (profile)
profiler.Update((int)type, profile);
}
}
*/
if (debugger.IsValid())
{
// Check for breakpoint.
debugger->CheckSuspendOnBreakpoint(source, line);
// Refresh the debugger UI.
debugger->SetDebugStackFrame(debugger->GetStackFrameIndex());
if (debugger->IsStepping())
debugger->CheckSuspendOnStep();
if (debugger->IsSuspended()) // might now be suspended after the previous step check
OnSuspendExecution(source, line);
// Suspend execution.
while (debugger->IsSuspended() && (vm->GetState() != IVM::StateDead))
if (!OnUpdateSuspendedExecution()) // wait for handler resume signal
debugger->Resume();
}
}
//------------------------------------------------------------------------------
IDebuggerProfiler::IDebuggerProfiler(IVM *script_vm, IDebugger *idbg) : vm(script_vm), debugger(idbg) {}

View File

@ -0,0 +1,78 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "script/scripted_object.h"
#include "script/script_unit.h"
#include "metafile/nml.h"
#include "log/log.h"
using namespace GS::Script;
using namespace GS::NML;
//------------------------------------------------------------------------------
Unit *ScriptedObject::AddUnit(Unit *unit)
{
unit_list.Add(unit);
return unit;
}
bool ScriptedObject::RemoveUnit(Unit *unit)
{
if (!unit || !unit_list.Remove(unit))
return false;
_safe_delete(unit);
return true;
}
Unit *ScriptedObject::GetUnit(const char *script_file, const char *script_class) const
{
ListForeachPtr(Unit *, unit, GetUnitList())
if ((unit->script_file == script_file) && (unit->script_class == script_class))
return unit;
return NULL;
}
void ScriptedObject::RemoveAllUnit()
{ ListDeleteAllPtr(Unit *, unit_list) }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool ScriptedObject::FromMetaTag(Tag &tag)
{
if (tag.name != "ScriptedObject")
__ERR__(__LOG_E__ << "Could not parse scripted object, incorrect root tag (" << tag.name << ").\n", false)
ListDeleteAllPtr(Unit *, unit_list)
NMLTagForeach(pt, tag)
{
if (pt->name == "ScriptUnit")
{
if (Unit *unit = AddUnit(NewUnit()))
if (unit->FromMetaTag(*pt))
unit->Open();
}
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <ScriptedObject>.\n";
}
return true;
}
Tag *ScriptedObject::AsMetaTag() const
{
if (!GetUnitList().GetCount())
return NULL;
Tag *root = new Tag("ScriptedObject");
if (!root)
__ERR__(__LOG_E__ << "Could not create root tag to serialize.\n", NULL)
// Dump script units.
ListForeachPtr(Unit *, unit, GetUnitList())
root->AddChild(unit->AsMetaTag());
return root;
}
//------------------------------------------------------------------------------
ScriptedObject::~ScriptedObject()
{ RemoveAllUnit(); }