File: NamespaceManager.cpp

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (255 lines) | stat: -rw-r--r-- 6,379 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#include "NamespaceManager.h"

#include "iregistry.h"
#include "itextstream.h"
#include "string/predicate.h"
#include "gamelib.h"

namespace entity 
{

namespace
{
    const char* const NAME_KEY("name");
}

NamespaceManager::NamespaceManager(SpawnArgs& entity) :
    _namespace(nullptr),
    _entity(entity),
    _updateMutex(false),
    _nameKey(NAME_KEY)
{
    // Attach <self> to the observed entity
    _entity.attachObserver(this);
}

NamespaceManager::~NamespaceManager() 
{
    // Detach <self> from the observed Entity
    _entity.detachObserver(this);

    if (_namespace != nullptr) 
    {
        // We're still attached to a namespace, break the connection
        disconnectNameObservers();
        detachNames();

        setNamespace(nullptr);
    }
}

// Gets/sets the namespace of this named object
void NamespaceManager::setNamespace(INamespace* space) 
{
    _namespace = space;
}

INamespace* NamespaceManager::getNamespace() const 
{
    return _namespace;
}

void NamespaceManager::detachNames() 
{
    if (_namespace == nullptr) return;

    // Detach all names of the observed D3Entity
    detachNameKeys();
}

void NamespaceManager::connectNameObservers() 
{
    if (_namespace == nullptr) return;

    // Setup the keyobservers
    attachKeyObservers();
}

void NamespaceManager::disconnectNameObservers() 
{
    if (_namespace == nullptr) return;

    // Remove all keyobservers
    detachKeyObservers();

    // The namekeyobservers should be gone at this point, all of them
    assert(_nameKeyObservers.empty());
}

std::string NamespaceManager::getName() const
{
    return _entity.getKeyValue(_nameKey);
}

void NamespaceManager::changeName(const std::string& newName) 
{
    // Set the value, this should trigger the nameChanged() event on all observers
    _entity.setKeyValue(_nameKey, newName);
}

void NamespaceManager::onKeyInsert(const std::string& key, EntityKeyValue& value) 
{
    // avoid double-updates when the keyvalue gets updated during this process
    if (_updateMutex) return;

    // Check if the key is relevant
    if (keyIsName(key)) 
    {
        // Key is a "name", remember that one
        _nameKeys.insert(std::make_pair(key, &value));

        // Now, register this key in the namespace, if we have one
        attachKeyToNamespace(key, value);
    }

    // Now setup the keyobservers
    attachKeyObserver(key, value);
}

void NamespaceManager::onKeyErase(const std::string& key, EntityKeyValue& value) 
{
    // avoid double-updates when the keyvalue gets updated during this process
    if (_updateMutex) return;

    // Check if the key is relevant
    if (keyIsName(key)) 
    {
        // Remove the key from the namespace
        detachKeyFromNamespace(key, value);

        // Remove they key from the map
        _nameKeys.erase(key);
    }

    // Remove the keyobserver from this key
    detachKeyObserver(key, value);
}

bool NamespaceManager::keyIsName(const std::string& key) 
{
    // In D3, only "name" spawnargs are actual names
    return key == _nameKey;
}

bool NamespaceManager::keyIsReferringToEntityDef(const std::string& key)
{
    return key == "classname" || string::starts_with(key, "def_");
}

// Freshly attaches all "names" to our namespace
void NamespaceManager::attachNames()
{
    if (!_namespace) return;

    for (const auto& pair : _nameKeys)
    {
        attachKeyToNamespace(pair.first, *pair.second);
    }
}

void NamespaceManager::detachNameKeys() 
{
    for (const auto& pair : _nameKeys)
    {
        detachKeyFromNamespace(pair.first, *pair.second);
    }
}

void NamespaceManager::attachKeyToNamespace(const std::string& key, EntityKeyValue& keyValue)
{
    if (!_namespace) return;

    std::string nameValue = keyValue.get();

    // Check if the name already exists in that namespace
    if (_namespace->nameExists(nameValue))
    {
        // We need to change our name, it seems, acquire a new one (and insert it)
        nameValue = _namespace->addUniqueName(nameValue);

        // Lock this class, to avoid this class from being called twice
        _updateMutex = true;

        // Update the entity keyvalue
        keyValue.assign(nameValue);

        _updateMutex = false;
    }
    // Name is valid and not yet known to this namespace, insert it
    else if (!_namespace->insert(nameValue))
    {
        rError() << "Could not insert name: " << nameValue << " into namespace!\n";
    }
}

void NamespaceManager::detachKeyFromNamespace(const std::string& key, EntityKeyValue& keyValue) 
{
    if (_namespace == nullptr) return;

    // Remove the key from the namespace
    _namespace->erase(keyValue.get());
}

void NamespaceManager::attachKeyObserver(const std::string& key, EntityKeyValue& keyValue)
{
    if (_namespace == nullptr) return;

    if (keyIsName(key))
    {
        // Instantiate a new observer
        auto observer = std::make_shared<NameKeyObserver>(keyValue, _namespace);

        // Store this observer object in the local map
        _nameKeyObservers.insert(std::make_pair(&keyValue, observer));
    }
    else if (!keyIsReferringToEntityDef(key))
    {
        // Instantiate a new observer
        auto observer = std::make_shared<KeyValueObserver>(keyValue, _namespace);

        // Store this observer object in the local map
        _keyValueObservers.insert(std::make_pair(&keyValue, observer));
    }
}

void NamespaceManager::attachKeyObservers()
{
    // May not be called with empty namespace
    assert(_namespace);

    // Traverse the entity
    _entity.forEachEntityKeyValue([this](const std::string& key, EntityKeyValue& value)
    {
        attachKeyObserver(key, value);
    });
}

void NamespaceManager::detachKeyObserver(const std::string& key, EntityKeyValue& keyValue) 
{
    if (_namespace == nullptr) return;

    if (keyIsName(key))
    {
        // Destroy the NameKeyObserver object
        _nameKeyObservers.erase(&keyValue);
    }
    else if (!keyIsReferringToEntityDef(key))
    {
        // Not a name key, destroy the KeyValueObserver
        _keyValueObservers.erase(&keyValue);
    }
}

void NamespaceManager::detachKeyObservers() 
{
    // May not be called with empty namespace
    assert(_namespace);

    // Traverse the entity
    _entity.forEachEntityKeyValue([this](const std::string& key, EntityKeyValue& value)
    {
        detachKeyObserver(key, value);
    });
}

} // namespace entity