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
pull/562/head
Jonathan G Rennison 11 months ago
commit d09b504bc5

@ -81,24 +81,30 @@ jobs:
echo "::endgroup::"
linux:
name: Linux
strategy:
fail-fast: false
matrix:
include:
- compiler: clang
- name: Clang
compiler: clang
cxxcompiler: clang++
libsdl: libsdl2-dev
- compiler: gcc
libraries: libsdl2-dev nlohmann-json3-dev
- name: GCC - SDL2
compiler: gcc
cxxcompiler: g++
libsdl: libsdl2-dev
- compiler: gcc
libraries: libsdl2-dev nlohmann-json3-dev
- name: GCC - SDL1.2
compiler: gcc
cxxcompiler: g++
libsdl: libsdl1.2-dev
- compiler: gcc
libraries: libsdl1.2-dev nlohmann-json3-dev
- name: GCC - Dedicated
compiler: gcc
cxxcompiler: g++
extra-cmake-parameters: -DOPTION_DEDICATED=ON -DCMAKE_CXX_FLAGS_INIT="-DRANDOM_DEBUG"
# Compile without SDL / SDL2 / nlohmann-json, as that should compile fine too.
name: Linux (${{ matrix.name }})
runs-on: ubuntu-20.04
env:
@ -132,7 +138,7 @@ jobs:
liblzma-dev \
libzstd-dev \
liblzo2-dev \
${{ matrix.libsdl }} \
${{ matrix.libraries }} \
zlib1g-dev \
# EOF
echo "::endgroup::"
@ -172,8 +178,6 @@ jobs:
echo "::endgroup::"
macos:
name: Mac OS
strategy:
fail-fast: false
matrix:
@ -181,6 +185,8 @@ jobs:
- arch: x64
full_arch: x86_64
name: Mac OS (${{ matrix.arch }})
runs-on: macos-latest
env:
MACOSX_DEPLOYMENT_TARGET: 10.13
@ -214,7 +220,7 @@ jobs:
uses: actions/cache@v3
with:
path: /usr/local/share/vcpkg/installed
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-0 # Increase the number whenever dependencies are modified
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-1 # Increase the number whenever dependencies are modified
restore-keys: |
${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}
@ -225,6 +231,7 @@ jobs:
liblzma \
libpng \
lzo \
nlohmann-json \
zlib \
zstd \
# EOF
@ -232,7 +239,7 @@ jobs:
- name: Install OpenGFX
run: |
mkdir -p ~/Documents/OpenTTD/baseset
cd ~/Documents//OpenTTD/baseset
cd ~/Documents/OpenTTD/baseset
echo "::group::Download OpenGFX"
curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip
@ -253,7 +260,7 @@ jobs:
cd build
echo "::group::CMake"
cmake ${GITHUB_WORKSPACE} \
cmake .. \
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.full_arch }} \
-DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-osx \
-DCMAKE_TOOLCHAIN_FILE=/usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake \
@ -266,14 +273,14 @@ jobs:
echo "::endgroup::"
windows:
name: Windows
strategy:
fail-fast: false
matrix:
os: [windows-latest, windows-2019]
arch: [x86, x64]
name: Windows (${{ matrix.os }} / ${{ matrix.arch }})
runs-on: ${{ matrix.os }}
steps:
@ -300,7 +307,7 @@ jobs:
uses: actions/cache@v3
with:
path: vcpkg/installed
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-0 # Increase the number whenever dependencies are modified
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-1 # Increase the number whenever dependencies are modified
restore-keys: |
${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}
@ -311,6 +318,7 @@ jobs:
liblzma \
libpng \
lzo \
nlohmann-json \
zlib \
zstd \
# EOF
@ -358,8 +366,6 @@ jobs:
echo "::endgroup::"
msys2:
name: msys2
strategy:
fail-fast: false
matrix:
@ -369,6 +375,8 @@ jobs:
- msystem: MINGW32
arch: i686
name: MinGW (${{ matrix.arch }})
runs-on: windows-latest
steps:
@ -394,6 +402,7 @@ jobs:
mingw-w64-${{ matrix.arch }}-gcc
mingw-w64-${{ matrix.arch }}-lzo2
mingw-w64-${{ matrix.arch }}-libpng
mingw-w64-${{ matrix.arch }}-nlohmann-json
- name: Install OpenGFX
shell: bash

@ -49,10 +49,12 @@ jobs:
liballegro4-dev \
libcurl4-openssl-dev \
libfontconfig-dev \
libharfbuzz-dev \
libicu-dev \
liblzma-dev \
liblzo2-dev \
libsdl2-dev \
nlohmann-json3-dev \
zlib1g-dev \
# EOF
echo "::endgroup::"

