Merge branch 'master' into jgrpp

# Conflicts:
#	.gitignore
#	CMakeLists.txt
#	src/3rdparty/optional/optional.hpp
#	src/group_cmd.cpp
#	src/industry_cmd.cpp
#	src/misc_gui.cpp
#	src/video/sdl2_v.cpp
pull/217/head
Jonathan G Rennison 3 years ago
commit 43980639de

@ -10,6 +10,55 @@ env:
CTEST_OUTPUT_ON_FAILURE: 1
jobs:
emscripten:
name: Emscripten
runs-on: ubuntu-20.04
container:
# If you change this version, change the number in the cache step too.
image: emscripten/emsdk:2.0.10
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup cache
uses: actions/cache@v2
with:
path: /emsdk/upstream/emscripten/cache
key: 2.0.10-${{ runner.os }}
- name: Build (host tools)
run: |
mkdir build-host
cd build-host
echo "::group::CMake"
cmake .. -DOPTION_TOOLS_ONLY=ON
echo "::endgroup::"
echo "::group::Build"
echo "Running on $(nproc) cores"
make -j$(nproc) tools
echo "::endgroup::"
- name: Install GCC problem matcher
uses: ammaraskar/gcc-problem-matcher@master
- name: Build
run: |
mkdir build
cd build
echo "::group::CMake"
emcmake cmake .. -DHOST_BINARY_DIR=../build-host
echo "::endgroup::"
echo "::group::Build"
echo "Running on $(nproc) cores"
emmake make -j$(nproc)
echo "::endgroup::"
linux:
name: Linux

@ -0,0 +1,133 @@
name: Preview build
on:
repository_dispatch:
types:
- Preview*
jobs:
preview:
name: Build preview
runs-on: ubuntu-20.04
container:
# If you change this version, change the number in the cache step too.
image: emscripten/emsdk:2.0.10
# uid=1001(runner) gid=121(docker)
options: -u 1001:121
steps:
- name: Update deployment status to in progress
uses: octokit/request-action@v2.x
with:
route: POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
mediaType: |
previews:
- ant-man
- flash
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
deployment_id: ${{ github.event.client_payload.deployment_id }}
state: in_progress
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.event.client_payload.sha }}
- name: Name branch
run: |
name=$(echo "${{ github.event.client_payload.folder }}")
git checkout -b ${name}
- name: Setup cache
uses: actions/cache@v2
with:
path: /emsdk/upstream/emscripten/cache
key: 2.0.10-${{ runner.os }}
- name: Build (host tools)
run: |
mkdir build-host
cd build-host
echo "::group::CMake"
cmake .. -DOPTION_TOOLS_ONLY=ON
echo "::endgroup::"
echo "::group::Build"
echo "Running on $(nproc) cores"
make -j$(nproc) tools
echo "::endgroup::"
- name: Install GCC problem matcher
uses: ammaraskar/gcc-problem-matcher@master
- name: Build
run: |
mkdir build
cd build
echo "::group::CMake"
emcmake cmake .. \
-DHOST_BINARY_DIR=../build-host \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
# EOF
echo "::endgroup::"
echo "::group::Build"
echo "Running on $(nproc) cores"
emmake make -j$(nproc)
echo "::endgroup::"
- name: Publish preview
run: |
# setuptools is missing in this Docker image, which breaks installing
# awscli. So we need to do this in two steps to recover sanity.
pip3 install setuptools
pip3 install awscli
~/.local/bin/aws s3 cp --only-show-errors build/openttd.data s3://${{ secrets.PREVIEW_S3_BUCKET }}/${{ github.event.client_payload.folder }}/
~/.local/bin/aws s3 cp --only-show-errors build/openttd.html s3://${{ secrets.PREVIEW_S3_BUCKET }}/${{ github.event.client_payload.folder }}/
~/.local/bin/aws s3 cp --only-show-errors build/openttd.js s3://${{ secrets.PREVIEW_S3_BUCKET }}/${{ github.event.client_payload.folder }}/
~/.local/bin/aws s3 cp --only-show-errors build/openttd.wasm s3://${{ secrets.PREVIEW_S3_BUCKET }}/${{ github.event.client_payload.folder }}/
# Invalidate the cache of the CloudFront distribution
~/.local/bin/aws cloudfront create-invalidation --distribution-id ${{ secrets.PREVIEW_CF_DISTRIBUTION_ID }} --paths "/${{ github.event.client_payload.folder }}/*"
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Update deployment status to success
uses: octokit/request-action@v2.x
with:
route: POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
mediaType: |
previews:
- ant-man
- flash
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
deployment_id: ${{ github.event.client_payload.deployment_id }}
state: success
environment_url: https://preview.openttd.org/${{ github.event.client_payload.folder }}/
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: failure()
name: Update deployment status to failure
uses: octokit/request-action@v2.x
with:
route: POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
mediaType: |
previews:
- ant-man
- flash
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
deployment_id: ${{ github.event.client_payload.deployment_id }}
state: failure
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}

@ -0,0 +1,66 @@
name: Preview label
on:
pull_request_target:
types:
- labeled
env:
TEAM_CORE_DEVELOPER: core-developers
jobs:
check_preview_label:
name: Check for preview label
if: github.event.action == 'labeled' && github.event.label.name == 'preview'
runs-on: ubuntu-20.04
steps:
- name: Check if label was added by core developer
id: core_developer
continue-on-error: true
uses: octokit/request-action@v2.x
with:
route: GET /orgs/OpenTTD/teams/${{ env.TEAM_CORE_DEVELOPER }}/memberships/${{ github.event.sender.login }}
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: steps.core_developer.outcome == 'failure'
name: Remove preview label if not core developer
uses: octokit/request-action@v2.x
with:
route: DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/preview
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
issue_number: ${{ github.event.number }}
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: steps.core_developer.outcome == 'success'
name: Create deployment
id: deployment
uses: octokit/request-action@v2.x
with:
route: POST /repos/{owner}/{repo}/deployments
mediaType: |
previews:
- ant-man
- flash
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
ref: ${{ github.event.pull_request.head.sha }}
task: deploy:preview
auto_merge: false
required_contexts: "[]"
environment: preview-pr-${{ github.event.number }}
description: "Preview for Pull Request #${{ github.event.number }}"
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: steps.core_developer.outcome == 'success'
name: Trigger 'preview build'
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
event-type: "Preview build #${{ github.event.number }}"
client-payload: '{"folder": "pr${{ github.event.number }}", "sha": "${{ github.event.pull_request.head.sha }}", "deployment_id": "${{ fromJson(steps.deployment.outputs.data).id }}"}'

@ -0,0 +1,66 @@
name: Preview push
on:
pull_request_target:
types:
- synchronize
jobs:
check_new_preview:
name: Check preview needs update
runs-on: ubuntu-20.04
steps:
- name: Check if earlier preview exists
id: earlier_preview
uses: octokit/request-action@v2.x
with:
route: GET /repos/{owner}/{repo}/deployments
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
environment: preview-pr-${{ github.event.number }}
per_page: 1
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: toJson(fromJson(steps.earlier_preview.outputs.data)) != '[]'
name: Check for preview label
id: preview_label
uses: octokit/request-action@v2.x
with:
route: GET /repos/{owner}/{repo}/issues/{issue_number}/labels
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
issue_number: ${{ github.event.number }}
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: toJson(fromJson(steps.earlier_preview.outputs.data)) != '[]' && contains(fromJson(steps.preview_label.outputs.data).*.name, 'preview')
name: Create deployment
id: deployment
uses: octokit/request-action@v2.x
with:
route: POST /repos/{owner}/{repo}/deployments
mediaType: |
previews:
- ant-man
- flash
owner: ${{ github.event.repository.owner.login }}
repo: ${{ github.event.repository.name }}
ref: ${{ github.event.pull_request.head.sha }}
task: deploy:preview
auto_merge: false
required_contexts: "[]"
environment: preview-pr-${{ github.event.number }}
description: "Preview for Pull Request #${{ github.event.number }}"
env:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- if: toJson(fromJson(steps.earlier_preview.outputs.data)) != '[]' && contains(fromJson(steps.preview_label.outputs.data).*.name, 'preview')
name: Trigger 'preview build'
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
event-type: "Preview build #${{ github.event.number }}"
client-payload: '{"folder": "pr${{ github.event.number }}", "sha": "${{ github.event.pull_request.head.sha }}", "deployment_id": "${{ fromJson(steps.deployment.outputs.data).id }}"}'

2
.gitignore vendored

