• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

traintastic / traintastic / 20799539071

07 Jan 2026 11:10PM UTC coverage: 27.22% (+0.07%) from 27.155%
20799539071

push

github

reinder
[world] added scripting feature settings: show/hid scripting related stuff, see #71

53 of 108 new or added lines in 7 files covered. (49.07%)

1 existing line in 1 file now uncovered.

7918 of 29089 relevant lines covered (27.22%)

192.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/server/src/core/object.cpp
1
/**
2
 * This file is part of Traintastic,
3
 * see <https://github.com/traintastic/traintastic>.
4
 *
5
 * Copyright (C) 2019-2026 Reinder Feenstra
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License
9
 * as published by the Free Software Foundation; either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
 */
21

22
#include "object.hpp"
23
#include "idobject.hpp"
24
#include "subobject.hpp"
25
#include "abstractevent.hpp"
26
#include "abstractmethod.hpp"
27
#include "abstractproperty.hpp"
28
#include "abstractobjectproperty.hpp"
29
#include "abstractvectorproperty.hpp"
30
#include "../world/worldfeatures.hpp"
31
#include "../world/worldloader.hpp"
32
#include "../world/worldsaver.hpp"
33

34
Object::Object() :
6,448✔
35
  m_dying{false}
6,448✔
36
{
37
}
6,448✔
38

39
void Object::destroy()
542✔
40
{
41
  assert(!m_dying);
542✔
42
  if(!m_dying)
542✔
43
  {
44
    m_dying = true;
542✔
45
    auto keepAlive = shared_from_this(); // make sure object isn't deleted during destroying
542✔
46
    destroying();
542✔
47
    onDestroying(*this);
542✔
48
  }
542✔
49
}
542✔
50

51
const InterfaceItem* Object::getItem(std::string_view name) const
×
52
{
53
  return m_interfaceItems.find(name);
×
54
}
55

56
InterfaceItem* Object::getItem(std::string_view name)
179✔
57
{
58
  return m_interfaceItems.find(name);
179✔
59
}
60

61
const AbstractMethod* Object::getMethod(std::string_view name) const
×
62
{
63
  return dynamic_cast<const AbstractMethod*>(getItem(name));
×
64
}
65

66
AbstractMethod* Object::getMethod(std::string_view name)
3✔
67
{
68
  return dynamic_cast<AbstractMethod*>(getItem(name));
3✔
69
}
70

71
const AbstractProperty* Object::getProperty(std::string_view name) const
×
72
{
73
  return dynamic_cast<const AbstractProperty*>(getItem(name));
×
74
}
75

76
AbstractProperty* Object::getProperty(std::string_view name)
2✔
77
{
78
  return dynamic_cast<AbstractProperty*>(getItem(name));
2✔
79
}
80

81
const AbstractObjectProperty* Object::getObjectProperty(std::string_view name) const
×
82
{
83
  return dynamic_cast<const AbstractObjectProperty*>(getItem(name));
×
84
}
85

86
AbstractObjectProperty* Object::getObjectProperty(std::string_view name)
×
87
{
88
  return dynamic_cast<AbstractObjectProperty*>(getItem(name));
×
89
}
90

91
const AbstractVectorProperty* Object::getVectorProperty(std::string_view name) const
×
92
{
93
  return dynamic_cast<const AbstractVectorProperty*>(getItem(name));
×
94
}
95

96
AbstractVectorProperty* Object::getVectorProperty(std::string_view name)
2✔
97
{
98
  return dynamic_cast<AbstractVectorProperty*>(getItem(name));
2✔
99
}
100

101
const AbstractEvent* Object::getEvent(std::string_view name) const
×
102
{
103
  return dynamic_cast<const AbstractEvent*>(getItem(name));
×
104
}
105

106
AbstractEvent* Object::getEvent(std::string_view name)
3✔
107
{
108
  return dynamic_cast<AbstractEvent*>(getItem(name));
3✔
109
}
110

