Store Prototype directly in PropLists, not as a normal property

This is mostly to prevent crashes when prototypes are deleted.

Also change proplist savegame format to not include the constant flag - all
constant proplists are not stored in savegames anymore, but recreated from
the game data. Store the prototype at that position instead.
heavy-resources
Günther Brammer 2013-03-24 18:10:16 +01:00
parent 22016e075f
commit 69f64ea341
3 changed files with 76 additions and 40 deletions

View File

@ -221,8 +221,6 @@ C4PropList::C4PropList(C4PropList * prototype):
FirstRef(NULL), prototype(prototype),
constant(false), Status(1)
{
if (prototype)
SetProperty(P_Prototype, C4VPropList(prototype));
#ifdef _DEBUG
PropLists.Add(this);
#endif
@ -236,9 +234,7 @@ void C4PropList::Denumerate(C4ValueNumbers * numbers)
const_cast<C4Value &>(p->Value).Denumerate(numbers);
p = Properties.Next(p);
}
C4Value v;
if(GetProperty(P_Prototype, &v))
prototype = v.getPropList();
prototype.Denumerate(numbers);
}
C4PropList::~C4PropList()
@ -279,9 +275,41 @@ bool C4PropList::operator==(const C4PropList &b) const
void C4PropList::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers)
{
pComp->Value(constant);
bool oldFormat = false;
// constant proplists are not serialized to savegames, but recreated from the game data instead
assert(!constant);
if (pComp->isCompiler() && pComp->hasNaming())
{
// backwards compat to savegames and scenarios before 5.5
try
{
pComp->Value(constant);
oldFormat = true;
}
catch (StdCompiler::NotFoundException *pEx)
{
delete pEx;
pComp->Value(mkParAdapt(prototype, numbers));
}
}
else
pComp->Value(mkParAdapt(prototype, numbers));
pComp->Separator(StdCompiler::SEP_SEP2);
pComp->Value(mkParAdapt(Properties, numbers));
if (oldFormat)
{
if (Properties.Has(&::Strings.P[P_Prototype]))
{
prototype = Properties.Get(&::Strings.P[P_Prototype]).Value;
Properties.Remove(&::Strings.P[P_Prototype]);
}
}
for(C4PropList * it = GetPrototype(); it; it = it->GetPrototype())
if(it == this)
{
prototype.Set0();
pComp->excCorrupt("Cyclic prototype structure");
}
}
void CompileNewFunc(C4PropList *&pStruct, StdCompiler *pComp, C4ValueNumbers * const & rPar)
@ -400,43 +428,43 @@ void C4PropList::SetName(const char* NewName)
C4Object * C4PropList::GetObject()
{
if (prototype) return prototype->GetObject();
if (GetPrototype()) return GetPrototype()->GetObject();
return 0;
}
C4Def * C4PropList::GetDef()
{
if (prototype) return prototype->GetDef();
if (GetPrototype()) return GetPrototype()->GetDef();
return 0;
}
C4Def const * C4PropList::GetDef() const
{
if (prototype) return prototype->GetDef();
if (GetPrototype()) return GetPrototype()->GetDef();
return 0;
}
class C4MapScriptLayer * C4PropList::GetMapScriptLayer()
{
if (prototype) return prototype->GetMapScriptLayer();
if (GetPrototype()) return GetPrototype()->GetMapScriptLayer();
return NULL;
}
class C4MapScriptMap * C4PropList::GetMapScriptMap()
{
if (prototype) return prototype->GetMapScriptMap();
if (GetPrototype()) return GetPrototype()->GetMapScriptMap();
return NULL;
}
C4PropListNumbered * C4PropList::GetPropListNumbered()
{
if (prototype) return prototype->GetPropListNumbered();
if (GetPrototype()) return GetPrototype()->GetPropListNumbered();
return 0;
}
C4Effect * C4PropList::GetEffect()
{
if (prototype) return prototype->GetEffect();
if (GetPrototype()) return GetPrototype()->GetEffect();
return 0;
}
@ -479,8 +507,13 @@ bool C4PropList::GetPropertyByS(C4String * k, C4Value *pResult) const
*pResult = Properties.Get(k).Value;
return true;
}
else if(prototype)
return prototype->GetPropertyByS(k, pResult);
else if (k == &Strings.P[P_Prototype])
{
*pResult = prototype;
return true;
}
else if(GetPrototype())
return GetPrototype()->GetPropertyByS(k, pResult);
else
return false;
}
@ -492,9 +525,9 @@ C4String * C4PropList::GetPropertyStr(C4PropertyName n) const
{
return Properties.Get(k).Value.getStr();
}
if (prototype)
if (GetPrototype())
{
return prototype->GetPropertyStr(n);
return GetPrototype()->GetPropertyStr(n);
}
return 0;
}
@ -506,9 +539,9 @@ C4AulFunc * C4PropList::GetFunc(C4String * k) const
{
return Properties.Get(k).Value.getFunction();
}
if (prototype)
if (GetPrototype())
{
return prototype->GetFunc(k);
return GetPrototype()->GetFunc(k);
}
return 0;
}
@ -551,9 +584,9 @@ C4PropertyName C4PropList::GetPropertyP(C4PropertyName n) const
return C4PropertyName(v - &Strings.P[0]);
return P_LAST;
}
if (prototype)
if (GetPrototype())
{
return prototype->GetPropertyP(n);
return GetPrototype()->GetPropertyP(n);
}
return P_LAST;
}
@ -565,9 +598,9 @@ int32_t C4PropList::GetPropertyInt(C4PropertyName n) const
{
return Properties.Get(k).Value.getInt();
}
if (prototype)
if (GetPrototype())
{
return prototype->GetPropertyInt(n);
return GetPrototype()->GetPropertyInt(n);
}
return 0;
}
@ -579,9 +612,9 @@ C4PropList *C4PropList::GetPropertyPropList(C4PropertyName n) const
{
return Properties.Get(k).Value.getPropList();
}
if (prototype)
if (GetPrototype())
{
return prototype->GetPropertyPropList(n);
return GetPrototype()->GetPropertyPropList(n);
}
return NULL;
}
@ -590,9 +623,9 @@ C4ValueArray * C4PropList::GetProperties() const
{
C4ValueArray * a;
int i;
if (prototype)
if (GetPrototype())
{
a = prototype->GetProperties();
a = GetPrototype()->GetProperties();
i = a->GetSize();
a->SetSize(i + Properties.GetSize());
}
@ -626,16 +659,15 @@ void C4PropList::SetPropertyByS(C4String * k, const C4Value & to)
{
assert(!constant);
/*assert(Strings.Set.Has(k));*/
if (k == &Strings.P[P_Prototype] && to.GetType() == C4V_PropList)
if (k == &Strings.P[P_Prototype])
{
C4PropList * newpt = to.GetData().PropList;
for(C4PropList * it = newpt; it; it = it->prototype)
C4PropList * newpt = to.getPropList();
for(C4PropList * it = newpt; it; it = it->GetPrototype())
if(it == this)
throw new C4AulExecError("Trying to create cyclic prototype structure");
prototype = newpt;
//return;
prototype.SetPropList(newpt);
}
if (Properties.Has(k))
else if (Properties.Has(k))
{
Properties.Get(k).Value = to;
}
@ -657,7 +689,10 @@ void C4PropList::SetPropertyByS(C4String * k, const C4Value & to)
void C4PropList::ResetProperty(C4String * k)
{
Properties.Remove(k);
if (k == &Strings.P[P_Prototype])
prototype.Set0();
else
Properties.Remove(k);
}
void C4PropList::Iterator::Init()
@ -690,9 +725,9 @@ C4PropList::Iterator C4PropList::begin()
{
C4PropList::Iterator iter;
if (prototype)
if (GetPrototype())
{
iter = prototype->begin();
iter = GetPrototype()->begin();
}
else
{

View File

@ -67,7 +67,7 @@ class C4PropListNumbered;
class C4PropList
{
public:
void Clear() { constant = false; Properties.Clear(); prototype = 0; }
void Clear() { constant = false; Properties.Clear(); prototype.Set0(); }
const char *GetName() const;
virtual void SetName (const char *NewName = 0);
@ -77,10 +77,11 @@ public:
virtual C4Object * GetObject();
virtual C4Effect * GetEffect();
virtual C4PropListNumbered * GetPropListNumbered();
C4PropList * GetPrototype() const { return prototype; }
virtual class C4MapScriptLayer * GetMapScriptLayer();
virtual class C4MapScriptMap * GetMapScriptMap();
C4PropList * GetPrototype() const { return prototype._getPropList(); }
// saved as a reference to a global constant?
virtual class C4PropListStatic * IsStatic() { return NULL; }
// saved as a reference to separately saved objects?
@ -146,7 +147,7 @@ private:
void DelRef(const C4Value *pRef, C4Value * pNextRef);
C4Value *FirstRef; // No-Save
C4Set<C4Property> Properties;
C4PropList * prototype;
C4Value prototype;
bool constant; // if true, this proplist is not changeable
friend class C4Value;
friend class C4ScriptHost;

View File

@ -213,7 +213,7 @@ bool C4GameScriptHost::Load(C4Group & g, const char * f, const char * l, C4LangS
C4PropListStatic * pScen = new C4PropListScen(NULL, &::Strings.P[P_Scenario]);
ScenPropList.SetPropList(pScen);
::ScriptEngine.RegisterGlobalConstant("Scenario", ScenPropList);
ScenPrototype.SetPropList(pScen->GetPropertyPropList(P_Prototype));
ScenPrototype.SetPropList(pScen->GetPrototype());
Reg2List(&ScriptEngine);
return C4ScriptHost::Load(g, f, l, t);
}