2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/object/property.cpp,v 1.65 1999/11/05 14:26:37 Justin Exp $
33 // Must be last header
36 ////////////////////////////////////////////////////////////
37 // CLASS: cStoredPropertyStats
40 class cStoredPropertyStats
: public cCTDelegating
<IPropertyStats
>
42 cStoredProperty
* mpProp
;
45 cStoredPropertyStats(IUnknown
* outer
, cStoredProperty
* prop
)
46 : cCTDelegating
<IPropertyStats
> (outer
),
51 STDMETHOD_(sPropTimeStats
*,GetTimeStats
)()
53 return mpProp
->GetStoreTimeStats();
57 ////////////////////////////////////////////////////////////
58 // CLASS: cStoredPropertyNetworking
62 #define NetPropSpew(msg) {if (gmbDoSpew) {mprintf msg;}}
64 #define NetPropSpew(msg)
67 class cStoredPropertyNetworking
: public cCTDelegating
<IPropertyNetworking
>
69 cStoredProperty
* mpProp
;
70 BOOL mbHandlingMessage
; // Am I responding to a network broadcast message.
71 static tNetMsgHandlerID gmNetMsgHandlerID
; // shared handler ID for our callback.
72 // TRUE iff we should do extensive spewage:
73 static BOOL gmbDoSpew
;
75 // Networking message structure.
76 struct sPropertyNetMsg
78 tNetMsgHandlerID handlerID
; // handler ID of the property manager component (used by NetMan).
79 short propertyID
; // property sending message
80 NetObjID netObjID
; // ObjID of the object on its host machine.
81 ubyte type
; // the type of change happening to the property.
82 char valueData
[1]; // I wish this could be [0], but we get warnings if I do.
85 // Called when a property message is recieved. Will go to the appropriate prop for handling.
86 static void NetworkMessageCallback(const sNetMsg_Generic
*pMsg
, ulong size
, ObjID from
, void *)
88 sPropertyNetMsg
*msg
= (sPropertyNetMsg
*)pMsg
;
89 IProperty
*prop
= GetProperty(msg
->propertyID
);
90 IPropertyNetworking
*propnet
;
91 if (prop
->GetID() == PROPID_NULL
)
92 Warning(("Recieved invalid property ID"));
93 else if (!SUCCEEDED(prop
->QueryInterface(IID_IPropertyNetworking
, (void **)&propnet
)))
94 Warning(("Received property message with an ID of a property without networking\n"));
96 propnet
->ReceivePropertyMsg(pMsg
, size
, from
);
101 // Networking depends on in-place changes being made by calling
102 // Touch instead of Set, since Touch can't be requested from a
103 // remote object host. We detect whether Set is being called with
104 // the same pointer to value that get returns.
105 BOOL
IsInPlaceSet(ObjID obj
, sDatum value
)
107 IDataOps
* ops
= mpProp
->mpStore
->GetOps();
108 AssertMsg(ops
!=NULL
, "Networked properties need data_ops");
111 return (mpProp
->mpStore
->Get(obj
, ¤tVal
)
112 && ops
->BlockSize(currentVal
) >= 0 //... currentVal is a pointer
113 && currentVal
== value
); //... which is the same as value
117 cStoredPropertyNetworking(cStoredProperty
* prop
)
118 : cCTDelegating
<IPropertyNetworking
> (NULL
), // Outer set when it's first QI'ed from prop.
120 mbHandlingMessage(FALSE
)
122 if (gmNetMsgHandlerID
== 0) {
124 mpProp
->gmNetMan
->RegisterMessageParser(NetworkMessageCallback
,
128 gmbDoSpew
= config_is_defined("net_prop_spew")||config_is_defined("net_spew");
130 // Allow spew in opt builds *if* net_opt_spew is also set:
132 ((config_is_defined("net_prop_spew") && config_is_defined("net_prop_opt_spew")) ||
133 (config_is_defined("net_spew") && config_is_defined("net_opt_spew")));
139 // Send network message regarding a change to this property. If
140 // type's kListenPropRequest bit is set, then this will send a
141 // change request of to obj's host machine, otherwise it will send
142 // a broadcast of a change that has happened. Returns TRUE if it
143 // sent the message. @TODO: Send only the diff, the part that is
144 // different from the current value.
145 STDMETHOD_(BOOL
,SendPropertyMsg
)(ObjID obj
,
147 ePropertyListenMsg type
)
149 if (mbHandlingMessage
) {
150 // we started this property change via a net message
154 ObjID toHostPlayer
= OBJ_NULL
; // NULL means it will be broadcast.
156 // Are we requesting a change (as opposed to broadcasting one)
157 if (type
& kListenPropRequestFromHost
)
159 toHostPlayer
= mpProp
->gmObjNet
->ObjHostPlayer(obj
);
160 AssertMsg2(toHostPlayer
!= OBJ_NULL
,
161 "Requesting change to unhosted property %d.%s!",
162 obj
, mpProp
->mDesc
.name
);
163 // Check to make sure the value wasn't already changed
164 // in-place by the caller.
165 // We usually forbid in-place changes by clients, because it
166 // violates the basic principle of how we work: property changes
167 // are made on the host, *then* propagated to the client. We
168 // do permit it as a temporary workaround, though, if you set
169 // the net_permit_in_place_set config flag.
170 AssertMsg2(!IsInPlaceSet(obj
, value
)
171 || config_is_defined("net_permit_in_place_set"),
172 "Networking error: must use Touch for %d.%s"
173 "when changing its value in-place",
174 obj
, mpProp
->mDesc
.name
);
175 if (!(mpProp
->mDesc
.net_flags
& kPropertyProxyChangable
))
177 Warning(("Trying to change %d.%s from a proxy machine\n",
178 obj
, mpProp
->mDesc
.name
));
183 AssertMsg2((type
& kListenPropRequestFromHost
) ||
184 mpProp
->gmObjNet
->ObjHostedHere(obj
),
185 "Proxy machine is trying to change %d.%s!",
186 obj
, mpProp
->mDesc
.name
);
188 int valSize
; // size of 'value'
189 cDataOpsMemFile memFile
;
190 IDataOps
* ops
= mpProp
->mpStore
->GetOps();
191 AssertMsg(ops
!=NULL
, "Networked properties need data_ops");
193 if (type
== kListenPropUnset
)
194 valSize
= 0; // Don't send the vale for Unset messages.
197 // Reset the memFile buffer.
198 memFile
.Seek(kDataOpSeekFromStart
, 0);
199 // Cause value to be written to our memFile buffer, this
201 ops
->Write(value
, &memFile
);
202 // Determine the value's size, and create an appropriately
203 // sized network message. Note that, if the value is a NULL
204 // pointer, valSize should come out as 0:
205 valSize
= memFile
.Tell();
207 int msgSize
= valSize
+ sizeof(sPropertyNetMsg
);
208 sPropertyNetMsg
*msg
= (sPropertyNetMsg
*)malloc(msgSize
);
209 // Subtract out the compile-time known size of valueData, which
210 // the C compiler forced me to add in, since it wouldn't allow
211 // valueData to be defined with 0 size.
212 msgSize
-= sizeof(msg
->valueData
);
213 // Fill the valueData field with the flattened out value.
214 memcpy(msg
->valueData
, memFile
.GetBuffer(), valSize
);
215 // Fill in the header fields.
216 msg
->handlerID
= gmNetMsgHandlerID
;
217 msg
->propertyID
= mpProp
->mID
;
218 // the field has fewer bits, make sure no lost data:
219 Assert_(msg
->propertyID
== mpProp
->mID
);
220 msg
->netObjID
= mpProp
->gmObjNet
->ObjHostObjID(obj
);
222 // the field has fewer bits, make sure no lost data:
223 Assert_(msg
->type
== type
);
224 NetPropSpew(("SEND: property %d:%s type: 0x%x size: %d first word: %d\n",
225 msg
->netObjID
, mpProp
->mDesc
.name
, msg
->type
,
226 valSize
, *(int *)msg
->valueData
));
227 if (toHostPlayer
!= OBJ_NULL
)
228 mpProp
->gmNetMan
->Send(toHostPlayer
, msg
, msgSize
, TRUE
);
230 mpProp
->gmNetMan
->Broadcast(msg
, msgSize
, TRUE
);
234 // @TODO: Diffing. To send the diff we would:
235 // - flatten out the current value into memFile
236 // - make a copy of the memFile buffer
237 // - flatten the new value
238 // - Send the difference.
239 // To receive a diff we would need to:
240 // - flatten out the current value into memFile
242 // - read from the memFile with the diff applied.
245 STDMETHOD_(void,ReceivePropertyMsg
)(const sNetMsg_Generic
*pMsg
,
249 sPropertyNetMsg
*msg
= (sPropertyNetMsg
*)pMsg
;
251 if (fromPlayer
== OBJ_NULL
)
253 // We don't allow anonymous property sets, since we can't properly
254 // deproxify them. It probably comes from a pre-Reset anyway.
255 NetPropSpew(("Got property %d:%s message from unknown player.\n",
256 msg
->netObjID
, mpProp
->mDesc
.name
));
260 Assert_(msg
->type
& (kListenPropSet
|
263 Assert_(!(msg
->type
& (kListenPropLoad
| kListenPropRebuildConcrete
)));
265 // Don't broadcast changes made to proxies, that are done in
266 // response to a broadcast.
267 mbHandlingMessage
= !(msg
->type
& kListenPropRequestFromHost
);
269 // We intentionally do *not* assert that we own the object if this
270 // was a request to us; it's possible that the object was rehosted
271 // while the request was in transit. In this case, the Set() below
272 // should result in a new request, to the new host...
274 // Messages sent to the host arrive with the objID of the host.
275 ObjID obj
= (msg
->type
& kListenPropRequestFromHost
)
276 ? (ObjID
)msg
->netObjID
277 : mpProp
->gmObjNet
->ObjGetProxy(fromPlayer
, msg
->netObjID
);
279 AutoAppIPtr(ObjectSystem
);
280 if (!pObjectSystem
->Exists(obj
))
282 // Presumably, this object has been deleted, and this message
283 // represents a raceway condition:
284 NetPropSpew(("Got property %s for non-existent object %d.\n",
285 mpProp
->mDesc
.name
, obj
));
289 if (msg
->type
& kListenPropUnset
)
291 NetPropSpew(("RECEIVE: property %d:%s delete proxy: %d\n",
292 msg
->netObjID
, mpProp
->mDesc
.name
, obj
));
293 mpProp
->Delete(obj
); // Unset the property from obj.
297 IDataOps
* ops
= mpProp
->mpStore
->GetOps();
298 AssertMsg(ops
!=NULL
, "Networked properties need data_ops");
300 // Determine the size of valueData. Involves subtracting out
301 // the compile-time known size of valueData, which I wish I
302 // could have made 0, but C++ won't let me.
303 ulong headerSize
= sizeof(sPropertyNetMsg
) - sizeof(msg
->valueData
);
304 ulong valueSize
= size
- headerSize
;
306 // Unflatten the data from msg->valueData into value, by
307 // treating valueData as if it were a memory file.
308 cDataOpsMemFile
memFile(msg
->valueData
, valueSize
);
309 sDatum value
= ops
->New();
310 ops
->Read(&value
,&memFile
,ops
->Version());
312 NetPropSpew(("RECEIVE: property %d:%s type: 0x%x"
313 "size: %d first word: %d\n",
314 msg
->netObjID
, mpProp
->mDesc
.name
,
315 msg
->type
, valueSize
,
316 *(int *)msg
->valueData
));
318 mpProp
->Set(obj
, value
);
319 // Free up the space for the unflatten value.
322 // valueSize is 0, which indicates that they set the value
323 // to the NULL ptr. What *do* we do in response to this?
324 NetPropSpew(("RECEIVE: property %d:%s type: 0x%x NULL VALUE\n",
325 msg
->netObjID
, mpProp
->mDesc
.name
, msg
->type
));
326 // Can't do this -- the underlying Set mechanism can't deal
327 // with NULL pointers...
328 // mpProp->Set(obj, NULL);
331 mbHandlingMessage
= FALSE
;
333 }; // end of class cStoredPropertyNetworking
335 tNetMsgHandlerID
cStoredPropertyNetworking::gmNetMsgHandlerID
= 0;
336 BOOL
cStoredPropertyNetworking::gmbDoSpew
= FALSE
;
338 ////////////////////////////////////////////////////////////
340 // cPropertyBase and cStored property were once the same class, which
341 // is why their functions are intermingled here.
343 ////////////////////////////////////////////////////////////
345 INetManager
*cStoredProperty::gmNetMan
= NULL
;
346 IObjectNetworking
*cStoredProperty::gmObjNet
= NULL
;
352 HRESULT
cStoredProperty::QI(IUnknown
* me
, REFIID id
, void ** ppI
)
355 // @BUG: this violates COM rules; the interface isn't delegated.
356 if (id
== IID_ITrait
)
363 if (id
== IID_IPropertyStats
)
366 mpStats
= new cStoredPropertyStats(me
,this);
372 #ifdef NEW_NETWORK_ENABLED
373 if (id
== IID_IPropertyNetworking
&& mpNetProp
!= NULL
)
375 mpNetProp
->InitDelegation(me
); // Created without delgate. Initialize on (every) QI.
382 BOOL match
= IsEqualOrIUnknownGUID(id
,IID_IProperty
);
387 return ResultFromScode(E_NOINTERFACE
);
395 //////////////////////////////
397 // Constructor; pass in name, and preconstructed implementation
401 IPropertyStore
* InitBasePropertyStore(IPropertyStore
* store
, ulong flags
);
403 cPropertyBase::cPropertyBase(const sPropertyDesc
* desc
) :
408 CompileConstraints();
411 cStoredProperty::cStoredProperty(const sPropertyDesc
* desc
, IPropertyStore
*store
)
412 : cPropertyBase(desc
),
413 mpStore(InitBasePropertyStore(store
,desc
->flags
)),
420 #ifdef NEW_NETWORK_ENABLED
421 if (gmNetMan
== NULL
)
422 gmNetMan
= AppGetObj(INetManager
);
424 if (gmObjNet
== NULL
)
425 gmObjNet
= AppGetObj(IObjectNetworking
);
427 if (!(mDesc
.net_flags
& kPropertyChangeLocally
))
428 mpNetProp
= new cStoredPropertyNetworking(this);
431 // Clear the statistic accumulation totals.
432 memset(&mStoreStats
,0,sizeof(mStoreStats
));
435 mAllocName
= PROP_BLAME_NAME(desc
->name
);
438 //////////////////////////////
443 cPropertyBase::~cPropertyBase ()
449 cStoredProperty::~cStoredProperty ()
451 STOREDPROP_AUTO_BLAME();
454 // Give allocation credit to the particular property.
456 int refs
= mpStore
->Release();
459 ConfigSpew("propstore_ref_spew",("Property store for %s has %d refs on exit\n",mDesc
.name
,refs
));
461 while (mpStore
->Release() > 0)
473 SafeRelease(mpDonors
);
474 SafeRelease(mpExemplars
);
479 ////////////////////////////////////////
481 void cPropertyBase::Notify (ePropertyNotifyMsg msg
, PropNotifyData data
)
485 switch(NOTIFY_MSG(msg
))
487 case kObjNotifyDefault
:
488 case kObjNotifyPostLoad
:
489 // Recompile constraints
490 CompileConstraints();
497 // This is here to serve as a profiler entry point
498 ObjID
prop_inst_donor(ITrait
* trait
, ObjID obj
)
500 return trait
->GetDonor(obj
);
503 ////////////////////////////////////////
505 void cStoredProperty::Notify (ePropertyNotifyMsg msg
, PropNotifyData data
)
507 STOREDPROP_AUTO_BLAME();
508 cPropertyBase::Notify(msg
,data
);
513 switch(NOTIFY_MSG(msg
))
515 case kObjNotifyDelete
:
519 case kObjNotifyBeginCreate
:
520 // Instantiate the property if necessary
521 if ((mDesc
.flags
& kPropertyInstantiate
) && OBJ_IS_CONCRETE(info
.obj
))
523 ObjID example
= prop_inst_donor(mpExemplars
,info
.obj
);
524 if (example
!= OBJ_NULL
)
526 Copy(info
.obj
,example
);
533 case kObjNotifyDefault
:
534 case kObjNotifyPostLoad
:
536 sPropertyObjIter iter
;
537 // Give allocation credit to the particular property.
538 mpStore
->IterStart(&iter
);
542 // Give allocation credit to the particular property.
543 while(mpStore
->IterNext(&iter
,&obj
,&value
))
545 EnforceRequirements(obj
);
547 CallListeners(kListenPropSet
|kListenPropModify
|kListenPropLoad
,obj
,value
);
548 PROP_PUSH_BLAME(mAllocName
);
549 EnforceImplications(obj
);
551 mpStore
->IterStop(&iter
);
556 case kObjNotifyReset
:
557 // Give allocation credit to the particular property.
562 Save(info
.db
.save
, msg
);
566 Load(info
.db
.load
, msg
);
572 ////////////////////////////////////////
574 PropListenerHandle
cPropertyBase::Listener::gNextHandle
= 0 ;
576 PropListenerHandle
cPropertyBase::Listen(PropertyListenMsgSet interests
, PropertyListenFunc func
, PropListenerData data
)
578 int i
= mListeners
.Append(Listener(interests
,func
,data
));
579 return mListeners
[i
].handle
;
582 void cPropertyBase::Unlisten(PropListenerHandle handle
)
584 for (int i
= 0; i
< mListeners
.Size(); i
++)
586 if (mListeners
[i
].handle
== handle
)
588 mListeners
.DeleteItem(i
);
592 Warning(("Can't unlisten to handle %X, no such listener\n",handle
));
595 PropListenerHandle
cStoredProperty::Listen(PropertyListenMsgSet interests
, PropertyListenFunc func
, PropListenerData data
)
597 if (interests
& (kListenPropRebuildConcrete
|kListenPropRebuildConcreteRelevant
))
598 SetRebuildConcretes(TRUE
);
599 return cPropertyBase::Listen(interests
,func
,data
);
602 void cPropertyBase::CallListeners(ePropertyListenMsg msg
, ObjID obj
, sDatum dat
, ObjID donor
)
604 uPropListenerValue cbval
;
606 OnListenMsg(msg
,obj
,cbval
);
608 for (int i
= 0; i
< mListeners
.Size(); i
++)
610 Listener
& l
= mListeners
[i
];
611 PropertyListenMsgSet mask
= msg
& l
.interests
;
614 sPropertyListenMsg message
= { mask
, mID
, obj
, dat
.value
, donor
};
616 l
.func(&message
,l
.data
);
621 ////////////////////////////////////////
623 void cStoredProperty::CallListeners(ePropertyListenMsg msg
, ObjID obj
, sDatum value
, ObjID donor
)
625 STOREDPROP_TIMER(Listener
);
627 if ((msg
& (kListenPropSet
|kListenPropUnset
)) && !(msg
& kListenPropLoad
))
629 mpDonors
->Touch(obj
);
630 if (mpDonors
!= mpExemplars
)
631 mpExemplars
->Touch(obj
);
634 cPropertyBase::CallListeners(msg
,obj
,value
,donor
);
637 ////////////////////////////////////////
639 HRESULT
cStoredProperty::Create(ObjID obj
)
641 STOREDPROP_AUTO_BLAME();
643 // call my method to get the property timing stuff
644 if (cStoredProperty::IsSimplyRelevant(obj
))
647 int msg
= kListenPropSet
;
649 // Enforce "requires" constraints"
651 EnforceRequirements(obj
);
653 ObjID example
= mpExemplars
->GetDonor(obj
);
655 STOREDPROP_TIMER(Create
);
657 if (example
!= OBJ_NULL
)
659 // Give allocation credit to the particular property.
660 value
= mpStore
->Copy(obj
,example
);
661 msg
|= kListenPropModify
;
664 // Give allocation credit to the particular property.
665 value
= mpStore
->Create(obj
);
671 CallListeners(msg
,obj
,value
);
672 PROP_PUSH_BLAME(mAllocName
);
675 // Enforce "autocreate" constraints
677 EnforceImplications(obj
);
682 ////////////////////////////////////////
685 HRESULT
cStoredProperty::Copy(ObjID targ
, ObjID src
)
687 if (src
!= OBJ_NULL
) // if by example, copy from the example
691 // call our own relevance functions for timing
692 if (!cStoredProperty::IsSimplyRelevant(src
))
693 src
= cStoredProperty::GetDonor(src
);
697 PROP_PUSH_BLAME(mAllocName
);
700 ePropertyListenMsg msg
= kListenPropModify
;
701 if (!cStoredProperty::IsSimplyRelevant(targ
))
703 EnforceRequirements(targ
);
705 STOREDPROP_TIMER(Copy
);
707 dat
= mpStore
->Copy(targ
,src
);
711 EnforceImplications(targ
);
712 msg
|= kListenPropSet
;
716 STOREDPROP_TIMER(Copy
);
717 dat
= mpStore
->Copy(targ
,src
);
723 CallListeners(msg
,targ
,dat
);
730 HRESULT
cStoredProperty::Set(ObjID obj
, sDatum value
)
732 ePropertyListenMsg msg
= kListenPropModify
;
733 #ifdef NEW_NETWORK_ENABLED
734 // See if we need to synchronize changes to this property.
735 if (gmNetMan
->Networking() &&
736 !gmNetMan
->Suspended() &&
738 gmObjNet
->ObjIsProxy(obj
))
740 if (mpNetProp
->SendPropertyMsg(obj
, value
, msg
| kListenPropRequestFromHost
))
742 // Otherwise, we still have to change the property.
745 STOREDPROP_TIMER(Set
);
746 PROP_PUSH_BLAME(mAllocName
);
747 HRESULT result
= mpStore
->Set(obj
,value
);
749 msg
|= kListenPropSet
;
753 CallListeners(msg
,obj
,value
);
757 ////////////////////////////////////////
759 BOOL
cStoredProperty::Touch(ObjID obj
,sDatum
* value
)
762 if (!value
) // got passed in NULL
764 STOREDPROP_TIMER(Touch
);
766 // Give allocation credit to the particular property.
767 if (!mpStore
->Get(obj
, value
))
770 #ifdef NEW_NETWORK_ENABLED
771 // Note that we allow any property to be changed locally *IF* you
772 // suspend messaging around the Set(). We consider that to be a notice
773 // that you think you know what you're doing.
774 AssertMsg2(!(gmNetMan
->Networking()
775 && !gmNetMan
->Suspended()
776 && !(mDesc
.net_flags
& kPropertyChangeLocally
)
777 && gmObjNet
->ObjIsProxy(obj
)),
778 "Can't change property %d.%s in-place (i.e. Touch) on a proxy machine",
781 CallListeners(kListenPropModify
,obj
,*value
);
785 ////////////////////////////////////////
787 HRESULT
cStoredProperty::Delete(ObjID obj
)
792 STOREDPROP_TIMER(Get
);
793 BOOL relevant
= mpStore
->Get(obj
,&value
);
798 CallListeners(kListenPropUnset
,obj
,value
);
800 PROP_PUSH_BLAME(mAllocName
);
801 STOREDPROP_TIMER(Delete
);
803 HRESULT result
= mpStore
->Delete (obj
);
808 if (mFlags
& kRebuildConcretes
)
809 RebuildConcretes(obj
);
816 ////////////////////////////////////////
818 ObjID
cStoredProperty::GetDonor(ObjID obj
) const
820 STOREDPROP_TIMER(GetDonor
);
821 return mpDonors
->GetDonor(obj
);
825 ObjID
cStoredProperty::GetExemplar(ObjID obj
) const
827 STOREDPROP_TIMER(GetExemplar
);
828 return mpExemplars
->GetDonor(obj
);
831 ////////////////////////////////////////
833 void cStoredProperty::SetStore(IPropertyStore
* store
)
838 SafeRelease(mpStore
);
839 mpStore
= InitBasePropertyStore(store
,mDesc
.flags
);
845 SafeRelease(mpStore
);
849 ////////////////////////////////////////////////////////////
850 // cStoredProperty Implementation
854 //------------------------------------------------------------
858 void cPropertyBase::CreateEditor(IProperty
* prop
)
860 IObjEditors
* edit
= AppGetObj(IObjEditors
);
863 IUnknown
* trait
= (IUnknown
*)edit
->AddProperty(prop
);
864 // I don't want the trait. Just get rid of it.
872 //------------------------------------------------------------
875 // Create the traits for a property based on it's desc
881 BOOL
PropTraitPred(ObjID obj
, TraitPredicateData data
)
883 cStoredProperty
* base
= (cStoredProperty
*)data
;
884 return base
->IsSimplyRelevant(obj
);
887 void cStoredProperty::InitTraits(void)
890 memset(&tdesc
,0,sizeof(tdesc
));
891 Assert_(sizeof(tdesc
.name
) >= sizeof(mDesc
.name
));
892 strncpy(tdesc
.name
,mDesc
.name
,sizeof(mDesc
.name
));
893 tdesc
.name
[sizeof(tdesc
.name
)-1] = '\0';
895 sTraitPredicate pred
= { PropTraitPred
, (TraitPredicateData
)this};
897 AutoAppIPtr_(TraitManager
,pTraitMan
);
899 if (mDesc
.flags
& kPropertyNoCache
)
900 tdesc
.flags
|= kTraitUncached
;
902 if (mDesc
.flags
& kPropertyNoInherit
)
904 #define CACHE_INSTANTIATION
905 #ifndef CACHE_INSTANTIATION
906 tdesc
.flags
|= kTraitUncached
;
907 #endif // CACHE_INSTANTIATION
908 mpExemplars
= pTraitMan
->CreateTrait(&tdesc
,&pred
);
909 tdesc
.flags
|= kTraitUninherited
;
910 mpDonors
= pTraitMan
->CreateTrait(&tdesc
,&pred
);
914 mpExemplars
= pTraitMan
->CreateTrait(&tdesc
,&pred
);
915 mpDonors
= mpExemplars
;
920 //------------------------------------------------------------
921 // CompileConstraints
923 // Build constraint lists from descriptor
926 void cPropertyBase::CompileConstraints(void)
931 mpImplied
= mpRequired
= NULL
;
933 if (mDesc
.constraints
!= NULL
)
935 const sPropertyConstraint
* c
;
936 for (c
= mDesc
.constraints
; c
->kind
!= kPropertyNullConstraint
; c
++)
938 // look up the property referred to by the constriant
939 cAutoIPtr
<IProperty
> against(GetManager()->GetPropertyNamed(c
->against
));
943 case kPropertyAutoCreate
:
944 if (!mpImplied
) mpImplied
= new PropIDList
;
945 mpImplied
->Append(against
->GetID());
948 case kPropertyRequires
:
949 if (!mpRequired
) mpRequired
= new PropIDList
;
950 mpRequired
->Append(against
->GetID());
958 ////////////////////////////////////////
960 void cPropertyBase::EnforceRequirements(ObjID obj
)
962 if (!mpRequired
) return;
964 // check prerequisites
965 PropIDList::cIter iter
;
966 for (iter
= mpRequired
->Iter(); !iter
.Done(); iter
.Next())
968 cAutoIPtr
<IProperty
> prop(GetManager()->GetProperty(iter
.Value()));
969 if (!prop
->IsRelevant(obj
))
974 void cPropertyBase::EnforceImplications(ObjID obj
)
976 if (!mpImplied
) return;
978 // check prerequisites
979 PropIDList::cIter iter
;
980 for (iter
= mpImplied
->Iter(); !iter
.Done(); iter
.Next())
982 cAutoIPtr
<IProperty
> prop(GetManager()->GetProperty(iter
.Value()));
983 if (!prop
->IsSimplyRelevant(obj
))
989 //------------------------------------------------------------
990 // SetRebuildConcretes
993 void cStoredProperty::SetRebuildConcretes(BOOL fRebuild
)
997 mFlags
|= kRebuildConcretes
;
998 if (!(mFlags
& kListeningHierarchy
))
1000 AutoAppIPtr(TraitManager
);
1001 pTraitManager
->Listen(RebuildHierarchyListener
, this);
1002 mFlags
|= kListeningHierarchy
;
1006 mFlags
&= ~kRebuildConcretes
;
1010 //------------------------------------------------------------
1014 void cStoredProperty::RebuildOneConcrete(ObjID obj
)
1017 uPropListenerValue cbval
;
1019 // Give allocation credit to the particular property.
1020 BOOL relevant
= mpStore
->Get(obj
, &value
);
1024 donor
= mpDonors
->GetDonor(obj
);
1025 // Give allocation credit to the particular property.
1026 relevant
= mpStore
->Get(donor
, &value
);
1028 cbval
.ptrval
= value
;
1029 ePropertyListenMsg msg
= kListenPropRebuildConcrete
;
1031 msg
|= kListenPropRebuildConcreteRelevant
;
1032 CallListeners(msg
,obj
,value
,donor
);
1033 RebuildConcrete(obj
, relevant
, cbval
,donor
);
1036 void cStoredProperty::RebuildConcretes(ObjID obj
)
1038 STOREDPROP_TIMER(RebuildConcretes
);
1040 if (OBJ_IS_CONCRETE(obj
))
1041 RebuildOneConcrete(obj
);
1043 // This is no longer an "else" to deal with concrete archetypes...
1044 cAutoIPtr
<IObjectQuery
> pQuery(mpDonors
->GetAllHeirs(obj
, kObjectConcrete
));
1045 for (; !pQuery
->Done(); pQuery
->Next())
1046 RebuildOneConcrete(pQuery
->Object());
1049 //------------------------------------------------------------
1050 // RebuildHierarchyListener
1053 void LGAPI
cStoredProperty::RebuildHierarchyListener(const sHierarchyMsg
* msg
, HierarchyListenerData data
)
1055 if ((((cStoredProperty
*)data
)->mFlags
& kRebuildConcretes
) &&
1056 ((cStoredProperty
*)data
)->mpExemplars
->PossessedBy(msg
->donor
))
1057 ((cStoredProperty
*)data
)->RebuildConcretes(msg
->obj
);
1060 //------------------------------------------------------------
1064 void cStoredProperty::OnListenMsg(ePropertyListenMsg type
, ObjID obj
, uPropListenerValue cbVal
)
1066 if ((mFlags
& kRebuildConcretes
) &&
1067 (type
& (kListenPropModify
| kListenPropSet
)))
1068 RebuildConcretes(obj
);
1069 #ifdef NEW_NETWORK_ENABLED
1070 // Is this a network game & are we the host changing the property in a way that
1071 // needs to be broadcast.
1072 if (!(type
& (kListenPropLoad
| kListenPropRebuildConcrete
| kListenPropRequestFromHost
))
1073 && gmNetMan
->Networking()
1074 && !gmNetMan
->Suspended()
1075 && mpNetProp
!= NULL
1076 && gmObjNet
->ObjHostedHere(obj
))
1078 mpNetProp
->SendPropertyMsg(obj
, cbVal
.ptrval
, type
);
1084 //------------------------------------------------------------
1092 //------------------------------------------------------------
1093 // InitBasePropertyStore
1095 static IPropertyStore
* InitBasePropertyStore(IPropertyStore
* impl
, ulong flags
)
1100 if (flags
& kPropertyConcrete
)
1102 IPropertyStore
* abstract
= CreateGenericPropertyStore(kPropertyImplVerySparse
);
1103 IPropertyStore
* retval
= new cMixedPropertyStore(abstract
,impl
);
1104 SafeRelease(abstract
);
1115 ////////////////////////////////////////////////////////////
1120 // Hey look, here's where we use cPropertyManagerKnower to gratuitous advantage
1124 class LookerUpper
: public cPropertyManagerKnower
1127 static IProperty
* ByID(PropertyID id
)
1129 if (GetManager() == NULL
)
1131 return GetManager()->GetProperty(id
);
1134 static IProperty
* ByName(const char* name
)
1136 if (GetManager() == NULL
)
1138 return GetManager()->GetPropertyNamed(name
);
1142 IProperty
* GetProperty(PropertyID id
)
1144 IProperty
* prop
= LookerUpper::ByID(id
);
1148 IProperty
* GetPropertyNamed(const char* name
)
1150 IProperty
* prop
= LookerUpper::ByName(name
);
1154 IProperty
* _GetProperty(PropertyID id
)
1156 IProperty
* prop
= LookerUpper::ByID(id
);
1157 // we know the property manager is holding onto it, so we can release it.
1162 IProperty
* _GetPropertyNamed(const char* name
)
1164 IProperty
* prop
= LookerUpper::ByName(name
);
1165 // we know the property manager is holding onto it, so we can release it.
1170 ////////////////////////////////////////////////////////////
1171 // INSTRUMENTATION STUFF
1174 BOOL gBlameProperties
= FALSE
;
1176 #ifdef PROPERTY_BLAME
1178 static char gPropNameBuf
[16*1024];
1179 static char* gPropNextName
= gPropNameBuf
;
1181 const char* PropBlameName(const char* name
)
1183 static BOOL inited
= FALSE
;
1187 // @TODO: do this someplace more sensible
1188 gBlameProperties
= config_is_defined("blame_properties");
1192 char* s
= gPropNextName
;
1193 sprintf(s
,"Property '%s'",name
);
1194 gPropNextName
+= strlen(s
) + 1;
1195 AssertMsg(gPropNextName
< gPropNameBuf
+ sizeof(gPropNameBuf
),"Not enough memory for property blame names\n");
1199 // Push and pop allocation credit
1200 void PropPushBlame(const char* PropName
)
1203 if (gBlameProperties
)
1204 g_pMalloc
->PushCredit(PropName
,0);
1208 void PropPopBlame(void)
1211 if (gBlameProperties
)
1212 g_pMalloc
->PopCredit();
1216 #endif // PROPERTY_BLAME
1219 #ifdef PROPERTY_TIME
1221 // Get the current time in milliseconds
1222 long cPropTimer::GetTime()
1224 return tm_get_millisec_unrecorded();
1227 ulong
cPropTimer::gDummyStat
;
1229 #endif // PROPERTY_TIME