@ -1,5 +1,5 @@
/.vs
/build*/
/build*
CMakeSettings.json
docs/aidocs/*
docs/gamedocs/*

@ -10,6 +10,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the bin directory")
endif()
if (EMSCRIPTEN)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/os/emscripten/cmake")
endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9)
@ -27,6 +31,61 @@ set_directory_options()
include(Static)
set_static_if_needed()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_EXPORT_COMPILE_COMMANDS YES)
# An empty target for the tools
add_custom_target(tools)
include(Endian)
add_endian_definition()
include(CompileFlags)
compile_flags()
if(APPLE OR UNIX)
add_definitions(-DUNIX)
endif()
if(UNIX)
find_package(Doxygen)
endif()
list(APPEND GENERATED_SOURCE_FILES "${CMAKE_BINARY_DIR}/generated/rev.cpp")
if(WIN32)
list(APPEND GENERATED_SOURCE_FILES "${CMAKE_BINARY_DIR}/generated/ottdres.rc")
endif()
# Documentation
if(DOXYGEN_EXECUTABLE)
add_custom_target(docs)
add_custom_target(docs_source
${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/docs
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Generating documentation for source"
)
add_dependencies(docs_source
find_version
)
add_dependencies(docs
docs_source
)
endif()
include(AddCustomXXXTimestamp)
if(OPTION_TOOLS_ONLY)
if(HOST_BINARY_DIR)
unset(HOST_BINARY_DIR CACHE)
endif()
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
return()
endif()
# Prefer -pthread over -lpthread, which is often the better option of the two.
set(CMAKE_THREAD_PREFER_PTHREAD YES)
# Make sure we have Threads available.
@ -98,10 +157,6 @@ find_package(Xaudio2)
find_package(Grfcodec)
if(UNIX)
find_package(Doxygen)
endif()
# IPO is only properly supported from CMake 3.9. Despite the fact we are
# CMake 3.5, still enable IPO if we detect we are 3.9+.
if(POLICY CMP0069)
@ -129,47 +184,7 @@ if(APPLE)
endif()
endif()
if(MSVC)
# C++17 for MSVC
set(CMAKE_CXX_STANDARD 17)
else()
# C++11 for all other targets
set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_EXPORT_COMPILE_COMMANDS YES)
list(APPEND GENERATED_SOURCE_FILES "${CMAKE_BINARY_DIR}/generated/rev.cpp")
if(WIN32)
list(APPEND GENERATED_SOURCE_FILES "${CMAKE_BINARY_DIR}/generated/ottdres.rc")
endif()
# An empty target for the tools
add_custom_target(tools)
# Documentation
if(DOXYGEN_EXECUTABLE)
add_custom_target(docs)
add_custom_target(docs_source
${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/docs
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Generating documentation for source"
)
add_dependencies(docs_source
find_version
)
add_dependencies(docs
docs_source
)
endif()
include(SourceList)
include(Endian)
add_endian_definition()
# Needed by rev.cpp
include_directories(${CMAKE_SOURCE_DIR}/src)
@ -178,9 +193,6 @@ include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/squirrel/include)
include(MSVCFilters)
include(CompileFlags)
compile_flags()
add_executable(openttd WIN32 ${GENERATED_SOURCE_FILES})
set_target_properties(openttd PROPERTIES OUTPUT_NAME "${BINARY_NAME}")
# All other files are added via target_sources()
@ -215,7 +227,6 @@ else()
)
endif()
include(AddCustomXXXTimestamp)
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
add_subdirectory(${CMAKE_SOURCE_DIR}/media/baseset)
add_subdirectory(${CMAKE_SOURCE_DIR}/bin)
@ -248,10 +259,6 @@ endif()
set_target_properties(openttd PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")
process_compile_flags()
if(APPLE OR UNIX)
add_definitions(-DUNIX)
endif()
include(LinkPackage)
link_package(PNG TARGET PNG::PNG ENCOURAGED)
link_package(ZLIB TARGET ZLIB::ZLIB ENCOURAGED)
@ -288,6 +295,39 @@ if(APPLE)
)
endif()
if(EMSCRIPTEN)
add_library(WASM::WASM INTERFACE IMPORTED)
# Allow heap-growth, and start with a bigger memory size.
target_link_libraries(WASM::WASM INTERFACE "-s ALLOW_MEMORY_GROWTH=1")
target_link_libraries(WASM::WASM INTERFACE "-s INITIAL_MEMORY=33554432")
# Export functions to Javascript.
target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_FUNCTIONS='[\"_main\", \"_em_openttd_add_server\"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
# Preload all the files we generate during build.
# As we do not compile with FreeType / FontConfig, we also have no way to
# render several languages (like Chinese, ..), so where do you draw the
# line what languages to include and which not? In the end, especially as
# the more languages you add the slower downloading becomes, we decided to
# only ship the English language.
target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_BINARY_DIR}/baseset@/baseset")
target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_BINARY_DIR}/lang/english.lng@/lang/english.lng")
target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/bin/ai@/ai")
target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/bin/game@/game")
# We use IDBFS for persistent storage.
target_link_libraries(WASM::WASM INTERFACE "-lidbfs.js")
# Use custom pre-js and shell.html.
target_link_libraries(WASM::WASM INTERFACE "--pre-js ${CMAKE_SOURCE_DIR}/os/emscripten/pre.js")
target_link_libraries(WASM::WASM INTERFACE "--shell-file ${CMAKE_SOURCE_DIR}/os/emscripten/shell.html")
# Build the .html (which builds the .js, .wasm, and .data too).
set_target_properties(openttd PROPERTIES SUFFIX ".html")
target_link_libraries(openttd WASM::WASM)
endif()
if(NOT PERSONAL_DIR STREQUAL "(not set)")
add_definitions(
-DWITH_PERSONAL_DIR

@ -83,9 +83,9 @@ make
## Supported compilers
Every compiler that is supported by CMake and supports C++11, should be
Every compiler that is supported by CMake and supports C++17, should be
able to compile OpenTTD. As the exact list of compilers changes constantly,
we refer to the compiler manual to see if it supports C++11, and to CMake
we refer to the compiler manual to see if it supports C++17, and to CMake
to see if it supports your compiler.
## Compilation of base sets

@ -55,8 +55,20 @@ function(set_options)
option(OPTION_DEDICATED "Build dedicated server only (no GUI)" OFF)
option(OPTION_INSTALL_FHS "Install with Filesystem Hierarchy Standard folders" ${DEFAULT_OPTION_INSTALL_FHS})
option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" ON)
option(OPTION_USE_THREADS "Use threads" ON)
if(EMSCRIPTEN)
# Although pthreads is supported, it is not in a way yet that is
# useful for us.
option(OPTION_USE_THREADS "Use threads" OFF)
else()
option(OPTION_USE_THREADS "Use threads" ON)
endif()
option(OPTION_USE_NSIS "Use NSIS to create windows installer; enable only for stable releases" OFF)
option(OPTION_TOOLS_ONLY "Build only tools target" OFF)
option(OPTION_DOCS_ONLY "Build only docs target" OFF)
if (OPTION_DOCS_ONLY)
set(OPTION_TOOLS_ONLY ON PARENT_SCOPE)
endif()
endfunction()
# Show the values of the generic options.

@ -5,6 +5,7 @@ set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/os/macosx/openttd.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist")
set(CPACK_BUNDLE_STARTUP_COMMAND "${CMAKE_SOURCE_DIR}/os/macosx/launch.sh")
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/os/macosx/splash.png")
set(CPACK_DMG_FORMAT "UDBZ")
# Create a temporary Info.plist.in, where we will fill in the version via
# CPackProperties.cmake.in. This because at this point in time the version

@ -0,0 +1,4 @@
FROM emscripten/emsdk
COPY emsdk-liblzma.patch /
RUN cd /emsdk/upstream/emscripten && patch -p1 < /emsdk-liblzma.patch

@ -0,0 +1,40 @@
## How to build with Emscripten
Building with Emscripten works with emsdk 2.0.10 and above.
Currently there is no LibLZMA support upstream; for this we suggest to apply
the provided patch in this folder to your emsdk installation.
For convenience, a Dockerfile is supplied that does this patches for you
against upstream emsdk docker. Best way to use it:
Build the docker image:
```
docker build -t emsdk-lzma .
```
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 -j5 tools
```
Next, build the game with emscripten:
```
mkdir build
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emcmake cmake .. -DHOST_BINARY_DIR=$(pwd)/build-host -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOPTION_USE_ASSERTS=OFF
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emmake make -j5
```
And now you have in your build folder files like "openttd.html".
To run it locally, you would have to start a local webserver, like:
```
cd build
python3 -m http.server
````
Now you can play the game via http://127.0.0.1:8000/openttd.html .

@ -0,0 +1,20 @@
# LibLZMA is a recent addition to the emscripten SDK, so it is possible
# someone hasn't updated his SDK yet. Test out if the SDK supports LibLZMA.
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS "-sUSE_LIBLZMA=1")
check_cxx_source_compiles("
#include <lzma.h>
int main() { return 0; }"
LIBLZMA_FOUND
)
if (LIBLZMA_FOUND)
add_library(LibLZMA::LibLZMA INTERFACE IMPORTED)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
INTERFACE_COMPILE_OPTIONS "-sUSE_LIBLZMA=1"
INTERFACE_LINK_LIBRARIES "-sUSE_LIBLZMA=1"
)
else()
message(WARNING "You are using an emscripten SDK without LibLZMA support. Many savegames won't be able to load in OpenTTD. Please apply 'emsdk-liblzma.patch' to your local emsdk installation.")
endif()

@ -0,0 +1,7 @@
add_library(PNG::PNG INTERFACE IMPORTED)
set_target_properties(PNG::PNG PROPERTIES
INTERFACE_COMPILE_OPTIONS "-sUSE_LIBPNG=1"
INTERFACE_LINK_LIBRARIES "-sUSE_LIBPNG=1"
)
set(PNG_FOUND on)

@ -0,0 +1,7 @@
add_library(SDL2::SDL2 INTERFACE IMPORTED)
set_target_properties(SDL2::SDL2 PROPERTIES
INTERFACE_COMPILE_OPTIONS "-sUSE_SDL=2"
INTERFACE_LINK_LIBRARIES "-sUSE_SDL=2"
)
set(SDL2_FOUND on)

@ -0,0 +1,7 @@
add_library(ZLIB::ZLIB INTERFACE IMPORTED)
set_target_properties(ZLIB::ZLIB PROPERTIES
INTERFACE_COMPILE_OPTIONS "-sUSE_ZLIB=1"
INTERFACE_LINK_LIBRARIES "-sUSE_ZLIB=1"
)
set(ZLIB_FOUND on)

@ -0,0 +1,213 @@
From 90dd4d4c6b1cedec338ff5b375fffca93700f7bc Mon Sep 17 00:00:00 2001
From: milek7 <me@milek7.pl>
Date: Tue, 8 Dec 2020 01:03:31 +0100
Subject: [PATCH] Add liblzma port
---
Source: https://github.com/emscripten-core/emscripten/pull/12990
Modifed by OpenTTD to have the bare minimum needed to work. Otherwise there
are constantly conflicts when trying to apply this patch to different versions
of emsdk.
diff --git a/embuilder.py b/embuilder.py
index 818262190ed..ab7d5adb7b2 100755
--- a/embuilder.py
+++ b/embuilder.py
@@ -60,6 +60,7 @@
'harfbuzz',
'icu',
'libjpeg',
+ 'liblzma',
'libpng',
'ogg',
'regal',
@@ -197,6 +198,8 @@ def main():
build_port('ogg', libname('libogg'))
elif what == 'libjpeg':
build_port('libjpeg', libname('libjpeg'))
+ elif what == 'liblzma':
+ build_port('liblzma', libname('liblzma'))
elif what == 'libpng':
build_port('libpng', libname('libpng'))
elif what == 'sdl2':
diff --git a/src/settings.js b/src/settings.js
index 61cd98939ba..be6fcb678c6 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -1197,6 +1197,9 @@ var USE_BZIP2 = 0;
// 1 = use libjpeg from emscripten-ports
var USE_LIBJPEG = 0;
+// 1 = use liblzma from emscripten-ports
+var USE_LIBLZMA = 0;
+
// 1 = use libpng from emscripten-ports
var USE_LIBPNG = 0;
diff --git a/tools/ports/liblzma.py b/tools/ports/liblzma.py
new file mode 100644
index 00000000000..e9567ef36ff
--- /dev/null
+++ b/tools/ports/liblzma.py
@@ -0,0 +1,160 @@
+# Copyright 2020 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
+import shutil
+
+VERSION = '5.2.5'
+HASH = '7443674247deda2935220fbc4dfc7665e5bb5a260be8ad858c8bd7d7b9f0f868f04ea45e62eb17c0a5e6a2de7c7500ad2d201e2d668c48ca29bd9eea5a73a3ce'
+
+
+def needed(settings):
+ return settings.USE_LIBLZMA
+
+
+def get(ports, settings, shared):
+ libname = ports.get_lib_name('liblzma')
+ ports.fetch_project('liblzma', 'https://tukaani.org/xz/xz-' + VERSION + '.tar.gz', 'xz-' + VERSION, sha512hash=HASH)
+
+ def create():
+ ports.clear_project_build('liblzma')
+
+ source_path = os.path.join(ports.get_dir(), 'liblzma', 'xz-' + VERSION)
+ dest_path = os.path.join(ports.get_build_dir(), 'liblzma')
+
+ shared.try_delete(dest_path)
+ os.makedirs(dest_path)
+ shutil.rmtree(dest_path, ignore_errors=True)
+ shutil.copytree(source_path, dest_path)
+
+ build_flags = ['-DHAVE_CONFIG_H', '-DTUKLIB_SYMBOL_PREFIX=lzma_', '-fvisibility=hidden']
+ exclude_dirs = ['xzdec', 'xz', 'lzmainfo']
+ exclude_files = ['crc32_small.c', 'crc64_small.c', 'crc32_tablegen.c', 'crc64_tablegen.c', 'price_tablegen.c', 'fastpos_tablegen.c'
+ 'tuklib_exit.c', 'tuklib_mbstr_fw.c', 'tuklib_mbstr_width.c', 'tuklib_open_stdxxx.c', 'tuklib_progname.c']
+ include_dirs_rel = ['../common', 'api', 'common', 'check', 'lz', 'rangecoder', 'lzma', 'delta', 'simple']
+
+ open(os.path.join(dest_path, 'src', 'config.h'), 'w').write(config_h)
+
+ final = os.path.join(dest_path, libname)
+ include_dirs = [os.path.join(dest_path, 'src', 'liblzma', p) for p in include_dirs_rel]
+ ports.build_port(os.path.join(dest_path, 'src'), final, flags=build_flags, exclude_dirs=exclude_dirs, exclude_files=exclude_files, includes=include_dirs)
+
+ ports.install_headers(os.path.join(dest_path, 'src', 'liblzma', 'api'), 'lzma.h')
+ ports.install_headers(os.path.join(dest_path, 'src', 'liblzma', 'api', 'lzma'), '*.h', 'lzma')
+
+ return final
+
+ return [shared.Cache.get(libname, create, what='port')]
+
+
+def clear(ports, settings, shared):
+ shared.Cache.erase_file(ports.get_lib_name('liblzma'))
+
+
+def process_args(ports):
+ return []
+
+
+def show():
+ return 'liblzma (USE_LIBLZMA=1; public domain)'
+
+
+config_h = r'''
+#define ASSUME_RAM 128
+#define ENABLE_NLS 1
+#define HAVE_CHECK_CRC32 1
+#define HAVE_CHECK_CRC64 1
+#define HAVE_CHECK_SHA256 1
+#define HAVE_CLOCK_GETTIME 1
+#define HAVE_DCGETTEXT 1
+#define HAVE_DECL_CLOCK_MONOTONIC 1
+#define HAVE_DECL_PROGRAM_INVOCATION_NAME 1
+#define HAVE_DECODERS 1
+#define HAVE_DECODER_ARM 1
+#define HAVE_DECODER_ARMTHUMB 1
+#define HAVE_DECODER_DELTA 1
+#define HAVE_DECODER_IA64 1
+#define HAVE_DECODER_LZMA1 1
+#define HAVE_DECODER_LZMA2 1
+#define HAVE_DECODER_POWERPC 1
+#define HAVE_DECODER_SPARC 1
+#define HAVE_DECODER_X86 1
+#define HAVE_DLFCN_H 1
+#define HAVE_ENCODERS 1
+#define HAVE_ENCODER_ARM 1
+#define HAVE_ENCODER_ARMTHUMB 1
+#define HAVE_ENCODER_DELTA 1
+#define HAVE_ENCODER_IA64 1
+#define HAVE_ENCODER_LZMA1 1
+#define HAVE_ENCODER_LZMA2 1
+#define HAVE_ENCODER_POWERPC 1
+#define HAVE_ENCODER_SPARC 1
+#define HAVE_ENCODER_X86 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FUTIMENS 1
+#define HAVE_GETOPT_H 1
+#define HAVE_GETOPT_LONG 1
+#define HAVE_GETTEXT 1
+#define HAVE_IMMINTRIN_H 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_MBRTOWC 1
+#define HAVE_MEMORY_H 1
+#define HAVE_MF_BT2 1
+#define HAVE_MF_BT3 1
+#define HAVE_MF_BT4 1
+#define HAVE_MF_HC3 1
+#define HAVE_MF_HC4 1
+#define HAVE_OPTRESET 1
+#define HAVE_POSIX_FADVISE 1
+#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1
+#define HAVE_PTHREAD_PRIO_INHERIT 1
+#define HAVE_STDBOOL_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_UINTPTR_T 1
+#define HAVE_UNISTD_H 1
+#define HAVE_VISIBILITY 1
+#define HAVE_WCWIDTH 1
+#define HAVE__BOOL 1
+#define HAVE___BUILTIN_ASSUME_ALIGNED 1
+#define HAVE___BUILTIN_BSWAPXX 1
+#define MYTHREAD_POSIX 1
+#define NDEBUG 1
+#define PACKAGE "xz"
+#define PACKAGE_BUGREPORT "lasse.collin@tukaani.org"
+#define PACKAGE_NAME "XZ Utils"
+#define PACKAGE_STRING "XZ Utils 5.2.5"
+#define PACKAGE_TARNAME "xz"
+#define PACKAGE_VERSION "5.2.5"
+#define SIZEOF_SIZE_T 4
+#define STDC_HEADERS 1
+#define TUKLIB_CPUCORES_SYSCONF 1
+#define TUKLIB_FAST_UNALIGNED_ACCESS 1
+#define TUKLIB_PHYSMEM_SYSCONF 1
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+#define VERSION "5.2.5"
+'''

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

@ -0,0 +1,93 @@
Module.arguments.push('-mnull', '-snull', '-vsdl:relative_mode');
Module['websocket'] = { url: function(host, port, proto) {
/* openttd.org hosts a WebSocket proxy for the content service. */
if (host == "content.openttd.org" && port == 3978 && proto == "tcp") {
return "wss://content.openttd.org/";
}
/* Everything else just tries to make a default WebSocket connection.
* If you run your own server you can setup your own WebSocket proxy in
* front of it and let people connect to your server via the proxy. You
* are best to add another "if" statement as above for this. */
return null;
} };
Module.preRun.push(function() {
personal_dir = '/home/web_user/.openttd';
content_download_dir = personal_dir + '/content_download'
/* Because of the "-c" above, all user-data is stored in /user_data. */
FS.mkdir(personal_dir);
FS.mount(IDBFS, {}, personal_dir);
Module.addRunDependency('syncfs');
FS.syncfs(true, function (err) {
/* FS.mkdir() tends to fail if parent folders do not exist. */
if (!FS.analyzePath(content_download_dir).exists) {
FS.mkdir(content_download_dir);
}
if (!FS.analyzePath(content_download_dir + '/baseset').exists) {
FS.mkdir(content_download_dir + '/baseset');
}
/* Check if the OpenGFX baseset is already downloaded. */
if (!FS.analyzePath(content_download_dir + '/baseset/opengfx-0.6.0.tar').exists) {
window.openttd_downloaded_opengfx = true;
FS.createPreloadedFile(content_download_dir + '/baseset', 'opengfx-0.6.0.tar', 'https://installer.cdn.openttd.org/emscripten/opengfx-0.6.0.tar', true, true);
} else {
/* Fake dependency increase, so the counter is stable. */
Module.addRunDependency('opengfx');
Module.removeRunDependency('opengfx');
}
Module.removeRunDependency('syncfs');
});
window.openttd_syncfs_shown_warning = false;
window.openttd_syncfs = function() {
/* Copy the virtual FS to the persistent storage. */
FS.syncfs(false, function (err) { });
/* On first time, warn the user about the volatile behaviour of
* persistent storage. */
if (!window.openttd_syncfs_shown_warning) {
window.openttd_syncfs_shown_warning = true;
Module.onWarningFs();
}
}
window.openttd_exit = function() {
Module.onExit();
}
window.openttd_abort = function() {
Module.onAbort();
}
window.openttd_server_list = function() {
add_server = Module.cwrap("em_openttd_add_server", null, ["string", "number"]);
/* Add servers that support WebSocket here. Example:
* add_server("localhost", 3979); */
}
/* https://github.com/emscripten-core/emscripten/pull/12995 implements this
* properly. Till that time, we use a polyfill. */
SOCKFS.websocket_sock_ops.createPeer_ = SOCKFS.websocket_sock_ops.createPeer;
SOCKFS.websocket_sock_ops.createPeer = function(sock, addr, port)
{
let func = Module['websocket']['url'];
Module['websocket']['url'] = func(addr, port, (sock.type == 2) ? 'udp' : 'tcp');
let ret = SOCKFS.websocket_sock_ops.createPeer_(sock, addr, port);
Module['websocket']['url'] = func;
return ret;
}
});
Module.postRun.push(function() {
/* Check if we downloaded OpenGFX; if so, sync the virtual FS back to the
* IDBFS so OpenGFX is stored persistent. */
if (window['openttd_downloaded_opengfx']) {
FS.syncfs(false, function (err) { });
}
});