@ -120,6 +120,7 @@ find_package(LibLZMA)
find_package(LZO)
find_package(ZSTD 1.4)
find_package(PNG)
find_package(nlohmann_json)
if(WIN32 OR EMSCRIPTEN)
# Windows uses WinHttp for HTTP requests.
@ -345,6 +346,7 @@ link_package(ZLIB TARGET ZLIB::ZLIB ENCOURAGED)
link_package(LIBLZMA TARGET LibLZMA::LibLZMA ENCOURAGED)
link_package(LZO)
link_package(ZSTD TARGET ZSTD::ZSTD RECOMMENDED)
link_package(nlohmann_json ENCOURAGED)
if(NOT WIN32 AND NOT EMSCRIPTEN)
link_package(CURL ENCOURAGED)

@ -4,6 +4,7 @@
OpenTTD makes use of the following external libraries:
- (encouraged) nlohmann-json: JSON handling
- (encouraged) zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads,
heightmaps
- (encouraged) liblzma: (de)compressing of savegames (1.1.0 and later)
@ -54,13 +55,14 @@ the `static` versions, and OpenTTD currently needs the following dependencies:
- libzstd
- libpng
- lzo
- nlohmann-json
- zlib
To install both the x64 (64bit) and x86 (32bit) variants (though only one is necessary), you can use:
```ps
.\vcpkg install liblzma:x64-windows-static zstd:x64-windows-static libpng:x64-windows-static lzo:x64-windows-static zlib:x64-windows-static
.\vcpkg install liblzma:x86-windows-static zstd:x86-windows-static libpng:x86-windows-static lzo:x86-windows-static zlib:x86-windows-static
.\vcpkg install liblzma:x64-windows-static zstd:x64-windows-static libpng:x64-windows-static lzo:x64-windows-static nlohmann-json:x64-windows-static zlib:x64-windows-static
.\vcpkg install liblzma:x86-windows-static zstd:x86-windows-static libpng:x86-windows-static lzo:x86-windows-static nlohmann-json:x86-windows-static zlib:x86-windows-static
```
You can open the folder (as a CMake project). CMake will be detected, and you can compile from there.

@ -71,6 +71,8 @@ function(set_options)
if (OPTION_DOCS_ONLY)
set(OPTION_TOOLS_ONLY ON PARENT_SCOPE)
endif()
option(OPTION_SURVEY_KEY "Survey-key to use for the opt-in survey (empty if you have none)" "")
endfunction()
# Show the values of the generic options.
@ -84,6 +86,12 @@ function(show_options)
message(STATUS "Option Use assert - ${OPTION_USE_ASSERTS}")
message(STATUS "Option Use threads - ${OPTION_USE_THREADS}")
message(STATUS "Option Use NSIS - ${OPTION_USE_NSIS}")
if(OPTION_SURVEY_KEY)
message(STATUS "Option Survey Key - USED")
else()
message(STATUS "Option Survey Key - NOT USED")
endif()
endfunction()
# Add the definitions for the options that are selected.
@ -104,4 +112,8 @@ function(add_definitions_based_on_options)
else()
add_definitions(-DNDEBUG)
endif()
if(OPTION_SURVEY_KEY)
add_definitions(-DSURVEY_KEY="${OPTION_SURVEY_KEY}")
endif()
endfunction()