111
void Object::load(WorldLoader& loader, const nlohmann::json& data)
15✔
112
{
113
  for(auto& [name, value] : data.items())
132✔
114
    if(auto* baseProperty = dynamic_cast<BaseProperty*>(getItem(name)))
117✔
115
      loadJSON(loader, *baseProperty, value);
112✔
116

117
  // state values (optional):
118
  nlohmann::json state = loader.getState(getObjectId());
15✔
119
  for(auto& [name, value] : state.items())
34✔
120
    if(auto* baseProperty = dynamic_cast<BaseProperty*>(getItem(name)))
19✔
121
      loadJSON(loader, *baseProperty, value);
33✔
122
}
15✔
123

124
void Object::save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& state) const
15✔
125
{
126
  data["class_id"] = getClassId();
15✔
127

128
  for(const auto& item : interfaceItems())
309✔
129
    if(BaseProperty* baseProperty = dynamic_cast<BaseProperty*>(&item.second))
294✔
130
    {
131
      if(baseProperty->isStoreable())
224✔
132
      {
133
        data[std::string{baseProperty->name()}] = toJSON(saver, *baseProperty);
282✔
134
      }
135
      else if(baseProperty->isStateStoreable())
130✔
136
      {
137
        state[std::string{baseProperty->name()}] = toJSON(saver, *baseProperty);
54✔
138
      }
139
    }
140
}
15✔
141

142
void Object::loaded()
78✔
143
{
144
  for(const auto& it : m_interfaceItems)
444✔
145
  {
146
    if(AbstractProperty* property = dynamic_cast<AbstractProperty*>(&it.second);
366✔
147
        property && contains(property->flags(), PropertyFlags::SubObject))
366✔
148
    {
149
      property->toObject()->loaded();
68✔
150
    }
151
    else if(AbstractVectorProperty* vectorProperty = dynamic_cast<AbstractVectorProperty*>(&it.second);
298✔
152
        vectorProperty && contains(vectorProperty->flags(), PropertyFlags::SubObject))
298✔
153
    {
154
      const size_t size = vectorProperty->size();
1✔
155
      for(size_t i = 0; i < size; i++)
2✔
156
        vectorProperty->getObject(i)->loaded();
1✔
157
    }
158
  }
159
}
78✔
160

161
void Object::worldEvent(WorldState state, WorldEvent event)
566✔
162
{
163
  for(const auto& it : m_interfaceItems)
4,230✔
164
  {
165
    if(AbstractProperty* property = dynamic_cast<AbstractProperty*>(&it.second);
3,664✔
166
        property && contains(property->flags(), PropertyFlags::SubObject))
3,664✔
167
    {
168
      if(auto object = property->toObject())
387✔
169
      {
170
        object->worldEvent(state, event);
387✔
171
      }
387✔
172
    }
173
    else if(AbstractVectorProperty* vectorProperty = dynamic_cast<AbstractVectorProperty*>(&it.second);
3,277✔
174
        vectorProperty && contains(vectorProperty->flags(), PropertyFlags::SubObject))
3,277✔
175
    {
176
      const size_t size = vectorProperty->size();
89✔
177
      for(size_t i = 0; i < size; i++)
128✔
178
      {
179
        if(auto object = vectorProperty->getObject(i)) [[likely]]
39✔
180
        {
181
          object->worldEvent(state, event);
39✔
182
        }
39✔
183
      }
184
    }
185
  }
186
}
566✔
187

NEW
188
void Object::worldFeaturesChanged(WorldFeatures features, WorldFeature changed)
×
189
{
NEW
190
  for(const auto& it : m_interfaceItems)
×
191
  {
NEW
192
    if(AbstractProperty* property = dynamic_cast<AbstractProperty*>(&it.second);
×
NEW
193
        property && contains(property->flags(), PropertyFlags::SubObject))
×
194
    {
NEW
195
      if(auto object = property->toObject())
×
196
      {
NEW
197
        object->worldFeaturesChanged(features, changed);
×
NEW
198
      }
×
199
    }
NEW
200
    else if(AbstractVectorProperty* vectorProperty = dynamic_cast<AbstractVectorProperty*>(&it.second);
×
NEW
201
        vectorProperty && contains(vectorProperty->flags(), PropertyFlags::SubObject))
×
202
    {
NEW
203
      const size_t size = vectorProperty->size();
×
NEW
204
      for(size_t i = 0; i < size; i++)
×
205
      {
NEW
206
        if(auto object = vectorProperty->getObject(i)) [[likely]]
×
207
        {
NEW
208
          object->worldFeaturesChanged(features, changed);
×
NEW
209
        }
×
210
      }
211
    }
212
  }
UNCOV
213
}
×
214