File diff suppressed because one or more lines are too long

@ -9075,7 +9075,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
--Accounting--
GetCosts(): -5947
Should be: -5947
GetName(): Road Vehicle 1
GetName(): Road Vehicle #1
SetName(): true
GetName(): MyVehicleName
CloneVehicle(): 13

@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

@ -1,33 +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 ottd_optional.h Header to select between native. */
#ifndef OTTD_OPTIONAL_H
#define OTTD_OPTIONAL_H
#if defined(__has_include)
# if __has_include(<version>)
# include <version>
# endif
#endif
#if (__cplusplus >= 201703L) || (defined(__cpp_lib_optional) && __cpp_lib_optional >= 201606L)
/* Native std::optional. */
#include <optional>
namespace opt = std;
#else
/* No std::optional, use local copy instead. */
#include "optional.hpp"
namespace opt = std::experimental;
#endif
#endif /* OTTD_OPTIONAL_H */

@ -1,3 +1,11 @@
add_subdirectory(script)
add_subdirectory(settingsgen)
add_subdirectory(strgen)
if(OPTION_TOOLS_ONLY)
return()
endif()
add_subdirectory(3rdparty)
add_subdirectory(ai)
add_subdirectory(blitter)
@ -11,11 +19,8 @@ add_subdirectory(network)
add_subdirectory(os)
add_subdirectory(pathfinder)
add_subdirectory(saveload)
add_subdirectory(script)
add_subdirectory(settingsgen)
add_subdirectory(sound)
add_subdirectory(spriteloader)
add_subdirectory(strgen)
add_subdirectory(table)
add_subdirectory(video)
add_subdirectory(widgets)