@ -28,21 +28,21 @@ endmacro()
macro(dump_class_templates NAME)
string(REGEX REPLACE "^Script" "" REALNAME ${NAME})
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<const ${NAME} *> { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<const ${NAME} &> { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<const ${NAME} *> { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<const ${NAME} &> { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };")
if("${NAME}" STREQUAL "ScriptEvent")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };")
elseif("${NAME}" STREQUAL "ScriptText")
string(APPEND SQUIRREL_EXPORT "\n")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<Text *> {")
string(APPEND SQUIRREL_EXPORT "\n static inline Text *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) {")
string(APPEND SQUIRREL_EXPORT "\n static inline Text *Get(HSQUIRRELVM vm, int index) {")
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_INSTANCE) {")
string(APPEND SQUIRREL_EXPORT "\n return Param<ScriptText *>::Get(vm, index, ptr);")
string(APPEND SQUIRREL_EXPORT "\n return Param<ScriptText *>::Get(vm, index);")
string(APPEND SQUIRREL_EXPORT "\n }")
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_STRING) {")
string(APPEND SQUIRREL_EXPORT "\n return new RawText(Param<const char *>::Get(vm, index, ptr));")
string(APPEND SQUIRREL_EXPORT "\n return new RawText(Param<const std::string &>::Get(vm, index));")
string(APPEND SQUIRREL_EXPORT "\n }")
string(APPEND SQUIRREL_EXPORT "\n return nullptr;")
string(APPEND SQUIRREL_EXPORT "\n }")
@ -299,7 +299,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif()
string(APPEND SQUIRREL_EXPORT "\n /* Allow enums to be used as Squirrel parameters */")
foreach(ENUM IN LISTS ENUMS)
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${ENUM}> { static inline ${ENUM} Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (${ENUM})tmp; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${ENUM}> { static inline ${ENUM} Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (${ENUM})tmp; } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${ENUM}> { static inline int Set(HSQUIRRELVM vm, ${ENUM} res) { sq_pushinteger(vm, res); return 1; } };")
endforeach()
endif()
@ -631,6 +631,8 @@ foreach(LINE IN LISTS SOURCE_LINES)
string(APPEND TYPES "a")
elseif("${PARAM}" MATCHES "^Text")
string(APPEND TYPES ".")
elseif("${PARAM}" MATCHES "^std::string")
string(APPEND TYPES ".")
else()
string(APPEND TYPES "x")
endif()

@ -2,3 +2,6 @@ FROM emscripten/emsdk:3.1.37
COPY emsdk-liblzma.patch /
RUN cd /emsdk/upstream/emscripten && patch -p1 < /emsdk-liblzma.patch
COPY emsdk-nlohmann-json.patch /
RUN cd /emsdk/upstream/emscripten && patch -p1 < /emsdk-nlohmann-json.patch

@ -4,10 +4,11 @@ Please use docker with the supplied `Dockerfile` to build for emscripten.
It takes care of a few things:
- Use a version of emscripten we know works
- Patch in LibLZMA support (as this is not supported by upstream)
- Patch in nlohmann-json support (as this is not supported by upstream)
First, build the docker image by navigating in the folder this `README.md` is in, and executing:
```
docker build -t emsdk-lzma .
docker build -t emsdk-openttd .
```
Next, navigate back to the root folder of this project.
@ -15,15 +16,15 @@ Next, navigate back to the root folder of this project.
Now we build the host tools first:
```
mkdir build-host
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma cmake .. -DOPTION_TOOLS_ONLY=ON
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma make -j$(nproc) tools
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-openttd cmake .. -DOPTION_TOOLS_ONLY=ON
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-openttd make -j$(nproc) tools
```
Finally, we build the actual game:
```
mkdir build
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emcmake cmake .. -DHOST_BINARY_DIR=../build-host -DCMAKE_BUILD_TYPE=Release -DOPTION_USE_ASSERTS=OFF
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emmake make -j$(nproc)
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-openttd emcmake cmake .. -DHOST_BINARY_DIR=../build-host -DCMAKE_BUILD_TYPE=Release -DOPTION_USE_ASSERTS=OFF
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-openttd emmake make -j$(nproc)
```
In the `build` folder you will now see `openttd.html`.

@ -1,5 +1,5 @@
# LibLZMA is a recent addition to the emscripten SDK, so it is possible
# someone hasn't updated their SDK yet. Test out if the SDK supports LibLZMA.
# LibLZMA is a custom addition to the emscripten SDK, so it is possible
# someone patched their SDK. Test out if the SDK supports LibLZMA.
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS "-sUSE_LIBLZMA=1")

@ -0,0 +1,20 @@
# nlohmann-json is a custom addition to the emscripten SDK, so it is possible
# someone patched their SDK. Test out if the SDK supports nlohmann-json.
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS "-sUSE_NLOHMANN_JSON=1")
check_cxx_source_compiles("
#include <nlohmann/json.hpp>
int main() { return 0; }"
NLOHMANN_JSON_FOUND
)
if (NLOHMANN_JSON_FOUND)
add_library(nlohmann_json INTERFACE IMPORTED)
set_target_properties(nlohmann_json PROPERTIES
INTERFACE_COMPILE_OPTIONS "-sUSE_NLOHMANN_JSON=1"
INTERFACE_LINK_LIBRARIES "-sUSE_NLOHMANN_JSON=1"
)
else()
message(WARNING "You are using an emscripten SDK without nlohmann-json support. Please apply 'emsdk-nlohmann_json.patch' to your local emsdk installation.")
endif()

@ -0,0 +1,93 @@
From 0edcedbea375e59f41df10acaee0c483d245751f Mon Sep 17 00:00:00 2001
From: Patric Stout <truebrain@openttd.org>
Date: Tue, 2 May 2023 21:48:08 +0200
Subject: [PATCH] Add nlohmmann-json port
---
src/settings.js | 4 ++++
tools/ports/nlohmann_json.py | 46 ++++++++++++++++++++++++++++++++++++
tools/settings.py | 1 +
3 files changed, 51 insertions(+)
create mode 100644 tools/ports/nlohmann_json.py
diff --git a/src/settings.js b/src/settings.js
index f93140d..39f4366 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -1483,6 +1483,10 @@ var USE_MPG123 = false;
// [compile+link]
var USE_FREETYPE = false;
+// 1 = use nlohmann-json from emscripten-ports
+// [compile+link]
+var USE_NLOHMANN_JSON = false;
+
// Specify the SDL_mixer version that is being linked against.
// Doesn't *have* to match USE_SDL, but a good idea.
// [compile+link]
diff --git a/tools/ports/nlohmann_json.py b/tools/ports/nlohmann_json.py
new file mode 100644
index 0000000..9e44297
--- /dev/null
+++ b/tools/ports/nlohmann_json.py
@@ -0,0 +1,46 @@
+# Copyright 2023 The Emscripten Authors. All rights reserved.
+# Emscripten is available under two separate licenses, the MIT license and the
+# University of Illinois/NCSA Open Source License. Both these licenses can be
+# found in the LICENSE file.
+
+import os
+
+TAG = '3.11.2'
+HASH = '99d9e6d588cabe8913a37437f86acb5d4b8b98bce12423e633c11c13b61e6c7f92ef8f9a4e991baa590329ee2b5c09ca9db9894bee1e54bdd68e8d09d83cc245'
+
+
+def needed(settings):
+ return settings.USE_NLOHMANN_JSON
+
+
+def get(ports, settings, shared):
+ ports.fetch_project('nlohmann_json',
+ f'https://github.com/nlohmann/json/releases/download/v{TAG}/include.zip',
+ sha512hash=HASH)
+
+ def create(final):
+ source_path = os.path.join(ports.get_dir(), 'nlohmann_json')
+ source_path_include = os.path.join(source_path, 'include', 'nlohmann')
+ ports.install_header_dir(source_path_include, 'nlohmann')
+
+ # write out a dummy cpp file, to create an empty library
+ # this is needed as emscripten ports expect this, even if it is not used
+ dummy_file = os.path.join(source_path, 'dummy.cpp')
+ shared.safe_ensure_dirs(os.path.dirname(dummy_file))
+ ports.write_file(dummy_file, 'static void dummy() {}')
+
+ ports.build_port(source_path, final, 'nlohmann_json', srcs=['dummy.cpp'])
+
+ return [shared.cache.get_lib('libnlohmann_json.a', create, what='port')]
+
+
+def clear(ports, settings, shared):
+ shared.cache.erase_lib('libnlohmann_json.a')
+
+
+def process_args(ports):
+ return []
+
+
+def show():
+ return 'nlohmann-json'
diff --git a/tools/settings.py b/tools/settings.py
index 10d6ca0..8536092 100644
--- a/tools/settings.py
+++ b/tools/settings.py
@@ -47,6 +47,7 @@ PORTS_SETTINGS = {
'USE_MPG123',
'USE_GIFLIB',
'USE_FREETYPE',
+ 'USE_NLOHMANN_JSON',
'SDL2_MIXER_FORMATS',
'SDL2_IMAGE_FORMATS',
'USE_SQLITE3',
--
2.34.1

@ -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);

@ -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;
}

@ -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

@ -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));
}

@ -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 */

@ -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);
}

@ -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));
}
}

@ -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,14 +119,8 @@ template <> const char *GetClassName<AIInfo, ScriptType::AI>() { return "AIInfo"
AIInfo::AIInfo() :
min_loadable_version(0),
use_as_random(false),
api_version(nullptr)
{
}
AIInfo::~AIInfo()
use_as_random(false)
{
free(this->api_version);
}
bool AIInfo::CanLoadFromVersion(int version) const
@ -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;
}

@ -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 */

@ -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);
}

@ -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;

@ -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);

@ -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;

@ -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;

@ -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 */

@ -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);
};

@ -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"

@ -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;

@ -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);
};

@ -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();
}
}

@ -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;

@ -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 */

@ -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();

@ -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;
}

@ -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

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

@ -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 */

@ -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;
}

@ -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();

@ -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();

@ -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;

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

@ -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);
}

@ -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);

@ -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);

@ -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.

@ -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);
}

@ -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.

@ -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);

@ -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;
}

@ -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. */

@ -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 */

@ -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.

@ -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));
}

@ -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 */

@ -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);
}

@ -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();

@ -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;
}

@ -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 */

@ -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());
}

@ -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;

@ -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);

@ -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;

@ -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;
}

@ -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;

@ -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 */

@ -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);

@ -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;

@ -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;

@ -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;
}

@ -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;
}
/**

@ -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;

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

@ -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()

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

@ -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:
/**

@ -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 */

@ -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"

@ -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

@ -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 */

@ -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

@ -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

@ -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);

@ -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

@ -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/");
}

@ -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'

@ -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.

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

@ -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. */

@ -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();
}
}

@ -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));
}
}

@ -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.

@ -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());

@ -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

@ -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)

@ -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;
/* 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);
/* 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);
tb_buf = ChatTabCompletionFindText(pre_buf);
tb_len = strlen(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();
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

@ -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"

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

@ -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);
}

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

Loading…
Cancel
Save