openclonk/src/script/C4ScriptHost.cpp

396 lines
10 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, Matthes Bender
* 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.
*/
/* Handles script file components (calls, inheritance, function maps) */
#include "C4Include.h"
#include "script/C4ScriptHost.h"
#include "object/C4Def.h"
#include "script/C4AulCompiler.h"
#include "script/C4AulParse.h"
#include "script/C4AulScriptFunc.h"
#include "script/C4AulScriptFunc.h"
#include "script/C4Effect.h"
/*--- C4ScriptHost ---*/
C4ScriptHost::C4ScriptHost()
{
Script = nullptr;
stringTable = nullptr;
SourceScripts.push_back(this);
// prepare include list
IncludesResolved = false;
Resolving=false;
Includes.clear();
Appends.clear();
}
C4ScriptHost::~C4ScriptHost()
{
Unreg();
Clear();
}
void C4ScriptHost::Clear()
{
UnlinkOwnedFunctions();
C4ComponentHost::Clear();
ast.reset();
Script.Clear();
LocalValues.Clear();
DeleteOwnedPropLists();
SourceScripts.clear();
SourceScripts.push_back(this);
if (stringTable)
{
stringTable->DelRef();
stringTable = nullptr;
}
// remove includes
Includes.clear();
Appends.clear();
// reset flags
State = ASS_NONE;
enabledWarnings.clear();
}
void C4ScriptHost::UnlinkOwnedFunctions()
{
// Remove owned functions from their parents. This solves a problem
// where overloading a definition would unload the C4ScriptHost, but
// keep around global functions, which then contained dangling pointers.
for (const auto &box : ownedFunctions)
{
assert(box.GetType() == C4V_Function);
C4AulScriptFunc *func = box._getFunction()->SFunc();
C4PropList *parent = func->Parent;
if (parent == GetPropList())
continue;
assert(parent == &::ScriptEngine);
C4Value v;
parent->GetPropertyByS(func->Name, &v);
if (v.getFunction() == func)
{
// If the function we're deleting is the top-level function in
// the inheritance chain, promote the next one in its stead;
// if there is no overloaded function, remove the property.
parent->Thaw();
if (func->OwnerOverloaded)
parent->SetPropertyByS(func->Name, C4VFunction(func->OwnerOverloaded));
else
parent->ResetProperty(func->Name);
}
else
{
C4AulScriptFunc *func_chain = v.getFunction()->SFunc();
assert(func_chain != func);
while (func_chain)
{
// Unlink the removed function from the inheritance chain
if (func_chain->OwnerOverloaded == func)
{
func_chain->OwnerOverloaded = func->OwnerOverloaded;
break;
}
assert(func_chain->OwnerOverloaded && "Removed function not found in inheritance chain");
func_chain = func_chain->OwnerOverloaded->SFunc();
}
}
}
ownedFunctions.clear();
}
void C4ScriptHost::DeleteOwnedPropLists()
{
// delete all static proplists associated to this script host.
// Note that just clearing the vector is not enough in case of
// cyclic references.
for (C4Value& value: ownedPropLists)
{
C4PropList* plist = value.getPropList();
if (plist)
{
if (plist->Delete()) delete plist;
else plist->Clear();
}
}
ownedPropLists.clear();
}
void C4ScriptHost::Unreg()
{
// remove from list
if (Prev) Prev->Next = Next; else if (Engine) Engine->Child0 = Next;
if (Next) Next->Prev = Prev; else if (Engine) Engine->ChildL = Prev;
Prev = Next = nullptr;
Engine = nullptr;
}
void C4ScriptHost::Reg2List(C4AulScriptEngine *pEngine)
{
// already regged? (def reloaded)
if (Engine) return;
// reg to list
if ((Engine = pEngine))
{
if ((Prev = Engine->ChildL))
Prev->Next = this;
else
Engine->Child0 = this;
Engine->ChildL = this;
}
else
Prev = nullptr;
Next = nullptr;
}
bool C4ScriptHost::Load(C4Group &hGroup, const char *szFilename,
const char *szLanguage, class C4LangStringTable *pLocalTable)
{
// Base load
bool fSuccess = C4ComponentHost::Load(hGroup,szFilename,szLanguage);
// String Table
if (stringTable != pLocalTable)
{
if (stringTable) stringTable->DelRef();
stringTable = pLocalTable;
if (stringTable) stringTable->AddRef();
}
// set name
ScriptName.Ref(GetFilePath());
// preparse script
MakeScript();
// Success
return fSuccess;
}
bool C4ScriptHost::LoadData(const char *szFilename, const char *szData, class C4LangStringTable *pLocalTable)
{
// String Table
if (stringTable != pLocalTable)
{
if (stringTable) stringTable->DelRef();
stringTable = pLocalTable;
if (stringTable) stringTable->AddRef();
}
ScriptName.Copy(szFilename);
StdStrBuf tempScript;
tempScript.Copy(szData);
Script.Clear();
if(stringTable)
stringTable->ReplaceStrings(tempScript, Script);
else
Script.Take(tempScript);
Preparse();
return true;
}
void C4ScriptHost::MakeScript()
{
// clear prev
Script.Clear();
// create script
if (stringTable)
{
stringTable->ReplaceStrings(GetDataBuf(), Script);
}
else
{
Script.Ref(GetDataBuf());
}
// preparse script
Preparse();
}
bool C4ScriptHost::ReloadScript(const char *szPath, const char *szLanguage)
{
// this?
if (SEqualNoCase(szPath, GetFilePath()) || (stringTable && SEqualNoCase(szPath, stringTable->GetFilePath())))
{
// try reload
char szParentPath[_MAX_PATH + 1]; C4Group ParentGrp;
if (GetParentPath(szPath, szParentPath))
if (ParentGrp.Open(szParentPath))
if (Load(ParentGrp, nullptr, szLanguage, stringTable))
return true;
}
return false;
}
std::string C4ScriptHost::Translate(const std::string &text) const
{
if (stringTable)
return stringTable->Translate(text);
throw C4LangStringTable::NoSuchTranslation(text);
}
/*--- C4ExtraScriptHost ---*/
C4ExtraScriptHost::C4ExtraScriptHost(C4String *parent_key_name):
ParserPropList(C4PropList::NewStatic(nullptr, nullptr, parent_key_name))
{
}
C4ExtraScriptHost::~C4ExtraScriptHost()
{
Clear();
}
void C4ExtraScriptHost::Clear()
{
C4ScriptHost::Clear();
ParserPropList._getPropList()->Clear();
}
C4PropListStatic * C4ExtraScriptHost::GetPropList()
{
return ParserPropList._getPropList()->IsStatic();
}
/*--- C4ScenarioObjectsScriptHost ---*/
C4ScenarioObjectsScriptHost::C4ScenarioObjectsScriptHost() : C4ExtraScriptHost(::Strings.RegString("ScenarioObjects"))
{
// Note that "ScenarioObjects" is a fake key name under which you cannot access this prop list from script.
// It's just given to have a proper name when script errors are reported.
}
/*--- C4DefScriptHost ---*/
bool C4DefScriptHost::Parse()
{
bool r = C4ScriptHost::Parse();
assert(Def);
// Check category
if (!Def->GetPlane() && Def->Category & C4D_SortLimit)
{
int Plane; bool gotplane = true;
switch (Def->Category & C4D_SortLimit)
{
case C4D_StaticBack: Plane = 100; break;
case C4D_Structure: Plane = C4Plane_Structure; break;
case C4D_Vehicle: Plane = 300; break;
case C4D_Living: Plane = 400; break;
case C4D_Object: Plane = 500; break;
case C4D_StaticBack | C4D_Background: Plane = -500; break;
case C4D_Structure | C4D_Background: Plane = -400; break;
case C4D_Vehicle | C4D_Background: Plane = -300; break;
case C4D_Living | C4D_Background: Plane = -200; break;
case C4D_Object | C4D_Background: Plane = -100; break;
case C4D_StaticBack | C4D_Foreground: Plane = 1100; break;
case C4D_Structure | C4D_Foreground: Plane = 1200; break;
case C4D_Vehicle | C4D_Foreground: Plane = 1300; break;
case C4D_Living | C4D_Foreground: Plane = 1400; break;
case C4D_Object | C4D_Foreground: Plane = 1500; break;
default:
Warn("Def %s (%s) has invalid category", Def->GetName(), Def->GetDataString().getData());
gotplane = false;
break;
}
if (gotplane) Def->SetProperty(P_Plane, C4VInt(Plane));
}
if (!Def->GetPlane())
{
Warn("Def %s (%s) has invalid Plane", Def->GetName(), Def->GetDataString().getData());
Def->SetProperty(P_Plane, C4VInt(1));
}
return r;
}
C4PropListStatic * C4DefScriptHost::GetPropList() { return Def; }
class C4PropListScen: public C4PropListStatic
{
public:
C4PropListScen(const C4PropListStatic * parent, C4String * key): C4PropListStatic(nullptr, parent, key)
{
C4PropList * proto = C4PropList::NewStatic(ScriptEngine.GetPropList(), this, &::Strings.P[P_Prototype]);
C4PropListStatic::SetPropertyByS(&::Strings.P[P_Prototype], C4VPropList(proto));
}
void SetPropertyByS(C4String * k, const C4Value & to) override
{
if (k == &Strings.P[P_Prototype])
{
DebugLog("ERROR: Attempt to set Scenario.Prototype.");
return;
}
C4PropListStatic::SetPropertyByS(k, to);
}
};
/*--- C4GameScriptHost ---*/
C4GameScriptHost::C4GameScriptHost(): ScenPrototype(0), ScenPropList(0) { }
C4GameScriptHost::~C4GameScriptHost() = default;
bool C4GameScriptHost::Load(C4Group & g, const char * f, const char * l, C4LangStringTable * t)
{
assert(ScriptEngine.GetPropList());
C4PropListStatic * pScen = new C4PropListScen(nullptr, &::Strings.P[P_Scenario]);
ScenPropList.SetPropList(pScen);
::ScriptEngine.RegisterGlobalConstant("Scenario", ScenPropList);
ScenPrototype.SetPropList(pScen->GetPrototype());
Reg2List(&ScriptEngine);
return C4ScriptHost::Load(g, f, l, t);
}
bool C4GameScriptHost::LoadData(const char * f, const char * d, C4LangStringTable * t)
{
assert(ScriptEngine.GetPropList());
C4PropListStatic * pScen = new C4PropListScen(nullptr, &::Strings.P[P_Scenario]);
ScenPropList.SetPropList(pScen);
::ScriptEngine.RegisterGlobalConstant("Scenario", ScenPropList);
ScenPrototype.SetPropList(pScen->GetPrototype());
Reg2List(&ScriptEngine);
return C4ScriptHost::LoadData(f, d, t);
}
void C4GameScriptHost::Clear()
{
C4ScriptHost::Clear();
ScenPropList.Set0();
ScenPrototype.Set0();
delete pScenarioEffects; pScenarioEffects=nullptr;
}
C4PropListStatic * C4GameScriptHost::GetPropList()
{
C4PropList * p = ScenPrototype._getPropList();
return p ? p->IsStatic() : nullptr;
}
void C4GameScriptHost::Denumerate(C4ValueNumbers * numbers)
{
ScenPropList.Denumerate(numbers);
if (pScenarioEffects) pScenarioEffects->Denumerate(numbers);
}
C4Value C4GameScriptHost::Call(const char *szFunction, C4AulParSet *Pars, bool fPassError)
{
return ScenPropList._getPropList()->Call(szFunction, Pars, fPassError);
}
C4GameScriptHost GameScript;