• 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

71.68
/server/src/world/world.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 "world.hpp"
23

24
#include <boost/algorithm/string.hpp>
25
#include <boost/uuid/random_generator.hpp>
26
#include <boost/uuid/string_generator.hpp>
27
#include <boost/uuid/uuid_io.hpp>
28

29
#include "worldsaver.hpp"
30

31
#include "../log/log.hpp"
32
#include "../log/logmessageexception.hpp"
33
#include "../utils/datetimestr.hpp"
34
#include "../utils/displayname.hpp"
35
#include "../traintastic/traintastic.hpp"
36

37
#include "../core/method.tpp"
38
#include "../core/objectproperty.tpp"
39
#include "../core/objectvectorproperty.tpp"
40
#include "../core/objectlisttablemodel.hpp"
41
#include "../core/attributes.hpp"
42
#include "../core/abstractvectorproperty.hpp"
43
#include "../core/controllerlist.hpp"
44

45
#include "../hardware/booster/booster.hpp"
46
#include "../hardware/booster/list/boosterlist.hpp"
47
#include "../hardware/input/input.hpp"
48
#include "../hardware/input/monitor/inputmonitor.hpp"
49
#include "../hardware/input/list/inputlist.hpp"
50
#include "../hardware/identification/identification.hpp"
51
#include "../hardware/identification/list/identificationlist.hpp"
52
#include "../hardware/output/keyboard/outputkeyboard.hpp"
53
#include "../hardware/output/list/outputlist.hpp"
54
#include "../hardware/interface/interfacelist.hpp"
55
#include "../hardware/decoder/list/decoderlist.hpp"
56
#include "../hardware/programming/lncv/lncvprogrammer.hpp"
57
#include "../hardware/programming/lncv/lncvprogrammingcontroller.hpp"
58

59
#include "../clock/clock.hpp"
60

61
#include "../board/board.hpp"
62
#include "../board/boardlist.hpp"
63
#include "../board/list/blockrailtilelist.hpp"
64
#include "../board/list/linkrailtilelist.hpp"
65
#include "../board/nx/nxmanager.hpp"
66
#include "../board/tile/rail/nxbuttonrailtile.hpp"
67

68
#include "../zone/zone.hpp"
69
#include "../zone/zonelist.hpp"
70

71
#include "../throttle/list/throttlelist.hpp"
72
#include "../train/train.hpp"
73
#include "../train/trainlist.hpp"
74
#include "../vehicle/rail/railvehiclelist.hpp"
75
#include "../lua/scriptlist.hpp"
76
#include "../status/simulationstatus.hpp"
77
#include "../utils/category.hpp"
78

79
using nlohmann::json;
80

81
constexpr auto decoderListColumns = DecoderListColumn::Id | DecoderListColumn::Name | DecoderListColumn::Interface | DecoderListColumn::Protocol | DecoderListColumn::Address;
82
constexpr auto inputListColumns = InputListColumn::Interface | InputListColumn::Channel | InputListColumn::Address;
83
constexpr auto outputListColumns = OutputListColumn::Interface | OutputListColumn::Channel | OutputListColumn::Address;
84
constexpr auto identificationListColumns = IdentificationListColumn::Id | IdentificationListColumn::Name | IdentificationListColumn::Interface /*| IdentificationListColumn::Channel*/ | IdentificationListColumn::Address;
85
constexpr auto throttleListColumns = ThrottleListColumn::Name | ThrottleListColumn::Train | ThrottleListColumn::Interface;
86

87
template<class T>
88
inline static void deleteAll(T& objectList)
1,664✔
89
{
90
  while(!objectList.empty())
1,923✔
91
  {
92
    if constexpr(std::is_same_v<T, TrainList>)
93
    {
94
      if(objectList.front()->active)
19✔
95
      {
96
        objectList.front()->emergencyStop = true;
9✔
97
        objectList.front()->active = false;
9✔
98
      }
99
    }
100
    if constexpr(std::is_same_v<T, ThrottleList>)
101
    {
102
      auto& throttle = objectList[0];
×
103
      throttle->destroy();
×
104
      objectList.removeObject(throttle);
×
105
    }
106
    else
107
    {
108
      objectList.delete_(objectList.front());
259✔
109
    }
110
  }
111
}
1,664✔
112

