2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/object/propert_.h,v 1.31 2000/01/29 13:24:24 adurant Exp $
24 F_DECLARE_INTERFACE(ITrait
);
25 F_DECLARE_INTERFACE(IPropertyStore
);
26 F_DECLARE_INTERFACE(INetManager
);
27 F_DECLARE_INTERFACE(IObjectNetworking
);
29 template <class T
> class cSimpleDList
;
31 ////////////////////////////////////////////////////////////
35 // Basic support for constraints, listeners with no property store.
39 ////////////////////////////////////////////////////////////
41 class cPropertyBase
: public cPropertyManagerKnower
45 static PropListenerHandle gNextHandle
;
47 PropertyListenMsgSet interests
;
48 PropertyListenFunc func
;
49 PropListenerData data
;
50 PropListenerHandle handle
;
52 Listener(PropertyListenMsgSet i
, PropertyListenFunc f
,
54 :interests(i
),func(f
),data(d
),handle(gNextHandle
++) {};
58 typedef cSimpleDList
<PropertyID
> PropIDList
;
63 virtual ~cPropertyBase ();
66 const sPropertyDesc
* Describe () const {return &mDesc
;};
69 PropertyID
GetID() const { return mID
; } ;
72 PropListenerHandle
Listen(PropertyListenMsgSet interests
, PropertyListenFunc func
, PropListenerData data
);
74 void Unlisten(PropListenerHandle handle
);
77 // Notify the property system that the object no longer exists
78 void Notify (ePropertyNotifyMsg msg
, PropNotifyData data
);
81 // Call property listeners
82 void CallListeners(ePropertyListenMsg msg
, ObjID obj
, sDatum value
, ObjID donor
= OBJ_NULL
);
84 // Recompile constraints
85 void CompileConstraints(void);
87 // Enforce constraints
88 void EnforceRequirements(ObjID obj
);
89 void EnforceImplications(ObjID obj
);
91 void CreateEditor(IProperty
* prop
);
97 // A built-in listener for the property's own use
98 virtual void OnListenMsg(ePropertyListenMsg
, ObjID
, uPropListenerValue
) {};
100 // Create the property's editor
103 // Constructor; tell it the name of your property and the implementation.
104 // pimpl must be constructed first, but the cProperty then takes over
106 cPropertyBase (const sPropertyDesc
*desc
);
108 sPropertyDesc mDesc
; // Descriptor
109 PropertyID mID
; // unique to each cProperty
110 cDynArray
<Listener
> mListeners
; // listener set
111 PropIDList
* mpImplied
; // Properties I imply
112 PropIDList
* mpRequired
; // Properties I require
115 ////////////////////////////////////////////////////////////
119 // Common code for properties that use IPropertyStore, and
120 // Have ITraits for inheritance/instantiation.
124 ////////////////////////////////////////////////////////////
126 // Forward decl of our classes used for side interfaces.
127 class cStoredPropertyStats
;
128 class cStoredPropertyNetworking
;
130 // Stored property instrumentation macros
131 #define STOREDPROP_AUTO_BLAME() PROP_AUTO_BLAME(mAllocName)
132 #define STOREDPROP_TIMER(x) PROP_TIMER_STATS_IDX(mStoreStats,kProp##x##Time)
133 #define STOREDPROP_TIMER_RESTART(x) PROP_TIMER_RESTART(mStoreStats,kProp##x##Time)
135 class cStoredProperty
: public cPropertyBase
141 HRESULT
QI(IUnknown
* me
, REFIID id
, void ** ppI
);
143 // Make the property relevant for this object
144 HRESULT
Create(ObjID obj
);
146 // Decide that this property is irrelevant for the object
147 HRESULT
Delete (ObjID obj
);
150 HRESULT
Copy(ObjID targ
, ObjID src
);
152 // Notify the property of database changes
153 void Notify (ePropertyNotifyMsg msg
, PropNotifyData data
);
155 // Is this property relevant to the given object?
156 BOOL
IsSimplyRelevant(ObjID obj
) const
158 STOREDPROP_TIMER(Relevant
);
159 return mpStore
->Relevant (obj
);
162 // Is this property relevant to the given object?
163 BOOL
IsRelevant(ObjID obj
) const
165 STOREDPROP_TIMER(Relevant
);
166 if (mpStore
->Relevant(obj
))
173 return mpStore
->Relevant(obj
);
176 HRESULT
Set(ObjID obj
,sDatum val
);
178 // modify a property in place, call listeners
179 BOOL
Touch(ObjID obj
,sDatum
* val
);
182 void IterStart (sPropertyObjIter
* iter
) const
184 STOREDPROP_TIMER(IterStart
);
186 mpStore
->IterStart(iter
);
189 BOOL
IterNext (sPropertyObjIter
* iter
, ObjID
* next
) const
192 STOREDPROP_TIMER(IterNext
);
193 return mpStore
->IterNext(iter
,next
,&dummy
);
196 void IterStop(sPropertyObjIter
* iter
) const
198 STOREDPROP_TIMER(IterStop
);
199 mpStore
->IterStop(iter
);
202 // Find the object an object inherits from
203 ObjID
GetDonor(ObjID obj
) const;
205 // Find the object that the property would be copied from on create
206 ObjID
GetExemplar(ObjID obj
) const;
208 PropListenerHandle
Listen(PropertyListenMsgSet interests
, PropertyListenFunc func
, PropListenerData data
);
212 // Swap out for a new store
213 void SetStore(IPropertyStore
* store
) ;
215 void SetOps(IDataOps
* ops
) { mpStore
->SetOps(ops
); };
217 // Concrete requirements enforcement
218 void SetRebuildConcretes(BOOL fRebuild
);
219 void RebuildConcretes(ObjID obj
);
220 void RebuildOneConcrete(ObjID obj
);
221 static void LGAPI
RebuildHierarchyListener(const sHierarchyMsg
* msg
,
222 HierarchyListenerData data
);
224 // Return the mpStats structure pointer.
225 sPropTimeStats
* GetStoreTimeStats(){return &mStoreStats
;};
231 // A built-in listener for the property's own use
232 virtual void OnListenMsg(ePropertyListenMsg
, ObjID
, uPropListenerValue
);
235 virtual void RebuildConcrete(ObjID obj
, BOOL fIsRelevant
, uPropListenerValue
, ObjID donor
) {};
238 friend class cStoredPropertyNetworking
;
242 kListeningHierarchy
= 0x01,
243 kRebuildConcretes
= 0x02
246 // Call property listeners, possibly doing our own updating
247 void CallListeners(ePropertyListenMsg msg
, ObjID obj
, sDatum value
, ObjID donor
= OBJ_NULL
);
250 cStoredProperty (const sPropertyDesc
*desc
, IPropertyStore
* pStore
);
253 void InitTraits(void);
254 void Save(ITagFile
* file
, edbFiletype filetype
);
255 void Load(ITagFile
* file
, edbFiletype filetype
);
256 void write_obj(ObjID obj
, IDataOpsFile
* file
, edbFiletype filetype
);
257 void read_obj(IDataOpsFile
* file
, edbFiletype filetype
, uint version
);
259 IPropertyStore
*mpStore
; // Property store
260 ITrait
* mpDonors
; // My trait for inheritance
261 ITrait
* mpExemplars
; // My trait for creation
264 // This is for the IPropertyNetworking interface, which handles network messages.
265 // NULL for properties that don't require networking (kPropertyChangeLocally).
266 cStoredPropertyNetworking
* mpNetProp
;
268 // This should be #Ifndef SHIP or something
269 cStoredPropertyStats
* mpStats
; // our stat interface
270 sPropTimeStats mStoreStats
; // actual gather stats
271 const char* mAllocName
; // our name for lgalloc blame
273 // Interfaces that are needed by & shared by all properties.
274 static INetManager
*gmNetMan
;
275 static IObjectNetworking
*gmObjNet
;
279 ////////////////////////////////////////////////////////////
283 // Has only IUnknown methods
285 ////////////////////////////////////////////////////////////
287 template <class IFACE
, const GUID
* pIID
>
288 class cUnknownProperty
: public cCTUnaggregated
<IFACE
,pIID
,kCTU_Default
>
292 virtual ~cUnknownProperty() {};
297 ////////////////////////////////////////////////////////////
301 // Templatized on interface, but only implements IProperty methods
302 // If you want to roll your own (slow) accessors, use this
304 ////////////////////////////////////////////////////////////
306 template <class IFACE
, const GUID
*pIID
>
307 class cProperty
: public cUnknownProperty
<IFACE
,pIID
>,
308 public cStoredProperty
310 typedef cStoredProperty cBase
;
316 STDMETHOD_(const sPropertyDesc
*, Describe
) () const { return cBase::Describe(); };
319 STDMETHOD_(PropertyID
,GetID
)() const { return cBase::GetID(); } ;
321 // Make this property relevant for this object
322 STDMETHOD(Create
) (ObjID obj
) { return cBase::Create(obj
); } ;
324 // Decide that this property is irrelevant for the object
325 STDMETHOD(Delete
) (ObjID obj
) { return cBase::Delete(obj
); } ;
327 // Copy the prop from one obj to another
328 STDMETHOD(Copy
) (ObjID targ
,ObjID src
) { return cBase::Copy(targ
,src
); } ;
330 // Notify the property system that the object no longer exists
331 STDMETHOD_(void,Notify
) (ePropertyNotifyMsg msg
, PropNotifyData data
)
333 // @TODO: move this someplace more appropriate!
334 if (!mCalledCreateEditor
)
337 mCalledCreateEditor
= TRUE
;
339 cBase::Notify(msg
,data
);
342 // Is this property relevant to the given object?
343 STDMETHOD_(BOOL
,IsRelevant
)(ObjID obj
) const {return cBase::IsRelevant(obj
); } ;
344 STDMETHOD_(BOOL
,IsSimplyRelevant
)(ObjID obj
) const {return cBase::IsSimplyRelevant(obj
); } ;
346 // modify an object in place
347 STDMETHOD_(BOOL
,Touch
)(ObjID obj
) {return cBase::Touch(obj
,NULL
); } ;
350 STDMETHOD_(PropListenerHandle
, Listen
)(PropertyListenMsgSet interests
, PropertyListenFunc func
, PropListenerData data
) { return cBase::Listen(interests
,func
,data
); } ;
352 STDMETHOD(Unlisten
)(PropListenerHandle handle
) { cBase::Unlisten(handle
); return S_OK
; };
354 STDMETHOD_(void, IterStart
) (sPropertyObjIter
* iter
) const { cBase::IterStart(iter
);} ;
355 STDMETHOD_(BOOL
, IterNext
) (sPropertyObjIter
* iter
, ObjID
* next
) const
356 { return cBase::IterNext(iter
,next
);};
357 STDMETHOD_(void, IterStop
) (sPropertyObjIter
* iter
) const { cBase::IterStop(iter
);} ;
359 STDMETHOD_(const sPropertyTypeDesc
*, DescribeType
)() const
360 { static sPropertyTypeDesc desc
= { "Unknown", 0}; return &desc
; };
362 STDMETHOD(QueryInterface
)(REFIID id
, void ** ppI
)
370 if (id
== IID_IPropertyStore
)
377 return cBase::QI((IUnknown
*)this,id
,ppI
);
380 void SetStore(IPropertyStore
* store
)
382 cBase::SetStore(store
);
383 mStoreDel
.SetStore(mpStore
);
387 // Create the property editor
388 virtual void CreateEditor() { cPropertyBase::CreateEditor(this); };
391 cProperty(const sPropertyDesc
*desc
, IPropertyStore
*store
)
393 mStoreDel(mpStore
,this),
394 mCalledCreateEditor(FALSE
)
396 GetManager()->AddProperty (this, &mID
);
402 virtual ~cProperty () { GetManager()->DelProperty(this); };
405 cDelegatingPropertyStore mStoreDel
; // the store I hand out when QI'd for one.
406 BOOL mCalledCreateEditor
; // have I created an editor yet? (sigh)
409 ////////////////////////////////////////////////////////////
413 // Generic property, with (slow) accessors, templatized on type
414 // Accessors must be by 32-bit (or smaller) value.
416 ////////////////////////////////////////////////////////////
418 template <class IFACE
, const GUID
* IID
, class TYPE
>
419 class cGenericProperty
: public cProperty
<IFACE
,IID
>
422 // We use this to do type-conversion
428 uPropVal(const TYPE
& tt
) : t(tt
) {};
432 cGenericProperty(const sPropertyDesc
* desc
, IPropertyStore
* store
= NULL
, IDataOps
* ops
= NULL
)
433 : cProperty
<IFACE
,IID
>(desc
,store
)
435 AssertMsg(sizeof(TYPE
) == sizeof(sDatum
),"Can't make property accessors for non-32 bit type");
440 STDMETHOD_(BOOL
,Get
)(ObjID obj
, TYPE (*pval
)) const
442 STOREDPROP_TIMER(Get
);
444 sDatum
* pdat
= (sDatum
*)pval
;
445 BOOL result
= mpStore
->Get(obj
,pdat
);
449 ObjID donor
= GetDonor(obj
);
451 if (donor
!= OBJ_NULL
)
452 result
= mpStore
->Get(donor
,pdat
);
457 STDMETHOD_ (BOOL
, GetSimple
) (ObjID obj
, TYPE (*ptr
)) const
459 STOREDPROP_TIMER(Get
);
460 sDatum
* pdat
= (sDatum
*)ptr
;
461 return mpStore
->Get(obj
,pdat
);
464 STDMETHOD(Set
) (ObjID obj
, TYPE val
)
467 return cBase::Set(obj
, dat
.d
);
470 STDMETHOD_ (BOOL
, IterNextValue
) (sPropertyObjIter
* iter
,ObjID
* next
, TYPE (*val
)) const
472 STOREDPROP_TIMER(IterNext
);
473 sDatum
* pdat
= (sDatum
*)val
;
474 return mpStore
->IterNext(iter
,next
,pdat
);
477 STDMETHOD_(BOOL
,TouchValue
)(ObjID obj
, TYPE val
)
481 return cBase::Touch(obj
,&d
);
486 ////////////////////////////////////////////////////////////
490 // Generic property, templatized on type and on store type for speed
491 // Accessors must be by 32-bit value.
493 ////////////////////////////////////////////////////////////
496 // Keep my store from deleting itself
499 template<class STORE
>
500 class cNonDeletingStore
: public STORE
502 void OnFinalRelease() {};
506 template <class IFACE
, const GUID
* IID
, class TYPE
, class STORE
>
507 class cSpecificProperty
: public cProperty
<IFACE
,IID
>
510 // We use this to do type-conversion
516 uPropVal(const TYPE
& tt
) : t(tt
) {};
522 cSpecificProperty(const sPropertyDesc
* desc
)
523 : cProperty
<IFACE
,IID
>(desc
,NULL
)
525 AssertMsg(sizeof(TYPE
) == sizeof(sDatum
),"Can't make property accessors for non-32 bit type");
527 // Since we circumvent the mpStore a lot, we can't deal with the auto-mixing
528 // done by kpropertyconcrete
529 Assert_(!(desc
->flags
& kPropertyConcrete
));
531 // We can't pass &mStore into the constructor, because it hasn't been initialized
532 // yet, so we set it up here instead.
539 // unset the store before it goes away
547 STORE
& Store() { return mStore
; };
553 STDMETHOD_(BOOL
,Get
)(ObjID obj
, TYPE (*pval
)) const
555 STOREDPROP_TIMER(Get
);
556 sDatum
* pdat
= (sDatum
*)pval
;
557 BOOL result
= mStore
.Get(obj
,pdat
);
561 ObjID donor
= GetDonor(obj
);
564 if (donor
!= OBJ_NULL
)
565 result
= mStore
.Get(donor
,pdat
);
570 STDMETHOD_ (BOOL
, GetSimple
) (ObjID obj
, TYPE (*ptr
)) const
572 STOREDPROP_TIMER(Get
);
573 sDatum
* pdat
= (sDatum
*)ptr
;
574 return mStore
.Get(obj
,pdat
);
577 STDMETHOD(Set
) (ObjID obj
, TYPE val
)
580 return cBase::Set(obj
, dat
.d
);
583 STDMETHOD_ (BOOL
, IterNextValue
) (sPropertyObjIter
* iter
,ObjID
* next
, TYPE (*val
)) const
585 STOREDPROP_TIMER(IterNext
);
586 sDatum
* pdat
= (sDatum
*)val
;
587 return mStore
.IterNext(iter
,next
,pdat
);
590 STDMETHOD_(BOOL
,TouchValue
)(ObjID obj
, TYPE val
)
594 return cBase::Touch(obj
,&d
);
598 cNonDeletingStore
<STORE
> mStore
;
605 // Use this macro to create the standard DescribeType method for a type
608 #define STANDARD_DESCRIBE_TYPE(type) \
609 STDMETHOD_(const sPropertyTypeDesc*, DescribeType)() const \
610 { static sPropertyTypeDesc desc = { #type, sizeof(type)}; return &desc; }