1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2009-2016, The OpenClonk Team and contributors 5 * 6 * Distributed under the terms of the ISC license; see accompanying file 7 * "COPYING" for details. 8 * 9 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 10 * See accompanying file "TRADEMARK" for details. 11 * 12 * To redistribute this file separately, substitute the full license texts 13 * for the above references. 14 */ 15 16 /* Property lists */ 17 18 #include "script/C4Value.h" 19 #include "script/C4StringTable.h" 20 21 #ifndef C4PROPLIST_H 22 #define C4PROPLIST_H 23 24 /* C4PropList have a somewhat complicated reference counting scheme. You can: 25 - Store a C4Proplist* in a C4Value. This is the preferred and often only way. 26 - Store a C4Object* from GetObject in a C4Object* or C4PropList* if there is a ClearPointer function for it 27 Use a C4ObjectPtr for help with storing the Object in Savegames. 28 - Store a C4Def* from GetDef() in a C4Def* or C4PropList* 29 30 All PropLists can be destroyed while there are still C4Values referencing them, though 31 Definitions do not get destroyed during the game. So always check for nullpointers. 32 33 The linked list formed by C4PropList::FirstRef and C4Value::NextRef is used to 34 change all C4Values referencing the destroyed Proplist to contain nil instead. 35 Objects are also cleaned up via various ClearPointer functions. 36 The list is also used as a reference count to remove unused Proplists. 37 The exception are C4PropListNumbered and C4Def, which have implicit references 38 from C4GameObjects, C4Object and C4DefList. They have to be destroyed when loosing that reference.*/ 39 40 class C4Property 41 { 42 public: 43 C4Property() = default; C4Property(C4String * Key,const C4Value & Value)44 C4Property(C4String *Key, const C4Value &Value) : Key(Key), Value(Value) 45 { assert(Key); Key->IncRef(); } C4Property(const C4Property & o)46 C4Property(const C4Property &o) : Key(o.Key), Value(o.Value) { if (Key) Key->IncRef(); } 47 C4Property & operator = (const C4Property &o) 48 { if(o.Key) o.Key->IncRef(); if (Key) Key->DecRef(); Key = o.Key; Value = o.Value; return *this; } C4Property(C4Property && o)49 C4Property(C4Property && o) : Key(o.Key), Value(std::move(o.Value)) { o.Key = nullptr; } 50 C4Property & operator = (C4Property && o) 51 { if (Key) Key->DecRef(); Key = o.Key; o.Key = nullptr; Value = std::move(o.Value); return *this; } ~C4Property()52 ~C4Property() { if (Key) Key->DecRef(); } 53 void CompileFunc(StdCompiler *pComp, C4ValueNumbers *); 54 C4String * Key{nullptr}; 55 C4Value Value; 56 operator const void * () const { return Key; } 57 C4Property & operator = (void * p) 58 { assert(!p); if (Key) Key->DecRef(); Key = nullptr; Value.Set0(); return *this; } 59 bool operator < (const C4Property &cmp) const { return strcmp(GetSafeKey(), cmp.GetSafeKey())<0; } GetSafeKey()60 const char *GetSafeKey() const { if (Key && Key->GetCStr()) return Key->GetCStr(); return ""; } // get key as C string; return "" if undefined. never return nullptr 61 }; 62 class C4PropListNumbered; 63 class C4PropList 64 { 65 public: Clear()66 void Clear() { constant = false; Properties.Clear(); prototype.Set0(); } 67 virtual const char *GetName() const; 68 virtual void SetName (const char *NewName = nullptr); SetOnFire(bool OnFire)69 virtual void SetOnFire(bool OnFire) { } 70 71 // These functions return this or a prototype. 72 virtual C4Def const * GetDef() const; 73 virtual C4Def * GetDef(); 74 virtual C4Object * GetObject(); 75 virtual C4Object const * GetObject() const; 76 virtual C4Effect * GetEffect(); 77 virtual C4PropListNumbered * GetPropListNumbered(); 78 virtual class C4MapScriptLayer * GetMapScriptLayer(); 79 virtual class C4MapScriptMap * GetMapScriptMap(); 80 GetPrototype()81 C4PropList * GetPrototype() const { return prototype._getPropList(); } 82 void RemoveCyclicPrototypes(); 83 84 // saved as a reference to a global constant? IsStatic()85 virtual class C4PropListStatic * IsStatic() { return nullptr; } IsStatic()86 const class C4PropListStatic * IsStatic() const { return const_cast<C4PropList*>(this)->IsStatic(); } 87 // saved as a reference to separately saved objects? IsNumbered()88 virtual bool IsNumbered() const { return false; } 89 // some proplists have references that are not reference-counted Delete()90 virtual bool Delete() { return false; } 91 92 // These four operate on properties as seen by script, which can be dynamic 93 // or reflect C++ variables 94 virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const; 95 virtual C4ValueArray * GetProperties() const; 96 // not allowed on frozen proplists 97 virtual void SetPropertyByS(C4String * k, const C4Value & to); 98 virtual void ResetProperty(C4String * k); 99 100 // helper functions to get dynamic properties from other parts of the engine GetProperty(C4PropertyName k,C4Value * pResult)101 bool GetProperty(C4PropertyName k, C4Value *pResult) const 102 { return GetPropertyByS(&Strings.P[k], pResult); } 103 C4String * GetPropertyStr(C4PropertyName k) const; 104 C4ValueArray * GetPropertyArray(C4PropertyName n) const; GetFunc(C4PropertyName k)105 C4AulFunc * GetFunc(C4PropertyName k) const 106 { return GetFunc(&Strings.P[k]); } 107 C4AulFunc * GetFunc(C4String * k) const; 108 C4AulFunc * GetFunc(const char * k) const; 109 C4String * EnumerateOwnFuncs(C4String * prev = nullptr) const; 110 C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false) 111 { return Call(&Strings.P[k], pPars, fPassErrors); } 112 C4Value Call(C4String * k, C4AulParSet *pPars=nullptr, bool fPassErrors=false); 113 C4Value Call(const char * k, C4AulParSet *pPars=nullptr, bool fPassErrors=false); 114 C4PropertyName GetPropertyP(C4PropertyName k) const; 115 int32_t GetPropertyBool(C4PropertyName n, bool default_val = false) const; 116 int32_t GetPropertyInt(C4PropertyName k, int32_t default_val = 0) const; 117 C4PropList *GetPropertyPropList(C4PropertyName k) const; HasProperty(C4String * k)118 bool HasProperty(C4String * k) const { return Properties.Has(k); } 119 // not allowed on frozen proplists SetProperty(C4PropertyName k,const C4Value & to)120 void SetProperty(C4PropertyName k, const C4Value & to) 121 { SetPropertyByS(&Strings.P[k], to); } 122 123 static C4PropList * New(C4PropList * prototype = nullptr); 124 static C4PropListStatic * NewStatic(C4PropList * prototype, const C4PropListStatic * parent, C4String * key); 125 126 // only freeze proplists which are not going to be modified 127 // FIXME: Only C4PropListStatic get frozen. Optimize accordingly. Freeze()128 void Freeze() { constant = true; } Thaw()129 void Thaw() { constant = false; } 130 void ThawRecursively(); IsFrozen()131 bool IsFrozen() const { return constant; } 132 133 // Freeze this and all proplist in properties and ensure they are static proplists 134 // If a proplist is not static, replace it with a static proplist and replace all instances 135 // Place references to all proplists made static in the given value array 136 C4PropListStatic *FreezeAndMakeStaticRecursively(std::vector<C4Value>* prop_lists, const C4PropListStatic *parent = nullptr, C4String * key = nullptr); 137 138 virtual void Denumerate(C4ValueNumbers *); 139 virtual ~C4PropList(); 140 141 void CompileFunc(StdCompiler *pComp, C4ValueNumbers *); 142 void AppendDataString(StdStrBuf * out, const char * delim, int depth = 3, bool ignore_reference_parent = false) const; 143 StdStrBuf ToJSON(int depth = 10, bool ignore_reference_parent = false) const; 144 std::vector< C4String * > GetSortedLocalProperties(bool add_prototype=true) const; 145 std::vector< C4String * > GetSortedLocalProperties(const char *prefix, const C4PropList *ignore_overridden) const; 146 std::vector< C4String * > GetUnsortedProperties(const char *prefix, C4PropList *ignore_parent = nullptr) const; 147 std::vector< C4String * > GetSortedProperties(const char *prefix, C4PropList *ignore_parent = nullptr) const; 148 149 bool operator==(const C4PropList &b) const; 150 #ifdef _DEBUG 151 static C4Set<C4PropList *> PropLists; 152 #endif 153 154 protected: 155 C4PropList(C4PropList * prototype = nullptr); ClearRefs()156 void ClearRefs() { while (FirstRef) FirstRef->Set0(); } 157 158 private: 159 void AddRef(C4Value *pRef); 160 void DelRef(const C4Value *pRef, C4Value * pNextRef); 161 C4Value *FirstRef{nullptr}; // No-Save 162 C4Set<C4Property> Properties; 163 C4Value prototype; 164 bool constant{false}; // if true, this proplist is not changeable 165 friend class C4Value; 166 friend class C4ScriptHost; 167 public: 168 int32_t Status{1}; 169 170 class Iterator 171 { 172 private: 173 std::shared_ptr<std::vector<const C4Property*> > properties; 174 std::vector<const C4Property*>::iterator iter; 175 // needed when constructing the iterator 176 // adds a property or overwrites existing property with same name 177 void AddProperty(const C4Property * prop); 178 void Reserve(size_t additionalAmount); 179 // Initializes internal iterator. Needs to be called before actually using the iterator. 180 void Init(); 181 public: Iterator()182 Iterator() : properties(nullptr) { } 183 184 const C4Property * operator*() const { return *iter; } 185 const C4Property * operator->() const { return *iter; } 186 void operator++() { ++iter; }; 187 void operator++(int) { operator++(); } 188 189 bool operator==(const Iterator & other) const 190 { 191 if ((properties == nullptr || iter == properties->end()) && (other.properties == nullptr || other.iter == other.properties->end())) 192 return true; 193 return properties == other.properties && iter == other.iter; 194 } 195 196 bool operator!=(const Iterator & other) const 197 { 198 return !(*this == other); 199 } 200 201 friend class C4PropList; 202 }; 203 204 // do not modify the proplist while iterating over it! 205 Iterator begin(); end()206 Iterator end() { return Iterator(); } 207 }; 208 209 void CompileNewFunc(C4PropList *&pStruct, StdCompiler *pComp, C4ValueNumbers *rPar); 210 211 // Proplists that are created during a game and get saved in a savegame 212 // Examples: Objects, Effects 213 class C4PropListNumbered: public C4PropList 214 { 215 public: 216 int32_t Number{-1}; 217 ~C4PropListNumbered() override; 218 void CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers); 219 C4PropListNumbered * GetPropListNumbered() override; IsNumbered()220 bool IsNumbered() const override { return true; } 221 222 static C4PropList * GetByNumber(int32_t iNumber); // pointer by number 223 static bool CheckPropList(C4PropList *); // sanity check: true when the proplist is in the list and not a stale pointer 224 static void SetEnumerationIndex(int32_t iMaxObjectNumber); GetEnumerationIndex()225 static int32_t GetEnumerationIndex() { return EnumerationIndex; } 226 static void ResetEnumerationIndex(); 227 static void ShelveNumberedPropLists(); // unnumber all proplists and put them on the shelve. To be used on remaining objects before a savegame load. 228 static void UnshelveNumberedPropLists(); // re-insert shelved proplists into main list 229 static void ClearShelve(); 230 static void ClearNumberedPropLists(); // empty all properties in numbered prop lists. Used on game clear to ensure prop lists with circular references get cleared. 231 protected: 232 C4PropListNumbered(C4PropList * prototype = nullptr); 233 void AcquireNumber(); // acquire a number and add to internal list 234 void ClearNumber(); // clear number and remove from internal list 235 236 static C4Set<C4PropListNumbered *> PropLists; 237 static std::vector<C4PropListNumbered *> ShelvedPropLists; // temporary storage for existing proplists while a new section loaded 238 static int32_t EnumerationIndex; 239 friend class C4Game; 240 friend class C4GameObjects; 241 }; 242 243 // Proplists created by script at runtime 244 class C4PropListScript: public C4PropList 245 { 246 public: C4PropList(prototype)247 C4PropListScript(C4PropList * prototype = nullptr) : C4PropList(prototype) { PropLists.Add(this); } ~C4PropListScript()248 ~C4PropListScript() override { PropLists.Remove(this); } Delete()249 bool Delete() override { return true; } 250 251 static void ClearScriptPropLists(); // empty all properties in script-created prop lists. Used on game clear to ensure prop lists with circular references get cleared. 252 253 protected: 254 static C4Set<C4PropListScript *> PropLists; 255 }; 256 257 // PropLists declared in the game data 258 // examples: Definitions, local variable initializers 259 class C4PropListStatic: public C4PropList 260 { 261 public: C4PropListStatic(C4PropList * prototype,const C4PropListStatic * parent,C4String * key)262 C4PropListStatic(C4PropList * prototype, const C4PropListStatic * parent, C4String * key): 263 C4PropList(prototype), Parent(parent), ParentKeyName(key) { } 264 ~C4PropListStatic() override = default; Delete()265 bool Delete() override { return true; } IsStatic()266 C4PropListStatic * IsStatic() override { return this; } 267 void RefCompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers) const; 268 StdStrBuf GetDataString() const; 269 const char *GetName() const override; GetParent()270 const C4PropListStatic * GetParent() const { return Parent; } GetParentKeyName()271 C4String * GetParentKeyName() { return ParentKeyName; } 272 protected: 273 const C4PropListStatic * Parent; 274 C4RefCntPointer<C4String> ParentKeyName; // property in parent this proplist was created in 275 }; 276 277 // static PropList of which another class owns the pointer 278 class C4PropListStaticMember : public C4PropListStatic 279 { 280 public: C4PropListStaticMember(C4PropList * prototype,const C4PropListStatic * parent,C4String * key)281 C4PropListStaticMember(C4PropList * prototype, const C4PropListStatic * parent, C4String * key): 282 C4PropListStatic(prototype, parent, key) {} Delete()283 bool Delete() override { return false; } 284 }; 285 286 #endif // C4PROPLIST_H 287