113
std::shared_ptr<World> World::create()
208✔
114
{
115
  auto world = std::make_shared<World>(Private());
208✔
116
  init(*world);
208✔
117
  return world;
208✔
118
}
×
119

120
void World::init(World& world)
208✔
121
{
122
  world.decoderControllers.setValueInternal(std::make_shared<ControllerList<DecoderController>>(world, world.decoderControllers.name()));
208✔
123
  world.inputControllers.setValueInternal(std::make_shared<ControllerList<InputController>>(world, world.inputControllers.name()));
208✔
124
  world.outputControllers.setValueInternal(std::make_shared<ControllerList<OutputController>>(world, world.outputControllers.name()));
208✔
125
  world.identificationControllers.setValueInternal(std::make_shared<ControllerList<IdentificationController>>(world, world.identificationControllers.name()));
208✔
126
  world.lncvProgrammingControllers.setValueInternal(std::make_shared<ControllerList<LNCVProgrammingController>>(world, world.lncvProgrammingControllers.name()));
208✔
127
  world.loconetInterfaces.setValueInternal(std::make_shared<ControllerList<LocoNetInterface>>(world, world.loconetInterfaces.name()));
208✔
128

129
  world.interfaces.setValueInternal(std::make_shared<InterfaceList>(world, world.interfaces.name()));
208✔
130
  world.decoders.setValueInternal(std::make_shared<DecoderList>(world, world.decoders.name(), decoderListColumns));
208✔
131
  world.inputs.setValueInternal(std::make_shared<InputList>(world, world.inputs.name(), inputListColumns));
208✔
132
  world.outputs.setValueInternal(std::make_shared<OutputList>(world, world.outputs.name(), outputListColumns));
208✔
133
  world.identifications.setValueInternal(std::make_shared<IdentificationList>(world, world.outputs.name(), identificationListColumns));
208✔
134
  world.boosters.setValueInternal(std::make_shared<BoosterList>(world, world.boosters.name()));
208✔
135
  world.boards.setValueInternal(std::make_shared<BoardList>(world, world.boards.name()));
208✔
136
  world.zones.setValueInternal(std::make_shared<ZoneList>(world, world.zones.name()));
208✔
137
  world.clock.setValueInternal(std::make_shared<Clock>(world, world.clock.name()));
208✔
138
  world.throttles.setValueInternal(std::make_shared<ThrottleList>(world, world.throttles.name(), throttleListColumns));
208✔
139
  world.trains.setValueInternal(std::make_shared<TrainList>(world, world.trains.name()));
208✔
140
  world.railVehicles.setValueInternal(std::make_shared<RailVehicleList>(world, world.railVehicles.name()));
208✔
141
  world.luaScripts.setValueInternal(std::make_shared<Lua::ScriptList>(world, world.luaScripts.name()));
208✔
142

143
  world.blockRailTiles.setValueInternal(std::make_shared<BlockRailTileList>(world, world.blockRailTiles.name()));
208✔
144
  world.linkRailTiles.setValueInternal(std::make_shared<LinkRailTileList>(world, world.linkRailTiles.name()));
208✔
145
  world.nxManager.setValueInternal(std::make_shared<NXManager>(world, world.nxManager.name()));
208✔
146

147
  world.simulationStatus.setValueInternal(std::make_shared<SimulationStatus>(world, world.simulationStatus.name()));
208✔
148
}
208✔
149

150
World::World(Private /*unused*/) :
208✔
151
  uuid{this, "uuid", to_string(boost::uuids::random_generator()()), PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
208✔
152
  name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly},
416✔
153
  scale{this, "scale", WorldScale::H0, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly, [this](WorldScale /*value*/){ updateScaleRatio(); }},
208✔
154
  scaleRatio{this, "scale_ratio", 87, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly},
