convert line ends
[canaan.git] / prj / cam / src / object / property.cpp
blobf724c045ae21a2c9c6f36d46091e583814381830
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/object/property.cpp,v 1.65 1999/11/05 14:26:37 Justin Exp $
7 #include <mprintf.h>
9 #include <propert_.h>
10 #include <propstat.h>
11 #include <propnet.h>
12 #include <propguid.h>
13 #include <objnotif.h>
14 #include <trait.h>
15 #include <traitman.h>
16 #include <traitbas.h>
17 #include <propmix.h>
18 #include <osysbase.h>
19 #include <dlistsim.h>
20 #include <dlisttem.h>
21 #include <iobjed.h>
22 #include <objquery.h>
23 #include <dataops_.h>
24 #include <playrobj.h>
25 #include <timer.h>
27 #include <config.h>
28 #include <cfgdbg.h>
29 #include <netman.h>
30 #include <iobjnet.h>
31 #include <allocapi.h>
33 // Must be last header
34 #include <dbmem.h>
36 ////////////////////////////////////////////////////////////
37 // CLASS: cStoredPropertyStats
40 class cStoredPropertyStats: public cCTDelegating<IPropertyStats>
42 cStoredProperty* mpProp;
44 public:
45 cStoredPropertyStats(IUnknown* outer, cStoredProperty* prop)
46 : cCTDelegating<IPropertyStats> (outer),
47 mpProp(prop)
51 STDMETHOD_(sPropTimeStats*,GetTimeStats)()
53 return mpProp->GetStoreTimeStats();
55 } ;
57 ////////////////////////////////////////////////////////////
58 // CLASS: cStoredPropertyNetworking
61 #ifndef SHIP
62 #define NetPropSpew(msg) {if (gmbDoSpew) {mprintf msg;}}
63 #else
64 #define NetPropSpew(msg)
65 #endif
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"));
95 else
96 propnet->ReceivePropertyMsg(pMsg, size, from);
97 SafeRelease(propnet);
98 SafeRelease(prop);
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");
110 sDatum currentVal;
111 return (mpProp->mpStore->Get(obj, &currentVal)
112 && ops->BlockSize(currentVal) >= 0 //... currentVal is a pointer
113 && currentVal == value); //... which is the same as value
116 public:
117 cStoredPropertyNetworking(cStoredProperty* prop)
118 : cCTDelegating<IPropertyNetworking> (NULL), // Outer set when it's first QI'ed from prop.
119 mpProp(prop),
120 mbHandlingMessage(FALSE)
122 if (gmNetMsgHandlerID == 0) {
123 gmNetMsgHandlerID =
124 mpProp->gmNetMan->RegisterMessageParser(NetworkMessageCallback,
125 "Prop", 0, NULL);
126 #ifndef SHIP
127 #ifdef SPEW_ON
128 gmbDoSpew = config_is_defined("net_prop_spew")||config_is_defined("net_spew");
129 #else
130 // Allow spew in opt builds *if* net_opt_spew is also set:
131 gmbDoSpew =
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")));
134 #endif
135 #endif
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,
146 sDatum value,
147 ePropertyListenMsg type)
149 if (mbHandlingMessage) {
150 // we started this property change via a net message
151 return FALSE;
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));
179 return FALSE;
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.
195 else
197 // Reset the memFile buffer.
198 memFile.Seek(kDataOpSeekFromStart, 0);
199 // Cause value to be written to our memFile buffer, this
200 // flattens it out.
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);
221 msg->type = type;
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);
229 else
230 mpProp->gmNetMan->Broadcast(msg, msgSize, TRUE);
231 free(msg);
232 return 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
241 // - apply the diff.
242 // - read from the memFile with the diff applied.
245 STDMETHOD_(void,ReceivePropertyMsg)(const sNetMsg_Generic *pMsg,
246 ulong size,
247 ObjID fromPlayer)
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));
257 return;
260 Assert_(msg->type & (kListenPropSet |
261 kListenPropModify |
262 kListenPropUnset));
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));
286 return;
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.
295 else
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;
305 if (valueSize > 0) {
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));
317 // Now call set
318 mpProp->Set(obj, value);
319 // Free up the space for the unflatten value.
320 ops->Delete(value);
321 } else {
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;
349 // QueryInterface
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)
358 *ppI = mpDonors;
359 mpDonors->AddRef();
360 return S_OK;
363 if (id == IID_IPropertyStats)
365 if (!mpStats)
366 mpStats = new cStoredPropertyStats(me,this);
367 *ppI = mpStats;
368 mpStats->AddRef();
369 return S_OK;
372 #ifdef NEW_NETWORK_ENABLED
373 if (id == IID_IPropertyNetworking && mpNetProp != NULL)
375 mpNetProp->InitDelegation(me); // Created without delgate. Initialize on (every) QI.
376 *ppI = mpNetProp;
377 mpNetProp->AddRef();
378 return S_OK;
380 #endif
382 BOOL match = IsEqualOrIUnknownGUID(id,IID_IProperty);
384 if (!match)
386 *ppI = 0;
387 return ResultFromScode(E_NOINTERFACE);
389 *ppI = me;
390 me->AddRef();
391 return S_OK;
395 //////////////////////////////
397 // Constructor; pass in name, and preconstructed implementation
401 IPropertyStore* InitBasePropertyStore(IPropertyStore* store, ulong flags);
403 cPropertyBase::cPropertyBase(const sPropertyDesc* desc) :
404 mDesc(*desc),
405 mpImplied(NULL),
406 mpRequired(NULL)
408 CompileConstraints();
411 cStoredProperty::cStoredProperty(const sPropertyDesc* desc, IPropertyStore *store)
412 : cPropertyBase(desc),
413 mpStore(InitBasePropertyStore(store,desc->flags)),
414 mpDonors(NULL),
415 mpExemplars(NULL),
416 mFlags(0),
417 mpStats(NULL),
418 mpNetProp(NULL)
420 #ifdef NEW_NETWORK_ENABLED
421 if (gmNetMan == NULL)
422 gmNetMan = AppGetObj(INetManager);
423 Assert_(gmNetMan);
424 if (gmObjNet == NULL)
425 gmObjNet = AppGetObj(IObjectNetworking);
426 Assert_(gmObjNet);
427 if (!(mDesc.net_flags & kPropertyChangeLocally))
428 mpNetProp = new cStoredPropertyNetworking(this);
429 #endif
431 // Clear the statistic accumulation totals.
432 memset(&mStoreStats,0,sizeof(mStoreStats));
433 InitTraits();
435 mAllocName = PROP_BLAME_NAME(desc->name);
438 //////////////////////////////
440 // Destructor
443 cPropertyBase::~cPropertyBase ()
445 delete mpImplied;
446 delete mpRequired;
449 cStoredProperty::~cStoredProperty ()
451 STOREDPROP_AUTO_BLAME();
452 if (mpStore)
454 // Give allocation credit to the particular property.
455 mpStore->Reset();
456 int refs = mpStore->Release();
457 if (refs > 0)
459 ConfigSpew("propstore_ref_spew",("Property store for %s has %d refs on exit\n",mDesc.name,refs));
461 while (mpStore->Release() > 0)
466 if (mpNetProp)
468 delete mpNetProp;
469 mpNetProp = NULL;
472 mpStore = NULL;
473 SafeRelease(mpDonors);
474 SafeRelease(mpExemplars);
475 delete mpStats;
479 ////////////////////////////////////////
481 void cPropertyBase::Notify (ePropertyNotifyMsg msg, PropNotifyData data)
483 uObjNotifyData info;
484 info.raw = data;
485 switch(NOTIFY_MSG(msg))
487 case kObjNotifyDefault:
488 case kObjNotifyPostLoad:
489 // Recompile constraints
490 CompileConstraints();
491 break;
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);
511 uObjNotifyData info;
512 info.raw = data;
513 switch(NOTIFY_MSG(msg))
515 case kObjNotifyDelete:
516 Delete(info.obj);
517 break;
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);
527 break;
531 break;
533 case kObjNotifyDefault:
534 case kObjNotifyPostLoad:
536 sPropertyObjIter iter;
537 // Give allocation credit to the particular property.
538 mpStore->IterStart(&iter);
540 ObjID obj;
541 sDatum value;
542 // Give allocation credit to the particular property.
543 while(mpStore->IterNext(&iter,&obj,&value))
545 EnforceRequirements(obj);
546 PROP_POP_BLAME();
547 CallListeners(kListenPropSet|kListenPropModify|kListenPropLoad,obj,value);
548 PROP_PUSH_BLAME(mAllocName);
549 EnforceImplications(obj);
551 mpStore->IterStop(&iter);
553 break;
556 case kObjNotifyReset:
557 // Give allocation credit to the particular property.
558 mpStore->Reset();
559 break;
561 case kObjNotifySave:
562 Save(info.db.save, msg);
563 break;
565 case kObjNotifyLoad:
566 Load(info.db.load, msg);
567 break;
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);
589 return;
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;
605 cbval.ptrval = dat;
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;
612 if (mask != 0)
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);
626 // invalidate trait
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))
645 return S_FALSE;
647 int msg = kListenPropSet;
649 // Enforce "requires" constraints"
651 EnforceRequirements(obj);
653 ObjID example = mpExemplars->GetDonor(obj);
655 STOREDPROP_TIMER(Create);
656 sDatum value;
657 if (example != OBJ_NULL)
659 // Give allocation credit to the particular property.
660 value = mpStore->Copy(obj,example);
661 msg |= kListenPropModify;
663 else {
664 // Give allocation credit to the particular property.
665 value = mpStore->Create(obj);
668 PROP_TIMER_STOP();
670 PROP_POP_BLAME();
671 CallListeners(msg,obj,value);
672 PROP_PUSH_BLAME(mAllocName);
675 // Enforce "autocreate" constraints
677 EnforceImplications(obj);
679 return S_OK;
682 ////////////////////////////////////////
685 HRESULT cStoredProperty::Copy(ObjID targ, ObjID src)
687 if (src != OBJ_NULL) // if by example, copy from the example
689 sDatum srcval;
691 // call our own relevance functions for timing
692 if (!cStoredProperty::IsSimplyRelevant(src))
693 src = cStoredProperty::GetDonor(src);
695 if (src != OBJ_NULL)
697 PROP_PUSH_BLAME(mAllocName);
699 sDatum dat;
700 ePropertyListenMsg msg = kListenPropModify;
701 if (!cStoredProperty::IsSimplyRelevant(targ))
703 EnforceRequirements(targ);
705 STOREDPROP_TIMER(Copy);
707 dat = mpStore->Copy(targ,src);
709 PROP_TIMER_STOP();
711 EnforceImplications(targ);
712 msg |= kListenPropSet;
714 else
716 STOREDPROP_TIMER(Copy);
717 dat = mpStore->Copy(targ,src);
718 PROP_TIMER_STOP();
721 PROP_POP_BLAME();
723 CallListeners(msg,targ,dat);
724 return S_OK;
727 return S_FALSE;
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() &&
737 mpNetProp != NULL &&
738 gmObjNet->ObjIsProxy(obj))
740 if (mpNetProp->SendPropertyMsg(obj, value, msg | kListenPropRequestFromHost))
741 return S_FALSE;
742 // Otherwise, we still have to change the property.
744 #endif
745 STOREDPROP_TIMER(Set);
746 PROP_PUSH_BLAME(mAllocName);
747 HRESULT result = mpStore->Set(obj,value);
748 if (result == S_OK)
749 msg |= kListenPropSet;
751 PROP_POP_BLAME();
752 PROP_TIMER_STOP();
753 CallListeners(msg,obj,value);
754 return result;
757 ////////////////////////////////////////
759 BOOL cStoredProperty::Touch(ObjID obj,sDatum* value)
761 sDatum val;
762 if (!value) // got passed in NULL
764 STOREDPROP_TIMER(Touch);
765 value = &val;
766 // Give allocation credit to the particular property.
767 if (!mpStore->Get(obj, value))
768 return FALSE;
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",
779 obj, mDesc.name);
780 #endif
781 CallListeners(kListenPropModify,obj,*value);
782 return TRUE;
785 ////////////////////////////////////////
787 HRESULT cStoredProperty::Delete(ObjID obj)
791 sDatum value;
792 STOREDPROP_TIMER(Get);
793 BOOL relevant = mpStore->Get(obj,&value);
794 PROP_TIMER_STOP();
796 if (relevant)
798 CallListeners(kListenPropUnset,obj,value);
800 PROP_PUSH_BLAME(mAllocName);
801 STOREDPROP_TIMER(Delete);
803 HRESULT result = mpStore->Delete (obj);
805 PROP_TIMER_STOP();
806 PROP_POP_BLAME();
808 if (mFlags & kRebuildConcretes)
809 RebuildConcretes(obj);
811 return result;
813 return S_FALSE;
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)
835 if (store)
837 store->AddRef();
838 SafeRelease(mpStore);
839 mpStore = InitBasePropertyStore(store,mDesc.flags);
840 mpStore->SetID(mID);
841 store->Release();
843 else
845 SafeRelease(mpStore);
849 ////////////////////////////////////////////////////////////
850 // cStoredProperty Implementation
854 //------------------------------------------------------------
855 // CreateEditor
858 void cPropertyBase::CreateEditor(IProperty* prop)
860 IObjEditors* edit = AppGetObj(IObjEditors);
861 if (edit != NULL)
863 IUnknown* trait = (IUnknown*)edit->AddProperty(prop);
864 // I don't want the trait. Just get rid of it.
865 SafeRelease(trait);
866 SafeRelease(edit);
872 //------------------------------------------------------------
873 // InitTraits()
875 // Create the traits for a property based on it's desc
879 // Trait predicate
881 BOOL PropTraitPred(ObjID obj, TraitPredicateData data)
883 cStoredProperty* base = (cStoredProperty*)data;
884 return base->IsSimplyRelevant(obj);
887 void cStoredProperty::InitTraits(void)
889 sTraitDesc tdesc;
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);
912 else
914 mpExemplars = pTraitMan->CreateTrait(&tdesc,&pred);
915 mpDonors = mpExemplars;
916 mpDonors->AddRef();
920 //------------------------------------------------------------
921 // CompileConstraints
923 // Build constraint lists from descriptor
926 void cPropertyBase::CompileConstraints(void)
928 delete mpImplied;
929 delete mpRequired;
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));
941 switch(c->kind)
943 case kPropertyAutoCreate:
944 if (!mpImplied) mpImplied = new PropIDList;
945 mpImplied->Append(against->GetID());
946 break;
948 case kPropertyRequires:
949 if (!mpRequired) mpRequired = new PropIDList;
950 mpRequired->Append(against->GetID());
951 break;
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))
970 prop->Create(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))
984 prop->Create(obj);
989 //------------------------------------------------------------
990 // SetRebuildConcretes
993 void cStoredProperty::SetRebuildConcretes(BOOL fRebuild)
995 if (fRebuild)
997 mFlags |= kRebuildConcretes;
998 if (!(mFlags & kListeningHierarchy))
1000 AutoAppIPtr(TraitManager);
1001 pTraitManager->Listen(RebuildHierarchyListener, this);
1002 mFlags |= kListeningHierarchy;
1005 else
1006 mFlags &= ~kRebuildConcretes;
1010 //------------------------------------------------------------
1011 // RebuildConcretes
1014 void cStoredProperty::RebuildOneConcrete(ObjID obj)
1016 sDatum value;
1017 uPropListenerValue cbval;
1019 // Give allocation credit to the particular property.
1020 BOOL relevant = mpStore->Get(obj, &value);
1021 ObjID donor = obj;
1022 if (!relevant)
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;
1030 if (relevant)
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 //------------------------------------------------------------
1061 // OnListenMsg
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);
1080 #endif
1084 //------------------------------------------------------------
1085 // CreateEditor
1088 // Overridables
1092 //------------------------------------------------------------
1093 // InitBasePropertyStore
1095 static IPropertyStore* InitBasePropertyStore(IPropertyStore* impl, ulong flags)
1098 if (impl)
1100 if (flags & kPropertyConcrete)
1102 IPropertyStore* abstract = CreateGenericPropertyStore(kPropertyImplVerySparse);
1103 IPropertyStore* retval = new cMixedPropertyStore(abstract,impl);
1104 SafeRelease(abstract);
1105 return retval;
1107 else
1108 impl->AddRef();
1111 return impl;
1115 ////////////////////////////////////////////////////////////
1116 // GetProperty()
1120 // Hey look, here's where we use cPropertyManagerKnower to gratuitous advantage
1124 class LookerUpper : public cPropertyManagerKnower
1126 public:
1127 static IProperty* ByID(PropertyID id)
1129 if (GetManager() == NULL)
1130 return NULL;
1131 return GetManager()->GetProperty(id);
1134 static IProperty* ByName(const char* name)
1136 if (GetManager() == NULL)
1137 return NULL;
1138 return GetManager()->GetPropertyNamed(name);
1142 IProperty* GetProperty(PropertyID id)
1144 IProperty* prop = LookerUpper::ByID(id);
1145 return prop;
1148 IProperty* GetPropertyNamed(const char* name)
1150 IProperty* prop = LookerUpper::ByName(name);
1151 return prop;
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.
1158 prop->Release();
1159 return prop;
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.
1166 prop->Release();
1167 return prop;
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;
1185 if (!inited)
1187 // @TODO: do this someplace more sensible
1188 gBlameProperties = config_is_defined("blame_properties");
1189 inited = TRUE;
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");
1196 return s;
1199 // Push and pop allocation credit
1200 void PropPushBlame(const char* PropName)
1202 #ifdef DEBUG
1203 if (gBlameProperties)
1204 g_pMalloc->PushCredit(PropName,0);
1205 #endif
1208 void PropPopBlame(void)
1210 #ifdef DEBUG
1211 if (gBlameProperties)
1212 g_pMalloc->PopCredit();
1213 #endif
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