215
nlohmann::json Object::toJSON(WorldSaver& saver, const BaseProperty& baseProperty)
112✔
216
{
217
  if(baseProperty.type() == ValueType::Object)
112✔
218
  {
219
    if(const AbstractProperty* property = dynamic_cast<const AbstractProperty*>(&baseProperty))
14✔
220
    {
221
      if(ObjectPtr value = property->toObject())
8✔
222
      {
223
        if(IdObject* idObject = dynamic_cast<IdObject*>(value.get()))
6✔
224
          return idObject->id.toJSON();
1✔
225

226
        if(SubObject* subObject = dynamic_cast<SubObject*>(value.get()))
5✔
227
        {
228
          if((property->flags() & PropertyFlags::SubObject) == PropertyFlags::SubObject)
5✔
229
            return saver.saveObject(value);
5✔
230

231
          return subObject->getObjectId();
×
232
        }
233
      }
8✔
234

235
      return nullptr;
2✔
236
    }
237

238
    if(const AbstractVectorProperty* vectorProperty = dynamic_cast<const AbstractVectorProperty*>(&baseProperty))
6✔
239
    {
240
      nlohmann::json values(nlohmann::json::value_t::array);
6✔
241

242
      for(size_t i = 0; i < vectorProperty->size(); i++)
8✔
243
      {
244
        if(ObjectPtr value = vectorProperty->getObject(i))
2✔
245
        {
246
          if((vectorProperty->flags() & PropertyFlags::SubObject) == PropertyFlags::SubObject)
2✔
247
            values.emplace_back(saver.saveObject(value));
1✔
248
          else
249
            values.emplace_back(value->getObjectId());
1✔
250
        }
251
        else
252
          values.emplace_back(nullptr);
2✔
253
      }
254

255
      return values;
6✔
256
    }
6✔
257
  }
258
  else
259
    return baseProperty.toJSON();
98✔
260

261
  assert(false);
×
262
  return {};
263
}
264

265
void Object::loadJSON(WorldLoader& loader, BaseProperty& baseProperty, const nlohmann::json& value)
115✔
266
{
267
  if(auto* property = dynamic_cast<AbstractProperty*>(&baseProperty))
115✔
268
  {
269
    if(property->type() == ValueType::Object)
109✔
270
    {
271
      if(contains(property->flags(), PropertyFlags::SubObject))
8✔
272
      {
273
        property->toObject()->load(loader, value);
5✔
274
      }
275
      else
276
      {
277
        if(value.is_string())
3✔
278
          property->loadObject(loader.getObject(value.get<std::string_view>()));
1✔
279
        else if(value.is_null())
2✔
280
          property->loadObject(ObjectPtr());
2✔
281
      }
282
    }
283
    else
284
      property->loadJSON(value);
101✔
285
  }
286
  else if(auto* vectorProperty = dynamic_cast<AbstractVectorProperty*>(&baseProperty))
6✔
287
  {
288
    if(vectorProperty->type() == ValueType::Object)
6✔
289
    {
290
      if(contains(vectorProperty->flags(), PropertyFlags::SubObject))
6✔
291
      {
292
        const size_t size = std::min(value.size(), vectorProperty->size());
1✔
293
        for(size_t i = 0; i < size; i++)
2✔
294
          vectorProperty->getObject(i)->load(loader, value[i]);
1✔
295
      }
296
      else
297
      {
298
        std::vector<ObjectPtr> objects;
5✔
299
        objects.reserve(value.size());
5✔
300
        for(const auto& v : value)
6✔
301
        {
302
          assert(v.is_string());
1✔
303
          objects.emplace_back(loader.getObject(v.get<std::string_view>()));
1✔
304
        }
305
        vectorProperty->loadObjects(objects);
5✔
306
      }
5✔
307
    }
308
    else
309
      vectorProperty->loadJSON(value);
×
310
  }
311
}
115✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc