Merge branch 'master' into jgrpp

# Conflicts:
#	.github/workflows/ci-build.yml
#	.github/workflows/release-linux.yml
#	.github/workflows/release-macos.yml
#	.github/workflows/release-source.yml
#	.github/workflows/release.yml
#	CMakeLists.txt
#	COMPILING.md
#	src/ai/ai_core.cpp
#	src/ai/ai_gui.cpp
#	src/bridge_gui.cpp
#	src/company_gui.cpp
#	src/console_cmds.cpp
#	src/core/CMakeLists.txt
#	src/core/smallmap_type.hpp
#	src/disaster_vehicle.h
#	src/effectvehicle_base.h
#	src/fontcache.cpp
#	src/game/game_core.cpp
#	src/game/game_gui.cpp
#	src/gamelog.cpp
#	src/gamelog_internal.h
#	src/group_gui.cpp
#	src/linkgraph/linkgraph.h
#	src/misc.cpp
#	src/network/core/config.h
#	src/network/core/udp.cpp
#	src/network/network_chat_gui.cpp
#	src/network/network_content_gui.cpp
#	src/network/network_gui.cpp
#	src/newgrf.cpp
#	src/newgrf_gui.cpp
#	src/newgrf_profiling.cpp
#	src/newgrf_profiling.h
#	src/object_gui.cpp
#	src/openttd.cpp
#	src/openttd.h
#	src/order_gui.cpp
#	src/os/windows/font_win32.cpp
#	src/rail_gui.cpp
#	src/road.cpp
#	src/road_gui.cpp
#	src/saveload/afterload.cpp
#	src/saveload/saveload.h
#	src/script/api/script_controller.cpp
#	src/script/api/script_roadtypelist.cpp
#	src/script/script_config.cpp
#	src/script/script_config.hpp
#	src/script/script_instance.cpp
#	src/script/script_scanner.cpp
#	src/script/squirrel.cpp
#	src/script/squirrel_helper.hpp
#	src/settings_gui.cpp
#	src/settings_internal.h
#	src/settings_type.h
#	src/table/settings/network_private_settings.ini
#	src/timetable_gui.cpp
#	src/vehicle.cpp
#	src/vehicle_base.h
#	src/window_gui.h
This commit is contained in:
Jonathan G Rennison
2023-07-01 01:08:35 +01:00
246 changed files with 2023 additions and 1211 deletions

View File

@@ -307,7 +307,8 @@ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror,
SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror);
const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err);
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err, SQInteger len = -1);
static inline SQRESULT sq_throwerror(HSQUIRRELVM v, const std::string_view err) { return sq_throwerror(v, err.data(), err.size()); }
void sq_reseterror(HSQUIRRELVM v);
void sq_getlasterror(HSQUIRRELVM v);

View File

@@ -930,9 +930,9 @@ void sq_resetobject(HSQOBJECT *po)
po->_unVal.pUserPointer=nullptr;po->_type=OT_NULL;
}
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err)
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err, SQInteger len)
{
v->_lasterror=SQString::Create(_ss(v),err);
v->_lasterror=SQString::Create(_ss(v),err, len);
return -1;
}

View File

@@ -120,9 +120,9 @@ public:
/** Wrapper function for AIScanner::GetUniqueAIInfoList */
static const ScriptInfoList *GetUniqueInfoList();
/** Wrapper function for AIScanner::FindInfo */
static class AIInfo *FindInfo(const char *name, int version, bool force_exact_match);
static class AIInfo *FindInfo(const std::string &name, int version, bool force_exact_match);
/** Wrapper function for AIScanner::FindLibrary */
static class AILibrary *FindLibrary(const char *library, int version);
static class AILibrary *FindLibrary(const std::string &library, int version);
/**
* Rescans all searchpaths for available AIs. If a used AI is no longer

View File

@@ -33,7 +33,7 @@ class AIInfo *AIConfig::GetInfo() const
return static_cast<class AIInfo *>(ScriptConfig::GetInfo());
}
ScriptInfo *AIConfig::FindInfo(const char *name, int version, bool force_exact_match)
ScriptInfo *AIConfig::FindInfo(const std::string &name, int version, bool force_exact_match)
{
return static_cast<ScriptInfo *>(AI::FindInfo(name, version, force_exact_match));
}

View File

@@ -41,7 +41,7 @@ public:
bool ResetInfo(bool force_exact_match);
protected:
ScriptInfo *FindInfo(const char *name, int version, bool force_exact_match) override;
ScriptInfo *FindInfo(const std::string &name, int version, bool force_exact_match) override;
};
#endif /* AI_CONFIG_HPP */

View File

@@ -81,7 +81,7 @@
Backup<CompanyID> cur_company(_current_company, FILE_LINE);
for (const Company *c : Company::Iterate()) {
if (c->is_ai) {
SCOPE_INFO_FMT([&], "AI::GameLoop: %i: %s (v%d)\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
SCOPE_INFO_FMT([&], "AI::GameLoop: %i: %s (v%d)\n", (int)c->index, c->ai_info->GetName().c_str(), c->ai_info->GetVersion());
PerformanceMeasurer framerate((PerformanceElement)(PFE_AI0 + c->index));
cur_company.Change(c->index);
c->ai_instance->GameLoop();
@@ -205,8 +205,8 @@
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
if (_settings_game.ai_config[c] != nullptr && _settings_game.ai_config[c]->HasScript()) {
if (!_settings_game.ai_config[c]->ResetInfo(true)) {
DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
_settings_game.ai_config[c]->Change(nullptr);
DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName().c_str());
_settings_game.ai_config[c]->Change(std::nullopt);
if (Company::IsValidAiID(c)) {
/* The code belonging to an already running AI was deleted. We can only do
* one thing here to keep everything sane and that is kill the AI. After
@@ -222,8 +222,8 @@
}
if (_settings_newgame.ai_config[c] != nullptr && _settings_newgame.ai_config[c]->HasScript()) {
if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
_settings_newgame.ai_config[c]->Change(nullptr);
DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName().c_str());
_settings_newgame.ai_config[c]->Change(std::nullopt);
}
}
}
@@ -307,12 +307,12 @@
return AI::scanner_info->GetUniqueInfoList();
}
/* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
/* static */ AIInfo *AI::FindInfo(const std::string &name, int version, bool force_exact_match)
{
return AI::scanner_info->FindInfo(name, version, force_exact_match);
}
/* static */ AILibrary *AI::FindLibrary(const char *library, int version)
/* static */ AILibrary *AI::FindLibrary(const std::string &library, int version)
{
return AI::scanner_library->FindLibrary(library, version);
}

View File

@@ -181,7 +181,7 @@ struct AIConfigWindow : public Window {
void OnClick(Point pt, int widget, int click_count) override
{
if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_END) {
if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_CONTENT_END) {
if (this->selected_slot == INVALID_COMPANY || AIConfig::GetConfig(this->selected_slot) == nullptr) return;
ShowScriptTextfileWindow((TextfileType)(widget - WID_AIC_TEXTFILE), this->selected_slot);
@@ -282,7 +282,7 @@ struct AIConfigWindow : public Window {
this->SetWidgetDisabledState(WID_AIC_MOVE_UP, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot - 1)));
this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot + 1)));
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, this->selected_slot == INVALID_COMPANY || (AIConfig::GetConfig(this->selected_slot)->GetTextfile(tft, this->selected_slot) == nullptr));
}
}

View File

