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

traintastic / traintastic / 25757282194

12 May 2026 07:28PM UTC coverage: 25.55% (-0.02%) from 25.571%
25757282194

push

github

reinder
[test] fix: new WorldSaver options argument

8428 of 32986 relevant lines covered (25.55%)

176.89 hits per line

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

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

23
#include "settings.hpp"
24
#include <fstream>
25
#include <iomanip>
26
#include "../core/attributes.hpp"
27
#include "traintastic.hpp"
28
#include "../network/server.hpp"
29
#include "../log/log.hpp"
30
#include "../os/localtime.hpp"
31
#include "../utils/category.hpp"
32

33
using nlohmann::json;
34

35
namespace {
36

37
constexpr uint8_t autoSaveIntervalDefault = 15;
38

39
constexpr std::array<uint8_t, 5> autoSaveIntervalValues{{
40
  Settings::autoSaveIntervalOff,
41
  5,
42
  15,
43
  30,
44
  60,
45
}};
46

47
constexpr std::array<uint8_t, 1> autoSaveIntervalAliasKeys{{Settings::autoSaveIntervalOff}};
48
constexpr std::array<std::string_view, 1> autoSaveIntervalAliasValues{{"$settings:off$"}};
49

50
}
51

52
Settings::PreStart Settings::getPreStartSettings(const std::filesystem::path& path)
×
53
{
54
  std::ifstream file(path / filename);
×
55
  if(file.is_open())
×
56
  {
57
    try
58
    {
59
      json settings = json::parse(file);
×
60
      PreStart preStart;
×
61
      preStart.memoryLoggerSize = settings.value(Name::memoryLoggerSize, Default::memoryLoggerSize);
×
62
      preStart.enableFileLogger = settings.value(Name::enableFileLogger, Default::enableFileLogger);
×
63
      preStart.language = settings.value(Name::language, Default::language);
×
64
      return preStart;
×
65
    }
×
66
    catch(const std::exception& e)
×
67
    {
68
      Log::log(classId, LogMessage::C1020_LOADING_SETTINGS_FAILED_X, e.what());
×
69
    }
×
70
  }
71
  return {};
×
72
}
×
73

74
Settings::Settings(const std::filesystem::path& path)
×
75
  : m_filename{path / filename}
×
76
#ifndef NO_LOCALHOST_ONLY_SETTING
77
  , localhostOnly{this, "localhost_only", true, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }}
×
78
#endif
79
  , port{this, "port", Server::defaultPort, PropertyFlags::ReadWrite, [this](const uint16_t& /*value*/){ saveToFile(); }}
×
80
  , discoverable{this, "discoverable", true, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }}
×
81
  , language{this, "language", std::string{Default::language}, PropertyFlags::ReadWrite | PropertyFlags::Internal, [this](const std::string& /*value*/){ saveToFile(); }}
×
82
  , lastWorld{this, "last_world", "", PropertyFlags::ReadWrite | PropertyFlags::Internal, [this](const std::string& /*value*/){ saveToFile(); }}
×
83
  , loadLastWorldOnStartup{this, "load_last_world_on_startup", true, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }}
×
84
  , autoSaveWorldOnExit{this, "auto_save_world_on_exit", true, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }}
×
85
  , autoSaveInterval{this, "auto_save_interval", autoSaveIntervalDefault, PropertyFlags::ReadWrite,
×
86
    [this](const bool& /*value*/)
×
87
    {
88
      saveToFile();
×
89
      Traintastic::instance->restartAutoSaveTimer();
×
90
    }}
×
91
  , saveWorldUncompressed{this, "save_world_uncompressed", false, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }}
×
92
  , allowClientServerRestart{this, "allow_client_server_restart", false, PropertyFlags::ReadWrite | PropertyFlags::Internal, [this](const bool& /*value*/){ saveToFile(); }}
×
93
  , allowClientServerShutdown{this, "allow_client_server_shutdown", false, PropertyFlags::ReadWrite | PropertyFlags::Internal, [this](const bool& /*value*/){ saveToFile(); }}
×
94
  , memoryLoggerSize{this, Name::memoryLoggerSize, Default::memoryLoggerSize, PropertyFlags::ReadWrite, [this](const uint32_t& /*value*/){ saveToFile(); }}
×
95
  , enableFileLogger{this, Name::enableFileLogger, Default::enableFileLogger, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }}