@ -55,15 +55,15 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
}
fetch_metadata("shortname");
for (uint i = 0; item->value.value()[i] != '\0' && i < 4; i++) {
this->shortname |= ((uint8)item->value.value()[i]) << (i * 8);
for (uint i = 0; (*item->value)[i] != '\0' && i < 4; i++) {
this->shortname |= ((uint8)(*item->value)[i]) << (i * 8);
}
fetch_metadata("version");
this->version = atoi(item->value->c_str());
item = metadata->GetItem("fallback", false);
this->fallback = (item != nullptr && item->value && item->value.value() != "0" && item->value.value() != "false");
this->fallback = (item != nullptr && item->value && *item->value != "0" && *item->value != "false");
/* For each of the file types we want to find the file, MD5 checksums and warning messages. */
IniGroup *files = ini->GetGroup("files");

@ -442,8 +442,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
/* Re-check bridge building possibility is initial bridge builindg query indicated a bridge type dependent failure */
if (query_per_bridge_type && DoCommand(end, start, type | brd_type, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)) | DC_QUERY_COST, CMD_BUILD_BRIDGE).Failed()) continue;
/* bridge is accepted, add to list */
/*C++17: BuildBridgeData &item = */ bl->emplace_back();
BuildBridgeData &item = bl->back();
BuildBridgeData &item = bl->emplace_back();
item.index = brd_type;
item.spec = GetBridgeSpec(brd_type);
/* Add to terraforming & bulldozing costs the cost of the

@ -142,8 +142,7 @@ struct SmallMap : std::vector<std::pair<T, U> > {
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;
}
/*C++17: Pair &n = */ std::vector<Pair>::emplace_back();
Pair &n = std::vector<Pair>::back();
Pair &n = std::vector<Pair>::emplace_back();
n.first = key;
return n.second;
}

@ -26,14 +26,14 @@
* | | | | | | | */
/** The original currency specifications. */
static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
{ 1, "", CF_NOEURO, "\xC2\xA3", "", 0, STR_GAME_OPTIONS_CURRENCY_GBP }, ///< british pound
{ 1, "", CF_NOEURO, u8"\u00a3", "", 0, STR_GAME_OPTIONS_CURRENCY_GBP }, ///< british pound
{ 2, "", CF_NOEURO, "$", "", 0, STR_GAME_OPTIONS_CURRENCY_USD }, ///< american dollar
{ 2, "", CF_ISEURO, "\xE2\x82\xAC", "", 0, STR_GAME_OPTIONS_CURRENCY_EUR }, ///< euro
{ 220, "", CF_NOEURO, "\xC2\xA5", "", 0, STR_GAME_OPTIONS_CURRENCY_JPY }, ///< japanese yen
{ 2, "", CF_ISEURO, u8"\u20ac", "", 0, STR_GAME_OPTIONS_CURRENCY_EUR }, ///< euro
{ 220, "", CF_NOEURO, u8"\u00a5", "", 0, STR_GAME_OPTIONS_CURRENCY_JPY }, ///< japanese yen
{ 27, "", 2002, "", NBSP "S.", 1, STR_GAME_OPTIONS_CURRENCY_ATS }, ///< austrian schilling
{ 81, "", 2002, "BEF" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_BEF }, ///< belgian franc
{ 2, "", CF_NOEURO, "CHF" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_CHF }, ///< swiss franc
{ 41, "", CF_NOEURO, "", NBSP "K\xC4\x8D", 1, STR_GAME_OPTIONS_CURRENCY_CZK }, ///< czech koruna
{ 41, "", CF_NOEURO, "", NBSP u8"K\u010d", 1, STR_GAME_OPTIONS_CURRENCY_CZK }, ///< czech koruna
{ 4, "", 2002, "DM" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_DEM }, ///< deutsche mark
{ 11, "", CF_NOEURO, "", NBSP "kr", 1, STR_GAME_OPTIONS_CURRENCY_DKK }, ///< danish krone
{ 333, "", 2002, "Pts" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_ESP }, ///< spanish peseta
@ -45,7 +45,7 @@ static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
{ 3873, "", 2002, "", NBSP "L.", 1, STR_GAME_OPTIONS_CURRENCY_ITL }, ///< italian lira
{ 4, "", 2002, "NLG" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_NLG }, ///< dutch gulden
{ 12, "", CF_NOEURO, "", NBSP "Kr", 1, STR_GAME_OPTIONS_CURRENCY_NOK }, ///< norwegian krone
{ 6, "", CF_NOEURO, "", NBSP "z\xC5\x82", 1, STR_GAME_OPTIONS_CURRENCY_PLN }, ///< polish zloty
{ 6, "", CF_NOEURO, "", NBSP u8"z\u0142", 1, STR_GAME_OPTIONS_CURRENCY_PLN }, ///< polish zloty
{ 5, "", CF_NOEURO, "", NBSP "Lei", 1, STR_GAME_OPTIONS_CURRENCY_RON }, ///< romanian leu
{ 50, "", CF_NOEURO, "", NBSP "p", 1, STR_GAME_OPTIONS_CURRENCY_RUR }, ///< russian rouble
{ 479, "", 2007, "", NBSP "SIT", 1, STR_GAME_OPTIONS_CURRENCY_SIT }, ///< slovenian tolar
@ -55,7 +55,7 @@ static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
{ 4, "", CF_NOEURO, "R$" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_BRL }, ///< brazil real
{ 31, "", 2011, "", NBSP "EEK", 1, STR_GAME_OPTIONS_CURRENCY_EEK }, ///< estonian krooni
{ 4, "", 2015, "", NBSP "Lt", 1, STR_GAME_OPTIONS_CURRENCY_LTL }, ///< lithuanian litas
{ 1850, "", CF_NOEURO, "\xE2\x82\xA9", "", 0, STR_GAME_OPTIONS_CURRENCY_KRW }, ///< south korean won
{ 1850, "", CF_NOEURO, u8"\u20a9", "", 0, STR_GAME_OPTIONS_CURRENCY_KRW }, ///< south korean won
{ 13, "", CF_NOEURO, "R" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_ZAR }, ///< south african rand
{ 1, "", CF_NOEURO, "", "", 2, STR_GAME_OPTIONS_CURRENCY_CUSTOM }, ///< custom currency (add further languages below)
{ 3, "", CF_NOEURO, "", NBSP "GEL", 1, STR_GAME_OPTIONS_CURRENCY_GEL }, ///< Georgian Lari
@ -63,9 +63,9 @@ static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
{ 80, "", CF_NOEURO, "", NBSP "rub", 1, STR_GAME_OPTIONS_CURRENCY_RUB }, ///< New Russian Ruble
{ 24, "", CF_NOEURO, "$", "", 0, STR_GAME_OPTIONS_CURRENCY_MXN }, ///< Mexican peso
{ 40, "", CF_NOEURO, "NTD" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_NTD }, ///< new taiwan dollar
{ 8, "", CF_NOEURO, "\xC2\xA5", "", 0, STR_GAME_OPTIONS_CURRENCY_CNY }, ///< chinese renminbi
{ 8, "", CF_NOEURO, u8"\u00a5", "", 0, STR_GAME_OPTIONS_CURRENCY_CNY }, ///< chinese renminbi
{ 10, "", CF_NOEURO, "HKD" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_HKD }, ///< hong kong dollar
{ 90, "", CF_NOEURO, "\xE2\x82\xB9", "", 0, STR_GAME_OPTIONS_CURRENCY_INR }, ///< Indian Rupee
{ 90, "", CF_NOEURO, u8"\u20b9", "", 0, STR_GAME_OPTIONS_CURRENCY_INR }, ///< Indian Rupee
};
/** Array of currencies used by the system */

@ -496,8 +496,7 @@ void EngineOverrideManager::ResetToDefaultMapping()
this->clear();
for (VehicleType type = VEH_TRAIN; type <= VEH_AIRCRAFT; type++) {
for (uint internal_id = 0; internal_id < _engine_counts[type]; internal_id++) {
/*C++17: EngineIDMapping &eid = */ this->emplace_back();
EngineIDMapping &eid = this->back();
EngineIDMapping &eid = this->emplace_back();
eid.type = type;
eid.grfid = INVALID_GRFID;
eid.internal_id = internal_id;

@ -126,8 +126,7 @@ public:
*/
inline FiosItem *Append()
{
/*C++17: return &*/ this->files.emplace_back();
return &this->files.back();
return &this->files.emplace_back();
}
/**

@ -26,7 +26,6 @@
#include "safeguards.h"
static const int ASCII_LETTERSTART = 32; ///< First printable ASCII letter.
static const int MAX_FONT_SIZE = 72; ///< Maximum font size.
/** Default heights for the different sizes of fonts. */
static const int _default_font_height[FS_END] = {10, 6, 18, 10};
@ -200,6 +199,8 @@ void UpdateFontHeightCache()
FreeTypeSettings _freetype;
static const int MAX_FONT_SIZE = 72; ///< Maximum font size.
static const byte FACE_COLOUR = 1;
static const byte SHADOW_COLOUR = 2;

@ -2090,6 +2090,30 @@ void SetAnimatedMouseCursor(const AnimCursor *table)
SwitchAnimatedCursor();
}
/**
* Update cursor position on mouse movement for relative modes.
* @param delta_x How much change in the X position.
* @param delta_y How much change in the Y position.
*/
void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y)
{
if (this->fix_at) {
this->delta.x = delta_x;
this->delta.y = delta_y;
} else {
int last_position_x = this->pos.x;
int last_position_y = this->pos.y;
this->pos.x = Clamp(this->pos.x + delta_x, 0, _cur_resolution.width - 1);
this->pos.y = Clamp(this->pos.y + delta_y, 0, _cur_resolution.height - 1);
this->delta.x = last_position_x - this->pos.x;
this->delta.y = last_position_y - this->pos.y;
this->dirty = true;
}
}
/**
* Update cursor position on mouse movement.
* @param x New X position.

@ -144,6 +144,7 @@ struct CursorVars {
/* Drag data */
bool vehchain; ///< vehicle chain is dragged
void UpdateCursorPositionRelative(int delta_x, int delta_y);
bool UpdateCursorPosition(int x, int y, bool queued_warp);
private:
@ -163,7 +164,9 @@ struct DrawPixelInfo {
union Colour {
uint32 data; ///< Conversion of the channel information to a 32 bit number.
struct {
#if TTD_ENDIAN == TTD_BIG_ENDIAN
#if defined(__EMSCRIPTEN__)
uint8 r, g, b, a; ///< colour channels as used in browsers
#elif TTD_ENDIAN == TTD_BIG_ENDIAN
uint8 a, r, g, b; ///< colour channels in BE order
#else
uint8 b, g, r, a; ///< colour channels in LE order
@ -178,7 +181,9 @@ union Colour {
* @param a The channel for the alpha/transparency.
*/
Colour(uint8 r, uint8 g, uint8 b, uint8 a = 0xFF) :
#if TTD_ENDIAN == TTD_BIG_ENDIAN
#if defined(__EMSCRIPTEN__)
r(r), g(g), b(b), a(a)
#elif TTD_ENDIAN == TTD_BIG_ENDIAN
a(a), r(r), g(g), b(b)
#else
b(b), g(g), r(r), a(a)

@ -504,11 +504,11 @@ bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *ful
IniItem *item;
fetch_metadata("palette");
this->palette = (item->value.value()[0] == 'D' || item->value.value()[0] == 'd') ? PAL_DOS : PAL_WINDOWS;
this->palette = ((*item->value)[0] == 'D' || (*item->value)[0] == 'd') ? PAL_DOS : PAL_WINDOWS;
/* Get optional blitter information. */
item = metadata->GetItem("blitter", false);
this->blitter = (item != nullptr && item->value.value()[0] == '3') ? BLT_32BPP : BLT_8BPP;
this->blitter = (item != nullptr && (*item->value)[0] == '3') ? BLT_32BPP : BLT_8BPP;
}
return ret;
}

@ -473,6 +473,8 @@ CommandCost CmdAlterGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type, 1);
InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
InvalidateWindowData(WC_COMPANY_COLOUR, g->owner, g->vehicle_type);
InvalidateWindowClassesData(WC_VEHICLE_VIEW);
InvalidateWindowClassesData(WC_VEHICLE_DETAILS);
InvalidateWindowData(WC_TEMPLATEGUI_MAIN, 0, 0, 0);
}
@ -609,6 +611,8 @@ CommandCost CmdAddVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, u
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
InvalidateWindowData(GetWindowClassForVehicleType(v->type), VehicleListIdentifier(VL_GROUP_LIST, v->type, _current_company).Pack());
InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
InvalidateWindowData(WC_VEHICLE_DETAILS, v->index);
}
return CommandCost();