@@ -15,6 +15,7 @@
#include "../debug.h"
#include "../string_func.h"
#include "../rev.h"
#include "../3rdparty/fmt/format.h"
#include <set>
#include "../safeguards.h"
@@ -23,7 +24,7 @@
* Check if the API version provided by the AI is supported.
* @param api_version The API version as provided by the AI.
*/
static bool CheckAPIVersion(const char *api_version)
static bool CheckAPIVersion(const std::string &api_version)
{
static const std::set<std::string> versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14" };
return versions.find(api_version) != versions.end();
@@ -69,26 +70,26 @@ template <> const char *GetClassName<AIInfo, ScriptType::AI>() { return "AIInfo"
SQInteger res = ScriptInfo::Constructor(vm, info);
if (res != 0) return res;
if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
if (info->engine->MethodExists(info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
} else {
info->min_loadable_version = info->GetVersion();
}
/* When there is an UseAsRandomAI function, call it. */
if (info->engine->MethodExists(*info->SQ_instance, "UseAsRandomAI")) {
if (!info->engine->CallBoolMethod(*info->SQ_instance, "UseAsRandomAI", &info->use_as_random, MAX_GET_OPS)) return SQ_ERROR;
if (info->engine->MethodExists(info->SQ_instance, "UseAsRandomAI")) {
if (!info->engine->CallBoolMethod(info->SQ_instance, "UseAsRandomAI", &info->use_as_random, MAX_GET_OPS)) return SQ_ERROR;
} else {
info->use_as_random = true;
}
/* Try to get the API version the AI is written for. */
if (info->engine->MethodExists(*info->SQ_instance, "GetAPIVersion")) {
if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
if (info->engine->MethodExists(info->SQ_instance, "GetAPIVersion")) {
if (!info->engine->CallStringMethod(info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
if (!CheckAPIVersion(info->api_version)) {
DEBUG(script, 1, "Loading info.nut from (%s.%d): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion());
DEBUG(script, 1, "Loading info.nut from (%s.%d): GetAPIVersion returned invalid version", info->GetName().c_str(), info->GetVersion());
return SQ_ERROR;
}
} else {
info->api_version = stredup("0.7");
info->api_version = "0.7";
}
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
@@ -104,15 +105,11 @@ template <> const char *GetClassName<AIInfo, ScriptType::AI>() { return "AIInfo"
SQUserPointer instance;
sq_getinstanceup(vm, 2, &instance, nullptr);
AIInfo *info = (AIInfo *)instance;
info->api_version = nullptr;
info->api_version = fmt::format("{}.{}", GB(_openttd_newgrf_version, 28, 4), GB(_openttd_newgrf_version, 24, 4));
SQInteger res = ScriptInfo::Constructor(vm, info);
if (res != 0) return res;
char buf[8];
seprintf(buf, lastof(buf), "%d.%d", GB(_openttd_newgrf_version, 28, 4), GB(_openttd_newgrf_version, 24, 4));
info->api_version = stredup(buf);
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
sq_setinstanceup(vm, 2, nullptr);
/* Register the AI to the base system */
@@ -122,16 +119,10 @@ template <> const char *GetClassName<AIInfo, ScriptType::AI>() { return "AIInfo"
AIInfo::AIInfo() :
min_loadable_version(0),
use_as_random(false),
api_version(nullptr)
use_as_random(false)
{
}
AIInfo::~AIInfo()
{
free(this->api_version);
}
bool AIInfo::CanLoadFromVersion(int version) const
{
if (version == -1) return true;
@@ -139,11 +130,6 @@ bool AIInfo::CanLoadFromVersion(int version) const
}
AILibrary::~AILibrary()
{
free(this->category);
}
/* static */ void AILibrary::RegisterAPI(Squirrel *engine)
{
/* Create the AILibrary class, and add the RegisterLibrary function */
@@ -164,7 +150,7 @@ AILibrary::~AILibrary()
}
/* Cache the category */
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
delete library;
return SQ_ERROR;
}

View File

@@ -16,7 +16,6 @@
class AIInfo : public ScriptInfo {
public:
AIInfo();
~AIInfo();
/**
* Register the functions of this class.
@@ -46,19 +45,18 @@ public:
/**
* Get the API version this AI is written for.
*/
const char *GetAPIVersion() const { return this->api_version; }
const std::string &GetAPIVersion() const { return this->api_version; }
private:
int min_loadable_version; ///< The AI can load savegame data if the version is equal or greater than this.
bool use_as_random; ///< Should this AI be used when the user wants a "random AI"?
const char *api_version; ///< API version used by this AI.
std::string api_version; ///< API version used by this AI.
};
/** All static information from an AI library like name, version, etc. */
class AILibrary : public ScriptInfo {
public:
AILibrary() : ScriptInfo(), category(nullptr) {};
~AILibrary();
AILibrary() : ScriptInfo() {};
/**
* Register the functions of this class.
@@ -73,10 +71,10 @@ public:
/**
* Get the category this library is in.
*/
const char *GetCategory() const { return this->category; }
const std::string &GetCategory() const { return this->category; }
private:
const char *category; ///< The category this library is in.
std::string category; ///< The category this library is in.
};
#endif /* AI_INFO_HPP */

View File

@@ -69,7 +69,7 @@ void AIInstance::Died()
if (info != nullptr) {
ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
if (info->GetURL() != nullptr) {
if (!info->GetURL().empty()) {
ScriptLog::Info("Please report the error to the following URL:");
ScriptLog::Info(info->GetURL());
}
@@ -82,12 +82,12 @@ void AIInstance::LoadDummyScript()
Script_CreateDummy(this->engine->GetVM(), STR_ERROR_AI_NO_AI_FOUND, "AI");
}
int AIInstance::GetSetting(const char *name)
int AIInstance::GetSetting(const std::string &name)
{
return AIConfig::GetConfig(_current_company)->GetSetting(name);
}
ScriptInfo *AIInstance::FindLibrary(const char *library, int version)
ScriptInfo *AIInstance::FindLibrary(const std::string &library, int version)
{
return (ScriptInfo *)AI::FindLibrary(library, version);
}

View File

@@ -23,8 +23,8 @@ public:
*/
void Initialize(class AIInfo *info);
int GetSetting(const char *name) override;
ScriptInfo *FindLibrary(const char *library, int version) override;
int GetSetting(const std::string &name) override;
ScriptInfo *FindLibrary(const std::string &library, int version) override;
private:
void RegisterAPI() override;

View File

@@ -93,10 +93,10 @@ AIInfo *AIScannerInfo::SelectRandomAI() const
#undef GetAIInfo
}
AIInfo *AIScannerInfo::FindInfo(const char *name, int version, bool force_exact_match)
AIInfo *AIScannerInfo::FindInfo(const std::string &name, int version, bool force_exact_match)
{
if (this->info_list.size() == 0) return nullptr;
if (name == nullptr) return nullptr;
if (name.empty()) return nullptr;
if (version == -1) {
/* We want to load the latest version of this AI; so find it */
@@ -146,7 +146,7 @@ void AIScannerLibrary::RegisterAPI(class Squirrel *engine)
AILibrary::RegisterAPI(engine);
}
AILibrary *AIScannerLibrary::FindLibrary(const char *library, int version)
AILibrary *AIScannerLibrary::FindLibrary(const std::string &library, int version)
{
/* Internally we store libraries as 'library.version' */
std::string library_name = fmt::format("{}.{}", library, version);

View File

@@ -32,7 +32,7 @@ public:
* @param force_exact_match Only match name+version, never latest.
* @return nullptr if no match found, otherwise the AI that matched.
*/
class AIInfo *FindInfo(const char *name, int version, bool force_exact_match);
class AIInfo *FindInfo(const std::string &name, int version, bool force_exact_match);
/**
* Set the Dummy AI.
@@ -60,7 +60,7 @@ public:
* @param version The version the library should have.
* @return The library if found, nullptr otherwise.
*/
class AILibrary *FindLibrary(const char *library, int version);
class AILibrary *FindLibrary(const std::string &library, int version);
protected:
std::string GetScriptName(ScriptInfo *info) override;

View File

@@ -487,8 +487,8 @@ public:
break;
case WID_AP_AIRPORT_LIST: {
int num_clicked = this->vscroll->GetPosition() + (pt.y - this->GetWidget<NWidgetBase>(widget)->pos_y) / this->line_height;
if (num_clicked >= this->vscroll->GetCount()) break;
int num_clicked = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height);
if (num_clicked == INT_MAX) break;
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked);
if (as->IsAvailable()) this->SelectOtherAirport(num_clicked);
break;

View File

@@ -193,10 +193,10 @@ class ReplaceVehicleWindow : public Window {
this->sel_engine[side] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
if (draw_left) {
EngList_Sort(&list, &EngineNumberSorter);
EngList_Sort(list, &EngineNumberSorter);
} else {
_engine_sort_direction = this->descending_sort_order;
EngList_Sort(&list, _engine_sort_functions[this->window_number][this->sort_criteria]);
EngList_Sort(list, _engine_sort_functions[this->window_number][this->sort_criteria]);
}
this->engines[side].clear();
@@ -617,12 +617,11 @@ public:
} else {
click_side = 1;
}
uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
size_t engine_count = this->engines[click_side].size();
EngineID e = INVALID_ENGINE;
if (i < engine_count) {
const auto &item = this->engines[click_side][i];
const auto it = this->vscroll[click_side]->GetScrolledItemFromWidget(this->engines[click_side], pt.y, this, widget);
if (it != this->engines[click_side].end()) {
const auto &item = *it;
const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
/* toggle folded flag on engine */

View File

@@ -33,7 +33,7 @@ struct BaseConsist {
uint32 vehicle_flags; ///< Used for gradual loading and other miscellaneous things (@see VehicleFlags enum)
virtual ~BaseConsist() {}
virtual ~BaseConsist() = default;
void CopyConsistPropertiesFrom(const BaseConsist *src);
};

View File

@@ -11,7 +11,6 @@
#define BASE_MEDIA_BASE_H
#include "fileio_func.h"
#include "core/smallmap_type.hpp"
#include "gfx_type.h"
#include "textfile_type.h"
#include "textfile_gui.h"

View File

@@ -29,7 +29,7 @@
/** Base methods for 32bpp SSE blitters. */
class Blitter_32bppSSE_Base {
public:
virtual ~Blitter_32bppSSE_Base() {}
virtual ~Blitter_32bppSSE_Base() = default;
struct MapValue {
uint8 m;

View File

@@ -273,7 +273,7 @@ public:
*/
virtual void PostResize() { };
virtual ~Blitter() { }
virtual ~Blitter() = default;
template <typename SetPixelT> void DrawLineGeneric(int x, int y, int x2, int y2, int screen_width, int screen_height, int width, int dash, SetPixelT set_pixel);
};

View File

@@ -100,7 +100,8 @@ public:
{
if (widget == WID_BEM_MESSAGE) {
*size = GetStringBoundingBox(STR_MISSING_GRAPHICS_ERROR);
size->height = GetStringHeight(STR_MISSING_GRAPHICS_ERROR, size->width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
size->width += WidgetDimensions::scaled.frametext.Horizontal();
size->height += WidgetDimensions::scaled.frametext.Vertical();
}
}

View File

@@ -277,9 +277,9 @@ public:
switch (widget) {
default: break;
case WID_BBS_BRIDGE_LIST: {
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BBS_BRIDGE_LIST);
if (i < this->bridges->size()) {
this->BuildBridge(i);
auto it = this->vscroll->GetScrolledItemFromWidget(*this->bridges, pt.y, this, WID_BBS_BRIDGE_LIST);
if (it != this->bridges->end()) {
this->BuildBridge(it - this->bridges->begin());
delete this;
}
break;

View File

@@ -41,6 +41,8 @@
#include "table/strings.h"
#include <optional>
#include "safeguards.h"
/**
@@ -1702,14 +1704,14 @@ struct BuildVehicleWindow : BuildVehicleWindowBase {
/* make engines first, and then wagons, sorted by selected sort_criteria */
_engine_sort_direction = false;
EngList_Sort(&list, TrainEnginesThenWagonsSorter);
EngList_Sort(list, TrainEnginesThenWagonsSorter);
/* and then sort engines */
_engine_sort_direction = this->descending_sort_order;
EngList_SortPartial(&list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
/* and finally sort wagons */
EngList_SortPartial(&list, _engine_sort_functions[0][this->sort_criteria], num_engines, list.size() - num_engines);
EngList_SortPartial(list, _engine_sort_functions[0][this->sort_criteria], num_engines, list.size() - num_engines);
}
/* Figure out what road vehicle EngineIDs to put in the list */
@@ -1833,7 +1835,7 @@ struct BuildVehicleWindow : BuildVehicleWindowBase {
}
_engine_sort_direction = this->descending_sort_order;
EngList_Sort(&this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
this->eng_list.swap(list);
AddChildren(this->eng_list, list, INVALID_ENGINE, 0);
@@ -1860,11 +1862,10 @@ struct BuildVehicleWindow : BuildVehicleWindowBase {
break;
case WID_BV_LIST: {
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
size_t num_items = this->eng_list.size();
EngineID e = INVALID_ENGINE;
if (i < num_items) {
const auto &item = this->eng_list[i];
const auto it = this->vscroll->GetScrolledItemFromWidget(this->eng_list, pt.y, this, WID_BV_LIST);
if (it != this->eng_list.end()) {
const auto &item = *it;
const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
/* toggle folded flag on engine */
@@ -2637,7 +2638,7 @@ struct BuildVehicleWindowTrainAdvanced final : BuildVehicleWindowBase {
/* Sort */
_engine_sort_direction = state.descending_sort_order;
EngList_Sort(&list, sorters[state.sort_criteria]);
EngList_Sort(list, sorters[state.sort_criteria]);
}
/* Generate the list of vehicles */

View File

@@ -759,9 +759,9 @@ private:
return r < 0;
}
void AddChildren(GUIGroupList *source, GroupID parent, int indent)
void AddChildren(GUIGroupList &source, GroupID parent, int indent)
{
for (const Group *g : *source) {
for (const Group *g : source) {
if (g->parent != parent) continue;
this->groups.push_back(g);
this->indents.push_back(indent);
@@ -793,7 +793,7 @@ private:
list.Sort(&GroupNameSorter);
AddChildren(&list, INVALID_GROUP, 0);
AddChildren(list, INVALID_GROUP, 0);
}
this->groups.shrink_to_fit();

View File

@@ -3351,8 +3351,8 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
IConsoleHelp(" Select one or more GRFs for profiling.");
IConsoleHelp("Usage: newgrf_profile unselect <grf-num>...");
IConsoleHelp(" Unselect one or more GRFs from profiling. Use the keyword \"all\" instead of a GRF number to unselect all. Removing an active profiler aborts data collection.");
IConsoleHelp("Usage: newgrf_profile start [<num-days>]");
IConsoleHelp(" Begin profiling all selected GRFs. If a number of days is provided, profiling stops after that many in-game days.");
IConsoleHelp("Usage: 'newgrf_profile start [<num-ticks>]':");
IConsoleHelp(" Begin profiling all selected GRFs. If a number of ticks is provided, profiling stops after that many game ticks. There are 74 ticks in a calendar day.");
IConsoleHelp("Usage: newgrf_profile stop");
IConsoleHelp(" End profiling and write the collected data to CSV files.");
IConsoleHelp("Usage: newgrf_profile abort");
@@ -3433,15 +3433,9 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
if (started > 0) {
IConsolePrintF(CC_DEBUG, "Started profiling for GRFID%s %s", (started > 1) ? "s" : "", grfids.c_str());
if (argc >= 3) {
int days = std::max(atoi(argv[2]), 1);
_newgrf_profile_end_date = _date + days;
char datestrbuf[32]{ 0 };
SetDParam(0, _newgrf_profile_end_date);
GetString(datestrbuf, STR_JUST_DATE_ISO, lastof(datestrbuf));
IConsolePrintF(CC_DEBUG, "Profiling will automatically stop on game date %s", datestrbuf);
} else {
_newgrf_profile_end_date = MAX_DAY;
uint64 ticks = std::max(atoi(argv[2]), 1);
NewGRFProfiler::StartTimer(ticks);
IConsolePrintF(CC_DEBUG, "Profiling will automatically stop after %u ticks.", (uint)ticks);
}
} else if (_newgrf_profilers.empty()) {
IConsolePrintF(CC_WARNING, "No GRFs selected for profiling, did not start.");
@@ -3462,7 +3456,7 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
for (NewGRFProfiler &pr : _newgrf_profilers) {
pr.Abort();
}
_newgrf_profile_end_date = MAX_DAY;
NewGRFProfiler::AbortTimer();
return true;
}

View File

@@ -29,7 +29,6 @@ add_files(
random_func.hpp
serialisation.cpp
serialisation.hpp
smallmap_type.hpp
smallstack_type.hpp
smallvec_type.hpp
tinystring_type.hpp

View File

@@ -86,7 +86,7 @@ class ZeroedMemoryAllocator
{
public:
ZeroedMemoryAllocator() {}
virtual ~ZeroedMemoryAllocator() {}
virtual ~ZeroedMemoryAllocator() = default;
/**
* Memory allocator for a single class instance.

View File

@@ -1,147 +0,0 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file smallmap_type.hpp Simple mapping class targeted for small sets of data. Stored data shall be POD ("Plain Old Data")! */
#ifndef SMALLMAP_TYPE_HPP
#define SMALLMAP_TYPE_HPP
#include "smallvec_type.hpp"
#include <utility>
/**
* Implementation of simple mapping class.
* It has inherited accessors from std::vector().
* @tparam T Key type.
* @tparam U Value type.
* @tparam S Unit of allocation.
*
* @see std::vector
*/
template <typename T, typename U>
struct SmallMap : std::vector<std::pair<T, U> > {
typedef std::pair<T, U> Pair;
typedef Pair *iterator;
typedef const Pair *const_iterator;
/** Creates new SmallMap. Data are initialized in std::vector constructor */
inline SmallMap() { }
/** Data are freed in std::vector destructor */
inline ~SmallMap() { }
/**
* Finds given key in this map
* @param key key to find
* @return &Pair(key, data) if found, this->End() if not
*/
inline typename std::vector<Pair>::const_iterator Find(const T &key) const
{
return std::find_if(std::vector<Pair>::begin(), std::vector<Pair>::end(), [&key](const Pair &pair) { return key == pair.first; });
}
/**
* Finds given key in this map
* @param key key to find
* @return &Pair(key, data) if found, this->End() if not
*/
inline Pair *Find(const T &key)
{
for (uint i = 0; i < std::vector<Pair>::size(); i++) {
if (key == std::vector<Pair>::operator[](i).first) return &std::vector<Pair>::operator[](i);
}
return this->End();
}
inline const Pair *End() const
{
return std::vector<Pair>::data() + std::vector<Pair>::size();
}
inline Pair *End()
{
return std::vector<Pair>::data() + std::vector<Pair>::size();
}
/**
* Tests whether a key is assigned in this map.
* @param key key to test
* @return true iff the item is present
*/
inline bool Contains(const T &key) const
{
return this->Find(key) != std::vector<Pair>::end();
}
/**
* Tests whether a key is assigned in this map.
* @param key key to test
* @return true iff the item is present
*/
inline bool Contains(const T &key)
{
return this->Find(key) != this->End();
}
/**
* Removes given pair from this map
* @param pair pair to remove
* @note it has to be pointer to pair in this map. It is overwritten by the last item.
*/
inline void Erase(Pair *pair)
{
assert(pair >= std::vector<Pair>::data() && pair < this->End());
auto distance = pair - std::vector<Pair>::data();
std::vector<Pair>::erase(std::vector<Pair>::begin() + distance);
}
/**
* Removes given key from this map
* @param key key to remove
* @return true iff the key was found
* @note last item is moved to its place, so don't increase your iterator if true is returned!
*/
inline bool Erase(const T &key)
{
Pair *pair = this->Find(key);
if (pair == this->End()) return false;
this->Erase(pair);
return true;
}
/**
* Adds new item to this map.
* @param key key
* @param data data
* @return true iff the key wasn't already present
*/
inline bool Insert(const T &key, const U &data)
{
if (this->Contains(key)) return false;
std::vector<Pair>::emplace_back(key, data);
return true;
}
/**
* Returns data belonging to this key
* @param key key
* @return data belonging to this key
* @note if this key wasn't present, new entry is created
*/
inline U &operator[](const T &key)
{
for (uint i = 0; i < std::vector<Pair>::size(); i++) {
if (key == std::vector<Pair>::operator[](i).first) return std::vector<Pair>::operator[](i).second;
}
Pair &n = std::vector<Pair>::emplace_back();
n.first = key;
return n.second;
}
};
#endif /* SMALLMAP_TYPE_HPP */

View File

@@ -24,6 +24,7 @@
#include "screenshot.h"
#include "gfx_func.h"
#include "network/network.h"
#include "network/network_survey.h"
#include "language.h"
#include "fontcache.h"
#include "news_gui.h"
@@ -308,12 +309,12 @@ char *CrashLog::LogConfiguration(char *buffer, const char *last) const
if (c->ai_info == nullptr) {
buffer += seprintf(buffer, last, " %2i: Human\n", (int)c->index);
} else {
buffer += seprintf(buffer, last, " %2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
buffer += seprintf(buffer, last, " %2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName().c_str(), c->ai_info->GetVersion());
}
}
if (Game::GetInfo() != nullptr) {
buffer += seprintf(buffer, last, " GS: %s (v%d)\n", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
buffer += seprintf(buffer, last, " GS: %s (v%d)\n", Game::GetInfo()->GetName().c_str(), Game::GetInfo()->GetVersion());
}
buffer += seprintf(buffer, last, "\n");
@@ -1173,6 +1174,10 @@ bool CrashLog::MakeCrashSavegameAndScreenshot() const
printf("Writing crash screenshot failed.\n\n");
}
if (_game_mode == GM_NORMAL) {
_survey.Transmit(NetworkSurveyHandler::Reason::CRASH, true);
}
return ret;
}

View File

@@ -161,7 +161,7 @@ public:
const char *crash_buffer_write = nullptr;
/** Stub destructor to silence some compilers. */
virtual ~CrashLog() {}
virtual ~CrashLog() = default;
char *FillCrashLog(char *buffer, const char *last);
void FlushCrashLogBuffer();

View File

@@ -322,10 +322,6 @@ static void OnNewDay()
SetWindowDirty(WC_STATUS_BAR, 0);
}
if (!_newgrf_profilers.empty() && _newgrf_profile_end_date <= _date) {
NewGRFProfiler::FinishAll();
}
if (_network_server) NetworkServerDailyLoop();
DisasterDailyLoop();

View File

@@ -445,6 +445,8 @@ struct DepotWindow : Window {
DepotGUIAction GetVehicleFromDepotWndPt(int x, int y, const Vehicle **veh, GetDepotVehiclePtData *d) const
{
const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(WID_D_MATRIX);
/* Make X relative to widget. Y is left alone for GetScrolledRowFromWidget(). */
x -= matrix_widget->pos_x;
/* In case of RTL the widgets are swapped as a whole */
if (_current_text_dir == TD_RTL) x = matrix_widget->current_x - x;
@@ -456,12 +458,12 @@ struct DepotWindow : Window {
xm = x % this->resize.step_width;
if (xt >= this->num_columns) return MODE_ERROR;
}
ym = y % this->resize.step_height;
ym = (y - matrix_widget->pos_y) % this->resize.step_height;
uint row = y / this->resize.step_height;
if (row >= this->vscroll->GetCapacity()) return MODE_ERROR;
int row = this->vscroll->GetScrolledRowFromWidget(y, this, WID_D_MATRIX);
if (row == INT_MAX) return MODE_ERROR;
uint pos = ((row + this->vscroll->GetPosition()) * this->num_columns) + xt;
uint pos = (row * this->num_columns) + xt;
if (this->vehicle_list.size() + this->wagon_list.size() <= pos) {
/* Clicking on 'line' / 'block' without a vehicle */
@@ -762,11 +764,9 @@ struct DepotWindow : Window {
void OnClick(Point pt, int widget, int click_count) override
{
switch (widget) {
case WID_D_MATRIX: { // List
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_D_MATRIX);
this->DepotClick(pt.x - nwi->pos_x, pt.y - nwi->pos_y);
case WID_D_MATRIX: // List
this->DepotClick(pt.x, pt.y);
break;
}
case WID_D_BUILD: // Build vehicle
ResetObjectToPlace();
@@ -849,8 +849,7 @@ struct DepotWindow : Window {
GetDepotVehiclePtData gdvp = { nullptr, nullptr };
const Vehicle *v = nullptr;
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_D_MATRIX);
DepotGUIAction mode = this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, &gdvp);
DepotGUIAction mode = this->GetVehicleFromDepotWndPt(pt.x, pt.y, &v, &gdvp);
if (this->type == VEH_TRAIN) v = gdvp.wagon;
@@ -1021,11 +1020,10 @@ struct DepotWindow : Window {
return;
}
NWidgetBase *matrix = this->GetWidget<NWidgetBase>(widget);
const Vehicle *v = nullptr;
GetDepotVehiclePtData gdvp = {nullptr, nullptr};
if (this->GetVehicleFromDepotWndPt(pt.x - matrix->pos_x, pt.y - matrix->pos_y, &v, &gdvp) != MODE_DRAG_VEHICLE) return;
if (this->GetVehicleFromDepotWndPt(pt.x, pt.y, &v, &gdvp) != MODE_DRAG_VEHICLE) return;
VehicleID new_vehicle_over = INVALID_VEHICLE;
if (gdvp.head != nullptr) {
@@ -1058,11 +1056,10 @@ struct DepotWindow : Window {
this->sel = INVALID_VEHICLE;
this->SetDirty();
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_D_MATRIX);
if (this->type == VEH_TRAIN) {
GetDepotVehiclePtData gdvp = { nullptr, nullptr };
if (this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, &gdvp) == MODE_DRAG_VEHICLE && sel != INVALID_VEHICLE) {
if (this->GetVehicleFromDepotWndPt(pt.x, pt.y, &v, &gdvp) == MODE_DRAG_VEHICLE && sel != INVALID_VEHICLE) {
if (gdvp.wagon != nullptr && gdvp.wagon->index == sel && _ctrl_pressed) {
DoCommandP(Vehicle::Get(sel)->tile, Vehicle::Get(sel)->index, true,
CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE));
@@ -1073,7 +1070,7 @@ struct DepotWindow : Window {
ShowVehicleViewWindow(gdvp.head);
}
}
} else if (this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, nullptr) == MODE_DRAG_VEHICLE && v != nullptr && sel == v->index) {
} else if (this->GetVehicleFromDepotWndPt(pt.x, pt.y, &v, nullptr) == MODE_DRAG_VEHICLE && v != nullptr && sel == v->index) {
ShowVehicleViewWindow(v);
}
break;

View File

@@ -33,7 +33,7 @@ public:
*/
virtual void Stop() = 0;
virtual ~Driver() { }
virtual ~Driver() = default;
/** The type of driver */
enum Type {

View File

@@ -323,10 +323,10 @@ void DrawVehicleEngine(int left, int right, int preferred_x, int y, EngineID eng
* @param el list to be sorted
* @param compare function for evaluation of the quicksort
*/
void EngList_Sort(GUIEngineList *el, EngList_SortTypeFunction compare)
void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare)
{
if (el->size() < 2) return;
std::sort(el->begin(), el->end(), compare);
if (el.size() < 2) return;
std::sort(el.begin(), el.end(), compare);
}
/**
@@ -336,11 +336,11 @@ void EngList_Sort(GUIEngineList *el, EngList_SortTypeFunction compare)
* @param begin start of sorting
* @param num_items count of items to be sorted
*/
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, size_t begin, size_t num_items)
void EngList_SortPartial(GUIEngineList &el, EngList_SortTypeFunction compare, size_t begin, size_t num_items)
{
if (num_items < 2) return;
assert(begin < el->size());
assert(begin + num_items <= el->size());
std::sort(el->begin() + begin, el->begin() + begin + num_items, compare);
assert(begin < el.size());
assert(begin + num_items <= el.size());
std::sort(el.begin() + begin, el.begin() + begin + num_items, compare);
}

View File

@@ -32,8 +32,8 @@ struct GUIEngineListItem {
typedef GUIList<GUIEngineListItem, CargoID> GUIEngineList;
typedef bool EngList_SortTypeFunction(const GUIEngineListItem&, const GUIEngineListItem&); ///< argument type for #EngList_Sort.
void EngList_Sort(GUIEngineList *el, EngList_SortTypeFunction compare);
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, size_t begin, size_t num_items);
void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare);
void EngList_SortPartial(GUIEngineList &el, EngList_SortTypeFunction compare, size_t begin, size_t num_items);
StringID GetEngineCategoryName(EngineID engine);
StringID GetEngineInfoString(EngineID engine);

View File

@@ -1259,7 +1259,7 @@ uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool r
* @return the number of found files, i.e. the number of times that
* AddFile returned true.
*/
uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
uint FileScanner::Scan(const char *extension, const std::string &directory, bool recursive)
{
std::string path(directory);
AppendPathSeparator(path);

View File

@@ -41,10 +41,10 @@ protected:
Subdirectory subdir; ///< The current sub directory we are searching through
public:
/** Destruct the proper one... */
virtual ~FileScanner() {}
virtual ~FileScanner() = default;
uint Scan(const char *extension, Subdirectory sd, bool tars = true, bool recursive = true);
uint Scan(const char *extension, const char *directory, bool recursive = true);
uint Scan(const char *extension, const std::string &directory, bool recursive = true);
/**
* Add a file with the given filename.

View File

@@ -412,7 +412,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c
/* Show files */
FiosFileScanner scanner(fop, callback_proc, file_list);
if (subdir == NO_DIRECTORY) {
scanner.Scan(nullptr, _fios_path->c_str(), false);
scanner.Scan(nullptr, *_fios_path, false);
} else {
scanner.Scan(nullptr, subdir, true, true);
}

View File

@@ -23,7 +23,7 @@ enum SaveLoadInvalidateWindowData {
SLIWD_FILTER_CHANGES, ///< The filename filter has changed (via the editbox)
};
typedef SmallMap<uint, CompanyProperties *> CompanyPropertiesMap;
using CompanyPropertiesMap = std::map<uint, std::unique_ptr<CompanyProperties>>;
/**
* Container for loading in mode SL_LOAD_CHECK.

View File

@@ -55,9 +55,6 @@ void LoadCheckData::Clear()
this->current_date = 0;
this->settings = {};
for (auto &pair : this->companies) {
delete pair.second;
}
companies.clear();
GamelogFree(this->gamelog_action, this->gamelog_actions);

View File

@@ -167,14 +167,14 @@ const Sprite *TrueTypeFontCache::GetGlyph(GlyphID key)
const void *TrueTypeFontCache::GetFontTable(uint32 tag, size_t &length)
{
const FontTable::iterator iter = this->font_tables.Find(tag);
if (iter != this->font_tables.data() + this->font_tables.size()) {
const auto iter = this->font_tables.find(tag);
if (iter != this->font_tables.end()) {
length = iter->second.first;
return iter->second.second;
}
const void *result = this->InternalGetFontTable(tag, length);
this->font_tables.Insert(tag, std::pair<size_t, const void *>(length, result));
this->font_tables[tag] = std::pair<size_t, const void *>(length, result);
return result;
}

View File

@@ -10,8 +10,8 @@
#ifndef TRUETYPEFONTCACHE_H
#define TRUETYPEFONTCACHE_H
#include "../core/smallmap_type.hpp"
#include "../fontcache.h"
#include "../3rdparty/cpp-btree/btree_map.h"
static const int MAX_FONT_SIZE = 72; ///< Maximum font size.
@@ -28,7 +28,7 @@ protected:
int req_size; ///< Requested font size.
int used_size; ///< Used font size.
typedef SmallMap<uint32, std::pair<size_t, const void *> > FontTable; ///< Table with font table cache
using FontTable = btree::btree_map<uint32_t, std::pair<size_t, const void *>>; ///< Table with font table cache
FontTable font_tables; ///< Cached font tables.
/** Container for information about a glyph. */

View File

@@ -362,7 +362,7 @@ static const PerformanceElement DISPLAY_ORDER_PFE[PFE_MAX] = {
static const char * GetAIName(int ai_index)
{
if (!Company::IsValidAiID(ai_index)) return "";
return Company::Get(ai_index)->ai_info->GetName();
return Company::Get(ai_index)->ai_info->GetName().c_str();
}
/** @hideinitializer */

View File

@@ -91,9 +91,9 @@ public:
/** Wrapper function for GameScanner::GetUniqueInfoList */
static const ScriptInfoList *GetUniqueInfoList();
/** Wrapper function for GameScannerInfo::FindInfo */
static class GameInfo *FindInfo(const char *name, int version, bool force_exact_match);
static class GameInfo *FindInfo(const std::string &name, int version, bool force_exact_match);
/** Wrapper function for GameScanner::FindLibrary */
static class GameLibrary *FindLibrary(const char *library, int version);
static class GameLibrary *FindLibrary(const std::string &library, int version);
/**
* Get the current active instance.

View File

@@ -32,7 +32,7 @@ class GameInfo *GameConfig::GetInfo() const
return static_cast<class GameInfo *>(ScriptConfig::GetInfo());
}
ScriptInfo *GameConfig::FindInfo(const char *name, int version, bool force_exact_match)
ScriptInfo *GameConfig::FindInfo(const std::string &name, int version, bool force_exact_match)
{
return static_cast<ScriptInfo *>(Game::FindInfo(name, version, force_exact_match));
}

View File

@@ -40,7 +40,7 @@ public:
bool ResetInfo(bool force_exact_match);
protected:
ScriptInfo *FindInfo(const char *name, int version, bool force_exact_match) override;
ScriptInfo *FindInfo(const std::string &name, int version, bool force_exact_match) override;
};
#endif /* GAME_CONFIG_HPP */

View File

@@ -171,8 +171,8 @@
* the GameConfig. If not, remove the Game from the list. */
if (_settings_game.game_config != nullptr && _settings_game.game_config->HasScript()) {
if (!_settings_game.game_config->ResetInfo(true)) {
DEBUG(script, 0, "After a reload, the GameScript by the name '%s' was no longer found, and removed from the list.", _settings_game.game_config->GetName());
_settings_game.game_config->Change(nullptr);
DEBUG(script, 0, "After a reload, the GameScript by the name '%s' was no longer found, and removed from the list.", _settings_game.game_config->GetName().c_str());
_settings_game.game_config->Change(std::nullopt);
if (Game::instance != nullptr) {
delete Game::instance;
Game::instance = nullptr;
@@ -184,8 +184,8 @@
}
if (_settings_newgame.game_config != nullptr && _settings_newgame.game_config->HasScript()) {
if (!_settings_newgame.game_config->ResetInfo(false)) {
DEBUG(script, 0, "After a reload, the GameScript by the name '%s' was no longer found, and removed from the list.", _settings_newgame.game_config->GetName());
_settings_newgame.game_config->Change(nullptr);
DEBUG(script, 0, "After a reload, the GameScript by the name '%s' was no longer found, and removed from the list.", _settings_newgame.game_config->GetName().c_str());
_settings_newgame.game_config->Change(std::nullopt);
}
}
}
@@ -236,12 +236,12 @@
return Game::scanner_info->GetUniqueInfoList();
}
/* static */ GameInfo *Game::FindInfo(const char *name, int version, bool force_exact_match)
/* static */ GameInfo *Game::FindInfo(const std::string &name, int version, bool force_exact_match)
{
return Game::scanner_info->FindInfo(name, version, force_exact_match);
}
/* static */ GameLibrary *Game::FindLibrary(const char *library, int version)
/* static */ GameLibrary *Game::FindLibrary(const std::string &library, int version)
{
return Game::scanner_library->FindLibrary(library, version);
}

View File

@@ -246,7 +246,7 @@ struct GSConfigWindow : public Window {
void OnClick(Point pt, int widget, int click_count) override
{
if (widget >= WID_GSC_TEXTFILE && widget < WID_GSC_TEXTFILE + TFT_END) {
if (widget >= WID_GSC_TEXTFILE && widget < WID_GSC_TEXTFILE + TFT_CONTENT_END) {
if (GameConfig::GetConfig() == nullptr) return;
ShowScriptTextfileWindow((TextfileType)(widget - WID_GSC_TEXTFILE), (CompanyID)OWNER_DEITY);
@@ -273,13 +273,13 @@ struct GSConfigWindow : public Window {
break;
case WID_GSC_SETTINGS: {
Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
int num = (pt.y - r.top) / this->line_height + this->vscroll->GetPosition();
if (num >= (int)this->visible_settings.size()) break;
auto it = this->vscroll->GetScrolledItemFromWidget(this->visible_settings, pt.y, this, widget);
if (it == this->visible_settings.end()) break;
const ScriptConfigItem &config_item = *this->visible_settings[num];
const ScriptConfigItem &config_item = **it;
if (!this->IsEditableItem(config_item)) return;
int num = it - this->visible_settings.begin();
if (this->clicked_row != num) {
this->DeleteChildWindows(WC_QUERY_STRING);
HideDropDownMenu(this);
@@ -289,6 +289,7 @@ struct GSConfigWindow : public Window {
bool bool_item = (config_item.flags & SCRIPTCONFIG_BOOLEAN) != 0;
Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
int x = pt.x - r.left;
if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x;
@@ -413,7 +414,7 @@ struct GSConfigWindow : public Window {
this->SetWidgetDisabledState(WID_GSC_CHANGE, !UserIsAllowedToChangeGameScript() || !IsEditable());
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
this->SetWidgetDisabledState(WID_GSC_TEXTFILE + tft, GameConfig::GetConfig()->GetTextfile(tft, (CompanyID)OWNER_DEITY) == nullptr);
}
this->RebuildVisibleSettings();

View File

@@ -21,7 +21,7 @@
* Check if the API version provided by the Game is supported.
* @param api_version The API version as provided by the Game.
*/
static bool CheckAPIVersion(const char *api_version)
static bool CheckAPIVersion(const std::string &api_version)
{
static const std::set<std::string> versions = { "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14" };
return versions.find(api_version) != versions.end();
@@ -60,22 +60,22 @@ template <> const char *GetClassName<GameInfo, ScriptType::GS>() { return "GSInf
SQInteger res = ScriptInfo::Constructor(vm, info);
if (res != 0) return res;
if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
if (info->engine->MethodExists(info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
} else {
info->min_loadable_version = info->GetVersion();
}
/* When there is an IsSelectable function, call it. */
if (info->engine->MethodExists(*info->SQ_instance, "IsDeveloperOnly")) {
if (!info->engine->CallBoolMethod(*info->SQ_instance, "IsDeveloperOnly", &info->is_developer_only, MAX_GET_OPS)) return SQ_ERROR;
if (info->engine->MethodExists(info->SQ_instance, "IsDeveloperOnly")) {
if (!info->engine->CallBoolMethod(info->SQ_instance, "IsDeveloperOnly", &info->is_developer_only, MAX_GET_OPS)) return SQ_ERROR;
} else {
info->is_developer_only = false;
}
/* Try to get the API version the AI is written for. */
if (!info->CheckMethod("GetAPIVersion")) return SQ_ERROR;
if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallStringMethod(info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
if (!CheckAPIVersion(info->api_version)) {
DEBUG(script, 1, "Loading info.nut from (%s.%d): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion());
DEBUG(script, 1, "Loading info.nut from (%s.%d): GetAPIVersion returned invalid version", info->GetName().c_str(), info->GetVersion());
return SQ_ERROR;
}
@@ -88,16 +88,10 @@ template <> const char *GetClassName<GameInfo, ScriptType::GS>() { return "GSInf
GameInfo::GameInfo() :
min_loadable_version(0),
is_developer_only(false),
api_version(nullptr)
is_developer_only(false)
{
}
GameInfo::~GameInfo()
{
free(this->api_version);
}
bool GameInfo::CanLoadFromVersion(int version) const
{
if (version == -1) return true;
@@ -105,11 +99,6 @@ bool GameInfo::CanLoadFromVersion(int version) const
}
GameLibrary::~GameLibrary()
{
free(this->category);
}
/* static */ void GameLibrary::RegisterAPI(Squirrel *engine)
{
/* Create the GameLibrary class, and add the RegisterLibrary function */
@@ -130,7 +119,7 @@ GameLibrary::~GameLibrary()
}
/* Cache the category */
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
delete library;
return SQ_ERROR;
}

View File

@@ -16,7 +16,6 @@
class GameInfo : public ScriptInfo {
public:
GameInfo();
~GameInfo();
/**
* Register the functions of this class.
@@ -36,21 +35,20 @@ public:
/**
* Get the API version this Game is written for.
*/
const char *GetAPIVersion() const { return this->api_version; }
const std::string &GetAPIVersion() const { return this->api_version; }
bool IsDeveloperOnly() const override { return this->is_developer_only; }
private:
int min_loadable_version; ///< The Game can load savegame data if the version is equal or greater than this.
bool is_developer_only; ///< Is the script selectable by non-developers?
const char *api_version; ///< API version used by this Game.
std::string api_version; ///< API version used by this Game.
};
/** All static information from an Game library like name, version, etc. */
class GameLibrary : public ScriptInfo {
public:
GameLibrary() : ScriptInfo(), category(nullptr) {};
~GameLibrary();
GameLibrary() : ScriptInfo() {};
/**
* Register the functions of this class.
@@ -65,10 +63,10 @@ public:
/**
* Get the category this library is in.
*/
const char *GetCategory() const { return this->category; }
const std::string &GetCategory() const { return this->category; }
private:
const char *category; ///< The category this library is in.
std::string category; ///< The category this library is in.
};
#endif /* GAME_INFO_HPP */

View File

@@ -52,12 +52,12 @@ void GameInstance::RegisterAPI()
if (!this->LoadCompatibilityScripts(this->versionAPI, GAME_DIR)) this->Died();
}
int GameInstance::GetSetting(const char *name)
int GameInstance::GetSetting(const std::string &name)
{
return GameConfig::GetConfig()->GetSetting(name);
}
ScriptInfo *GameInstance::FindLibrary(const char *library, int version)
ScriptInfo *GameInstance::FindLibrary(const std::string &library, int version)
{
return (ScriptInfo *)Game::FindLibrary(library, version);
}
@@ -72,7 +72,7 @@ void GameInstance::Died()
if (info != nullptr) {
ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
if (info->GetURL() != nullptr) {
if (!info->GetURL().empty()) {
ScriptLog::Info("Please report the error to the following URL:");
ScriptLog::Info(info->GetURL());
}

View File

@@ -23,8 +23,8 @@ public:
*/
void Initialize(class GameInfo *info);
int GetSetting(const char *name) override;
ScriptInfo *FindLibrary(const char *library, int version) override;
int GetSetting(const std::string &name) override;
ScriptInfo *FindLibrary(const std::string &library, int version) override;
private:
void RegisterAPI() override;

View File

@@ -33,10 +33,10 @@ void GameScannerInfo::RegisterAPI(class Squirrel *engine)
GameInfo::RegisterAPI(engine);
}
GameInfo *GameScannerInfo::FindInfo(const char *name, int version, bool force_exact_match)
GameInfo *GameScannerInfo::FindInfo(const std::string &name, int version, bool force_exact_match)
{
if (this->info_list.size() == 0) return nullptr;
if (name == nullptr) return nullptr;
if (name.empty()) return nullptr;
if (version == -1) {
/* We want to load the latest version of this Game script; so find it */
@@ -86,7 +86,7 @@ void GameScannerLibrary::RegisterAPI(class Squirrel *engine)
GameLibrary::RegisterAPI(engine);
}
GameLibrary *GameScannerLibrary::FindLibrary(const char *library, int version)
GameLibrary *GameScannerLibrary::FindLibrary(const std::string &library, int version)
{
/* Internally we store libraries as 'library.version' */
std::string library_name = fmt::format("{}.{}", library, version);

View File

@@ -23,7 +23,7 @@ public:
* @param force_exact_match Only match name+version, never latest.
* @return nullptr if no match found, otherwise the game script that matched.
*/
class GameInfo *FindInfo(const char *name, int version, bool force_exact_match);
class GameInfo *FindInfo(const std::string &name, int version, bool force_exact_match);
protected:
std::string GetScriptName(ScriptInfo *info) override;
@@ -44,7 +44,7 @@ public:
* @param version The version the library should have.
* @return The library if found, nullptr otherwise.
*/
class GameLibrary *FindLibrary(const char *library, int version);
class GameLibrary *FindLibrary(const std::string &library, int version);
protected:
std::string GetScriptName(ScriptInfo *info) override;

View File

@@ -16,6 +16,7 @@
#include "debug.h"
#include "date_func.h"
#include "rev.h"
#include "3rdparty/cpp-btree/btree_map.h"
#include <stdarg.h>
@@ -160,7 +161,7 @@ struct GRFPresence{
GRFPresence(const GRFConfig *gc) : gc(gc), was_missing(false) {}
GRFPresence() = default;
};
typedef SmallMap<uint32, GRFPresence> GrfIDMapping;
typedef btree::btree_map<uint32, GRFPresence> GrfIDMapping;
/**
* Prints active gamelog
@@ -249,25 +250,25 @@ void GamelogPrint(GamelogPrintProc *proc)
const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum);
buf += seprintf(buf, lastof(buffer), "Added NewGRF: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfadd.grfid, lc->grfadd.md5sum, gc);
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
if (gm != grf_names.End() && !gm->second.was_missing) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was already added!");
auto gm = grf_names.find(lc->grfrem.grfid);
if (gm != grf_names.end() && !gm->second.was_missing) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was already added!");
grf_names[lc->grfadd.grfid] = gc;
break;
}
case GLCT_GRFREM: {
/* A NewGRF got removed from the game, either manually or by it missing when loading the game. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
auto gm = grf_names.find(lc->grfrem.grfid);
buf += seprintf(buf, lastof(buffer), la->at == GLAT_LOAD ? "Missing NewGRF: " : "Removed NewGRF: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfrem.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) {
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfrem.grfid, nullptr, gm != grf_names.end() ? gm->second.gc : nullptr);
if (gm == grf_names.end()) {
buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
} else {
if (la->at == GLAT_LOAD) {
/* Missing grfs on load are not removed from the configuration */
gm->second.was_missing = true;
} else {
grf_names.Erase(gm);
grf_names.erase(gm);
}
}
break;
@@ -278,38 +279,38 @@ void GamelogPrint(GamelogPrintProc *proc)
const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum);
buf += seprintf(buf, lastof(buffer), "Compatible NewGRF loaded: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfcompat.grfid, lc->grfcompat.md5sum, gc);
if (!grf_names.Contains(lc->grfcompat.grfid)) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
if (grf_names.find(lc->grfcompat.grfid) == grf_names.end()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
grf_names[lc->grfcompat.grfid] = gc;
break;
}
case GLCT_GRFPARAM: {
/* A parameter of a NewGRF got changed after the game was started. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
auto gm = grf_names.find(lc->grfrem.grfid);
buf += seprintf(buf, lastof(buffer), "GRF parameter changed: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfparam.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfparam.grfid, nullptr, gm != grf_names.end() ? gm->second.gc : nullptr);
if (gm == grf_names.end()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
break;
}
case GLCT_GRFMOVE: {
/* The order of NewGRFs got changed, which might cause some other NewGRFs to behave differently. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
auto gm = grf_names.find(lc->grfrem.grfid);
buf += seprintf(buf, lastof(buffer), "GRF order changed: %08X moved %d places %s",
BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfmove.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfmove.grfid, nullptr, gm != grf_names.end() ? gm->second.gc : nullptr);
if (gm == grf_names.end()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
break;
}
case GLCT_GRFBUG: {
/* A specific bug in a NewGRF, that could cause wide spread problems, has been noted during the execution of the game. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
auto gm = grf_names.find(lc->grfrem.grfid);
assert (lc->grfbug.bug == GBUG_VEH_LENGTH);
buf += seprintf(buf, lastof(buffer), "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfbug.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfbug.grfid, nullptr, gm != grf_names.end() ? gm->second.gc : nullptr);
if (gm == grf_names.end()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
break;
}

View File

@@ -58,6 +58,7 @@ bool _check_special_modes;
std::atomic<bool> _exit_game;
GameMode _game_mode;
SwitchMode _switch_mode; ///< The next mainloop command.
std::chrono::steady_clock::time_point _switch_mode_time; ///< The time when the switch mode was requested.
PauseMode _pause_mode;
uint32 _pause_countdown;
Palette _cur_palette;

View File

@@ -103,8 +103,8 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
continue;
}
if (!fontMapping.Contains(buff - buff_begin)) {
fontMapping.Insert(buff - buff_begin, f);
if (fontMapping.count(buff - buff_begin) == 0) {
fontMapping[buff - buff_begin] = f;
}
f = Layouter::GetFont(state.fontsize, state.cur_colour);
}
@@ -112,8 +112,8 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
/* Better safe than sorry. */
*buff = '\0';
if (!fontMapping.Contains(buff - buff_begin)) {
fontMapping.Insert(buff - buff_begin, f);
if (fontMapping.count(buff - buff_begin) == 0) {
fontMapping[buff - buff_begin] = f;
}
line.layout = T::GetParagraphLayout(buff_begin, buff, fontMapping);
line.state_after = state;
@@ -296,12 +296,11 @@ ptrdiff_t Layouter::GetCharAtPosition(int x) const
*/
Font *Layouter::GetFont(FontSize size, TextColour colour)
{
FontColourMap::iterator it = fonts[size].Find(colour);
if (it != fonts[size].End()) return it->second;
FontColourMap::iterator it = fonts[size].find(colour);
if (it != fonts[size].end()) return it->second.get();
Font *f = new Font(size, colour);
fonts[size].emplace_back(colour, f);
return f;
fonts[size][colour] = std::make_unique<Font>(size, colour);
return fonts[size][colour].get();
}
/**
@@ -310,9 +309,6 @@ Font *Layouter::GetFont(FontSize size, TextColour colour)
*/
void Layouter::ResetFontCache(FontSize size)
{
for (auto &pair : fonts[size]) {
delete pair.second;
}
fonts[size].clear();
/* We must reset the linecache since it references the just freed fonts */

View File

@@ -12,7 +12,7 @@
#include "fontcache.h"
#include "gfx_func.h"
#include "core/smallmap_type.hpp"
#include "core/math_func.hpp"
#include <map>
#include <string>
@@ -84,20 +84,20 @@ public:
Font(FontSize size, TextColour colour);
};
/** Mapping from index to font. */
typedef SmallMap<int, Font *> FontMap;
/** Mapping from index to font. The pointer is owned by FontColourMap. */
using FontMap = std::map<int, Font *>;
/**
* Interface to glue fallback and normal layouter into one.
*/
class ParagraphLayouter {
public:
virtual ~ParagraphLayouter() {}
virtual ~ParagraphLayouter() = default;
/** Visual run contains data about the bit of text with the same font. */
class VisualRun {
public:
virtual ~VisualRun() {}
virtual ~VisualRun() = default;
virtual const Font *GetFont() const = 0;
virtual int GetGlyphCount() const = 0;
virtual const GlyphID *GetGlyphs() const = 0;
@@ -109,7 +109,7 @@ public:
/** A single line worth of VisualRuns. */
class Line {
public:
virtual ~Line() {}
virtual ~Line() = default;
virtual int GetLeading() const = 0;
virtual int GetWidth() const = 0;
virtual int CountRuns() const = 0;
@@ -173,7 +173,7 @@ private:
static LineCacheItem &GetCachedParagraphLayout(std::string_view str, const FontState &state);
typedef SmallMap<TextColour, Font *> FontColourMap;
using FontColourMap = std::map<TextColour, std::unique_ptr<Font>>;
static FontColourMap fonts[FS_END];
public:
static Font *GetFont(FontSize size, TextColour colour);

View File

@@ -266,7 +266,7 @@ const ParagraphLayouter::VisualRun &FallbackParagraphLayout::FallbackLine::GetVi
*/
FallbackParagraphLayout::FallbackParagraphLayout(WChar *buffer, int length, FontMap &runs) : buffer_begin(buffer), buffer(buffer), runs(runs)
{
assert(runs.End()[-1].first == length);
assert(runs.rbegin()->first == length);
}
/**
@@ -295,15 +295,15 @@ std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine
if (*this->buffer == '\0') {
/* Only a newline. */
this->buffer = nullptr;
l->emplace_back(this->runs.front().second, this->buffer, 0, 0);
l->emplace_back(this->runs.begin()->second, this->buffer, 0, 0);
return l;
}
int offset = this->buffer - this->buffer_begin;
FontMap::iterator iter = this->runs.data();
FontMap::iterator iter = this->runs.begin();
while (iter->first <= offset) {
iter++;
assert(iter != this->runs.End());
++iter;
assert(iter != this->runs.end());
}
const FontCache *fc = iter->second->fc;
@@ -325,8 +325,8 @@ std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine
if (this->buffer == next_run) {
int w = l->GetWidth();
l->emplace_back(iter->second, begin, this->buffer - begin, w);
iter++;
assert(iter != this->runs.End());
++iter;
assert(iter != this->runs.end());
next_run = this->buffer_begin + iter->first;
begin = this->buffer;

View File

@@ -1349,13 +1349,9 @@ struct PaymentRatesGraphWindow : BaseGraphWindow {
}
case WID_CPR_MATRIX: {
uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX);
if (row >= this->vscroll->GetCount()) return;
for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
if (row-- > 0) continue;
ToggleBit(_legend_excluded_cargo, cs->Index());
auto it = this->vscroll->GetScrolledItemFromWidget(_sorted_standard_cargo_specs, pt.y, this, WID_CPR_MATRIX);
if (it != _sorted_standard_cargo_specs.end()) {
ToggleBit(_legend_excluded_cargo, (*it)->Index());
this->UpdateExcludedData();
this->UpdateCargoExcludingGraphs();
break;

View File

@@ -172,17 +172,15 @@ private:
Money money_last_year;
uint32 occupancy_ratio;
/** return true if group has children */
void AddChildren(GUIGroupList *source, GroupID parent, int indent)
void AddChildren(GUIGroupList &source, GroupID parent, int indent)
{
for (const Group *g : *source) {
for (const Group *g : source) {
if (g->parent != parent) continue;
this->groups.push_back(g);
this->indents.push_back(indent);
if (g->folded) {
/* Test if this group has children at all. If not, the folded flag should be cleared to avoid lingering unfold buttons in the list. */
auto child = std::find_if(source->begin(), source->end(), [g](const Group *child){ return child->parent == g->index; });
bool has_children = child != source->end();
bool has_children = std::any_of(source.begin(), source.end(), [g](const Group *child){ return child->parent == g->index; });
Group::Get(g->index)->folded = has_children;
} else {
AddChildren(source, g->index, indent + 1);
@@ -230,7 +228,7 @@ private:
list.Sort(&GroupNameSorter);
AddChildren(&list, INVALID_GROUP, 0);
AddChildren(list, INVALID_GROUP, 0);
this->groups.shrink_to_fit();
this->groups.RebuildDone();
@@ -761,10 +759,11 @@ public:
break;
case WID_GL_LIST_GROUP: { // Matrix Group
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
if (id_g >= this->groups.size()) return;
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
if (it == this->groups.end()) return;
if (groups[id_g]->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) {
size_t id_g = it - this->groups.begin();
if ((*it)->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) {
/* The group has children, check if the user clicked the fold / unfold button. */
NWidgetCore *group_display = this->GetWidget<NWidgetCore>(widget);
int x = _current_text_dir == TD_RTL ?
@@ -801,10 +800,10 @@ public:
}
case WID_GL_LIST_VEHICLE: { // Matrix Vehicle
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE);
if (id_v >= this->vehgroups.size()) return; // click out of list bound
auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_GL_LIST_VEHICLE);
if (it == this->vehgroups.end()) return; // click out of list bound
const GUIVehicleGroup &vehgroup = this->vehgroups[id_v];
const GUIVehicleGroup &vehgroup = *it;
const Vehicle *v = nullptr;
@@ -937,8 +936,8 @@ public:
break;
case WID_GL_LIST_GROUP: { // Matrix group
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = id_g >= this->groups.size() ? INVALID_GROUP : this->groups[id_g]->index;
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = it == this->groups.end() ? INVALID_GROUP : (*it)->index;
if (this->group_sel != new_g && g->parent != new_g) {
DoCommandP(0, this->group_sel | (1 << 16), new_g, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_SET_PARENT));
@@ -970,8 +969,8 @@ public:
this->group_over = INVALID_GROUP;
this->SetDirty();
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index;
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = it == this->groups.end() ? NEW_GROUP : (*it)->index;
DoCommandP(0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr);
break;
@@ -983,10 +982,10 @@ public:
this->group_over = INVALID_GROUP;
this->SetDirty();
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE);
if (id_v >= this->vehgroups.size()) return; // click out of list bound
auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_GL_LIST_VEHICLE);
if (it == this->vehgroups.end()) return; // click out of list bound
const GUIVehicleGroup &vehgroup = this->vehgroups[id_v];
const GUIVehicleGroup &vehgroup = *it;
switch (this->grouping) {
case GB_NONE: {
const Vehicle *v = vehgroup.GetSingleVehicle();
@@ -1171,8 +1170,8 @@ public:
break;
case WID_GL_LIST_GROUP: { // ... the list of custom groups.
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
new_group_over = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index;
auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
new_group_over = it == this->groups.end() ? NEW_GROUP : (*it)->index;
break;
}

View File

@@ -170,53 +170,41 @@ static void ParseHotkeys(Hotkey *hotkey, const char *value)
* by a '+'.
* @param keycode The keycode to convert to a string.
* @return A string representation of this keycode.
* @note The return value is a static buffer, stredup the result before calling
* this function again.
*/
static const char *KeycodeToString(uint16 keycode)
static std::string KeycodeToString(uint16 keycode)
{
static char buf[32];
buf[0] = '\0';
bool first = true;
std::string str;
if (keycode & WKC_GLOBAL_HOTKEY) {
strecat(buf, "GLOBAL", lastof(buf));
first = false;
str += "GLOBAL";
}
if (keycode & WKC_SHIFT) {
if (!first) strecat(buf, "+", lastof(buf));
strecat(buf, "SHIFT", lastof(buf));
first = false;
if (!str.empty()) str += "+";
str += "SHIFT";
}
if (keycode & WKC_CTRL) {
if (!first) strecat(buf, "+", lastof(buf));
strecat(buf, "CTRL", lastof(buf));
first = false;
if (!str.empty()) str += "+";
str += "CTRL";
}
if (keycode & WKC_ALT) {
if (!first) strecat(buf, "+", lastof(buf));
strecat(buf, "ALT", lastof(buf));
first = false;
if (!str.empty()) str += "+";
str += "ALT";
}
if (keycode & WKC_META) {
if (!first) strecat(buf, "+", lastof(buf));
strecat(buf, "META", lastof(buf));
first = false;
if (!str.empty()) str += "+";
str += "META";
}
if (!first) strecat(buf, "+", lastof(buf));
if (!str.empty()) str += "+";
keycode = keycode & ~WKC_SPECIAL_KEYS;
for (uint i = 0; i < lengthof(_keycode_to_name); i++) {
if (_keycode_to_name[i].keycode == keycode) {
strecat(buf, _keycode_to_name[i].name, lastof(buf));
return buf;
str += _keycode_to_name[i].name;
return str;
}
}
assert(keycode < 128);
char key[2];
key[0] = keycode;
key[1] = '\0';
strecat(buf, key, lastof(buf));
return buf;
str.push_back(keycode);
return str;
}
/**
@@ -224,19 +212,15 @@ static const char *KeycodeToString(uint16 keycode)
* keycodes are attached to the hotkey they are split by a comma.
* @param hotkey The keycodes of this hotkey need to be converted to a string.
* @return A string representation of all keycodes.
* @note The return value is a static buffer, stredup the result before calling
* this function again.
*/
const char *SaveKeycodes(const Hotkey *hotkey)
std::string SaveKeycodes(const Hotkey *hotkey)
{
static char buf[128];
buf[0] = '\0';
std::string str;
for (uint i = 0; i < hotkey->keycodes.size(); i++) {
const char *str = KeycodeToString(hotkey->keycodes[i]);
if (i > 0) strecat(buf, ",", lastof(buf));
strecat(buf, str, lastof(buf));
if (i > 0) str += ",";
str += KeycodeToString(hotkey->keycodes[i]);
}
return buf;
return str;
}
/**

View File

@@ -652,9 +652,9 @@ public:
}
case WID_DPI_MATRIX_WIDGET: {
int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET);
if (y != INT_MAX) { // Is it within the boundaries of available data?
this->selected_type = this->list[y];
auto it = this->vscroll->GetScrolledItemFromWidget(this->list, pt.y, this, WID_DPI_MATRIX_WIDGET);
if (it != this->list.end()) { // Is it within the boundaries of available data?
this->selected_type = *it;
this->UpdateAvailability();
const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
@@ -1764,12 +1764,12 @@ public:
break;
case WID_ID_INDUSTRY_LIST: {
uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.top);
if (p < this->industries.size()) {
auto it = this->vscroll->GetScrolledItemFromWidget(this->industries, pt.y, this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.top);
if (it != this->industries.end()) {
if (_ctrl_pressed) {
ShowExtraViewportWindow(this->industries[p]->location.tile);
ShowExtraViewportWindow((*it)->location.tile);
} else {
ScrollMainWindowToTile(this->industries[p]->location.tile);
ScrollMainWindowToTile((*it)->location.tile);
}
}
break;

View File

@@ -14,8 +14,6 @@
#include <string>
#include <optional>
#include <string>
/** Types of groups */
enum IniGroupType {
IGT_VARIABLES = 0, ///< Values of the form "landscape = hilly".

View File

@@ -17,6 +17,7 @@
#include "genworld.h"
#include "network/network_gui.h"
#include "network/network_content.h"
#include "network/network_survey.h"
#include "landscape_type.h"
#include "landscape.h"
#include "strings_func.h"
@@ -504,7 +505,10 @@ void ShowSelectGameWindow()
static void AskExitGameCallback(Window *w, bool confirmed)
{
if (confirmed) _exit_game = true;
if (confirmed) {
_survey.Transmit(NetworkSurveyHandler::Reason::EXIT, true);
_exit_game = true;
}
}
void AskExitGame()

View File

@@ -40,7 +40,7 @@ public:
/**
* Virtual destructor has to be defined because of virtual Run().
*/
virtual ~DemandHandler() {}
virtual ~DemandHandler() = default;
};
#endif /* DEMANDS_H */

View File

@@ -30,10 +30,6 @@ public:
FlowMapper(bool scale) : scale(scale) {}
virtual void Run(LinkGraphJob &job) const;
/**
* Virtual destructor has to be defined because of virtual Run().
*/
virtual ~FlowMapper() {}
private:
/**

View File

@@ -17,11 +17,6 @@ public:
* @param job Job to be initialized.
*/
virtual void Run(LinkGraphJob &job) const { job.Init(); }
/**
* Virtual destructor has to be defined because of virtual Run().
*/
virtual ~InitHandler() {}
};
#endif /* INIT_H */

View File

@@ -11,7 +11,6 @@
#define LINKGRAPH_H
#include "../core/pool_type.hpp"
#include "../core/smallmap_type.hpp"
#include "../core/bitmath_func.hpp"
#include "../station_base.h"
#include "../cargotype.h"

View File

@@ -29,7 +29,7 @@ public:
/**
* Destroy the handler. Must be given due to virtual Run.
*/
virtual ~ComponentHandler() {}
virtual ~ComponentHandler() = default;
/**
* Run the handler. A link graph handler must not read or write any data

View File

@@ -82,11 +82,6 @@ public:
* @param graph Component to be calculated.
*/
virtual void Run(LinkGraphJob &job) const { Tpass pass(job); }
/**
* Destructor. Has to be given because of virtual Run().
*/
virtual ~MCFHandler() {}
};
#endif /* MCF_H */

View File

@@ -39,9 +39,11 @@
#include "cargopacket.h"
#include "tbtr_template_vehicle_func.h"
#include "event_logs.h"
#include "3rdparty/monocypher/monocypher.h"
#include "safeguards.h"
std::string _savegame_id; ///< Unique ID of the current savegame.
extern TileIndex _cur_tileloop_tile;
extern TileIndex _aux_tileloop_tile;
@@ -66,6 +68,36 @@ void InitializeCheats();
void InitializeNPF();
void InitializeOldNames();
/**
* Generate a unique ID.
*/
std::string GenerateUid(std::string_view subject)
{
extern void NetworkRandomBytesWithFallback(void *buf, size_t n);
extern std::string BytesToHexString(const byte *data, uint length);
uint8 random_bytes[32];
NetworkRandomBytesWithFallback(random_bytes, lengthof(random_bytes));
uint8 digest[16];
crypto_blake2b_ctx ctx;
crypto_blake2b_init (&ctx, lengthof(digest));
crypto_blake2b_update(&ctx, random_bytes, lengthof(random_bytes));
crypto_blake2b_update(&ctx, (const byte *)subject.data(), subject.size());
crypto_blake2b_final (&ctx, digest);
return BytesToHexString(digest, lengthof(digest));
}
/**
* Generate a unique savegame ID.
*/
void GenerateSavegameId()
{
_savegame_id = GenerateUid("OpenTTD Savegame ID");
}
void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings)
{
/* Make sure there isn't any window that can influence anything

View File

@@ -28,6 +28,8 @@ add_files(
network_server.h
network_stun.cpp
network_stun.h
network_survey.cpp
network_survey.h
network_turn.cpp
network_turn.h
network_type.h

View File

@@ -282,7 +282,7 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *
* of course totally unneeded ;) */
if (sockets != nullptr) {
NetworkAddress address(runp->ai_addr, (int)runp->ai_addrlen);
if (sockets->Contains(address)) continue;
if (std::any_of(sockets->begin(), sockets->end(), [&address](const auto &p) { return p.second == address; })) continue;
}
sock = func(runp);
if (sock == INVALID_SOCKET) continue;
@@ -307,7 +307,7 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *
}
NetworkAddress addr(runp->ai_addr, (int)runp->ai_addrlen);
(*sockets)[addr] = sock;
(*sockets)[sock] = addr;
sock = INVALID_SOCKET;
}
freeaddrinfo (ai);

View File

@@ -14,13 +14,13 @@
#include "config.h"
#include "../../company_type.h"
#include "../../string_func.h"
#include "../../core/smallmap_type.hpp"
#include <map>
#include <string>
class NetworkAddress;
typedef std::vector<NetworkAddress> NetworkAddressList; ///< Type for a list of addresses.
typedef SmallMap<NetworkAddress, SOCKET> SocketList; ///< Type for a mapping between address and socket.
using SocketList = std::map<SOCKET, NetworkAddress>; ///< Type for a mapping between address and socket.
/**
* Wrapper for (un)resolved network addresses; there's no reason to transform

View File

@@ -67,3 +67,13 @@ const char *NetworkContentMirrorUriString()
{
return GetEnv("OTTD_CONTENT_MIRROR_URI", "https://binaries.openttd.org/bananas");
}
/**
* Get the URI string for the survey from the environment variable OTTD_SURVEY_URI,
* or when it has not been set a hard coded URI of the production server.
* @return The survey's URI string.
*/
const char *NetworkSurveyUriString()
{
return GetEnv("OTTD_SURVEY_URI", "https://survey-participate.openttd.org/");
}

View File

@@ -16,6 +16,7 @@ const char *NetworkCoordinatorConnectionString();
const char *NetworkStunConnectionString();
const char *NetworkContentServerConnectionString();
const char *NetworkContentMirrorUriString();
const char *NetworkSurveyUriString();
static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP)
static const uint16 NETWORK_STUN_SERVER_PORT = 3975; ///< The default port of the STUN server (TCP)
@@ -27,6 +28,8 @@ static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The d
static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet
static const uint16 UDP_MTU_SHORT = 1400; ///< Number of bytes we can pack in a single UDP packet (conservative)
static const std::string NETWORK_SURVEY_DETAILS_LINK = "https://survey.openttd.org/participate"; ///< Link with more details & privacy statement of the survey.
/*
* Technically a TCP packet could become 64kiB, however the high bit is kept so it becomes possible in the future
* to go to (significantly) larger packets if needed. This would entail a strategy such as employed for UTF-8.
@@ -47,6 +50,7 @@ static const uint16 COMPAT_MTU = 1460; ///< Numbe
static const byte NETWORK_GAME_ADMIN_VERSION = 3; ///< What version of the admin network do we use?
static const byte NETWORK_GAME_INFO_VERSION = 6; ///< What version of game-info do we use?
static const byte NETWORK_COORDINATOR_VERSION = 6; ///< What version of game-coordinator-protocol do we use?
static const byte NETWORK_SURVEY_VERSION = 1; ///< What version of the survey do we use?
static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0'
static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0'

View File

@@ -48,7 +48,7 @@ public:
NetworkSocketHandler() { this->has_quit = false; }
/** Close the socket when destructing the socket handler */
virtual ~NetworkSocketHandler() {}
virtual ~NetworkSocketHandler() = default;
/**
* Mark the connection as closed.

View File

@@ -9,6 +9,7 @@
#include "../../stdafx.h"
#include "../../debug.h"
#include "../../core/alloc_func.hpp"
#include "address.h"
#include "../../safeguards.h"

View File

@@ -14,6 +14,8 @@
#include "tcp.h"
constexpr int HTTP_429_TOO_MANY_REQUESTS = 429;
/** Callback for when the HTTP handler has something to tell us. */
struct HTTPCallback {
/**
@@ -40,7 +42,7 @@ struct HTTPCallback {
virtual bool IsCancelled() const = 0;
/** Silentium */
virtual ~HTTPCallback() {}
virtual ~HTTPCallback() = default;
};
/** Base socket handler for HTTP traffic. */

View File

@@ -117,6 +117,7 @@ void HttpThread()
/* Reset to default settings. */
curl_easy_reset(curl);
curl_slist *headers = nullptr;
if (_debug_net_level >= 5) {
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
@@ -147,8 +148,16 @@ void HttpThread()
/* Prepare POST body and URI. */
if (!request->data.empty()) {
/* When the payload starts with a '{', it is a JSON payload. */
if (StrStartsWith(request->data, "{")) {
headers = curl_slist_append(headers, "Content-Type: application/json");
} else {
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
}
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->data.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
}
curl_easy_setopt(curl, CURLOPT_URL, request->uri.c_str());
@@ -175,11 +184,17 @@ void HttpThread()
/* Perform the request. */
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
if (res == CURLE_OK) {
Debug(net, 1, "HTTP request succeeded");
request->callback->OnReceiveData(nullptr, 0);
} else {
Debug(net, (request->callback->IsCancelled() || _http_thread_exit) ? 1 : 0, "HTTP request failed: {}", curl_easy_strerror(res));
long status_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
/* No need to be verbose about rate limiting. */
Debug(net, (request->callback->IsCancelled() || _http_thread_exit || status_code == HTTP_429_TOO_MANY_REQUESTS) ? 1 : 0, "HTTP request failed: status_code: {}, error: {}", status_code, curl_easy_strerror(res));
request->callback->OnFailure();
}
}

View File

@@ -131,7 +131,8 @@ void NetworkHTTPRequest::WinHttpCallback(DWORD code, void *info, DWORD length)
/* If there is any error, we simply abort the request. */
if (status_code >= 400) {
Debug(net, 0, "HTTP request failed: status-code {}", status_code);
/* No need to be verbose about rate limiting. */
Debug(net, status_code == HTTP_429_TOO_MANY_REQUESTS ? 1 : 0, "HTTP request failed: status-code {}", status_code);
this->finished = true;
this->callback->OnFailure();
return;
@@ -242,7 +243,9 @@ void NetworkHTTPRequest::Connect()
if (data.empty()) {
WinHttpSendRequest(this->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, reinterpret_cast<DWORD_PTR>(this));
} else {
WinHttpSendRequest(this->request, L"Content-Type: application/x-www-form-urlencoded\r\n", -1, const_cast<char *>(data.c_str()), static_cast<DWORD>(data.size()), static_cast<DWORD>(data.size()), reinterpret_cast<DWORD_PTR>(this));
/* When the payload starts with a '{', it is a JSON payload. */
LPCWSTR content_type = StrStartsWith(data, "{") ? L"Content-Type: application/json\r\n" : L"Content-Type: application/x-www-form-urlencoded\r\n";
WinHttpSendRequest(this->request, content_type, -1, const_cast<char *>(data.c_str()), static_cast<DWORD>(data.size()), static_cast<DWORD>(data.size()), reinterpret_cast<DWORD_PTR>(this));
}
}

View File

@@ -565,7 +565,7 @@ public:
* @param status The reason the connection got closed.
*/
virtual NetworkRecvStatus CloseConnection(NetworkRecvStatus status) = 0;
virtual ~NetworkGameSocketHandler() {}
virtual ~NetworkGameSocketHandler() = default;
/**
* Sets the client info for this socket handler.

View File

@@ -114,7 +114,7 @@ public:
/* take care of listener port */
for (auto &s : sockets) {
FD_SET(s.second, &read_fd);
FD_SET(s.first, &read_fd);
}
tv.tv_sec = tv.tv_usec = 0; // don't block at all.
@@ -122,7 +122,7 @@ public:
/* accept clients.. */
for (auto &s : sockets) {
if (FD_ISSET(s.second, &read_fd)) AcceptClient(s.second);
if (FD_ISSET(s.first, &read_fd)) AcceptClient(s.first);
}
/* read stuff from clients */
@@ -164,7 +164,7 @@ public:
static void CloseListeners()
{
for (auto &s : sockets) {
closesocket(s.second);
closesocket(s.first);
}
sockets.clear();
DEBUG(net, 5, "[%s] Closed listeners", Tsocket::GetName());

View File

@@ -61,7 +61,7 @@ bool NetworkUDPSocketHandler::Listen()
void NetworkUDPSocketHandler::CloseSocket()
{
for (auto &s : this->sockets) {
closesocket(s.second);
closesocket(s.first);
}
this->sockets.clear();
}
@@ -113,20 +113,20 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
NetworkAddress send(*recv);
/* Not the same type */
if (!send.IsFamily(s.first.GetAddress()->ss_family)) continue;
if (!send.IsFamily(s.second.GetAddress()->ss_family)) continue;
p->PrepareToSend();
if (broadcast) {
/* Enable broadcast */
unsigned long val = 1;
if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
if (setsockopt(s.first, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
DEBUG(net, 1, "Setting broadcast mode failed: %s", NetworkError::GetLast().AsString());
}
}
/* Send the buffer */
ssize_t res = p->TransferOut<int>(sendto, s.second, 0, (const struct sockaddr *)send.GetAddress(), send.GetAddressLength());
ssize_t res = p->TransferOut<int>(sendto, s.first, 0, (const struct sockaddr *)send.GetAddress(), send.GetAddressLength());
DEBUG(net, 7, "sendto(%s)", NetworkAddressDumper().GetAddressAsString(&send));
/* Check for any errors, but ignore it otherwise */
@@ -151,8 +151,8 @@ void NetworkUDPSocketHandler::ReceivePackets()
socklen_t client_len = sizeof(client_addr);
/* Try to receive anything */
SetNonBlocking(s.second); // Some OSes seem to lose the non-blocking status of the socket
ssize_t nbytes = p.TransferIn<int>(recvfrom, s.second, 0, (struct sockaddr *)&client_addr, &client_len);
SetNonBlocking(s.first); // Some OSes seem to lose the non-blocking status of the socket
ssize_t nbytes = p.TransferIn<int>(recvfrom, s.first, 0, (struct sockaddr *)&client_addr, &client_len);
/* Did we get the bytes for the base header of the packet? */
if (nbytes <= 0) break; // No data, i.e. no packet

View File

@@ -102,6 +102,8 @@ static_assert((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_CHARS
/** The amount of clients connected */
byte _network_clients_connected = 0;
extern std::string GenerateUid(std::string_view subject);
/**
* Return whether there is any client connected or trying to connect at all.
* @return whether we have any client activity
@@ -1298,24 +1300,7 @@ void NetworkGameLoop()
static void NetworkGenerateServerId()
{
Md5 checksum;
uint8 digest[16];
char hex_output[16 * 2 + 1];
char coding_string[NETWORK_NAME_LENGTH];
int di;
seprintf(coding_string, lastof(coding_string), "%d%s", (uint)Random(), "OpenTTD Server ID");
/* Generate the MD5 hash */
checksum.Append((const uint8*)coding_string, strlen(coding_string));
checksum.Finish(digest);
for (di = 0; di < 16; ++di) {
seprintf(hex_output + di * 2, lastof(hex_output), "%02x", digest[di]);
}
/* _settings_client.network.network_id is our id */
_settings_client.network.network_id = hex_output;
_settings_client.network.network_id = GenerateUid("OpenTTD Server ID");
}
std::string BytesToHexString(const byte *data, uint length)

View File

@@ -21,6 +21,7 @@
#include "network.h"
#include "network_client.h"
#include "network_base.h"
#include "../3rdparty/fmt/format.h"
#include "../widgets/network_chat_widget.h"
@@ -28,6 +29,7 @@
#include <stdarg.h> /* va_list */
#include <deque>
#include <optional>
#include "../safeguards.h"
@@ -326,18 +328,16 @@ struct NetworkChatWindow : public Window {
* Find the next item of the list of things that can be auto-completed.
* @param item The current indexed item to return. This function can, and most
* likely will, alter item, to skip empty items in the arrays.
* @return Returns the char that matched to the index.
* @return Returns the view that matched to the index.
*/
const char *ChatTabCompletionNextItem(uint *item)
std::optional<std::string> ChatTabCompletionNextItem(uint *item)
{
static char chat_tab_temp_buffer[64];
/* First, try clients */
if (*item < MAX_CLIENT_SLOTS) {
/* Skip inactive clients */
for (NetworkClientInfo *ci : NetworkClientInfo::Iterate(*item)) {
*item = ci->index;
return ci->client_name.c_str();
return ci->client_name;
}
*item = MAX_CLIENT_SLOTS;
}
@@ -349,12 +349,11 @@ struct NetworkChatWindow : public Window {
for (const Town *t : Town::Iterate(*item - MAX_CLIENT_SLOTS)) {
/* Get the town-name via the string-system */
SetDParam(0, t->index);
GetString(chat_tab_temp_buffer, STR_TOWN_NAME, lastof(chat_tab_temp_buffer));
return &chat_tab_temp_buffer[0];
return GetString(STR_TOWN_NAME);
}
}
return nullptr;
return std::nullopt;
}
/**
@@ -362,13 +361,14 @@ struct NetworkChatWindow : public Window {
* the word right from that as to complete. It also writes a \0 at the
* position of the space (if any). If nothing found, buf is returned.
*/
static char *ChatTabCompletionFindText(char *buf)
static std::string_view ChatTabCompletionFindText(std::string_view &buf)
{
char *p = strrchr(buf, ' ');
if (p == nullptr) return buf;
auto it = buf.find_last_of(' ');
if (it == std::string_view::npos) return buf;
*p = '\0';
return p + 1;
std::string_view res = buf.substr(it + 1);
buf.remove_suffix(res.size() + 1);
return res;
}
/**
@@ -376,46 +376,44 @@ struct NetworkChatWindow : public Window {
*/
void ChatTabCompletion()
{
static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH];
assert(this->message_editbox.text.max_bytes == lengthof(_chat_tab_completion_buf));
static std::string _chat_tab_completion_buf;
Textbuf *tb = &this->message_editbox.text;
size_t len, tb_len;
uint item;
char *tb_buf, *pre_buf;
const char *cur_name;
uint item = 0;
bool second_scan = false;
item = 0;
/* Create views, so we do not need to copy the data for now. */
std::string_view pre_buf = _chat_tab_completion_active ? std::string_view(_chat_tab_completion_buf) : std::string_view(tb->buf);
std::string_view tb_buf = ChatTabCompletionFindText(pre_buf);
/* Copy the buffer so we can modify it without damaging the real data */
pre_buf = (_chat_tab_completion_active) ? stredup(_chat_tab_completion_buf) : stredup(tb->buf);
/*
* Comparing pointers of the data, as both "Hi:<tab>" and "Hi: Hi:<tab>" will result in
* tb_buf and pre_buf being "Hi:", which would be equal in content but not in context.
*/
bool begin_of_line = tb_buf.data() == pre_buf.data();
tb_buf = ChatTabCompletionFindText(pre_buf);
tb_len = strlen(tb_buf);
while ((cur_name = ChatTabCompletionNextItem(&item)) != nullptr) {
std::optional<std::string> cur_item;
while ((cur_item = ChatTabCompletionNextItem(&item)).has_value()) {
std::string_view cur_name = cur_item.value();
item++;
if (_chat_tab_completion_active) {
/* We are pressing TAB again on the same name, is there another name
* that starts with this? */
if (!second_scan) {
size_t offset;
size_t length;
std::string_view view;
/* If we are completing at the begin of the line, skip the ': ' we added */
if (tb_buf == pre_buf) {
offset = 0;
length = (tb->bytes - 1) - 2;
if (begin_of_line) {
view = std::string_view(tb->buf, (tb->bytes - 1) - 2);
} else {
/* Else, find the place we are completing at */
offset = strlen(pre_buf) + 1;
length = (tb->bytes - 1) - offset;
size_t offset = pre_buf.size() + 1;
view = std::string_view(tb->buf + offset, (tb->bytes - 1) - offset);
}
/* Compare if we have a match */
if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
if (cur_name == view) second_scan = true;
continue;
}
@@ -423,21 +421,19 @@ struct NetworkChatWindow : public Window {
/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
}
len = strlen(cur_name);
if (tb_len < len && StrStartsWith(cur_name, tb_buf)) {
if (tb_buf.size() < cur_name.size() && StrStartsWith(cur_name, tb_buf)) {
/* Save the data it was before completion */
if (!second_scan) seprintf(_chat_tab_completion_buf, lastof(_chat_tab_completion_buf), "%s", tb->buf);
if (!second_scan) _chat_tab_completion_buf = tb->buf;
_chat_tab_completion_active = true;
/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
if (pre_buf == tb_buf) {
this->message_editbox.text.Print("%s: ", cur_name);
if (begin_of_line) {
this->message_editbox.text.Assign(fmt::format("{}: ", cur_name));
} else {
this->message_editbox.text.Print("%s %s", pre_buf, cur_name);
this->message_editbox.text.Assign(fmt::format("{} {}", pre_buf, cur_name));
}
this->SetDirty();
free(pre_buf);
return;
}
}
@@ -449,7 +445,6 @@ struct NetworkChatWindow : public Window {
this->SetDirty();
}
free(pre_buf);
}
Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override

View File

@@ -31,6 +31,7 @@
#include "../thread.h"
#include "../crashlog.h"
#include "../core/checksum_func.hpp"
#include "../core/alloc_func.hpp"
#include "../fileio_func.h"
#include "../debug_settings.h"
#include "../3rdparty/monocypher/monocypher.h"

View File

@@ -57,7 +57,7 @@ struct ContentCallback {
virtual void OnDownloadComplete(ContentID cid) {}
/** Silentium */
virtual ~ContentCallback() {}
virtual ~ContentCallback() = default;
};
/**

View File

@@ -791,7 +791,7 @@ public:
void OnClick(Point pt, int widget, int click_count) override
{
if (widget >= WID_NCL_TEXTFILE && widget < WID_NCL_TEXTFILE + TFT_END) {
if (widget >= WID_NCL_TEXTFILE && widget < WID_NCL_TEXTFILE + TFT_CONTENT_END) {
if (this->selected == nullptr || this->selected->state != ContentInfo::ALREADY_HERE) return;
ShowContentTextfileWindow((TextfileType)(widget - WID_NCL_TEXTFILE), this->selected);
@@ -800,11 +800,11 @@ public:
switch (widget) {
case WID_NCL_MATRIX: {
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NCL_MATRIX);
if (id_v >= this->content.size()) return; // click out of bounds
auto it = this->vscroll->GetScrolledItemFromWidget(this->content, pt.y, this, WID_NCL_MATRIX);
if (it == this->content.end()) return; // click out of bounds
this->selected = this->content[id_v];
this->list_pos = id_v;
this->selected = *it;
this->list_pos = it - this->content.begin();
const NWidgetBase *checkbox = this->GetWidget<NWidgetBase>(WID_NCL_CHECKBOX);
if (click_count > 1 || IsInsideBS(pt.x, checkbox->pos_x, checkbox->current_x)) {
@@ -998,7 +998,7 @@ public:
this->SetWidgetDisabledState(WID_NCL_SELECT_ALL, !show_select_all);
this->SetWidgetDisabledState(WID_NCL_SELECT_UPDATE, !show_select_upgrade);
this->SetWidgetDisabledState(WID_NCL_OPEN_URL, this->selected == nullptr || this->selected->url.empty());
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
this->SetWidgetDisabledState(WID_NCL_TEXTFILE + tft, this->selected == nullptr || this->selected->state != ContentInfo::ALREADY_HERE || this->selected->GetTextfile(tft) == nullptr);
}

View File

@@ -19,6 +19,7 @@
#include "network_content.h"
#include "network_server.h"
#include "network_coordinator.h"
#include "network_survey.h"
#include "../gui.h"
#include "network_udp.h"
#include "../window_func.h"
@@ -38,6 +39,7 @@
#include "../zoom_func.h"
#include "../sprite.h"
#include "../settings_internal.h"
#include "../textfile_gui.h"
#include "../widgets/network_widget.h"
@@ -764,9 +766,9 @@ public:
break;
case WID_NG_MATRIX: { // Show available network games
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NG_MATRIX);
this->server = (id_v < this->servers.size()) ? this->servers[id_v] : nullptr;
this->list_pos = (server == nullptr) ? SLP_INVALID : id_v;
auto it = this->vscroll->GetScrolledItemFromWidget(this->servers, pt.y, this, WID_NG_MATRIX);
this->server = (it != this->servers.end()) ? *it : nullptr;
this->list_pos = (server == nullptr) ? SLP_INVALID : it - this->servers.begin();
this->SetDirty();
/* FIXME the disabling should go into some InvalidateData, which is called instead of the SetDirty */
@@ -1462,7 +1464,7 @@ public:
this->height = d.height + WidgetDimensions::scaled.framerect.Vertical();
this->width = d.width + WidgetDimensions::scaled.framerect.Horizontal();
}
virtual ~ButtonCommon() {}
virtual ~ButtonCommon() = default;
/**
* OnClick handler for when the button is pressed.
@@ -2448,7 +2450,8 @@ struct NetworkAskRelayWindow : public Window {
{
if (widget == WID_NAR_TEXT) {
*size = GetStringBoundingBox(STR_NETWORK_ASK_RELAY_TEXT);
size->height = GetStringHeight(STR_NETWORK_ASK_RELAY_TEXT, size->width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
size->width += WidgetDimensions::scaled.frametext.Horizontal();
size->height += WidgetDimensions::scaled.frametext.Vertical();
}
}
@@ -2534,3 +2537,119 @@ void ShowNetworkAskRelay(const std::string &server_connection_string, const std:
Window *parent = GetMainWindow();
new NetworkAskRelayWindow(&_network_ask_relay_desc, parent, server_connection_string, relay_connection_string, token);
}
/**
* Window used for asking if the user wants to participate in the automated survey.
*/
struct NetworkAskSurveyWindow : public Window {
NetworkAskSurveyWindow(WindowDesc *desc, Window *parent) :
Window(desc)
{
this->parent = parent;
this->InitNested(0);
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
if (widget == WID_NAS_TEXT) {
*size = GetStringBoundingBox(STR_NETWORK_ASK_SURVEY_TEXT);
size->width += WidgetDimensions::scaled.frametext.Horizontal();
size->height += WidgetDimensions::scaled.frametext.Vertical();
}
}
void DrawWidget(const Rect &r, int widget) const override
{
if (widget == WID_NAS_TEXT) {
DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.frametext), STR_NETWORK_ASK_SURVEY_TEXT, TC_BLACK, SA_CENTER);
}
}
void FindWindowPlacementAndResize(int def_width, int def_height) override
{
/* Position query window over the calling window, ensuring it's within screen bounds. */
this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width);
this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height);
this->SetDirty();
}
void OnClick(Point pt, int widget, int click_count) override
{
switch (widget) {
case WID_NAS_PREVIEW:
ShowSurveyResultTextfileWindow();
break;
case WID_NAS_LINK:
OpenBrowser(NETWORK_SURVEY_DETAILS_LINK.c_str());
break;
case WID_NAS_NO:
_settings_client.network.participate_survey = PS_NO;
this->Close();
break;
case WID_NAS_YES:
_settings_client.network.participate_survey = PS_YES;
this->Close();
break;
}
}
};
static const NWidgetPart _nested_network_ask_survey_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_NAS_CAPTION), SetDataTip(STR_NETWORK_ASK_SURVEY_CAPTION, STR_NULL),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY), SetPIP(0, 4, 8),
NWidget(WWT_TEXT, COLOUR_GREY, WID_NAS_TEXT), SetAlignment(SA_HOR_CENTER), SetFill(1, 1),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 15, 10),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NAS_PREVIEW), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_SURVEY_PREVIEW, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NAS_LINK), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_SURVEY_LINK, STR_NULL),
EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 15, 10),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NAS_NO), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_SURVEY_NO, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NAS_YES), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_SURVEY_YES, STR_NULL),
EndContainer(),
EndContainer(),
};
static WindowDesc _network_ask_survey_desc(
WDP_CENTER, nullptr, 0, 0,
WC_NETWORK_ASK_SURVEY, WC_NONE,
WDF_MODAL,
_nested_network_ask_survey_widgets, lengthof(_nested_network_ask_survey_widgets)
);
/**
* Show a modal confirmation window with "no" / "preview" / "yes" buttons.
*/
void ShowNetworkAskSurvey()
{
/* If we can't send a survey, don't ask the question. */
if constexpr (!NetworkSurveyHandler::IsSurveyPossible()) return;
CloseWindowByClass(WC_NETWORK_ASK_SURVEY);
Window *parent = GetMainWindow();
new NetworkAskSurveyWindow(&_network_ask_survey_desc, parent);
}
/** Window for displaying the textfile of a survey result. */
struct SurveyResultTextfileWindow : public TextfileWindow {
const GRFConfig *grf_config; ///< View the textfile of this GRFConfig.
SurveyResultTextfileWindow(TextfileType file_type) : TextfileWindow(file_type)
{
auto result = _survey.CreatePayload(NetworkSurveyHandler::Reason::PREVIEW, true);
this->LoadText(result);
this->InvalidateData();
}
};
void ShowSurveyResultTextfileWindow()
{
CloseWindowById(WC_TEXTFILE, TFT_SURVEY_RESULT);
new SurveyResultTextfileWindow(TFT_SURVEY_RESULT);
}

View File

@@ -25,7 +25,8 @@ void ShowNetworkGameWindow();
void ShowClientList();
void ShowNetworkCompanyPasswordWindow(Window *parent);
void ShowNetworkAskRelay(const std::string &server_connection_string, const std::string &relay_connection_string, const std::string &token);
void ShowNetworkAskSurvey();
void ShowSurveyResultTextfileWindow();
/** Company information stored at the client side */
struct NetworkCompanyInfo : NetworkCompanyStats {

View File

@@ -0,0 +1,366 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file network_survey.cpp Opt-in survey part of the network protocol. */
#include "../stdafx.h"
#include "network_survey.h"
#include "network.h"
#include "network_internal.h"
#include "../company_base.h"
#include "../debug.h"
#include "../debug_fmt.h"
#include "../rev.h"
#include "../settings_type.h"
#include "../settings_internal.h"
#include "../timer/timer_game_tick.h"
#include "../sl/saveload.h"
#include "../date_func.h"
#include "../currency.h"
#include "../fontcache.h"
#include "../language.h"
#include "../ai/ai_info.hpp"
#include "../game/game.hpp"
#include "../game/game_info.hpp"
#include "../music/music_driver.hpp"
#include "../sound/sound_driver.hpp"
#include "../video/video_driver.hpp"
#include "../base_media_base.h"
#include "../blitter/factory.hpp"
#ifdef WITH_NLOHMANN_JSON
#include <nlohmann/json.hpp>
#endif /* WITH_NLOHMANN_JSON */
#include "../safeguards.h"
extern std::string _savegame_id;
NetworkSurveyHandler _survey = {};
#ifdef WITH_NLOHMANN_JSON
NLOHMANN_JSON_SERIALIZE_ENUM(NetworkSurveyHandler::Reason, {
{NetworkSurveyHandler::Reason::PREVIEW, "preview"},
{NetworkSurveyHandler::Reason::LEAVE, "leave"},
{NetworkSurveyHandler::Reason::EXIT, "exit"},
{NetworkSurveyHandler::Reason::CRASH, "crash"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus, {
{GRFStatus::GCS_UNKNOWN, "unknown"},
{GRFStatus::GCS_DISABLED, "disabled"},
{GRFStatus::GCS_NOT_FOUND, "not found"},
{GRFStatus::GCS_INITIALISED, "initialised"},
{GRFStatus::GCS_ACTIVATED, "activated"},
})
static const std::string _vehicle_type_to_string[] = {
"train",
"roadveh",
"ship",
"aircraft",
};
/* Defined in one of the os/ survey files. */
extern void SurveyOS(nlohmann::json &json);
/**
* Convert a settings table to JSON.
*
* @param survey The JSON object.
* @param table The settings table to convert.
* @param object The object to get the settings from.
*/
static void SurveySettingsTable(nlohmann::json &survey, const SettingTable &table, void *object)
{
char buf[512];
for (auto &sd : table) {
/* Skip any old settings we no longer save/load. */
if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue;
auto name = sd->name;
sd->FormatValue(buf, lastof(buf), object);
survey[name] = buf;
}
}
/**
* Convert settings to JSON.
*
* @param survey The JSON object.
*/
static void SurveySettings(nlohmann::json &survey)
{
IterateSettingsTables([&](const SettingTable &table, void *object) {
SurveySettingsTable(survey, table, object);
});
}
/**
* Convert generic OpenTTD information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyOpenTTD(nlohmann::json &survey)
{
survey["version"] = std::string(_openttd_revision);
survey["newgrf_version"] = _openttd_newgrf_version;
survey["build_date"] = std::string(_openttd_build_date);
survey["bits"] =
#ifdef POINTER_IS_64BIT
64
#else
32
#endif
;
survey["endian"] =
#if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
"little"
#else
"big"
#endif
;
survey["dedicated_build"] =
#ifdef DEDICATED
"yes"
#else
"no"
#endif
;
}
/**
* Convert generic game information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyConfiguration(nlohmann::json &survey)
{
survey["network"] = _networking ? (_network_server ? "server" : "client") : "no";
if (_current_language != nullptr) {
std::string_view language_basename(_current_language->file);
auto e = language_basename.rfind(PATHSEPCHAR);
if (e != std::string::npos) {
language_basename = language_basename.substr(e + 1);
}
survey["language"]["filename"] = language_basename;
survey["language"]["name"] = _current_language->name;
survey["language"]["isocode"] = _current_language->isocode;
}
if (BlitterFactory::GetCurrentBlitter() != nullptr) {
survey["blitter"] = BlitterFactory::GetCurrentBlitter()->GetName();
}
if (MusicDriver::GetInstance() != nullptr) {
survey["music_driver"] = MusicDriver::GetInstance()->GetName();
}
if (SoundDriver::GetInstance() != nullptr) {
survey["sound_driver"] = SoundDriver::GetInstance()->GetName();
}
if (VideoDriver::GetInstance() != nullptr) {
survey["video_driver"] = VideoDriver::GetInstance()->GetName();
survey["video_info"] = VideoDriver::GetInstance()->GetInfoString();
}
if (BaseGraphics::GetUsedSet() != nullptr) {
survey["graphics_set"] = fmt::format("{}.{}", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
}
if (BaseMusic::GetUsedSet() != nullptr) {
survey["music_set"] = fmt::format("{}.{}", BaseMusic::GetUsedSet()->name, BaseMusic::GetUsedSet()->version);
}
if (BaseSounds::GetUsedSet() != nullptr) {
survey["sound_set"] = fmt::format("{}.{}", BaseSounds::GetUsedSet()->name, BaseSounds::GetUsedSet()->version);
}
}
/**
* Convert font information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyFont(nlohmann::json &survey)
{
survey["small"] = FontCache::Get(FS_SMALL)->GetFontName();
survey["medium"] = FontCache::Get(FS_NORMAL)->GetFontName();
survey["large"] = FontCache::Get(FS_LARGE)->GetFontName();
survey["mono"] = FontCache::Get(FS_MONO)->GetFontName();
}
/**
* Convert company information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyCompanies(nlohmann::json &survey)
{
for (const Company *c : Company::Iterate()) {
auto &company = survey[std::to_string(c->index)];
if (c->ai_info == nullptr) {
company["type"] = "human";
} else {
company["type"] = "ai";
company["script"] = fmt::format("{}.{}", c->ai_info->GetName(), c->ai_info->GetVersion());
}
for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
uint amount = c->group_all[type].num_vehicle;
company["vehicles"][_vehicle_type_to_string[type]] = amount;
}
company["infrastructure"]["road"] = c->infrastructure.GetRoadTotal();
company["infrastructure"]["tram"] = c->infrastructure.GetTramTotal();
company["infrastructure"]["rail"] = c->infrastructure.GetRailTotal();
company["infrastructure"]["signal"] = c->infrastructure.signal;
company["infrastructure"]["water"] = c->infrastructure.water;
company["infrastructure"]["station"] = c->infrastructure.station;
company["infrastructure"]["airport"] = c->infrastructure.airport;
}
}
/**
* Convert GRF information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyGrfs(nlohmann::json &survey)
{
for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
auto grfid = fmt::format("{:08x}", BSWAP32(c->ident.grfid));
auto &grf = survey[grfid];
grf["md5sum"] = BytesToHexString(c->ident.md5sum, 16);
grf["status"] = c->status;
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_UNSET) grf["palette"] = "unset";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_DOS) grf["palette"] = "dos";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_WINDOWS) grf["palette"] = "windows";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_ANY) grf["palette"] = "any";
if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_UNSET) grf["blitter"] = "unset";
if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_32BPP) grf["blitter"] = "32bpp";
grf["is_static"] = HasBit(c->flags, GCF_STATIC);
std::vector<uint32> parameters;
for (int i = 0; i < c->num_params; i++) {
parameters.push_back(c->param[i]);
}
grf["parameters"] = parameters;
}
}
/**
* Convert game-script information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyGameScript(nlohmann::json &survey)
{
if (Game::GetInfo() == nullptr) return;
survey = fmt::format("{}.{}", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
}
#endif /* WITH_NLOHMANN_JSON */
/**
* Create the payload for the survey.
*
* @param reason The reason for sending the survey.
* @param for_preview Whether the payload is meant for preview. This indents the result, and filters out the id/key.
* @return std::string The JSON payload as string for the survey.
*/
std::string NetworkSurveyHandler::CreatePayload(Reason reason, bool for_preview)
{
#ifndef WITH_NLOHMANN_JSON
return "";
#else
nlohmann::json survey;
survey["schema"] = NETWORK_SURVEY_VERSION;
survey["reason"] = reason;
survey["id"] = _savegame_id;
#ifdef SURVEY_KEY
/* We censor the key to avoid people trying to be "clever" and use it to send their own surveys. */
survey["key"] = for_preview ? "(redacted)" : SURVEY_KEY;
#else
survey["key"] = "";
#endif
{
auto &info = survey["info"];
SurveyOS(info["os"]);
info["os"]["hardware_concurrency"] = std::thread::hardware_concurrency();
SurveyOpenTTD(info["openttd"]);
SurveyConfiguration(info["configuration"]);
SurveyFont(info["font"]);
}
{
auto &game = survey["game"];
game["ticks"] = _scaled_tick_counter;
game["time"] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - _switch_mode_time).count();
SurveyCompanies(game["companies"]);
SurveySettings(game["settings"]);
SurveyGrfs(game["grfs"]);
SurveyGameScript(game["game_script"]);
}
/* For preview, we indent with 4 whitespaces to make things more readable. */
int indent = for_preview ? 4 : -1;
return survey.dump(indent);
#endif /* WITH_NLOHMANN_JSON */
}
/**
* Transmit the survey.
*
* @param reason The reason for sending the survey.
* @param blocking Whether to block until the survey is sent.
*/
void NetworkSurveyHandler::Transmit(Reason reason, bool blocking)
{
if constexpr (!NetworkSurveyHandler::IsSurveyPossible()) {
Debug(net, 4, "Survey: not possible to send survey; most likely due to missing JSON library at compile-time");
return;
}
if (_settings_client.network.participate_survey != PS_YES) {
Debug(net, 5, "Survey: user is not participating in survey; skipping survey");
return;
}
Debug(net, 1, "Survey: sending survey results");
NetworkHTTPSocketHandler::Connect(NetworkSurveyUriString(), this, this->CreatePayload(reason));
if (blocking) {
std::unique_lock<std::mutex> lock(this->mutex);
/* Block no longer than 2 seconds. If we failed to send the survey in that time, so be it. */
this->loaded.wait_for(lock, std::chrono::seconds(2));
}
}
void NetworkSurveyHandler::OnFailure()
{
Debug(net, 1, "Survey: failed to send survey results");
this->loaded.notify_all();
}
void NetworkSurveyHandler::OnReceiveData(const char *data, size_t length)
{
if (data == nullptr) {
Debug(net, 1, "Survey: survey results sent");
this->loaded.notify_all();
}
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file network_survey.h Part of the network protocol handling opt-in survey. */
#ifndef NETWORK_SURVEY_H
#define NETWORK_SURVEY_H
#include <condition_variable>
#include <mutex>
#include "core/http.h"
/**
* Socket handler for the survey connection
*/
class NetworkSurveyHandler : public HTTPCallback {
protected:
void OnFailure() override;
void OnReceiveData(const char *data, size_t length) override;
bool IsCancelled() const override { return false; }
public:
enum class Reason {
PREVIEW, ///< User is previewing the survey result.
LEAVE, ///< User is leaving the game (but not exiting the application).
EXIT, ///< User is exiting the application.
CRASH, ///< Game crashed.
};
void Transmit(Reason reason, bool blocking = false);
std::string CreatePayload(Reason reason, bool for_preview = false);
constexpr static bool IsSurveyPossible()
{
#ifndef WITH_NLOHMANN_JSON
/* Without JSON library, we cannot send a payload; so we disable the survey. */
return false;
#else
return true;
#endif /* WITH_NLOHMANN_JSON */
}
private:
std::mutex mutex; ///< Mutex for the condition variable.
std::condition_variable loaded; ///< Condition variable to wait for the survey to be sent.
};
extern NetworkSurveyHandler _survey;
#endif /* NETWORK_SURVEY_H */

View File

@@ -82,7 +82,7 @@ public:
* @param addresses The addresses to bind on.
*/
ServerNetworkUDPSocketHandler(NetworkAddressList *addresses) : NetworkUDPSocketHandler(addresses) {}
virtual ~ServerNetworkUDPSocketHandler() {}
virtual ~ServerNetworkUDPSocketHandler() = default;
};
void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr)
@@ -117,7 +117,7 @@ protected:
void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) override;
void Receive_EX_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) override;
public:
virtual ~ClientNetworkUDPSocketHandler() {}
virtual ~ClientNetworkUDPSocketHandler() = default;
};
void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr)

View File

@@ -9291,13 +9291,13 @@ static bool ChangeGRFParamValueNames(ByteReader *buf)
byte langid = buf->ReadByte();
const char *name_string = buf->ReadString();
std::pair<uint32, GRFTextList> *val_name = _cur_parameter->value_names.Find(id);
if (val_name != _cur_parameter->value_names.End()) {
auto val_name = _cur_parameter->value_names.find(id);
if (val_name != _cur_parameter->value_names.end()) {
AddGRFTextToList(val_name->second, langid, _cur.grfconfig->ident.grfid, false, name_string);
} else {
GRFTextList list;
AddGRFTextToList(list, langid, _cur.grfconfig->ident.grfid, false, name_string);
_cur_parameter->value_names.Insert(id, list);
_cur_parameter->value_names[id] = list;
}
type = buf->ReadByte();

View File

@@ -204,7 +204,7 @@ public:
std::vector<EntityIDMapping> mappings; ///< mapping of ids from grf files. Public out of convenience
OverrideManagerBase(uint16 offset, uint16 maximum, uint16 invalid);
virtual ~OverrideManagerBase() {}
virtual ~OverrideManagerBase() = default;
void ResetOverride();
void ResetMapping();

View File

@@ -274,7 +274,7 @@ void GRFParameterInfo::Finalize()
{
this->complete_labels = true;
for (uint32 value = this->min_value; value <= this->max_value; value++) {
if (!this->value_names.Contains(value)) {
if (this->value_names.count(value) == 0) {
this->complete_labels = false;
break;
}

View File

@@ -12,11 +12,11 @@
#include "strings_type.h"
#include "core/alloc_type.hpp"
#include "core/smallmap_type.hpp"
#include "misc/countedptr.hpp"
#include "fileio_type.h"
#include "textfile_type.h"
#include "newgrf_text.h"
#include <map>
static const uint MAX_NON_STATIC_GRF_COUNT = 256;
@@ -145,7 +145,7 @@ struct GRFParameterInfo {
byte param_nr; ///< GRF parameter to store content in
byte first_bit; ///< First bit to use in the GRF parameter
byte num_bit; ///< Number of bits to use for this parameter
SmallMap<uint32, GRFTextList> value_names; ///< Names for each value.
std::map<uint32_t, GRFTextList> value_names; ///< Names for each value.
bool complete_labels; ///< True if all values have a label.
uint32 GetValue(struct GRFConfig *config) const;
@@ -222,7 +222,7 @@ extern bool _grf_bug_too_many_strings;///< NewGRF bug: Insufficient available st
/** Callback for NewGRF scanning. */
struct NewGRFScanCallback {
/** Make sure the right destructor gets called. */
virtual ~NewGRFScanCallback() {}
virtual ~NewGRFScanCallback() = default;
/** Called whenever the NewGRF scan completed. */
virtual void OnNewGRFsScanned() = 0;
};

View File

@@ -132,7 +132,7 @@ struct NIExtraInfoOutput {
class NIHelper {
public:
/** Silence a warning. */
virtual ~NIHelper() {}
virtual ~NIHelper() = default;
/**
* Is the item with the given index inspectable?
@@ -1266,7 +1266,7 @@ struct SpriteAlignerWindow : Window {
SpriteID current_sprite; ///< The currently shown sprite.
Scrollbar *vscroll;
SmallMap<SpriteID, XyOffs> offs_start_map; ///< Mapping of starting offsets for the sprites which have been aligned in the sprite aligner window.
std::map<SpriteID, XyOffs> offs_start_map; ///< Mapping of starting offsets for the sprites which have been aligned in the sprite aligner window.
static bool centre;
static bool crosshair;
@@ -1302,7 +1302,7 @@ struct SpriteAlignerWindow : Window {
/* Relative offset is new absolute offset - starting absolute offset.
* Show 0, 0 as the relative offsets if entry is not in the map (meaning they have not been changed yet).
*/
const auto key_offs_pair = this->offs_start_map.Find(this->current_sprite);
const auto key_offs_pair = this->offs_start_map.find(this->current_sprite);
if (key_offs_pair != this->offs_start_map.end()) {
SetDParam(0, spr->x_offs - key_offs_pair->second.first);
SetDParam(1, spr->y_offs - key_offs_pair->second.second);
@@ -1411,12 +1411,9 @@ struct SpriteAlignerWindow : Window {
break;
case WID_SA_LIST: {
const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget);
int step_size = nwid->resize_y;
uint i = this->vscroll->GetPosition() + (pt.y - nwid->pos_y) / step_size;
if (i < _newgrf_debug_sprite_picker.sprites.size()) {
SpriteID spr = _newgrf_debug_sprite_picker.sprites[i];
auto it = this->vscroll->GetScrolledItemFromWidget(_newgrf_debug_sprite_picker.sprites, pt.y, this, widget);
if (it != _newgrf_debug_sprite_picker.sprites.end()) {
SpriteID spr = *it;
if (GetSpriteType(spr) == SpriteType::Normal) this->current_sprite = spr;
}
this->SetDirty();
@@ -1443,8 +1440,8 @@ struct SpriteAlignerWindow : Window {
Sprite *spr = const_cast<Sprite *>(GetSprite(this->current_sprite, SpriteType::Normal));
/* Remember the original offsets of the current sprite, if not already in mapping. */
if (!(this->offs_start_map.Contains(this->current_sprite))) {
this->offs_start_map.Insert(this->current_sprite, XyOffs(spr->x_offs, spr->y_offs));
if (this->offs_start_map.count(this->current_sprite) == 0) {
this->offs_start_map[this->current_sprite] = XyOffs(spr->x_offs, spr->y_offs);
}
switch (widget) {
/* Move eight units at a time if ctrl is pressed. */
@@ -1461,7 +1458,7 @@ struct SpriteAlignerWindow : Window {
case WID_SA_RESET_REL:
/* Reset the starting offsets for the current sprite. */
this->offs_start_map.Erase(this->current_sprite);
this->offs_start_map.erase(this->current_sprite);
this->SetDirty();
break;

View File

@@ -289,8 +289,9 @@ struct NewGRFParametersWindow : public Window {
}
SetDParam(2, STR_JUST_INT);
SetDParam(3, current_value);
if (par_info->value_names.Contains(current_value)) {
const char *label = GetGRFStringFromGRFText(par_info->value_names.Find(current_value)->second);
auto it = par_info->value_names.find(current_value);
if (it != par_info->value_names.end()) {
const char *label = GetGRFStringFromGRFText(it->second);
if (label != nullptr) {
SetDParam(2, STR_JUST_RAW_STRING);
SetDParamStr(3, label);
@@ -344,8 +345,10 @@ struct NewGRFParametersWindow : public Window {
case WID_NP_BACKGROUND: {
if (!this->editable) break;
uint num = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NP_BACKGROUND);
if (num >= this->vscroll->GetCount()) break;
auto it = this->vscroll->GetScrolledItemFromWidget(this->grf_config->param_info, pt.y, this, WID_NP_BACKGROUND);
if (it == this->grf_config->param_info.end()) break;
uint num = it - this->grf_config->param_info.begin();
if (this->clicked_row != num) {
DeleteChildWindows(WC_QUERY_STRING);
HideDropDownMenu(this);
@@ -357,7 +360,7 @@ struct NewGRFParametersWindow : public Window {
int x = pt.x - r.left;
if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x;
GRFParameterInfo *par_info = (num < this->grf_config->param_info.size()) ? this->grf_config->param_info[num] : nullptr;
GRFParameterInfo *par_info = *it;
if (par_info == nullptr) par_info = GetDummyParameterInfo(num);
/* One of the arrows is clicked */
@@ -384,7 +387,7 @@ struct NewGRFParametersWindow : public Window {
DropDownList list;
for (uint32 i = par_info->min_value; i <= par_info->max_value; i++) {
list.emplace_back(new DropDownListCharStringItem(GetGRFStringFromGRFText(par_info->value_names.Find(i)->second), i, false));
list.emplace_back(new DropDownListCharStringItem(GetGRFStringFromGRFText(par_info->value_names.find(i)->second), i, false));
}
ShowDropDownListAt(this, std::move(list), old_val, -1, wi_rect, COLOUR_ORANGE);
@@ -927,7 +930,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback {
void OnClick(Point pt, int widget, int click_count) override
{
if (widget >= WID_NS_NEWGRF_TEXTFILE && widget < WID_NS_NEWGRF_TEXTFILE + TFT_END) {
if (widget >= WID_NS_NEWGRF_TEXTFILE && widget < WID_NS_NEWGRF_TEXTFILE + TFT_CONTENT_END) {
if (this->active_sel == nullptr && this->avail_sel == nullptr) return;
ShowNewGRFTextfileWindow((TextfileType)(widget - WID_NS_NEWGRF_TEXTFILE), this->active_sel != nullptr ? this->active_sel : this->avail_sel);
@@ -1081,13 +1084,13 @@ struct NewGRFWindow : public Window, NewGRFScanCallback {
case WID_NS_AVAIL_LIST: { // Select a non-active GRF.
ResetObjectToPlace();
uint i = this->vscroll2->GetScrolledRowFromWidget(pt.y, this, WID_NS_AVAIL_LIST);
auto it = this->vscroll2->GetScrolledItemFromWidget(this->avails, pt.y, this, WID_NS_AVAIL_LIST);
this->active_sel = nullptr;
DeleteWindowByClass(WC_GRF_PARAMETERS);
if (i < this->avails.size()) {
if (this->avail_sel != this->avails[i]) DeleteWindowByClass(WC_TEXTFILE);
this->avail_sel = this->avails[i];
this->avail_pos = i;
if (it != this->avails.end()) {
if (this->avail_sel != *it) DeleteWindowByClass(WC_TEXTFILE);
this->avail_sel = *it;
this->avail_pos = it - this->avails.begin();
}
this->InvalidateData();
if (click_count == 1) {
@@ -1295,7 +1298,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback {
);
const GRFConfig *selected_config = (this->avail_sel == nullptr) ? this->active_sel : this->avail_sel;
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
this->SetWidgetDisabledState(WID_NS_NEWGRF_TEXTFILE + tft, selected_config == nullptr || selected_config->GetTextfile(tft) == nullptr);
}
this->SetWidgetDisabledState(WID_NS_OPEN_URL, selected_config == nullptr || StrEmpty(selected_config->GetURL()));
@@ -2152,10 +2155,10 @@ struct SavePresetWindow : public Window {
{
switch (widget) {
case WID_SVP_PRESET_LIST: {
uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SVP_PRESET_LIST);
if (row < this->presets.size()) {
this->selected = row;
this->presetname_editbox.text.Assign(this->presets[row].c_str());
auto it = this->vscroll->GetScrolledItemFromWidget(this->presets, pt.y, this, WID_SVP_PRESET_LIST);
if (it != this->presets.end()) {
this->selected = it - this->presets.begin();
this->presetname_editbox.text.Assign(it->c_str());
this->SetWidgetDirty(WID_SVP_PRESET_LIST);
this->SetWidgetDirty(WID_SVP_EDITBOX);
}

Some files were not shown because too many files have changed in this diff Show More