208✔
155
  onlineWhenLoaded{this, "online_when_loaded", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
156
  powerOnWhenLoaded{this, "power_on_when_loaded", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript,
416✔
157
    [this](bool value)
208✔
158
    {
159
      if(!value)
×
160
      {
161
        runWhenLoaded = false; // can't run without power
×
162
      }
163
    }},
×
164
  runWhenLoaded{this, "run_when_loaded", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript,
416✔
165
    [this](bool value)
208✔
166
    {
167
      if(value)
×
168
      {
169
        powerOnWhenLoaded = true; // can't run without power
×
170
      }
171
    }},
×
172
  correctOutputPosWhenLocked{this, "correct_output_pos_when_locked", true, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
173
  extOutputChangeAction{this, "ext_output_change_action", ExternalOutputChangeAction::EmergencyStopTrain, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
174
  pathReleaseDelay{this, "path_release_delay", 5000, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
175
    featureScripting{this, "feature_scripting", true, PropertyFlags::ReadWrite | PropertyFlags::Store,
416✔
176
    [this](bool value)
208✔
177
    {
NEW
178
      setFeature(WorldFeature::Scripting, value);
×
NEW
179
    }},
×
180
  debugBlockEvents{this, "debug_block_events", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
181
  debugTrainEvents{this, "debug_train_events", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
182
  debugZoneEvents{this, "debug_zone_events", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript},
208✔
183
  decoderControllers{this, "input_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
184
  inputControllers{this, "input_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
185
  outputControllers{this, "output_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
186
  identificationControllers{this, "identification_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
187
  lncvProgrammingControllers{this, "lncv_programming_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
188
  loconetInterfaces{this, "loconet_interfaces", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
189
  interfaces{this, "interfaces", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
190
  decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
191
  inputs{this, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
192
  outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
193
  identifications{this, "identifications", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
194
  boosters{this, "boosters", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
195
  boards{this, "boards", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
208✔
196
  zones{this, "zones", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
208✔
197
  clock{this, "clock", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::Store | PropertyFlags::ScriptReadOnly},
208✔
198
  throttles{this, "throttles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
199
  trains{this, "trains", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
208✔
200
  railVehicles{this, "rail_vehicles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
208✔
201
  luaScripts{this, "lua_scripts", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
202
  blockRailTiles{this, "block_rail_tiles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
203
  linkRailTiles{this, "link_rail_tiles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
204
  nxManager{this, "nx_manager", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
208✔
205
  statuses(*this, "statuses", {}, PropertyFlags::ReadOnly | PropertyFlags::Store),
208✔
206
  hardwareThrottles{this, "hardware_throttles", 0, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::NoScript},
208✔
207
  state{this, "state", WorldState(), PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
208✔
208
  edit{this, "edit", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
416✔
209
    [this](bool value)
208✔
210
    {
211
      if(value)
2✔
212
      {
213
        Log::log(*this, LogMessage::N1010_EDIT_MODE_ENABLED);
1✔
214
        state.setValueInternal(state.value() + WorldState::Edit);
1✔
215
        event(WorldEvent::EditEnabled);
1✔
216
      }
217
      else
218
      {
219
        Log::log(*this, LogMessage::N1011_EDIT_MODE_DISABLED);
1✔
220
        state.setValueInternal(state.value() - WorldState::Edit);
1✔
221
        event(WorldEvent::EditDisabled);
1✔
222
      }
223
    }},
2✔
224
  offline{*this, "offline",
416✔
225
    [this]()
208✔
226
    {
227
      event(WorldEvent::Offline);
×
228
    }},
×
229
  online{*this, "online",
416✔
230
    [this]()
208✔
231
    {
232
      event(WorldEvent::Online);
×
233
    }},
×
234
  powerOff{*this, "power_off", MethodFlags::ScriptCallable,
416✔
235
    [this]()
208✔
236
    {
237
      event(WorldEvent::PowerOff);
×
238
    }},
×
239
  powerOn{*this, "power_on",
416✔
240
    [this]()
208✔
241
    {
242
      event(WorldEvent::PowerOn);
×
243
    }},
×
244
  run{*this, "run",
416✔
245
    [this]()
208✔
246
    {
247
      event(WorldEvent::Run);
8✔
248
    }},
8✔
249
  stop{*this, "stop", MethodFlags::ScriptCallable,
416✔
250
    [this]()
208✔
251
    {
252
      event(WorldEvent::Stop);
2✔
253
    }},
2✔
254
  mute{this, "mute", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
416✔
255
    [this](bool value)
208✔
256
    {
257
      event(value ? WorldEvent::Mute : WorldEvent::Unmute);
×
258
    }},
×
259
  noSmoke{this, "no_smoke", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
416✔
260
    [this](bool value)
208✔
261
    {
262
      event(value ? WorldEvent::NoSmoke : WorldEvent::Smoke);
×
263
    }},
×
264
  simulation{this, "simulation", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
416✔
265
    [this](bool value)
208✔
266
    {
267
      simulationStatus->enabled.setValueInternal(value);
1✔
268
      if(value)
1✔
269
      {
270
        statuses.appendInternal(simulationStatus.value());
1✔
271
      }
272
      else
273
      {
274
        statuses.removeInternal(simulationStatus.value());
×
275
      }
276
      event(value ? WorldEvent::SimulationEnabled : WorldEvent::SimulationDisabled);
1✔
277
    }},
1✔
278
  simulationStatus{this, "simulation_status", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::Internal},
208✔
279
  save{*this, "save", MethodFlags::NoScript,
416✔
280
    [this]()
208✔
281
    {
282
      try
283
      {
284
        // backup world:
285
        const std::filesystem::path worldDir = Traintastic::instance->worldDir();
×
286
        const std::filesystem::path worldBackupDir = Traintastic::instance->worldBackupDir();
×
287

288
        if(!std::filesystem::is_directory(worldBackupDir))
×
289
        {
290
          std::error_code ec;
×
291
          std::filesystem::create_directories(worldBackupDir, ec);
×
292
          if(ec)
×
293
            Log::log(*this, LogMessage::C1007_CREATING_WORLD_BACKUP_DIRECTORY_FAILED_X, ec);
×
294
        }
295

296
        if(std::filesystem::is_directory(worldDir / uuid.value()))
×
297
        {
298
          std::error_code ec;
×
299
          std::filesystem::rename(worldDir / uuid.value(), worldBackupDir / uuid.value() += dateTimeStr(), ec);
×
300
          if(ec)
×
301
            Log::log(*this, LogMessage::C1006_CREATING_WORLD_BACKUP_FAILED_X, ec);
×
302
        }
303

304
        if(std::filesystem::is_regular_file(worldDir / uuid.value() += dotCTW))
×
305
        {
306
          std::error_code ec;
×
307
          std::filesystem::rename(worldDir / uuid.value() += dotCTW, worldBackupDir / uuid.value() += dateTimeStr() += dotCTW, ec);
×
308
          if(ec)
×
309
            Log::log(*this, LogMessage::C1006_CREATING_WORLD_BACKUP_FAILED_X, ec);
×
310
        }
311

312
        // save world:
313
        std::filesystem::path savePath = worldDir / uuid.value();
×
314
        if(!Traintastic::instance->settings->saveWorldUncompressed)
×
315
          savePath += dotCTW;
×
316

317
        WorldSaver saver(*this, savePath);
×
318

319
        if(Traintastic::instance)
×
320
        {
321
          Traintastic::instance->settings->lastWorld = uuid.value();
×
322
          Traintastic::instance->worldList->update(*this, savePath);
×
323
        }
324

325
        Log::log(*this, LogMessage::N1022_SAVED_WORLD_X, name.value());
×
326
      }
×
327
      catch(const std::exception& e)
×
328
      {
329
        Log::log(*this, LogMessage::C1005_SAVING_WORLD_FAILED_X, e);
×
330
      }
×
331
    }}
×
332
  , getObject_{*this, "get_object", MethodFlags::Internal | MethodFlags::ScriptCallable,
416✔
333
      [this](const std::string& objectId)
208✔
334
      {
335
        return getObjectById(objectId);
15✔
336
      }}
337
  , getLNCVProgrammer{*this, "get_lncv_programmer", MethodFlags::NoScript,
208✔
338
      [](const ObjectPtr& interface) -> std::shared_ptr<LNCVProgrammer>
×
339
      {
340
        if(auto controller = std::dynamic_pointer_cast<LNCVProgrammingController>(interface))
×
341
          return std::make_shared<LNCVProgrammer>(*controller);
×
342
        return {};
×
343
      }}
344
  , onEvent{*this, "on_event", EventFlags::Scriptable}
416✔
345
{
346
  Attributes::addDisplayName(uuid, DisplayName::World::uuid);
208✔
347
  m_interfaceItems.add(uuid);
208✔
348
  Attributes::addDisplayName(name, DisplayName::Object::name);
208✔
349
  m_interfaceItems.add(name);
208✔
350
  Attributes::addEnabled(scale, false);
208✔
351
  Attributes::addValues(scale, WorldScaleValues);
208✔
352
  m_interfaceItems.add(scale);
208✔
353
  Attributes::addEnabled(scaleRatio, false);
208✔
354
  Attributes::addMinMax(scaleRatio, 1., 1000.);
208✔
355
  Attributes::addVisible(scaleRatio, false);
208✔
356
  m_interfaceItems.add(scaleRatio);
208✔
357

358
  m_interfaceItems.add(onlineWhenLoaded);
208✔
359
  m_interfaceItems.add(powerOnWhenLoaded);
208✔
360
  m_interfaceItems.add(runWhenLoaded);
208✔
361

362
  Attributes::addCategory(correctOutputPosWhenLocked, Category::trains);
208✔
363
  Attributes::addEnabled(correctOutputPosWhenLocked, true);
208✔
364
  m_interfaceItems.add(correctOutputPosWhenLocked);
208✔
365

366
  Attributes::addCategory(extOutputChangeAction, Category::trains);
208✔
367
  Attributes::addEnabled(extOutputChangeAction, true);
208✔
368
  Attributes::addValues(extOutputChangeAction, extOutputChangeActionValues);
208✔
369
  m_interfaceItems.add(extOutputChangeAction);
208✔
370

371
  Attributes::addCategory(pathReleaseDelay, Category::trains);
208✔
372
  Attributes::addEnabled(pathReleaseDelay, true);
208✔
373
  Attributes::addMinMax(pathReleaseDelay, {0, 15000}); // Up to 15 seconds
208✔
374
  m_interfaceItems.add(pathReleaseDelay);
208✔
375

376
  // Features:
377
  Attributes::addCategory(featureScripting, Category::features);
208✔
378
  m_interfaceItems.add(featureScripting);
208✔
379

380
  // Debug options:
381
  Attributes::addCategory(debugBlockEvents, Category::debug);
208✔
382
  m_interfaceItems.add(debugBlockEvents);
208✔
383
  Attributes::addCategory(debugTrainEvents, Category::debug);
208✔
384
  m_interfaceItems.add(debugTrainEvents);
208✔
385
  Attributes::addCategory(debugZoneEvents, Category::debug);
208✔
386
  m_interfaceItems.add(debugZoneEvents);
208✔
387

388
  Attributes::addObjectEditor(decoderControllers, false);
208✔
389
  m_interfaceItems.add(decoderControllers);
208✔
390
  Attributes::addObjectEditor(inputControllers, false);
208✔
391
  m_interfaceItems.add(inputControllers);
208✔
392
  Attributes::addObjectEditor(outputControllers, false);
208✔
393
  m_interfaceItems.add(outputControllers);
208✔
394
  Attributes::addObjectEditor(identificationControllers, false);
208✔
395
  m_interfaceItems.add(identificationControllers);
208✔
396
  Attributes::addObjectEditor(lncvProgrammingControllers, false);
208✔
397
  m_interfaceItems.add(lncvProgrammingControllers);
208✔
398
  Attributes::addObjectEditor(loconetInterfaces, false);
208✔
399
  m_interfaceItems.add(loconetInterfaces);
208✔
400

401
  Attributes::addObjectEditor(interfaces, false);
208✔
402
  m_interfaceItems.add(interfaces);
208✔
403
  Attributes::addObjectEditor(decoders, false);
208✔
404
  m_interfaceItems.add(decoders);
208✔
405
  Attributes::addObjectEditor(inputs, false);
208✔
406
  m_interfaceItems.add(inputs);
208✔
407
  Attributes::addObjectEditor(outputs, false);
208✔
408
  m_interfaceItems.add(outputs);
208✔
409
  Attributes::addObjectEditor(identifications, false);
208✔
410
  m_interfaceItems.add(identifications);
208✔
411
  Attributes::addObjectEditor(boosters, false);
208✔
412
  m_interfaceItems.add(boosters);
208✔
413
  Attributes::addObjectEditor(throttles, false);
208✔
414
  m_interfaceItems.add(throttles);
208✔
415
  Attributes::addObjectEditor(boards, false);
208✔
416
  m_interfaceItems.add(boards);
208✔
417

418
  Attributes::addObjectEditor(zones, false);
208✔
419
  m_interfaceItems.add(zones);
208✔
420

421
  Attributes::addObjectEditor(clock, false);
208✔
422
  m_interfaceItems.add(clock);
208✔
423
  Attributes::addObjectEditor(trains, false);
208✔
424
  m_interfaceItems.add(trains);
208✔
425
  Attributes::addObjectEditor(railVehicles, false);
208✔
426
  m_interfaceItems.add(railVehicles);
208✔
427
  Attributes::addObjectEditor(luaScripts, false);
208✔
428
  Attributes::addVisible(luaScripts, featureScripting);
208✔
429
  m_interfaceItems.add(luaScripts);
208✔
430

431
  Attributes::addObjectEditor(blockRailTiles, false);
208✔
432
  m_interfaceItems.add(blockRailTiles);
208✔
433

434
  Attributes::addObjectEditor(linkRailTiles, false);
208✔
435
  m_interfaceItems.add(linkRailTiles);
208✔
436
  Attributes::addObjectEditor(nxManager, false);
208✔
437
  m_interfaceItems.add(nxManager);
208✔
438

439
  Attributes::addObjectEditor(statuses, false);
208✔
440
  m_interfaceItems.add(statuses);
208✔
441

442
  Attributes::addObjectEditor(hardwareThrottles, false);
208✔
443
  m_interfaceItems.add(hardwareThrottles);
208✔
444

445
  Attributes::addObjectEditor(state, false);
208✔
446
  m_interfaceItems.add(state);
208✔
447
  Attributes::addObjectEditor(edit, false);
208✔
448
  m_interfaceItems.add(edit);
208✔
449
  Attributes::addObjectEditor(offline, false);
208✔
450
  m_interfaceItems.add(offline);
208✔
451
  Attributes::addObjectEditor(online, false);
208✔
452
  m_interfaceItems.add(online);
208✔
453
  Attributes::addObjectEditor(powerOff, false);
208✔
454
  m_interfaceItems.add(powerOff);
208✔
455
  Attributes::addObjectEditor(powerOn, false);
208✔
456
  m_interfaceItems.add(powerOn);
208✔
457
  Attributes::addObjectEditor(stop, false);
208✔
458
  m_interfaceItems.add(stop);
208✔
459
  Attributes::addObjectEditor(run, false);
208✔
460
  m_interfaceItems.add(run);
208✔
461
  Attributes::addObjectEditor(mute, false);
208✔
462
  m_interfaceItems.add(mute);
208✔
463
  Attributes::addObjectEditor(noSmoke, false);
208✔
464
  m_interfaceItems.add(noSmoke);
208✔
465
  Attributes::addEnabled(simulation, false);
208✔
466
  Attributes::addObjectEditor(simulation, false);
208✔
467
  m_interfaceItems.add(simulation);
208✔
468

469
  m_interfaceItems.add(simulationStatus);
208✔
470

471
  Attributes::addObjectEditor(save, false);
208✔
472
  m_interfaceItems.add(save);
208✔
473

474
  m_interfaceItems.add(getObject_);
208✔
475

476
  Attributes::addObjectEditor(getLNCVProgrammer, false);
208✔
477
  m_interfaceItems.add(getLNCVProgrammer);
208✔
478

479
  m_interfaceItems.add(onEvent);
208✔
480

481
  updateEnabled();
208✔
482
  updateFeatures();
208✔
483
}
208✔
484

485
World::~World()
208✔
486
{
487
  luaScripts->stopAll(); // no surprise event actions during destruction
208✔
488

489
  deleteAll(*interfaces);
208✔
490
  deleteAll(*identifications);
208✔
491
  deleteAll(*boards);
208✔
492
  deleteAll(*zones);
208✔
493
  deleteAll(*throttles);
208✔
494
  deleteAll(*trains);
208✔
495
  deleteAll(*railVehicles);
208✔
496
  deleteAll(*luaScripts);
208✔
497
  luaScripts.setValueInternal(nullptr);
208✔
498
}
208✔
499

500
std::string World::getUniqueId(std::string_view prefix) const
515✔
501
{
502
  std::string uniqueId{prefix};
515✔
503
  uniqueId.append("_");
515✔
504
  uint32_t number = 0;
515✔
505
  do
506
  {
507
    uniqueId.resize(prefix.size() + 1);
659✔
508
    uniqueId.append(std::to_string(++number));
659✔
509
  }
510
  while(isObject(uniqueId));
659✔
511

512
  return uniqueId;
515✔
513
}
×
514

515
bool World::isObject(const std::string& _id) const
659✔
516
{
517
  return m_objects.find(_id) != m_objects.end() || _id == id || _id == Traintastic::id;
659✔
518
}
519

520
ObjectPtr World::getObjectById(const std::string& _id) const
26✔
521
{
522
  auto it = m_objects.find(_id);
26✔
523
  if(it != m_objects.end())
26✔
524
    return it->second.lock();
17✔
525
  if(_id == classId)
9✔
526
    return std::const_pointer_cast<Object>(shared_from_this());
9✔
527
  return ObjectPtr();
×
528
}
529

530
ObjectPtr World::getObjectByPath(std::string_view path) const
11✔
531
{
532
  std::vector<std::string> ids;
11✔
533
  boost::split(ids, path, [](char c){ return c == '.'; });
66✔
534
  auto it = ids.cbegin();
11✔
535

536
  ObjectPtr obj = getObjectById(*it);
11✔
537
  while(obj && ++it != ids.cend())
11✔
538
  {
539
    if(AbstractProperty* property = obj->getProperty(*it); property && property->type() == ValueType::Object)
×
540
      obj = property->toObject();
×
541
    else if(AbstractVectorProperty* vectorProperty = obj->getVectorProperty(*it); vectorProperty && vectorProperty->type() == ValueType::Object)
×
542
    {
543
      obj.reset();
×
544
      const size_t size = vectorProperty->size();
×
545
      for(size_t i = 0; i < size; i++)
×
546
      {
547
        ObjectPtr v = vectorProperty->getObject(i);
×
548
        if(path == v->getObjectId())
×
549
        {
550
          obj = v;
×
551
          it++;
×
552
          break;
×
553
        }
554
      }
×
555
    }
556
    else
557
      obj.reset();
×
558
  }
559
  return obj;
22✔
560
}
11✔
561

562
void World::export_(std::vector<std::byte>& data)
×
563
{
564
  try
565
  {
566
    WorldSaver saver(*this, data);
×
567
    Log::log(*this, LogMessage::N1025_EXPORTED_WORLD_SUCCESSFULLY);
×
568
    //return true;
569
  }
×
570
  catch(const std::exception& e)
×
571
  {
572
    throw LogMessageException(LogMessage::C1010_EXPORTING_WORLD_FAILED_X, e);
×
573
  }
×
574
}
×
575

576
void World::loaded()
3✔
577
{
578
  updateFeatures();
3✔
579
  updateScaleRatio();
3✔
580
  Object::loaded();
3✔
581
}
3✔
582

583
void World::worldEvent(WorldState worldState, WorldEvent worldEvent)
13✔
584
{
585
  Object::worldEvent(worldState, worldEvent);
13✔
586

587
  const bool editState = contains(worldState, WorldState::Edit);
13✔
588
  const bool runState = contains(worldState, WorldState::Run);
13✔
589

590
  Attributes::setEnabled(scale, editState && !runState);
13✔
591
  Attributes::setEnabled(scaleRatio, editState && !runState);
13✔
592

593
  fireEvent(onEvent, worldState, worldEvent);
13✔
594
}
13✔
595

NEW
596
void World::worldFeaturesChanged(const WorldFeatures features, WorldFeature changed)
×
597
{
NEW
598
  Object::worldFeaturesChanged(features, changed);
×
599

NEW
600
  Attributes::setVisible(luaScripts, features[WorldFeature::Scripting]);
×
NEW
601
}
×
602

603
void World::event(const WorldEvent value)
13✔
604
{
605
  // Update state:
606
  switch(value)
13✔
607
  {
608
    case WorldEvent::EditDisabled:
1✔
609
      state.setValueInternal(state.value() - WorldState::Edit);
1✔
610
      break;
1✔
611

612
    case WorldEvent::EditEnabled:
1✔
613
      state.setValueInternal(state.value() + WorldState::Edit);
1✔
614
      break;
1✔
615

616
    case WorldEvent::Offline:
×
617
      Log::log(*this, LogMessage::N1013_COMMUNICATION_DISABLED);
×
618
      state.setValueInternal(state.value() - WorldState::Online);
×
619
      break;
×
620

621
    case WorldEvent::Online:
×
622
      Log::log(*this, LogMessage::N1012_COMMUNICATION_ENABLED);
×
623
      state.setValueInternal(state.value() + WorldState::Online);
×
624
      break;
×
625

626
    case WorldEvent::PowerOff:
×
627
      Log::log(*this, LogMessage::N1015_POWER_OFF);
×
628
      state.setValueInternal(state.value() - WorldState::PowerOn - WorldState::Run);
×
629
      break;
×
630

631
    case WorldEvent::PowerOn:
×
632
      Log::log(*this, LogMessage::N1014_POWER_ON);
×
633
      state.setValueInternal(state.value() + WorldState::PowerOn);
×
634
      break;
×
635

636
    case WorldEvent::Stop:
2✔
637
      Log::log(*this, LogMessage::N1017_STOPPED);
2✔
638
      state.setValueInternal(state.value() - WorldState::Run);
2✔
639
      break;
2✔
640

641
    case WorldEvent::Run:
8✔
642
      Log::log(*this, LogMessage::N1016_RUNNING);
8✔
643
      state.setValueInternal(state.value() + WorldState::PowerOn + WorldState::Run);
8✔
644
      break;
8✔
645

646
    case WorldEvent::Unmute:
×
647
      Log::log(*this, LogMessage::N1019_MUTE_DISABLED);
×
648
      state.setValueInternal(state.value() - WorldState::Mute);
×
649
      break;
×
650

651
    case WorldEvent::Mute:
×
652
      Log::log(*this, LogMessage::N1018_MUTE_ENABLED);
×
653
      state.setValueInternal(state.value() + WorldState::Mute);
×
654
      break;
×
655

656
    case WorldEvent::NoSmoke:
×
657
      Log::log(*this, LogMessage::N1021_SMOKE_DISABLED);
×
658
      state.setValueInternal(state.value() + WorldState::NoSmoke);
×
659
      break;
×
660

661
    case WorldEvent::Smoke:
×
662
      Log::log(*this, LogMessage::N1020_SMOKE_ENABLED);
×
663
      state.setValueInternal(state.value() - WorldState::NoSmoke);
×
664
      break;
×
665

666
    case WorldEvent::SimulationDisabled:
×
667
      Log::log(*this, LogMessage::N1023_SIMULATION_DISABLED);
×
668
      state.setValueInternal(state.value() - WorldState::Simulation);
×
669
      break;
×
670

671
    case WorldEvent::SimulationEnabled:
1✔
672
      Log::log(*this, LogMessage::N1024_SIMULATION_ENABLED);
1✔
673
      state.setValueInternal(state.value() + WorldState::Simulation);
1✔
674
      break;
1✔
675
  }
676

677
  updateEnabled();
13✔
678

679
  const WorldState worldState = state;
13✔
680
  worldEvent(worldState, value);
13✔
681
  for(auto& it : m_objects)
140✔
682
    it.second.lock()->worldEvent(worldState, value);
127✔
683
}
13✔
684

NEW
685
void World::setFeature(WorldFeature feature, bool value)
×
686
{
NEW
687
  if(m_features[feature] != value)
×
688
  {
NEW
689
    m_features.set(feature, value);
×
690

NEW
691
    worldFeaturesChanged(m_features, feature);
×
NEW
692
    for(auto& it : m_objects)
×
693
    {
NEW
694
      it.second.lock()->worldFeaturesChanged(m_features, feature);
×
695
    }
696
  }
NEW
697
}
×
698

699
void World::updateEnabled()
221✔
700
{
701
  const bool isOnline = contains(state.value(), WorldState::Online);
221✔
702
  const bool isPoweredOn = contains(state.value(), WorldState::PowerOn);
221✔
703
  const bool isRunning = contains(state.value(), WorldState::Run);
221✔
704

705
  Attributes::setEnabled(simulation, !isOnline && !isPoweredOn && !isRunning);
221✔
706
}
221✔
707

708
void World::updateFeatures()
211✔
709
{
710
  m_features.set(WorldFeature::Scripting, featureScripting);
211✔
711
  Attributes::setVisible(luaScripts, feature(WorldFeature::Scripting));
211✔
712
}
211✔
713

714
void World::updateScaleRatio()
3✔
715
{
716
  if(scale != WorldScale::Custom)
3✔
717
  {
718
    scaleRatio.setValueInternal(getScaleRatio(scale));
3✔
719
    Attributes::setVisible(scaleRatio, false);
3✔
720
  }
721
  else
722
    Attributes::setVisible(scaleRatio, true);
×
723
}
3✔
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