@ -2633,8 +2633,7 @@ struct IndustryCargoesWindow : public Window {
_displayed_industries.set(it);
this->fields.clear();
/*C++17: CargoesRow &row = */ this->fields.emplace_back();
CargoesRow &row = this->fields.back();
CargoesRow &row = this->fields.emplace_back();
row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
row.columns[1].MakeEmpty(CFT_SMALL_EMPTY);
row.columns[2].MakeEmpty(CFT_SMALL_EMPTY);
@ -2649,8 +2648,7 @@ struct IndustryCargoesWindow : public Window {
int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
int num_indrows = max(3, max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
for (int i = 0; i < num_indrows; i++) {
/*C++17: CargoesRow &row = */ this->fields.emplace_back();
CargoesRow &row = this->fields.back();
CargoesRow &row = this->fields.emplace_back();
row.columns[0].MakeEmpty(CFT_EMPTY);
row.columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
row.columns[2].MakeEmpty(CFT_EMPTY);
@ -2713,8 +2711,7 @@ struct IndustryCargoesWindow : public Window {
_displayed_industries.reset();
this->fields.clear();
/*C++17: CargoesRow &row = */ this->fields.emplace_back();
CargoesRow &row = this->fields.back();
CargoesRow &row = this->fields.emplace_back();
row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
row.columns[1].MakeEmpty(CFT_SMALL_EMPTY);
row.columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
@ -2727,8 +2724,7 @@ struct IndustryCargoesWindow : public Window {
int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
int num_indrows = max(num_supp, num_cust);
for (int i = 0; i < num_indrows; i++) {
/*C++17: CargoesRow &row = */ this->fields.emplace_back();
CargoesRow &row = this->fields.back();
CargoesRow &row = this->fields.emplace_back();
row.columns[0].MakeEmpty(CFT_EMPTY);
row.columns[1].MakeCargo(&cid, 1);
row.columns[2].MakeEmpty(CFT_EMPTY);

@ -13,6 +13,9 @@
#include "string_func.h"
#include "fileio_func.h"
#include <fstream>
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
#endif
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500)
# include <unistd.h>
@ -115,6 +118,10 @@ bool IniFile::SaveToDisk(const char *filename)
}
#endif
#ifdef __EMSCRIPTEN__
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
#endif
return true;
}

@ -12,7 +12,7 @@
#include "fileio_type.h"
#include <string>
#include "3rdparty/optional/ottd_optional.h"
#include <optional>
#include <string>
@ -27,7 +27,7 @@ enum IniGroupType {
struct IniItem {
IniItem *next; ///< The next item in this group
std::string name; ///< The name of this item
opt::optional<std::string> value; ///< The value of this item
std::optional<std::string> value; ///< The value of this item
std::string comment; ///< The comment associated with this item
IniItem(struct IniGroup *parent, const std::string &name);

@ -5825,10 +5825,10 @@ STR_INDUSTRY_NAME_SUGAR_MINE :Sugar Mine
##id 0x6000
STR_SV_EMPTY :
STR_SV_UNNAMED :Unnamed
STR_SV_TRAIN_NAME :Train {COMMA}
STR_SV_ROAD_VEHICLE_NAME :Road Vehicle {COMMA}
STR_SV_SHIP_NAME :Ship {COMMA}
STR_SV_AIRCRAFT_NAME :Aircraft {COMMA}
STR_SV_TRAIN_NAME :Train #{COMMA}
STR_SV_ROAD_VEHICLE_NAME :Road Vehicle #{COMMA}
STR_SV_SHIP_NAME :Ship #{COMMA}
STR_SV_AIRCRAFT_NAME :Aircraft #{COMMA}
STR_SV_STNAME :{STRING1}
STR_SV_STNAME_NORTH :{STRING1} North
@ -6135,6 +6135,7 @@ STR_FORMAT_BUOY_NAME :{TOWN} Buoy
STR_FORMAT_BUOY_NAME_SERIAL :{TOWN} Buoy #{COMMA}
STR_FORMAT_COMPANY_NUM :(Company {COMMA})
STR_FORMAT_GROUP_NAME :Group {COMMA}
STR_FORMAT_GROUP_VEHICLE_NAME :{GROUP} #{COMMA}
STR_FORMAT_INDUSTRY_NAME :{TOWN} {STRING}
STR_FORMAT_WAYPOINT_NAME :{TOWN} Waypoint
STR_FORMAT_WAYPOINT_NAME_SERIAL :{TOWN} Waypoint #{COMMA}

@ -450,73 +450,73 @@ static WindowDesc _about_desc(
);
static const char * const _credits[] = {
"Original design by Chris Sawyer",
"Original graphics by Simon Foster",
"",
"The OpenTTD team (in alphabetical order):",
" Grzegorz Duczy\xC5\x84ski (adf88) - General coding (since 1.7.2)",
" Albert Hofkamp (Alberth) - GUI expert (since 0.7)",
" Matthijs Kooijman (blathijs) - Pathfinder-guru, Debian port (since 0.3)",
" Ulf Hermann (fonsinchen) - Cargo Distribution (since 1.3)",
" Christoph Elsenhans (frosch) - General coding (since 0.6)",
" Lo\xC3\xAF""c Guilloux (glx) - General / Windows Expert (since 0.4.5)",
" Charles Pigott (LordAro) - General / Correctness police (since 1.9)",
" Michael Lutz (michi_cc) - Path based signals (since 0.7)",
" Niels Martin Hansen (nielsm) - Music system, general coding (since 1.9)",
" Owen Rudge (orudge) - Forum host, OS/2 port (since 0.1)",
" Peter Nelson (peter1138) - Spiritual descendant from NewGRF gods (since 0.4.5)",
" Ingo von Borstel (planetmaker) - General, Support (since 1.1)",
" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)",
" Jos\xC3\xA9 Soler (Terkhen) - General coding (since 1.0)",
" Leif Linse (Zuu) - AI/Game Script (since 1.2)",
"",
"Inactive Developers:",
" Jean-Fran\xC3\xA7ois Claeys (Belugas) - GUI, NewGRF and more (0.4.5 - 1.0)",
" Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles (0.3 - 0.7)",
" Victor Fischer (Celestar) - Programming everywhere you need him to (0.3 - 0.6)",
" Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;) (0.4.5 - 0.6)",
" Jonathan Coome (Maedhros) - High priest of the NewGRF Temple (0.5 - 0.6)",
" Attila B\xC3\xA1n (MiHaMiX) - Developer WebTranslator 1 and 2 (0.3 - 0.5)",
" Zden\xC4\x9Bk Sojka (SmatZ) - Bug finder and fixer (0.6 - 1.3)",
" Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)",
" Patric Stout (TrueBrain) - NoAI, NoGo, Network (0.3 - 1.2), sys op (active)",
" Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)",
"",
"Retired Developers:",
" Tam\xC3\xA1s Farag\xC3\xB3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)",
" Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3 - 0.3)",
" Emil Djupfeld (egladil) - MacOSX (0.4.5 - 0.6)",
" Simon Sasburg (HackyKid) - Many bugfixes (0.4 - 0.4.5)",
" Ludvig Strigeus (ludde) - Original author of OpenTTD, main coder (0.1 - 0.3)",
" Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3)",
" Petr Baudi\xC5\xA1 (pasky) - Many patches, NewGRF support (0.3 - 0.3)",
" Benedikt Br\xC3\xBCggemeier (skidd13) - Bug fixer and code reworker (0.6 - 0.7)",
" Serge Paquet (vurlix) - 2nd contributor after ludde (0.1 - 0.3)",
"",
"Special thanks go out to:",
" Josef Drexler - For his great work on TTDPatch",
" Marcin Grzegorczyk - Track foundations and for describing TTD internals",
" Stefan Mei\xC3\x9Fner (sign_de) - For his work on the console",
" Mike Ragsdale - OpenTTD installer",
" Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
" Richard Kempton (richK) - additional airports, initial TGP implementation",
"",
" Alberto Demichelis - Squirrel scripting language \xC2\xA9 2003-2008",
" L. Peter Deutsch - MD5 implementation \xC2\xA9 1999, 2000, 2002",
" Michael Blunck - Pre-signals and semaphores \xC2\xA9 2003",
" George - Canal/Lock graphics \xC2\xA9 2003-2004",
" Andrew Parkhouse (andythenorth) - River graphics",
" David Dallaston (Pikka) - Tram tracks",
" All Translators - Who made OpenTTD a truly international game",
" Bug Reporters - Without whom OpenTTD would still be full of bugs!",
"",
"",
"Developer of this patchpack:",
" Jonathan G. Rennison (JGR)",
"",
"",
"And last but not least:",
" Chris Sawyer - For an amazing game!"
u8"Original design by Chris Sawyer",
u8"Original graphics by Simon Foster",
u8"",
u8"The OpenTTD team (in alphabetical order):",
u8" Grzegorz Duczy\u0144ski (adf88) - General coding (since 1.7.2)",
u8" Albert Hofkamp (Alberth) - GUI expert (since 0.7)",
u8" Matthijs Kooijman (blathijs) - Pathfinder-guru, Debian port (since 0.3)",
u8" Ulf Hermann (fonsinchen) - Cargo Distribution (since 1.3)",
u8" Christoph Elsenhans (frosch) - General coding (since 0.6)",
u8" Lo\u00efc Guilloux (glx) - General / Windows Expert (since 0.4.5)",
u8" Charles Pigott (LordAro) - General / Correctness police (since 1.9)",
u8" Michael Lutz (michi_cc) - Path based signals (since 0.7)",
u8" Niels Martin Hansen (nielsm) - Music system, general coding (since 1.9)",
u8" Owen Rudge (orudge) - Forum host, OS/2 port (since 0.1)",
u8" Peter Nelson (peter1138) - Spiritual descendant from NewGRF gods (since 0.4.5)",
u8" Ingo von Borstel (planetmaker) - General, Support (since 1.1)",
u8" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)",
u8" Jos\u00e9 Soler (Terkhen) - General coding (since 1.0)",
u8" Leif Linse (Zuu) - AI/Game Script (since 1.2)",
u8"",
u8"Inactive Developers:",
u8" Jean-Fran\u00e7ois Claeys (Belugas) - GUI, NewGRF and more (0.4.5 - 1.0)",
u8" Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles (0.3 - 0.7)",
u8" Victor Fischer (Celestar) - Programming everywhere you need him to (0.3 - 0.6)",
u8" Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;) (0.4.5 - 0.6)",
u8" Jonathan Coome (Maedhros) - High priest of the NewGRF Temple (0.5 - 0.6)",
u8" Attila B\u00e1n (MiHaMiX) - Developer WebTranslator 1 and 2 (0.3 - 0.5)",
u8" Zden\u011bk Sojka (SmatZ) - Bug finder and fixer (0.6 - 1.3)",
u8" Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)",
u8" Patric Stout (TrueBrain) - NoAI, NoGo, Network (0.3 - 1.2), sys op (active)",
u8" Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)",
u8"",
u8"Retired Developers:",
u8" Tam\u00e1s Farag\u00f3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)",
u8" Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3 - 0.3)",
u8" Emil Djupfeld (egladil) - MacOSX (0.4.5 - 0.6)",
u8" Simon Sasburg (HackyKid) - Many bugfixes (0.4 - 0.4.5)",
u8" Ludvig Strigeus (ludde) - Original author of OpenTTD, main coder (0.1 - 0.3)",
u8" Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3)",
u8" Petr Baudi\u0161 (pasky) - Many patches, NewGRF support (0.3 - 0.3)",
u8" Benedikt Br\u00fcggemeier (skidd13) - Bug fixer and code reworker (0.6 - 0.7)",
u8" Serge Paquet (vurlix) - 2nd contributor after ludde (0.1 - 0.3)",
u8"",
u8"Special thanks go out to:",
u8" Josef Drexler - For his great work on TTDPatch",
u8" Marcin Grzegorczyk - Track foundations and for describing TTD internals",
u8" Stefan Mei\u00dfner (sign_de) - For his work on the console",
u8" Mike Ragsdale - OpenTTD installer",
u8" Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
u8" Richard Kempton (richK) - additional airports, initial TGP implementation",
u8"",
u8" Alberto Demichelis - Squirrel scripting language \u00a9 2003-2008",
u8" L. Peter Deutsch - MD5 implementation \u00a9 1999, 2000, 2002",
u8" Michael Blunck - Pre-signals and semaphores \u00a9 2003",
u8" George - Canal/Lock graphics \u00a9 2003-2004",
u8" Andrew Parkhouse (andythenorth) - River graphics",
u8" David Dallaston (Pikka) - Tram tracks",
u8" All Translators - Who made OpenTTD a truly international game",
u8" Bug Reporters - Without whom OpenTTD would still be full of bugs!",
u8"",
u8"",
u8"Developer of this patchpack:",
u8" Jonathan G. Rennison (JGR)",
u8"",
u8"",
u8"And last but not least:",
u8" Chris Sawyer - For an amazing game!"
};
struct AboutWindow : public Window {

@ -265,6 +265,18 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *
this->address_length = (int)runp->ai_addrlen;
assert(sizeof(this->address) >= runp->ai_addrlen);
memcpy(&this->address, runp->ai_addr, runp->ai_addrlen);
#ifdef __EMSCRIPTEN__
/* Emscripten doesn't zero sin_zero, but as we compare addresses
* to see if they are the same address, we need them to be zero'd.
* Emscripten is, as far as we know, the only OS not doing this.
*
* https://github.com/emscripten-core/emscripten/issues/12998
*/
if (this->address.ss_family == AF_INET) {
sockaddr_in *address_ipv4 = (sockaddr_in *)&this->address;
memset(address_ipv4->sin_zero, 0, sizeof(address_ipv4->sin_zero));
}
#endif
break;
}
@ -297,7 +309,15 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
if (!SetNoDelay(sock)) DEBUG(net, 1, "[%s] setting TCP_NODELAY failed", type);
if (connect(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen);
#ifdef __EMSCRIPTEN__
/* Emscripten is asynchronous, and as such a connect() is still in
* progress by the time the call returns. */
if (err != 0 && errno != EINPROGRESS)
#else
if (err != 0)
#endif
{
DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, strerror(errno));
closesocket(sock);
return INVALID_SOCKET;

@ -99,6 +99,16 @@ typedef unsigned long in_addr_t;
# include <errno.h>
# include <sys/time.h>
# include <netdb.h>
# if defined(__EMSCRIPTEN__)
/* Emscripten doesn't support AI_ADDRCONFIG and errors out on it. */
# undef AI_ADDRCONFIG
# define AI_ADDRCONFIG 0
/* Emscripten says it supports FD_SETSIZE fds, but it really only supports 64.
* https://github.com/emscripten-core/emscripten/issues/1711 */
# undef FD_SETSIZE
# define FD_SETSIZE 64
# endif
#endif /* UNIX */
/* OS/2 stuff */
@ -160,6 +170,28 @@ typedef unsigned long in_addr_t;
#endif /* OS/2 */
#ifdef __EMSCRIPTEN__
/**
* Emscripten doesn't set 'addrlen' for accept(), getsockname(), getpeername()
* and recvfrom(), which confuses other functions and causes them to crash.
* This function needs to be called after these four functions to make sure
* 'addrlen' is patched up.
*
* https://github.com/emscripten-core/emscripten/issues/12996
*
* @param address The address returned by those four functions.
* @return The correct value for addrlen.
*/
static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address)
{
switch (address.ss_family) {
case AF_INET6: return sizeof(struct sockaddr_in6);
case AF_INET: return sizeof(struct sockaddr_in);
default: NOT_REACHED();
}
}
#endif
/**
* Try to set the socket into non-blocking mode.
* @param d The socket to set the non-blocking more for.
@ -167,12 +199,16 @@ typedef unsigned long in_addr_t;
*/
static inline bool SetNonBlocking(SOCKET d)
{
#ifdef _WIN32
u_long nonblocking = 1;
#ifdef __EMSCRIPTEN__
return true;
#else
# ifdef _WIN32
u_long nonblocking = 1;
# else
int nonblocking = 1;
#endif
# endif
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
#endif
}
/**
@ -197,10 +233,14 @@ static inline bool SetBlocking(SOCKET d)
*/
static inline bool SetNoDelay(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
/* XXX should this be done at all? */
int b = 1;
/* The (const char*) cast is needed for windows */
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
#endif
}

@ -42,6 +42,9 @@ public:
socklen_t sin_len = sizeof(sin);
SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len);
if (s == INVALID_SOCKET) return;
#ifdef __EMSCRIPTEN__
sin_len = FixAddrLenForEmscripten(sin);
#endif
SetNonBlocking(s); // XXX error handling?

@ -164,6 +164,9 @@ void NetworkUDPSocketHandler::ReceivePackets()
/* Did we get the bytes for the base header of the packet? */
if (nbytes <= 0) break; // No data, i.e. no packet
if (nbytes <= 2) continue; // Invalid data; try next packet
#ifdef __EMSCRIPTEN__
client_len = FixAddrLenForEmscripten(client_addr);
#endif
NetworkAddress address(client_addr, client_len);
p.PrepareToRead();

@ -1122,3 +1122,14 @@ bool IsNetworkCompatibleVersion(const char *other, bool extended)
{
return strncmp(_openttd_revision, other, (extended ? NETWORK_LONG_REVISION_LENGTH : NETWORK_REVISION_LENGTH) - 1) == 0;
}
#ifdef __EMSCRIPTEN__
extern "C" {
void CDECL em_openttd_add_server(const char *host, int port)
{
NetworkUDPQueryServer(NetworkAddress(host, port), true);
}
}
#endif

@ -23,6 +23,10 @@
#include <zlib.h>
#endif
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
#endif
#include "../safeguards.h"
extern bool HasScenario(const ContentInfo *ci, bool md5sum);
@ -296,6 +300,13 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uin
{
bytes = 0;
#ifdef __EMSCRIPTEN__
/* Emscripten is loaded via an HTTPS connection. As such, it is very
* difficult to make HTTP connections. So always use the TCP method of
* downloading content. */
fallback = true;
#endif
ContentIDList content;
for (const ContentInfo *ci : this->infos) {
if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
@ -542,6 +553,10 @@ void ClientNetworkContentSocketHandler::AfterDownload()
unlink(GetFullFilename(this->curInfo, false));
}
#ifdef __EMSCRIPTEN__
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
#endif
this->OnDownloadComplete(this->curInfo->id);
} else {
ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR);

@ -41,6 +41,9 @@
#include "../safeguards.h"
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
#endif
static void ShowNetworkStartServerWindow();
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
@ -490,6 +493,14 @@ public:
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
this->SetFocusedWidget(WID_NG_FILTER);
/* As the master-server doesn't support "websocket" servers yet, we
* let "os/emscripten/pre.js" hardcode a list of servers people can
* join. This means the serverlist is curated for now, but it is the
* best we can offer. */
#ifdef __EMSCRIPTEN__
EM_ASM(if (window["openttd_server_list"]) openttd_server_list());
#endif
this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
this->server = this->last_joined;
if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address);
@ -630,6 +641,12 @@ public:
this->GetWidget<NWidgetStacked>(WID_NG_NEWGRF_SEL)->SetDisplayedPlane(sel == nullptr || !sel->online || sel->info.grfconfig == nullptr);
this->GetWidget<NWidgetStacked>(WID_NG_NEWGRF_MISSING_SEL)->SetDisplayedPlane(sel == nullptr || !sel->online || sel->info.grfconfig == nullptr || !sel->info.version_compatible || sel->info.compatible);
#ifdef __EMSCRIPTEN__
this->SetWidgetDisabledState(WID_NG_FIND, true);
this->SetWidgetDisabledState(WID_NG_ADD, true);
this->SetWidgetDisabledState(WID_NG_START, true);
#endif
this->DrawWidgets();
}

@ -1951,8 +1951,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, cons
tmp_layout.clear();
for (;;) {
/* no relative bounding box support */
/*C++17: DrawTileSeqStruct &dtss = */ tmp_layout.emplace_back();
DrawTileSeqStruct &dtss = tmp_layout.back();
DrawTileSeqStruct &dtss = tmp_layout.emplace_back();
MemSetT(&dtss, 0);
dtss.delta_x = buf->ReadByte();
@ -5159,8 +5158,7 @@ static void NewSpriteGroup(ByteReader *buf)
/* Loop through the var adjusts. Unfortunately we don't know how many we have
* from the outset, so we shall have to keep reallocing. */
do {
/*C++17: DeterministicSpriteGroupAdjust &adjust = */ adjusts.emplace_back();
DeterministicSpriteGroupAdjust &adjust = adjusts.back();
DeterministicSpriteGroupAdjust &adjust = adjusts.emplace_back();
/* The first var adjust doesn't have an operation specified, so we set it to add. */
adjust.operation = adjusts.size() == 1 ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf->ReadByte();

@ -664,8 +664,7 @@ uint32 NewGRFSpriteLayout::PrepareLayout(uint32 orig_offset, uint32 newgrf_groun
/* Create a copy of the spritelayout, so we can modify some values.
* Also include the groundsprite into the sequence for easier processing. */
/*C++17: DrawTileSeqStruct *result = &*/ result_seq.emplace_back();
DrawTileSeqStruct *result = &result_seq.back();
DrawTileSeqStruct *result = &result_seq.emplace_back();
result->image = ground;
result->delta_x = 0;
result->delta_y = 0;
@ -675,8 +674,7 @@ uint32 NewGRFSpriteLayout::PrepareLayout(uint32 orig_offset, uint32 newgrf_groun
foreach_draw_tile_seq(dtss, this->seq) {
result_seq.push_back(*dtss);
}
result_seq.emplace_back() /*C++17: .MakeTerminator()*/;
result_seq.back().MakeTerminator();
result_seq.emplace_back().MakeTerminator();
/* Determine the var10 values the action-1-2-3 chains needs to be resolved for,
* and apply the default sprite offsets (unless disabled). */
const TileLayoutRegisters *regs = this->registers;

@ -87,6 +87,11 @@
#include "safeguards.h"
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
# include <emscripten/html5.h>
#endif
void CallLandscapeTick();
void IncreaseDate();
void MusicLoop();
@ -123,6 +128,15 @@ void CDECL usererror(const char *s, ...)
ShowOSErrorBox(buf, false);
if (VideoDriver::GetInstance() != nullptr) VideoDriver::GetInstance()->Stop();
#ifdef __EMSCRIPTEN__
emscripten_exit_pointerlock();
/* In effect, the game ends here. As emscripten_set_main_loop() caused
* the stack to be unwound, the code after MainLoop() in
* openttd_main() is never executed. */
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
EM_ASM(if (window["openttd_abort"]) openttd_abort());
#endif
exit(1);
}

@ -3293,8 +3293,7 @@ bool AfterLoadGame()
cur_skip = prev_tile_skip;
}
/*C++17: uint &this_skip = */ skip_frames.push_back(prev_tile_skip);
uint &this_skip = skip_frames.back();
uint &this_skip = skip_frames.emplace_back(prev_tile_skip);
/* The following 3 curves now take longer than before */
switch (u->state) {

@ -191,8 +191,7 @@ static void Load_EIDS()
_engine_mngr.clear();
while (SlIterateArray() != -1) {
/*C++17: EngineIDMapping *eid = &*/ _engine_mngr.emplace_back();
EngineIDMapping *eid = &_engine_mngr.back();
EngineIDMapping *eid = &_engine_mngr.emplace_back();
SlObject(eid, _engine_id_mapping_desc);
}
}

@ -46,6 +46,9 @@
#include "../scope.h"
#include <atomic>
#include <string>
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
#endif
#include "../tbtr_template_vehicle.h"
@ -2928,6 +2931,10 @@ static void SaveFileDone()
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
_sl.saveinprogress = false;
#ifdef __EMSCRIPTEN__
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
#endif
}
/** Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friends) */

@ -192,8 +192,7 @@ static void Load_WAYP()
int index;
while ((index = SlIterateArray()) != -1) {
/*C++17: OldWaypoint *wp = &*/ _old_waypoints.emplace_back();
OldWaypoint *wp = &_old_waypoints.back();
OldWaypoint *wp = &_old_waypoints.emplace_back();
wp->index = index;
SlObject(wp, _old_waypoint_desc);

@ -1,5 +1,9 @@
add_subdirectory(api)
if(OPTION_TOOLS_ONLY)
return()
endif()
add_files(
script_config.cpp
script_config.hpp

@ -131,6 +131,10 @@ foreach(API "ai;AI" "game;GS" "template;Template")
)
endforeach()
if(OPTION_TOOLS_ONLY)
return()
endif()
add_library(openttd::script_api ALIAS script_api)

@ -118,8 +118,7 @@ public:
text += stored_size;
}
while (length > 0) {
/*C++17: OutputBuffer &block =*/ this->output_buffer.emplace_back();
OutputBuffer &block = this->output_buffer.back();
OutputBuffer &block = this->output_buffer.emplace_back();
block.Clear(); // Initialize the new block.
size_t stored_size = block.Add(text, length);
length -= stored_size;

@ -22,6 +22,10 @@ if (NOT HOST_BINARY_DIR)
add_dependencies(tools strgen)
endif()
if(OPTION_TOOLS_ONLY)
return()
endif()
# Source Files
add_files(strgen_base.cpp)

@ -15,10 +15,10 @@
#include <string>
/** A non-breaking space. */
#define NBSP "\xC2\xA0"
#define NBSP u8"\u00a0"
/** A left-to-right marker, marks the next character as left-to-right. */
#define LRM "\xE2\x80\x8E"
#define LRM u8"\u200e"
/**
* Valid filter types for IsValidChar.

@ -74,8 +74,7 @@ void StringFilter::SetFilterTerm(const char *str)
/* Add to word */
if (word == nullptr) {
/*C++17: word = &*/ this->word_index.push_back({dest, false});
word = &this->word_index.back();
word = &this->word_index.emplace_back(WordState{ dest, false });
}
memcpy(dest, pos, len);

@ -1758,6 +1758,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
int64 args_array[] = {(int64)(size_t)v->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
} else if (v->group_id != DEFAULT_GROUP) {
/* The vehicle has no name, but is member of a group, so print group name */
int64 args_array[] = {v->group_id, v->unitnumber};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params, last);
} else {
int64 args_array[] = {v->unitnumber};
StringParameters tmp_params(args_array);

File diff suppressed because it is too large Load Diff

@ -359,7 +359,7 @@ static void Xunzip(byte **bufp, size_t *sizep)
}
/* Check for the byte-order-mark, and skip it if needed. */
char *p = this->text + (strncmp("\xEF\xBB\xBF", this->text, 3) == 0 ? 3 : 0);
char *p = this->text + (strncmp(u8"\ufeff", this->text, 3) == 0 ? 3 : 0);
/* Make sure the string is a valid UTF-8 sequence. */
str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);

@ -505,7 +505,7 @@ static char *MakeFinnishTownName(char *buf, const char *last, uint32 seed)
strstr(orig, "A") != nullptr || strstr(orig, "O") != nullptr || strstr(orig, "U") != nullptr) {
buf = strecpy(buf, "la", last);
} else {
buf = strecpy(buf, "l\xC3\xA4", last);
buf = strecpy(buf, u8"l\u00e4", last);
}
return buf;
}

@ -1013,8 +1013,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
* Do this for all tiles (like trees), not only objects. */
ClearedObjectArea *coa = FindClearedObject(end_tile);
if (coa == nullptr) {
/*C++17: coa = &*/ _cleared_object_areas.push_back({end_tile, TileArea(end_tile, 1, 1)});
coa = &_cleared_object_areas.back();
coa = &_cleared_object_areas.emplace_back(ClearedObjectArea{ end_tile, TileArea(end_tile, 1, 1) });
}
/* Hide the tile from the terraforming command */

@ -31,6 +31,10 @@
#include "../3rdparty/mingw-std-threads/mingw.mutex.h"
#include "../3rdparty/mingw-std-threads/mingw.condition_variable.h"
#endif
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
# include <emscripten/html5.h>
#endif
#if defined(WITH_FCITX)
#include <fcitx/frontend.h>
@ -61,6 +65,11 @@ static volatile bool _draw_continue;
static Palette _local_palette;
static SDL_Palette *_sdl_palette;
#ifdef __EMSCRIPTEN__
/** Whether we just had a window-enter event. */
static bool _cursor_new_in_window = false;
#endif
#define MAX_DIRTY_RECTS 100
static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
static int _num_dirty_rects;
@ -578,6 +587,9 @@ bool VideoDriver_SDL::CreateMainSurface(uint w, uint h, bool resize)
bool VideoDriver_SDL::ClaimMousePointer()
{
SDL_ShowCursor(0);
#ifdef __EMSCRIPTEN__
SDL_SetRelativeMouseMode(SDL_TRUE);
#endif
return true;
}
@ -796,9 +808,27 @@ int VideoDriver_SDL::PollEvent()
switch (ev.type) {
case SDL_MOUSEMOTION:
#ifdef __EMSCRIPTEN__
if (_cursor_new_in_window) {
/* The cursor just moved into the window; this means we don't
* know the absolutely position yet to move relative from.
* Before this time, SDL didn't know it either, and this is
* why we postpone it till now. Update the absolute position
* for this once, and work relative after. */
_cursor.pos.x = ev.motion.x;
_cursor.pos.y = ev.motion.y;
_cursor.dirty = true;
_cursor_new_in_window = false;
SDL_SetRelativeMouseMode(SDL_TRUE);
} else {
_cursor.UpdateCursorPositionRelative(ev.motion.xrel, ev.motion.yrel);
}
#else
if (_cursor.UpdateCursorPosition(ev.motion.x, ev.motion.y, true)) {
SDL_WarpMouseInWindow(_sdl_window, _cursor.pos.x, _cursor.pos.y);
}
#endif
HandleMouseEvents();
break;
@ -932,6 +962,12 @@ int VideoDriver_SDL::PollEvent()
} else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
// mouse entered the window, enable cursor
_cursor.in_window = true;
#ifdef __EMSCRIPTEN__
/* Disable relative mouse mode for the first mouse motion,
* so we can pick up the absolutely position again. */
_cursor_new_in_window = true;
SDL_SetRelativeMouseMode(SDL_FALSE);
#endif
} else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
// mouse left the window, undraw cursor
UndrawMouseCursor();
@ -968,7 +1004,8 @@ const char *VideoDriver_SDL::Start(const StringList &parm)
/* Explicitly disable hardware acceleration. Enabling this causes
* UpdateWindowSurface() to update the window's texture instead of
* its surface. */
SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION , "0");
SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "0");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
/* Just on the offchance the audio subsystem started before the video system,
* check whether any part of SDL has been initialised before getting here.
@ -1012,19 +1049,114 @@ void VideoDriver_SDL::Stop()
}
}
void VideoDriver_SDL::MainLoop()
void VideoDriver_SDL::LoopOnce()
{
uint32 cur_ticks = SDL_GetTicks();
uint32 last_cur_ticks = cur_ticks;
uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
uint32 mod;
int numkeys;
const Uint8 *keys;
uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
InteractiveRandom(); // randomness
while (PollEvent() == -1) {}
if (_exit_game) {
#ifdef __EMSCRIPTEN__
/* Emscripten is event-driven, and as such the main loop is inside
* the browser. So if _exit_game goes true, the main loop ends (the
* cancel call), but we still have to call the cleanup that is
* normally done at the end of the main loop for non-Emscripten.
* After that, Emscripten just halts, and the HTML shows a nice
* "bye, see you next time" message. */
emscripten_cancel_main_loop();
MainLoopCleanup();
#endif
return;
}
mod = SDL_GetModState();
keys = SDL_GetKeyboardState(&numkeys);
#if defined(_DEBUG)
if (_shift_pressed)
#else
/* Speedup when pressing tab, except when using ALT+TAB
* to switch to another application */
if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
#endif /* defined(_DEBUG) */
{
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
} else if (_fast_forward & 2) {
_fast_forward = 0;
}
cur_ticks = SDL_GetTicks();
if (SDL_TICKS_PASSED(cur_ticks, next_tick) || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
_realtime_tick += cur_ticks - last_cur_ticks;
last_cur_ticks = cur_ticks;
next_tick = cur_ticks + MILLISECONDS_PER_TICK;
bool old_ctrl_pressed = _ctrl_pressed;
bool old_shift_pressed = _shift_pressed;
_ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl;
_shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift;
/* determine which directional keys are down */
_dirkeys =
(keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
(keys[SDL_SCANCODE_UP] ? 2 : 0) |
(keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
(keys[SDL_SCANCODE_DOWN] ? 8 : 0);
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
/* The gameloop is the part that can run asynchronously. The rest
* except sleeping can't. */
if (_draw_mutex != nullptr) draw_lock.unlock();
GameLoop();
if (_draw_mutex != nullptr) draw_lock.lock();
GameLoopPaletteAnimations();
UpdateWindows();
_local_palette = _cur_palette;
} else {
/* Release the thread while sleeping */
if (_draw_mutex != nullptr) {
draw_lock.unlock();
CSleep(1);
draw_lock.lock();
} else {
/* Emscripten is running an event-based mainloop; there is already some
* downtime between each iteration, so no need to sleep. */
#ifndef __EMSCRIPTEN__
CSleep(1);
#endif
}
NetworkDrawChatMessage();
DrawMouseCursor();
}
/* End of the critical part. */
if (_draw_mutex != nullptr && !HasModalProgress()) {
_draw_signal->notify_one();
} else {
/* Oh, we didn't have threads, then just draw unthreaded */
CheckPaletteAnim();
DrawSurfaceToScreen();
}
}
void VideoDriver_SDL::MainLoop()
{
cur_ticks = SDL_GetTicks();
last_cur_ticks = cur_ticks;
next_tick = cur_ticks + MILLISECONDS_PER_TICK;
CheckPaletteAnim();
std::thread draw_thread;
std::unique_lock<std::recursive_mutex> draw_lock;
if (_draw_threaded) {
/* Initialise the mutex first, because that's the thing we *need*
* directly in the newly created thread. */
@ -1055,82 +1187,20 @@ void VideoDriver_SDL::MainLoop()
DEBUG(driver, 1, "SDL2: using %sthreads", _draw_threaded ? "" : "no ");
for (;;) {
uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
InteractiveRandom(); // randomness
while (PollEvent() == -1) {}
if (_exit_game) break;
mod = SDL_GetModState();
keys = SDL_GetKeyboardState(&numkeys);
#if defined(_DEBUG)
if (_shift_pressed)
#ifdef __EMSCRIPTEN__
/* Run the main loop event-driven, based on RequestAnimationFrame. */
emscripten_set_main_loop_arg(&this->EmscriptenLoop, this, 0, 1);
#else
/* Speedup when pressing tab, except when using ALT+TAB
* to switch to another application */
if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
#endif /* defined(_DEBUG) */
{
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
} else if (_fast_forward & 2) {
_fast_forward = 0;
}
cur_ticks = SDL_GetTicks();
if (SDL_TICKS_PASSED(cur_ticks, next_tick) || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
_realtime_tick += cur_ticks - last_cur_ticks;
last_cur_ticks = cur_ticks;
next_tick = cur_ticks + MILLISECONDS_PER_TICK;
bool old_ctrl_pressed = _ctrl_pressed;
bool old_shift_pressed = _shift_pressed;
_ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl;
_shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift;
/* determine which directional keys are down */
_dirkeys =
(keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
(keys[SDL_SCANCODE_UP] ? 2 : 0) |
(keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
(keys[SDL_SCANCODE_DOWN] ? 8 : 0);
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
/* The gameloop is the part that can run asynchronously. The rest
* except sleeping can't. */
if (_draw_mutex != nullptr) draw_lock.unlock();
GameLoop();
if (_draw_mutex != nullptr) draw_lock.lock();
GameLoopPaletteAnimations();
UpdateWindows();
_local_palette = _cur_palette;
} else {
/* Release the thread while sleeping */
if (_draw_mutex != nullptr) draw_lock.unlock();
CSleep(1);
if (_draw_mutex != nullptr) draw_lock.lock();
NetworkDrawChatMessage();
DrawMouseCursor();
}
/* End of the critical part. */
if (_draw_mutex != nullptr && !HasModalProgress()) {
_draw_signal->notify_one();
} else {
/* Oh, we didn't have threads, then just draw unthreaded */
CheckPaletteAnim();
DrawSurfaceToScreen();
}
while (!_exit_game) {
LoopOnce();
}
MainLoopCleanup();
#endif
}
void VideoDriver_SDL::MainLoopCleanup()
{
if (_draw_mutex != nullptr) {
_draw_continue = false;
/* Sending signal if there is no thread blocked
@ -1146,6 +1216,15 @@ void VideoDriver_SDL::MainLoop()
_draw_mutex = nullptr;
_draw_signal = nullptr;
}
#ifdef __EMSCRIPTEN__
emscripten_exit_pointerlock();
/* In effect, the game ends here. As emscripten_set_main_loop() caused
* the stack to be unwound, the code after MainLoop() in
* openttd_main() is never executed. */
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
EM_ASM(if (window["openttd_exit"]) openttd_exit());
#endif
}
bool VideoDriver_SDL::ChangeResolution(int w, int h)

@ -42,12 +42,26 @@ public:
const char *GetName() const override { return "sdl"; }
private:
int PollEvent();
void LoopOnce();
void MainLoopCleanup();
bool CreateMainSurface(uint w, uint h, bool resize);
#ifdef __EMSCRIPTEN__
/* Convert a constant pointer back to a non-constant pointer to a member function. */
static void EmscriptenLoop(void *self) { ((VideoDriver_SDL *)self)->LoopOnce(); }
#endif
/**
* This is true to indicate that keyboard input is in text input mode, and SDL_TEXTINPUT events are enabled.
*/
bool edit_box_focused;
uint32 cur_ticks;
uint32 last_cur_ticks;
uint32 next_tick;
std::thread draw_thread;
std::unique_lock<std::recursive_mutex> draw_lock;
};
/** Factory for the SDL video driver. */

@ -816,8 +816,7 @@ static void AddTileSpriteToDraw(SpriteID image, PaletteID pal, int32 x, int32 y,
{
assert((image & SPRITE_MASK) < MAX_SPRITES);
/*C++17: TileSpriteToDraw &ts = */ _vd.tile_sprites_to_draw.emplace_back();
TileSpriteToDraw &ts = _vd.tile_sprites_to_draw.back();
TileSpriteToDraw &ts = _vd.tile_sprites_to_draw.emplace_back();
ts.image = image;
ts.pal = pal;
ts.sub = sub;
@ -1038,8 +1037,7 @@ void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w,
return;
}
/*C++17: ParentSpriteToDraw &ps = */ _vd.parent_sprites_to_draw.emplace_back();
ParentSpriteToDraw &ps = _vd.parent_sprites_to_draw.back();
ParentSpriteToDraw &ps = _vd.parent_sprites_to_draw.emplace_back();
ps.x = tmp_x;
ps.y = tmp_y;
@ -1180,8 +1178,7 @@ void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool tran
*_vd.last_child = (uint)_vd.child_screen_sprites_to_draw.size();
/*C++17: ChildScreenSpriteToDraw &cs = */ _vd.child_screen_sprites_to_draw.emplace_back();
ChildScreenSpriteToDraw &cs = _vd.child_screen_sprites_to_draw.back();
ChildScreenSpriteToDraw &cs = _vd.child_screen_sprites_to_draw.emplace_back();
cs.image = image;
cs.pal = pal;
cs.sub = sub;
@ -1201,8 +1198,7 @@ void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool tran
static void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, Colours colour, uint16 width)
{
assert(width != 0);
/*C++17: StringSpriteToDraw &ss = */ _vd.string_sprites_to_draw.emplace_back();
StringSpriteToDraw &ss = _vd.string_sprites_to_draw.back();
StringSpriteToDraw &ss = _vd.string_sprites_to_draw.emplace_back();
ss.string = string;
ss.x = x;
ss.y = y;

Loading…
Cancel
Save