Hi @ferdinand,
Thanks for your reply!
Currently, I can’t use isCloneInit because I’m still developing on the Cinema 4D 2026 SDK. I do plan to migrate to the 2026 version later.
For context — my plugin is essentially a grouping system based on an ID parameter, similar in concept to how:
The Simulation system tracks objects containing simulation tags, or
The Arnold tag groups objects into “trace sets” based on a string ID.
I’m not scanning the scene constantly for updates. Instead, each tag updates itself through a SceneHook when needed, using the following function:
void NexusRegistry::UpdateGroupFromTag(BaseDocument* doc, BaseTag* tag)
{
    if (!tag) return;
    BaseObject* obj = tag->GetObject();
    if (!obj) return;
    BaseContainer* tagContainer = tag->GetDataInstance();
    // Read new values from tag
    String id = tagContainer->GetString(ID);
    Int32 hashedID = HashID(id);
    Bool includeChildren = tagContainer->GetBool(CHILDREN);
    // Handle old ID if changed
    Int32 oldIdHash = tagContainer->GetInt32(NEXUS_TAG_PREV_ID, NOTOK);
    if (oldIdHash != NOTOK && oldIdHash != hashedID)
    {
        auto it = groups.find(oldIdHash);
        if (it != groups.end())
        {
            auto& members = it->second.members;
            members.erase(std::remove_if(members.begin(), members.end(),
                [obj](const NexusMember& m) { return m.object == obj; }),
                members.end());
            it->second.dirtyLevel++;
            if ((Int32)members.size() == 0)
                groups.erase(it);
        }
    }
    // Update new group
    NexusGroup& group = groups[hashedID];
    // Remove duplicates of this object first
    group.members.erase(std::remove_if(group.members.begin(), group.members.end(),
        [obj](const NexusMember& m) { return m.object == obj; }),
        group.members.end());
    group.members.push_back({ obj, includeChildren });
    ((Nexus*)tag)->UpdateInfo(doc, tag);
    // Store current ID for next update
    tagContainer->SetInt32(NEXUS_TAG_PREV_ID, hashedID);
}
To circumvent the tag copy issue, I’m currently deferring registration:
During CopyTo, tags are added to a pending registration list.
Then, in the SceneHook Execute() method, I check for tags that have been attached to a document and process them there.
Here’s that part:
void NexusRegistry::ProcessPendingTags(BaseDocument* doc)
{
    if (pendingRegistration.IsEmpty())
        return;
    Int32 i = 0;
    while (i < pendingRegistration.GetCount())
    {
        BaseTag* tag = pendingRegistration[i];
        BaseObject* op = tag->GetObject();
        if (op)
        {
            UpdateGroupFromTag(doc, tag);
            maxon::ResultRef eraseresult = pendingRegistration.Erase(i, 1);
        }
        else
        {
            i++;
        }
    }
}
EXECUTIONRESULT NexusRegistry::Execute(BaseSceneHook* node, BaseDocument* doc,
                                       BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags)
{
    ProcessPendingTags(doc);
    return EXECUTIONRESULT::OK;
}
Here’s the SceneHook class structure for reference:
struct NexusMember
{
    BaseObject* object = nullptr; // weak ref, don’t own it
    Bool isChild = false;         // "include children" flag
};
struct NexusGroup
{
    Vector color;
    std::vector<String> links;
    std::vector<NexusMember> members;
    Int32 dirtyLevel = 0;
};
class NexusRegistry : public SceneHookData
{
public:
    static NexusRegistry* Get(BaseDocument* doc);
    static NodeData* Alloc();
    virtual Bool Message(GeListNode* node, Int32 type, void* data);
    virtual EXECUTIONRESULT Execute(BaseSceneHook* node, BaseDocument* doc,
                                    BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags);
    std::unordered_map<Int32, NexusGroup> groups;
    maxon::BaseArray<BaseTag*> pendingRegistration;
    void ProcessPendingTags(BaseDocument* doc);
    void RebuildRegistry(BaseDocument* doc);
    void UpdateGroupFromTag(BaseDocument* doc, BaseTag* tag);
    void RemoveObjectFromGroups(BaseObject* obj);
    void RemoveTagFromGroup(BaseTag* tag);
    const NexusGroup* GetGroup(Int32 hash) const;
};
The main purpose of this system is to allow other plugins (e.g., object plugins) to:
Reference tags that share the same ID,
Access their data via the registry, and
Rebuild themselves when the related group’s dirtyLevel changes.
Thanks for your time, and sorry if this looks a bit rough,  I just wanted to show the current design.
If you have any design suggestions or best practices for handling deferred tag registration or object cloning across documents, I’d really appreciate your insights!
 




