|
|
@@ -21,7 +21,7 @@
|
|
|
#include "config_util.h"
|
|
|
#include "pipelineCyclerBase.h"
|
|
|
|
|
|
-TypeHandle BamReader::_remove_flag;
|
|
|
+TypeHandle BamReaderAuxData::_type_handle;
|
|
|
|
|
|
WritableFactory *BamReader::_factory = (WritableFactory*)0L;
|
|
|
BamReader *const BamReader::Null = (BamReader*)0L;
|
|
|
@@ -43,6 +43,7 @@ BamReader(DatagramGenerator *generator, const Filename &name)
|
|
|
: _source(generator), _filename(name)
|
|
|
{
|
|
|
_num_extra_objects = 0;
|
|
|
+ _nesting_level = 0;
|
|
|
_now_creating = _created_objs.end();
|
|
|
_reading_cycler = (PipelineCyclerBase *)NULL;
|
|
|
_pta_id = -1;
|
|
|
@@ -58,6 +59,8 @@ BamReader(DatagramGenerator *generator, const Filename &name)
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
BamReader::
|
|
|
~BamReader() {
|
|
|
+ nassertv(_num_extra_objects == 0);
|
|
|
+ nassertv(_nesting_level == 0);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -195,6 +198,7 @@ get_aux_data(TypedWritable *obj, const string &name) const {
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: BamReader::read_object
|
|
|
// Access: Public
|
|
|
@@ -218,10 +222,42 @@ get_aux_data(TypedWritable *obj, const string &name) const {
|
|
|
// not be filled in; you must call resolve() to fill in
|
|
|
// all the available pointers before you can safely use
|
|
|
// any objects returned by read_object().
|
|
|
+//
|
|
|
+// This flavor of read_object() requires the caller to
|
|
|
+// know what type of object it has received in order to
|
|
|
+// properly manage the reference counts.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
TypedWritable *BamReader::
|
|
|
read_object() {
|
|
|
- nassertr(_num_extra_objects == 0, (TypedWritable *)NULL);
|
|
|
+ TypedWritable *ptr;
|
|
|
+ ReferenceCount *ref_ptr;
|
|
|
+
|
|
|
+ if (!read_object(ptr, ref_ptr)) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: BamReader::read_object
|
|
|
+// Access: Public
|
|
|
+// Description: Reads a single object from the Bam file.
|
|
|
+//
|
|
|
+// This flavor of read_object() returns both a
|
|
|
+// TypedWritable and a ReferenceCount pointer to the
|
|
|
+// same object, so the reference count may be tracked
|
|
|
+// reliably, without having to know precisely what type
|
|
|
+// of object we have. It returns true on success, or
|
|
|
+// false on failure.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool BamReader::
|
|
|
+read_object(TypedWritable *&ptr, ReferenceCount *&ref_ptr) {
|
|
|
+ ptr = NULL;
|
|
|
+ ref_ptr = NULL;
|
|
|
+ nassertr(_num_extra_objects == 0, false);
|
|
|
+
|
|
|
+ int start_level = _nesting_level;
|
|
|
|
|
|
// First, read the base object.
|
|
|
int object_id = p_read_object();
|
|
|
@@ -230,19 +266,28 @@ read_object() {
|
|
|
// objects, which may still need to be read. And those objects
|
|
|
// might in turn require reading additional objects. Read all the
|
|
|
// remaining objects.
|
|
|
+
|
|
|
+ // Prior to 6.21, we kept track of _num_extra_objects to know when
|
|
|
+ // we're done.
|
|
|
while (_num_extra_objects > 0) {
|
|
|
p_read_object();
|
|
|
_num_extra_objects--;
|
|
|
}
|
|
|
|
|
|
+ // Beginning with 6.21, we use explicit nesting commands to know
|
|
|
+ // when we're done.
|
|
|
+ while (_nesting_level > start_level) {
|
|
|
+ p_read_object();
|
|
|
+ }
|
|
|
+
|
|
|
// Now look up the pointer of the object we read first. It should
|
|
|
// be available now.
|
|
|
if (object_id == 0) {
|
|
|
if (bam_cat.is_spam()) {
|
|
|
bam_cat.spam()
|
|
|
- << "Returning NULL\n";
|
|
|
+ << "Returning false\n";
|
|
|
}
|
|
|
- return (TypedWritable *)NULL;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
CreatedObjs::iterator oi = _created_objs.find(object_id);
|
|
|
@@ -250,25 +295,27 @@ read_object() {
|
|
|
if (oi == _created_objs.end()) {
|
|
|
bam_cat.error()
|
|
|
<< "Undefined object encountered!\n";
|
|
|
- return (TypedWritable *)NULL;
|
|
|
+ return false;
|
|
|
|
|
|
} else {
|
|
|
CreatedObj &created_obj = (*oi).second;
|
|
|
- TypedWritable *object = created_obj._ptr;
|
|
|
+ ptr = created_obj._ptr;
|
|
|
+ ref_ptr = created_obj._ref_ptr;
|
|
|
|
|
|
if (bam_cat.is_spam()) {
|
|
|
- if (object != (TypedWritable *)NULL) {
|
|
|
+ if (ptr != (TypedWritable *)NULL) {
|
|
|
bam_cat.spam()
|
|
|
- << "Returning object of type " << object->get_type() << "\n";
|
|
|
+ << "Returning object of type " << ptr->get_type() << "\n";
|
|
|
}
|
|
|
}
|
|
|
- if (created_obj._change_this != NULL) {
|
|
|
+ if (created_obj._change_this != NULL ||
|
|
|
+ created_obj._change_this_ref != NULL) {
|
|
|
bam_cat.warning()
|
|
|
- << "Returning pointer to " << object->get_type()
|
|
|
+ << "Returning pointer to " << ptr->get_type()
|
|
|
<< " that might change.\n";
|
|
|
}
|
|
|
|
|
|
- return object;
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -296,6 +343,10 @@ resolve() {
|
|
|
bool any_completed_this_pass;
|
|
|
|
|
|
do {
|
|
|
+ if (bam_cat.is_spam()) {
|
|
|
+ bam_cat.spam()
|
|
|
+ << "resolve pass begin\n";
|
|
|
+ }
|
|
|
all_completed = true;
|
|
|
any_completed_this_pass = false;
|
|
|
|
|
|
@@ -312,6 +363,11 @@ resolve() {
|
|
|
|
|
|
TypedWritable *object_ptr = created_obj._ptr;
|
|
|
|
|
|
+ // Update _now_creating, so a call to get_int_tag() from within
|
|
|
+ // complete_pointers() will come to the right place.
|
|
|
+ CreatedObjs::iterator was_creating = _now_creating;
|
|
|
+ _now_creating = ci;
|
|
|
+
|
|
|
if (resolve_object_pointers(object_ptr, pref)) {
|
|
|
// Now remove this object from the list of things that need
|
|
|
// completion. We have to be a bit careful when deleting things
|
|
|
@@ -319,9 +375,35 @@ resolve() {
|
|
|
ObjectPointers::iterator old = oi;
|
|
|
++oi;
|
|
|
_object_pointers.erase(old);
|
|
|
+ any_completed_this_pass = true;
|
|
|
|
|
|
// Does the pointer need to change?
|
|
|
- if (created_obj._change_this != NULL) {
|
|
|
+ if (created_obj._change_this_ref != NULL) {
|
|
|
+ // Reference-counting variant.
|
|
|
+ TypedWritableReferenceCount *object_ref_ptr = (TypedWritableReferenceCount *)object_ptr;
|
|
|
+ nassertr(created_obj._ref_ptr == NULL || created_obj._ref_ptr == object_ref_ptr, false);
|
|
|
+ PT(TypedWritableReferenceCount) new_ptr = created_obj._change_this_ref(object_ref_ptr, this);
|
|
|
+ if (new_ptr != object_ref_ptr) {
|
|
|
+ // Also update the reverse
|
|
|
+ vector_int &old_refs = _created_objs_by_pointer[object_ptr];
|
|
|
+ vector_int &new_refs = _created_objs_by_pointer[new_ptr];
|
|
|
+ for (vector_int::const_iterator oi = old_refs.begin();
|
|
|
+ oi != old_refs.end();
|
|
|
+ ++oi) {
|
|
|
+ new_refs.push_back(*oi);
|
|
|
+ }
|
|
|
+ _created_objs_by_pointer.erase(object_ptr);
|
|
|
+
|
|
|
+ // Remove the pointer from the finalize list (the new
|
|
|
+ // pointer presumably doesn't require finalizing).
|
|
|
+ _finalize_list.erase(object_ptr);
|
|
|
+ }
|
|
|
+ created_obj.set_ptr(new_ptr, new_ptr);
|
|
|
+ created_obj._change_this = NULL;
|
|
|
+ created_obj._change_this_ref = NULL;
|
|
|
+
|
|
|
+ } else if (created_obj._change_this != NULL) {
|
|
|
+ // Non-reference-counting variant.
|
|
|
TypedWritable *new_ptr = created_obj._change_this(object_ptr, this);
|
|
|
if (new_ptr != object_ptr) {
|
|
|
// Also update the reverse
|
|
|
@@ -338,18 +420,26 @@ resolve() {
|
|
|
// pointer presumably doesn't require finalizing).
|
|
|
_finalize_list.erase(object_ptr);
|
|
|
}
|
|
|
- created_obj._ptr = new_ptr;
|
|
|
+ created_obj.set_ptr(new_ptr, new_ptr->as_reference_count());
|
|
|
created_obj._change_this = NULL;
|
|
|
+ created_obj._change_this_ref = NULL;
|
|
|
}
|
|
|
- any_completed_this_pass = true;
|
|
|
|
|
|
} else {
|
|
|
// Couldn't complete this object yet; it'll wait for next time.
|
|
|
++oi;
|
|
|
all_completed = false;
|
|
|
}
|
|
|
+
|
|
|
+ _now_creating = was_creating;
|
|
|
}
|
|
|
|
|
|
+ if (bam_cat.is_spam()) {
|
|
|
+ bam_cat.spam()
|
|
|
+ << "resolve pass end: all_completed = " << all_completed
|
|
|
+ << " any_completed_this_pass = " << any_completed_this_pass
|
|
|
+ << "\n";
|
|
|
+ }
|
|
|
} while (!all_completed && any_completed_this_pass);
|
|
|
|
|
|
if (all_completed) {
|
|
|
@@ -420,7 +510,8 @@ change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_point
|
|
|
nassertr(ci != _created_objs.end(), false);
|
|
|
nassertr((*ci).second._ptr == orig_pointer, false);
|
|
|
|
|
|
- (*ci).second._ptr = (TypedWritable *)new_pointer;
|
|
|
+ TypedWritable *ptr = (TypedWritable *)new_pointer;
|
|
|
+ (*ci).second.set_ptr(ptr, ptr->as_reference_count());
|
|
|
new_refs.push_back(object_id);
|
|
|
}
|
|
|
|
|
|
@@ -539,7 +630,7 @@ read_handle(DatagramIterator &scan) {
|
|
|
// pointer to an object that appears later in the Bam
|
|
|
// file). Later, when all pointers are available, the
|
|
|
// complete_pointers() callback function will be called
|
|
|
-// with an array of actual pointers, one for time
|
|
|
+// with an array of actual pointers, one for each time
|
|
|
// read_pointer() was called. It is then the calling
|
|
|
// object's responsibilty to store these pointers in the
|
|
|
// object properly.
|
|
|
@@ -566,10 +657,14 @@ read_pointer(DatagramIterator &scan) {
|
|
|
// If the object ID is zero (which indicates a NULL pointer), we
|
|
|
// don't have to do anything else.
|
|
|
if (object_id != 0) {
|
|
|
- if (_created_objs.count(object_id) == 0) {
|
|
|
- // If we don't already have an entry in the map for this object
|
|
|
- // ID (that is, we haven't encountered this object before), we
|
|
|
- // must remember to read the object definition later.
|
|
|
+ /*
|
|
|
+ CreatedObj new_created_obj;
|
|
|
+ _created_objs.insert(CreatedObjs::value_type(object_id, new_created_obj)).second;
|
|
|
+ */
|
|
|
+
|
|
|
+ if (get_file_minor_ver() < 21) {
|
|
|
+ // Prior to bam version 6.21, we expect to read an adjunct
|
|
|
+ // object for each non-NULL pointer we read.
|
|
|
_num_extra_objects++;
|
|
|
}
|
|
|
}
|
|
|
@@ -624,8 +719,8 @@ read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler) {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: BamReader::read_cdata
|
|
|
// Access: Public
|
|
|
-// Description: This flavor of BamReader allows passing an additional
|
|
|
-// parameter to cdata->fillin().
|
|
|
+// Description: This flavor of read_cdata allows passing an
|
|
|
+// additional parameter to cdata->fillin().
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void BamReader::
|
|
|
read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler,
|
|
|
@@ -638,6 +733,99 @@ read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler,
|
|
|
_reading_cycler = old_cycler;
|
|
|
}
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: BamReader::set_int_tag
|
|
|
+// Access: Public
|
|
|
+// Description: Allows the creating object to store a temporary data
|
|
|
+// value on the BamReader. This method may be called
|
|
|
+// during an object's fillin() method; it will associate
|
|
|
+// an integer value with an arbitrary string key (which
|
|
|
+// is in turn associated with the calling object only).
|
|
|
+// Later, in the complete_pointers() method, the same
|
|
|
+// object may query this data again via get_int_tag().
|
|
|
+//
|
|
|
+// The tag string need not be unique between different
|
|
|
+// objects, but it should be unique between an object
|
|
|
+// and its CData object(s).
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void BamReader::
|
|
|
+set_int_tag(const string &tag, int value) {
|
|
|
+ nassertv(_now_creating != _created_objs.end());
|
|
|
+ int requestor_id = (*_now_creating).first;
|
|
|
+
|
|
|
+ PointerReference &pref = _object_pointers[requestor_id];
|
|
|
+ pref._int_tags[tag] = value;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: BamReader::get_int_tag
|
|
|
+// Access: Public
|
|
|
+// Description: Returns the value previously set via set_int_tag().
|
|
|
+// It is an error if no value has been set.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+int BamReader::
|
|
|
+get_int_tag(const string &tag) const {
|
|
|
+ nassertr(_now_creating != _created_objs.end(), 0);
|
|
|
+ int requestor_id = (*_now_creating).first;
|
|
|
+
|
|
|
+ ObjectPointers::const_iterator opi = _object_pointers.find(requestor_id);
|
|
|
+ nassertr(opi != _object_pointers.end(), 0);
|
|
|
+ const PointerReference &pref = (*opi).second;
|
|
|
+
|
|
|
+ IntTags::const_iterator iti = pref._int_tags.find(tag);
|
|
|
+ nassertr(iti != pref._int_tags.end(), 0);
|
|
|
+ return (*iti).second;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: BamReader::set_aux_tag
|
|
|
+// Access: Public
|
|
|
+// Description: Allows the creating object to store a temporary data
|
|
|
+// value on the BamReader. This method may be called
|
|
|
+// during an object's fillin() method; it will associate
|
|
|
+// a newly-allocated BamReaderAuxData construct with an
|
|
|
+// arbitrary string key (which is in turn associated
|
|
|
+// with the calling object only). Later, in the
|
|
|
+// complete_pointers() method, the same object may query
|
|
|
+// this data again via get_aux_tag().
|
|
|
+//
|
|
|
+// The BamReader will maintain the reference count on
|
|
|
+// the BamReaderAuxData, and destruct it when it is
|
|
|
+// cleaned up.
|
|
|
+//
|
|
|
+// The tag string need not be unique between different
|
|
|
+// objects, but it should be unique between an object
|
|
|
+// and its CData object(s).
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void BamReader::
|
|
|
+set_aux_tag(const string &tag, BamReaderAuxData *value) {
|
|
|
+ nassertv(_now_creating != _created_objs.end());
|
|
|
+ int requestor_id = (*_now_creating).first;
|
|
|
+
|
|
|
+ PointerReference &pref = _object_pointers[requestor_id];
|
|
|
+ pref._aux_tags[tag] = value;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: BamReader::get_aux_tag
|
|
|
+// Access: Public
|
|
|
+// Description: Returns the value previously set via set_aux_tag().
|
|
|
+// It is an error if no value has been set.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+BamReaderAuxData *BamReader::
|
|
|
+get_aux_tag(const string &tag) const {
|
|
|
+ nassertr(_now_creating != _created_objs.end(), NULL);
|
|
|
+ int requestor_id = (*_now_creating).first;
|
|
|
+
|
|
|
+ ObjectPointers::const_iterator opi = _object_pointers.find(requestor_id);
|
|
|
+ nassertr(opi != _object_pointers.end(), NULL);
|
|
|
+ const PointerReference &pref = (*opi).second;
|
|
|
+
|
|
|
+ AuxTags::const_iterator ati = pref._aux_tags.find(tag);
|
|
|
+ nassertr(ati != pref._aux_tags.end(), NULL);
|
|
|
+ return (*ati).second;
|
|
|
+}
|
|
|
+
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: BamReader::register_finalize
|
|
|
// Access: Public
|
|
|
@@ -691,7 +879,7 @@ register_change_this(ChangeThisFunc func, TypedWritable *object) {
|
|
|
// Sanity check the pointer--it should always be the same pointer
|
|
|
// after we set it the first time.
|
|
|
if (created_obj._ptr == (TypedWritable *)NULL) {
|
|
|
- created_obj._ptr = object;
|
|
|
+ created_obj.set_ptr(object, object->as_reference_count());
|
|
|
} else {
|
|
|
// We've previously assigned this pointer, and we should have
|
|
|
// assigned it to the same this pointer we have now.
|
|
|
@@ -700,6 +888,47 @@ register_change_this(ChangeThisFunc func, TypedWritable *object) {
|
|
|
#endif // NDEBUG
|
|
|
|
|
|
created_obj._change_this = func;
|
|
|
+ created_obj._change_this_ref = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: BamReader::register_change_this
|
|
|
+// Access: Public
|
|
|
+// Description: Called by an object reading itself from the bam file
|
|
|
+// to indicate that the object pointer that will be
|
|
|
+// returned is temporary, and will eventually need to be
|
|
|
+// replaced with another pointer.
|
|
|
+//
|
|
|
+// The supplied function pointer will later be called on
|
|
|
+// the object, immediately after complete_pointers() is
|
|
|
+// called; it should return the new and final pointer.
|
|
|
+//
|
|
|
+// We use a static function pointer instead of a virtual
|
|
|
+// function (as in finalize()), to allow the function to
|
|
|
+// destruct the old pointer if necessary. (It is
|
|
|
+// invalid to destruct the this pointer within a virtual
|
|
|
+// function.)
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void BamReader::
|
|
|
+register_change_this(ChangeThisRefFunc func, TypedWritableReferenceCount *object) {
|
|
|
+ nassertv(_now_creating != _created_objs.end());
|
|
|
+ CreatedObj &created_obj = (*_now_creating).second;
|
|
|
+
|
|
|
+#ifndef NDEBUG
|
|
|
+ // Sanity check the pointer--it should always be the same pointer
|
|
|
+ // after we set it the first time.
|
|
|
+ if (created_obj._ptr == (TypedWritable *)NULL) {
|
|
|
+ created_obj.set_ptr(object, object);
|
|
|
+ } else {
|
|
|
+ // We've previously assigned this pointer, and we should have
|
|
|
+ // assigned it to the same this pointer we have now.
|
|
|
+ nassertv(created_obj._ptr == object);
|
|
|
+ nassertv(created_obj._ref_ptr == object);
|
|
|
+ }
|
|
|
+#endif // NDEBUG
|
|
|
+
|
|
|
+ created_obj._change_this = NULL;
|
|
|
+ created_obj._change_this_ref = func;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -901,15 +1130,26 @@ p_read_object() {
|
|
|
// Now extract the object definition from the datagram.
|
|
|
DatagramIterator scan(packet);
|
|
|
|
|
|
- // An object definition in a Bam file consists of a TypeHandle
|
|
|
- // definition, defining the object's type, followed by an object ID
|
|
|
- // index, defining the particular instance (e.g. pointer) of this
|
|
|
- // object.
|
|
|
+ // First, read the BamObjectCode. In bam versions prior to 6.21,
|
|
|
+ // there was no BamObjectCode in the stream.
|
|
|
+ BamObjectCode boc = BOC_adjunct;
|
|
|
+ if (get_file_minor_ver() >= 21) {
|
|
|
+ boc = (BamObjectCode)scan.get_uint8();
|
|
|
+ }
|
|
|
+ switch (boc) {
|
|
|
+ case BOC_push:
|
|
|
+ ++_nesting_level;
|
|
|
+ break;
|
|
|
|
|
|
- TypeHandle type = read_handle(scan);
|
|
|
+ case BOC_pop:
|
|
|
+ --_nesting_level;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ case BOC_adjunct:
|
|
|
+ break;
|
|
|
|
|
|
- if (type == _remove_flag) {
|
|
|
- // The _remove_flag TypeHandle is a special case; it begins a
|
|
|
+ case BOC_remove:
|
|
|
+ // The BOC_remove code is a special case; it begins a
|
|
|
// record that simply lists all of the object ID's that are no
|
|
|
// longer important to the file and may be released.
|
|
|
free_object_ids(scan);
|
|
|
@@ -920,6 +1160,13 @@ p_read_object() {
|
|
|
return p_read_object();
|
|
|
}
|
|
|
|
|
|
+ // An object definition in a Bam file consists of a TypeHandle
|
|
|
+ // definition, defining the object's type, followed by an object ID
|
|
|
+ // index, defining the particular instance (e.g. pointer) of this
|
|
|
+ // object.
|
|
|
+
|
|
|
+ TypeHandle type = read_handle(scan);
|
|
|
+
|
|
|
int object_id = read_object_id(scan);
|
|
|
|
|
|
// There are two cases (not counting the special _remove_flag case,
|
|
|
@@ -940,94 +1187,127 @@ p_read_object() {
|
|
|
if (type != TypeHandle::none()) {
|
|
|
// Now we are going to read and create a new object.
|
|
|
|
|
|
- // Define the parameters for passing to the object factory.
|
|
|
- FactoryParams fparams;
|
|
|
- fparams.add_param(new BamReaderParam(scan, this));
|
|
|
-
|
|
|
// First, we must add an entry into the map for this object ID, so
|
|
|
// that in case this function is called recursively during the
|
|
|
// object's factory constructor, we will have some definition for
|
|
|
// the object. For now, we give it a NULL pointer.
|
|
|
CreatedObj new_created_obj;
|
|
|
- new_created_obj._ptr = NULL;
|
|
|
- new_created_obj._change_this = NULL;
|
|
|
CreatedObjs::iterator oi =
|
|
|
_created_objs.insert(CreatedObjs::value_type(object_id, new_created_obj)).first;
|
|
|
CreatedObj &created_obj = (*oi).second;
|
|
|
|
|
|
- // Now we can call the factory to create the object. Update
|
|
|
- // _now_creating during this call so if this function calls
|
|
|
- // read_pointer() or register_change_this() we'll match it up
|
|
|
- // properly. This might recursively call back into this
|
|
|
- // p_read_object(), so be sure to save and restore the original
|
|
|
- // value of _now_creating.
|
|
|
- CreatedObjs::iterator was_creating = _now_creating;
|
|
|
- _now_creating = oi;
|
|
|
- TypedWritable *object =
|
|
|
- _factory->make_instance_more_general(type, fparams);
|
|
|
- _now_creating = was_creating;
|
|
|
-
|
|
|
- // And now we can store the new object pointer in the map.
|
|
|
- nassertr(created_obj._ptr == object || created_obj._ptr == NULL, object_id);
|
|
|
- created_obj._ptr = object;
|
|
|
-
|
|
|
- if (created_obj._change_this != NULL) {
|
|
|
- // If the pointer is scheduled to change after
|
|
|
- // complete_pointers(), but we have no entry in
|
|
|
- // _object_pointers for this object (and hence no plan to call
|
|
|
- // complete_pointers()), then just change the pointer
|
|
|
- // immediately.
|
|
|
- ObjectPointers::const_iterator ri = _object_pointers.find(object_id);
|
|
|
- if (ri == _object_pointers.end()) {
|
|
|
- Finalize::iterator fi = _finalize_list.find((TypedWritable *)object);
|
|
|
-
|
|
|
- TypedWritable *new_ptr = created_obj._change_this(object, this);
|
|
|
- created_obj._ptr = new_ptr;
|
|
|
- created_obj._change_this = NULL;
|
|
|
-
|
|
|
- // Remove the pointer from the finalize list (the new
|
|
|
- // pointer presumably doesn't require finalizing).
|
|
|
- if (new_ptr != object) {
|
|
|
- _finalize_list.erase(object);
|
|
|
- }
|
|
|
- object = new_ptr;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (created_obj._ptr != NULL) {
|
|
|
+ // This object had already existed; thus, we are just receiving
|
|
|
+ // an update for it.
|
|
|
|
|
|
- _created_objs_by_pointer[created_obj._ptr].push_back(object_id);
|
|
|
+ // Update _now_creating during this call so if this function
|
|
|
+ // calls read_pointer() or register_change_this() we'll match it
|
|
|
+ // up properly. This might recursively call back into this
|
|
|
+ // p_read_object(), so be sure to save and restore the original
|
|
|
+ // value of _now_creating.
|
|
|
+ CreatedObjs::iterator was_creating = _now_creating;
|
|
|
+ _now_creating = oi;
|
|
|
+ created_obj._ptr->fillin(scan, this);
|
|
|
+ _now_creating = was_creating;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // We are receiving a new object. Now we can call the factory
|
|
|
+ // to create the object.
|
|
|
+
|
|
|
+ // Define the parameters for passing to the object factory.
|
|
|
+ FactoryParams fparams;
|
|
|
+ fparams.add_param(new BamReaderParam(scan, this));
|
|
|
+
|
|
|
+ // As above, we update and preserve _now_creating during this
|
|
|
+ // call.
|
|
|
+ CreatedObjs::iterator was_creating = _now_creating;
|
|
|
+ _now_creating = oi;
|
|
|
+ TypedWritable *object =
|
|
|
+ _factory->make_instance_more_general(type, fparams);
|
|
|
+ _now_creating = was_creating;
|
|
|
+
|
|
|
+ // And now we can store the new object pointer in the map.
|
|
|
+ nassertr(created_obj._ptr == object || created_obj._ptr == NULL, object_id);
|
|
|
+ if (object == NULL) {
|
|
|
+ created_obj.set_ptr(NULL, NULL);
|
|
|
+ } else {
|
|
|
+ created_obj.set_ptr(object, object->as_reference_count());
|
|
|
+ }
|
|
|
+ created_obj._created = true;
|
|
|
+
|
|
|
+ if (created_obj._change_this_ref != NULL) {
|
|
|
+ // If the pointer is scheduled to change after
|
|
|
+ // complete_pointers(), but we have no entry in
|
|
|
+ // _object_pointers for this object (and hence no plan to call
|
|
|
+ // complete_pointers()), then just change the pointer
|
|
|
+ // immediately.
|
|
|
+ ObjectPointers::const_iterator ri = _object_pointers.find(object_id);
|
|
|
+ if (ri == _object_pointers.end()) {
|
|
|
+ PT(TypedWritableReferenceCount) object_ref = (*created_obj._change_this_ref)((TypedWritableReferenceCount *)object, this);
|
|
|
+ TypedWritable *new_ptr = object_ref;
|
|
|
+ created_obj.set_ptr(object_ref, object_ref);
|
|
|
+ created_obj._change_this = NULL;
|
|
|
+ created_obj._change_this_ref = NULL;
|
|
|
+
|
|
|
+ // Remove the pointer from the finalize list (the new
|
|
|
+ // pointer presumably doesn't require finalizing).
|
|
|
+ if (new_ptr != object) {
|
|
|
+ _finalize_list.erase(object);
|
|
|
+ }
|
|
|
+ object = new_ptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (created_obj._change_this != NULL) {
|
|
|
+ // Non-reference-counting variant.
|
|
|
+ ObjectPointers::const_iterator ri = _object_pointers.find(object_id);
|
|
|
+ if (ri == _object_pointers.end()) {
|
|
|
+ TypedWritable *new_ptr = (*created_obj._change_this)(object, this);
|
|
|
+ created_obj.set_ptr(new_ptr, new_ptr->as_reference_count());
|
|
|
+ created_obj._change_this = NULL;
|
|
|
+ created_obj._change_this_ref = NULL;
|
|
|
|
|
|
- // Just some sanity checks
|
|
|
- if (object == (TypedWritable *)NULL) {
|
|
|
- if (bam_cat.is_debug()) {
|
|
|
- bam_cat.debug()
|
|
|
- << "Unable to create an object of type " << type << endl;
|
|
|
+ if (new_ptr != object) {
|
|
|
+ _finalize_list.erase(object);
|
|
|
+ }
|
|
|
+ object = new_ptr;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ _created_objs_by_pointer[created_obj._ptr].push_back(object_id);
|
|
|
|
|
|
- } else if (object->get_type() != type) {
|
|
|
- if (_new_types.find(type) != _new_types.end()) {
|
|
|
- // This was a type we hadn't heard of before, so it's not
|
|
|
- // really surprising we didn't know how to create it.
|
|
|
- // Suppress the warning (make it a debug statement instead).
|
|
|
+ // Just some sanity checks
|
|
|
+ if (object == (TypedWritable *)NULL) {
|
|
|
if (bam_cat.is_debug()) {
|
|
|
+ bam_cat.debug()
|
|
|
+ << "Unable to create an object of type " << type << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (object->get_type() != type) {
|
|
|
+ if (_new_types.find(type) != _new_types.end()) {
|
|
|
+ // This was a type we hadn't heard of before, so it's not
|
|
|
+ // really surprising we didn't know how to create it.
|
|
|
+ // Suppress the warning (make it a debug statement instead).
|
|
|
+ if (bam_cat.is_debug()) {
|
|
|
+ bam_cat.warning()
|
|
|
+ << "Attempted to create a " << type.get_name() \
|
|
|
+ << " but a " << object->get_type() \
|
|
|
+ << " was created instead." << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // This was a normal type that we should have known how to
|
|
|
+ // create. Report the error.
|
|
|
bam_cat.warning()
|
|
|
- << "Attempted to create a " << type.get_name() \
|
|
|
- << " but a " << object->get_type() \
|
|
|
+ << "Attempted to create a " << type.get_name() \
|
|
|
+ << " but a " << object->get_type() \
|
|
|
<< " was created instead." << endl;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
} else {
|
|
|
- // This was a normal type that we should have known how to
|
|
|
- // create. Report the error.
|
|
|
- bam_cat.warning()
|
|
|
- << "Attempted to create a " << type.get_name() \
|
|
|
- << " but a " << object->get_type() \
|
|
|
- << " was created instead." << endl;
|
|
|
- }
|
|
|
-
|
|
|
- } else {
|
|
|
- if (bam_cat.is_spam()) {
|
|
|
- bam_cat.spam()
|
|
|
- << "Read a " << object->get_type() << ": " << (void *)object << "\n";
|
|
|
+ if (bam_cat.is_spam()) {
|
|
|
+ bam_cat.spam()
|
|
|
+ << "Read a " << object->get_type() << ": " << (void *)object << "\n";
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1074,6 +1354,11 @@ resolve_object_pointers(TypedWritable *object,
|
|
|
|
|
|
if (!pref._cycler_pointers.empty()) {
|
|
|
// If we didn't get all the cyclers, we have to wait.
|
|
|
+ if (bam_cat.is_spam()) {
|
|
|
+ bam_cat.spam()
|
|
|
+ << "some cyclers pending: complete_pointers for " << (void *)object
|
|
|
+ << " (" << object->get_type() << ")\n";
|
|
|
+ }
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
@@ -1105,10 +1390,12 @@ resolve_object_pointers(TypedWritable *object,
|
|
|
|
|
|
} else {
|
|
|
const CreatedObj &child_obj = (*oi).second;
|
|
|
- if (child_obj._change_this != NULL) {
|
|
|
+ if (!child_obj._created) {
|
|
|
+ // The child object hasn't yet been created.
|
|
|
+ is_complete = false;
|
|
|
+ } else if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) {
|
|
|
// It's been created, but the pointer might still change.
|
|
|
is_complete = false;
|
|
|
-
|
|
|
} else {
|
|
|
if (require_fully_complete &&
|
|
|
_object_pointers.find(child_id) != _object_pointers.end()) {
|
|
|
@@ -1142,6 +1429,7 @@ resolve_object_pointers(TypedWritable *object,
|
|
|
bam_cat.warning()
|
|
|
<< object->get_type() << " completed " << num_completed
|
|
|
<< " of " << references.size() << " pointers.\n";
|
|
|
+ nassertr(num_completed < (int)references.size(), true);
|
|
|
}
|
|
|
return true;
|
|
|
|
|
|
@@ -1194,7 +1482,7 @@ resolve_cycler_pointers(PipelineCyclerBase *cycler,
|
|
|
|
|
|
} else {
|
|
|
const CreatedObj &child_obj = (*oi).second;
|
|
|
- if (child_obj._change_this != NULL) {
|
|
|
+ if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) {
|
|
|
// It's been created, but the pointer might still change.
|
|
|
is_complete = false;
|
|
|
|
|
|
@@ -1227,6 +1515,7 @@ resolve_cycler_pointers(PipelineCyclerBase *cycler,
|
|
|
bam_cat.warning()
|
|
|
<< "CycleData object completed " << num_completed
|
|
|
<< " of " << references.size() << " pointers.\n";
|
|
|
+ nassertr(num_completed < (int)references.size(), true);
|
|
|
}
|
|
|
return true;
|
|
|
}
|