×
96
{
97
  m_interfaceItems.add(language);
×
98
  m_interfaceItems.add(lastWorld);
×
99
  m_interfaceItems.add(loadLastWorldOnStartup);
×
100
  m_interfaceItems.add(autoSaveWorldOnExit);
×
101

102
  Attributes::addValues(autoSaveInterval, autoSaveIntervalValues);
×
103
  Attributes::addAliases(autoSaveInterval, std::span<const uint8_t>{autoSaveIntervalAliasKeys}, std::span<const std::string_view>{autoSaveIntervalAliasValues});
×
104
  Attributes::addUnit(autoSaveInterval, "min");
×
105
  m_interfaceItems.add(autoSaveInterval);
×
106

107
#ifndef NO_LOCALHOST_ONLY_SETTING
108
  Attributes::addCategory(localhostOnly, Category::network);
×
109
  m_interfaceItems.add(localhostOnly);
×
110
#endif
111
  Attributes::addCategory(port, Category::network);
×
112
  m_interfaceItems.add(port);
×
113
  Attributes::addCategory(discoverable, Category::network);
×
114
  m_interfaceItems.add(discoverable);
×
115
  Attributes::addCategory(allowClientServerRestart, Category::network);
×
116
  m_interfaceItems.add(allowClientServerRestart);
×
117
  Attributes::addCategory(allowClientServerShutdown, Category::network);
×
118
  m_interfaceItems.add(allowClientServerShutdown);
×
119

120
  Attributes::addCategory(memoryLoggerSize, Category::log);
×
121
  Attributes::addMinMax(memoryLoggerSize, 0U, memoryLoggerSizeMax);
×
122
  m_interfaceItems.add(memoryLoggerSize);
×
123
  Attributes::addCategory(enableFileLogger, Category::log);
×
124
  m_interfaceItems.add(enableFileLogger);
×
125

126
  Attributes::addCategory(saveWorldUncompressed, Category::developer);
×
127
  m_interfaceItems.add(saveWorldUncompressed);
×
128

129
  loadFromFile();
×
130
}
×
131

132
void Settings::loadFromFile()
×
133
{
134
  std::ifstream file(m_filename);
×
135
  if(file.is_open())
×
136
  {
137
    try
138
    {
139
      json settings = json::parse(file);
×
140
      for(auto& [name, value] : settings.items())
×
141
      {
142
        AbstractProperty* property = getProperty(name);
×
143
        if(property)
×
144
          property->loadJSON(value);
×
145
        else
146
          Log::log(*this, LogMessage::W1002_SETTING_X_DOESNT_EXIST, name);
×
147
      }
×
148
      Log::log(*this, LogMessage::N1008_LOADED_SETTINGS);
×
149
    }
×
150
    catch(const std::exception& e)
×
151
    {
152
      Log::log(*this, LogMessage::W1004_SETTING_FILE_EMPTY_OR_CORRUPT_USING_DEFAULTS);
×
153
      saveToFile();
×
154
    }
×
155
  }
156
  else
157
  {
158
    Log::log(*this, LogMessage::I1002_SETTING_FILE_NOT_FOUND_USING_DEFAULTS);
×
159
    saveToFile();
×
160
  }
161
}
×
162

163
void Settings::saveToFile()
×
164
{
165
  // backup settings:
166
  if(std::filesystem::is_regular_file(m_filename))
×
167
  {
168
    const std::filesystem::path backupDir = Traintastic::instance->dataBackupDir();
×
169
    auto dateTimeStr =
170
      []()
×
171
      {
172
        const auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
×
173
        std::stringstream ss;
×
174
        tm tm;
175
        ss << std::put_time(localTime(&now, &tm), "_%Y%m%d_%H%M%S");
×
176
        return ss.str();
×
177
      };
×
178

179
    if(!std::filesystem::is_directory(backupDir))
×
180
    {
181
      std::error_code ec;
×
182
      std::filesystem::create_directories(backupDir, ec);
×
183
      if(ec)
×
184
        Log::log(*this, LogMessage::C1008_CREATING_BACKUP_DIRECTORY_FAILED_X, ec);
×
185
    }
186

187
    {
188
      std::error_code ec;
×
189
      std::filesystem::rename(m_filename, backupDir / (m_filename.stem() += dateTimeStr()) += m_filename.extension(), ec);
×
190
      if(ec)
×
191
        Log::log(*this, LogMessage::C1009_CREATING_SETTING_BACKUP_FAILED_X, ec);
×
192
    }
193
  }
×
194

195
  json settings = json::object();
×
196
  for(const auto& it : m_interfaceItems)
×
197
    if(auto* property = dynamic_cast<AbstractProperty*>(&it.second))
×
198
      settings[std::string{property->name()}] = property->toJSON();
×
199

200
  std::ofstream file(m_filename);
×
201
  if(file.is_open())
×
202
  {
203
    file << settings.dump(2);
×
204
    Log::log(*this, LogMessage::N1009_SAVED_SETTINGS);
×
205
  }
206
  else
207
    Log::log(*this, LogMessage::C1003_CANT_WRITE_TO_SETTINGS_FILE_X, m_filename);
×
208
}
×
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