forked from Mirrors/openclonk
233 lines
6.2 KiB
C++
233 lines
6.2 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
// links aul scripts; i.e. resolves includes & appends, etc
|
|
|
|
#include "C4Include.h"
|
|
#include "script/C4Aul.h"
|
|
|
|
#include "game/C4Game.h"
|
|
#include "landscape/C4Material.h"
|
|
#include "object/C4Def.h"
|
|
#include "object/C4DefList.h"
|
|
#include "object/C4GameObjects.h"
|
|
#include "script/C4Effect.h"
|
|
|
|
void C4ScriptHost::DoAppend(C4Def *def)
|
|
{
|
|
if (std::find(def->Script.SourceScripts.begin(), def->Script.SourceScripts.end(), this) == def->Script.SourceScripts.end())
|
|
{
|
|
def->Script.SourceScripts.push_back(this);
|
|
if (!Includes.empty())
|
|
{
|
|
Warn("#appendto contains #include");
|
|
// Try to fullfil #include, but it won't work properly: #appendtos
|
|
// are always appended, but #includes are prepended to the script.
|
|
def->Script.Includes.insert(def->Script.Includes.end(), Includes.begin(), Includes.end());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// ResolveAppends and ResolveIncludes must be called both
|
|
// for each script. ResolveAppends has to be called first!
|
|
bool C4ScriptHost::ResolveAppends(C4DefList *rDefs)
|
|
{
|
|
// resolve local appends
|
|
if (State != ASS_PREPARSED) return false;
|
|
for (auto & Append : Appends)
|
|
{
|
|
if (Append != "*" || !rDefs)
|
|
{
|
|
C4Def *Def = rDefs ? rDefs->GetByName(Append) : nullptr;
|
|
if (Def)
|
|
{
|
|
DoAppend(Def);
|
|
}
|
|
else
|
|
{
|
|
// save id in buffer because AulWarn will use the buffer of C4IdText
|
|
// to get the id of the object in which the error occurs...
|
|
// (stupid static buffers...)
|
|
Warn("#appendto %s not found", Append.getData());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// append to all defs
|
|
for (int i = 0; i < rDefs->GetDefCount(); i++)
|
|
{
|
|
C4Def *pDef = rDefs->GetDef(i);
|
|
if (!pDef) break;
|
|
if (pDef == GetPropList()) continue;
|
|
// append
|
|
DoAppend(pDef);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool C4ScriptHost::ResolveIncludes(C4DefList *rDefs)
|
|
{
|
|
// Had been preparsed?
|
|
if (State != ASS_PREPARSED) return false;
|
|
// has already been resolved?
|
|
if (IncludesResolved) return true;
|
|
// catch circular includes
|
|
if (Resolving)
|
|
{
|
|
Engine->GetErrorHandler()->OnError(C4AulParseError(this, "Circular include chain detected - ignoring all includes!").what());
|
|
IncludesResolved = true;
|
|
State = ASS_LINKED;
|
|
return false;
|
|
}
|
|
Resolving=true;
|
|
// append all includes to local script
|
|
for (std::list<StdCopyStrBuf>::reverse_iterator i = Includes.rbegin(); i != Includes.rend(); ++i)
|
|
{
|
|
C4Def *Def = rDefs ? rDefs->GetByName(*i) : nullptr;
|
|
if (Def)
|
|
{
|
|
// resolve #includes in included script first (#include-chains :( )
|
|
if (!Def->Script.IncludesResolved)
|
|
if (!Def->Script.ResolveIncludes(rDefs))
|
|
continue; // skip this #include
|
|
|
|
for (std::list<C4ScriptHost *>::reverse_iterator s = Def->Script.SourceScripts.rbegin(); s != Def->Script.SourceScripts.rend(); ++s)
|
|
{
|
|
if (std::find(SourceScripts.begin(), SourceScripts.end(), *s) == SourceScripts.end())
|
|
SourceScripts.push_front(*s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// save id in buffer because AulWarn will use the buffer of C4IdText
|
|
// to get the id of the object in which the error occurs...
|
|
// (stupid static buffers...)
|
|
Warn("#include %s not found", i->getData());
|
|
}
|
|
}
|
|
IncludesResolved = true;
|
|
// includes/appends are resolved now (for this script)
|
|
Resolving=false;
|
|
State = ASS_LINKED;
|
|
return true;
|
|
}
|
|
|
|
void C4ScriptHost::UnLink()
|
|
{
|
|
C4PropList * p = GetPropList();
|
|
if (p)
|
|
{
|
|
p->C4PropList::Clear();
|
|
p->SetProperty(P_Prototype, C4VPropList(Engine->GetPropList()));
|
|
}
|
|
|
|
// Delete cyclic references of owned proplists
|
|
DeleteOwnedPropLists();
|
|
|
|
// includes will have to be re-resolved now
|
|
IncludesResolved = false;
|
|
|
|
if (State > ASS_PREPARSED) State = ASS_PREPARSED;
|
|
}
|
|
|
|
void C4AulScriptEngine::UnLink()
|
|
{
|
|
warnCnt = errCnt = lineCnt = 0;
|
|
|
|
// Make everything writeable
|
|
GetPropList()->ThawRecursively();
|
|
for (C4ScriptHost *s = Child0; s; s = s->Next)
|
|
s->GetPropList()->ThawRecursively();
|
|
|
|
// unlink scripts
|
|
for (C4ScriptHost *s = Child0; s; s = s->Next)
|
|
s->UnLink();
|
|
// Do not clear global variables and constants, because they are registered by the
|
|
// preparser or other parts. Note that keeping those fields means that you cannot delete a global
|
|
// variable or constant at runtime by removing it from the script.
|
|
}
|
|
|
|
void C4AulScriptEngine::Link(C4DefList *rDefs)
|
|
{
|
|
try
|
|
{
|
|
// resolve appends
|
|
for (C4ScriptHost *s = Child0; s; s = s->Next)
|
|
s->ResolveAppends(rDefs);
|
|
|
|
// resolve includes
|
|
for (C4ScriptHost *s = Child0; s; s = s->Next)
|
|
s->ResolveIncludes(rDefs);
|
|
|
|
// parse the scripts to byte code
|
|
for (C4ScriptHost *s = Child0; s; s = s->Next)
|
|
s->Parse();
|
|
|
|
if (rDefs)
|
|
{
|
|
rDefs->SortByPriority();
|
|
rDefs->CallEveryDefinition();
|
|
}
|
|
|
|
// Done modifying the proplists now
|
|
for (C4ScriptHost *s = Child0; s; s = s->Next)
|
|
s->GetPropList()->FreezeAndMakeStaticRecursively(&s->ownedPropLists);
|
|
|
|
GetPropList()->FreezeAndMakeStaticRecursively(&OwnedPropLists);
|
|
}
|
|
catch (C4AulError &err)
|
|
{
|
|
// error??! show it!
|
|
ErrorHandler->OnError(err.what());
|
|
}
|
|
|
|
// Set name list for globals (FIXME: is this necessary?)
|
|
ScriptEngine.GlobalNamed.SetNameList(&ScriptEngine.GlobalNamedNames);
|
|
}
|
|
|
|
|
|
void C4AulScriptEngine::ReLink(C4DefList *rDefs)
|
|
{
|
|
// unlink scripts
|
|
UnLink();
|
|
|
|
// unlink defs
|
|
if (rDefs) rDefs->ResetIncludeDependencies();
|
|
|
|
// re-link
|
|
Link(rDefs);
|
|
|
|
// display state
|
|
LogF("C4AulScriptEngine linked - %d line%s, %d warning%s, %d error%s",
|
|
lineCnt, (lineCnt != 1 ? "s" : ""), warnCnt, (warnCnt != 1 ? "s" : ""), errCnt, (errCnt != 1 ? "s" : ""));
|
|
|
|
// adjust global effects
|
|
if (pGlobalEffects) pGlobalEffects->ReAssignAllCallbackFunctions();
|
|
if (GameScript.pScenarioEffects) GameScript.pScenarioEffects->ReAssignAllCallbackFunctions();
|
|
}
|
|
|
|
bool C4AulScriptEngine::ReloadScript(const char *szScript, const char *szLanguage)
|
|
{
|
|
C4ScriptHost * s;
|
|
for (s = Child0; s; s = s->Next)
|
|
if (s->ReloadScript(szScript, szLanguage))
|
|
break;
|
|
return !!s;
|
|
}
|
|
|