420 changes: 359 additions & 61 deletions CMakeLists.txt

Large diffs are not rendered by default.

44 changes: 43 additions & 1 deletion README.md
Expand Up @@ -51,17 +51,59 @@ MySQL (or MariaDB) support in the server is not included in the binary releases

Note that the bundled MySQL libraries might not work properly on your system. If you run into connection problems with the MySQL server, for example that it connects as root while you chose another user, make sure to install your system libraries for the MySQL client and C++ connector. Make sure that the CMake configuration summary says that it found MySQL libs that were not bundled (no "using bundled libs").

Running tests (Debian/Ubuntu)
-----------------------------

In order to run the tests, you need to install the following library `libgtest-dev`.

This library isn't compiled, so you have to do it:
```bash
sudo apt install libgtest-dev
cd /usr/src/gtest
sudo cmake CMakeLists.txt
sudo make

# copy or symlink libgtest.a and libgtest_main.a to your /usr/lib folder
sudo cp *.a /usr/lib
```

To run the tests you must target `run_tests` with make:
`make run_tests`

Building on Windows with Visual Studio
--------------------------------------

Download and install some version of [Microsoft Visual Studio](https://www.visualstudio.com/) (as of writing, MSVS Community 2017) with **C++ support**, install [Python 3](https://www.python.org/downloads/) **for all users** and install [CMake](https://cmake.org/download/#latest).

Start CMake and select the source code folder (where DDNet resides, the directory with `CMakeLists.txt`). Additionally select a build folder, e.g. create a build subdirectory in the source code directory. Click "Configure" and select the Visual Studio generator (it should be pre-selected, so pressing "Finish" will suffice). After configuration finishes and the "Generate" reactivates, click it. When that finishes, click "Open Project". Visual Studio should open. You can compile the DDNet client by right-clicking the DDNet project (not the solution) and select "Select as StartUp project". Now you should be able to compile DDNet by clicking the green, triangular "Run" button.

Cross-compiling on Linux to Windows x86/x86\_64
-----------------------------------------------

Install MinGW cross-compilers of the form `i686-w64-mingw32-gcc` (32 bit) or
`x86_64-w64-mingw32-gcc` (64 bit). This is probably the hard part. ;)

Then add `-DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/mingw64.toolchain` to the
**initial** CMake command line.

Cross-compiling on Linux to macOS
---------------------------------

Install [osxcross](https://github.com/tpoechtrager/osxcross), then add
`-DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/darwin.toolchain` and
`-DCMAKE_OSX_SYSROOT=/path/to/osxcross/target/SDK/MacOSX10.11.sdk/` to the
**initial** CMake command line.

Install `dmg` and `hfsplus` from
[libdmg-hfsplus](https://github.com/mozilla/libdmg-hfsplus) and `newfs_hfs`
from
[diskdev_cmds](http://pkgs.fedoraproject.org/repo/pkgs/hfsplus-tools/diskdev_cmds-540.1.linux3.tar.gz/0435afc389b919027b69616ad1b05709/diskdev_cmds-540.1.linux3.tar.gz)
to unlock the `package_dmg` target that outputs a macOS disk image.

Importing the official DDNet Database
-------------------------------------

```
```bash
$ wget https://ddnet.tw/stats/ddnet-sql.zip
$ unzip ddnet-sql.zip
$ yaourt -S mariadb mysql-connector-c++
Expand Down
41 changes: 37 additions & 4 deletions appveyor.yml
@@ -1,9 +1,42 @@
image: Visual Studio 2015

before_build:
- cmd: >-
- cmd: |
git submodule update --init
md build
md build32 & cd build32
cmake -Werror=dev -G "Visual Studio 14 2015" ..
cd ..
md build64 & cd build64
cmake -Werror=dev -G "Visual Studio 14 2015 Win64" ..
cd ..
build_script:
- cmd: cmake --build build32 --config Release --target everything
- cmd: cmake --build build64 --config Release --target everything

test_script:
- cmd: cmake --build build32 --config Debug --target run_tests
- cmd: cmake --build build64 --config Debug --target run_tests
- cmd: cmake --build build32 --config Release --target run_tests
- cmd: cmake --build build64 --config Release --target run_tests
- cmd: build32\Release\DDNet-Server shutdown
- cmd: build64\Release\DDNet-Server shutdown

after_build:
- cmd: cmake --build build32 --config Release --target package
- cmd: cmake --build build64 --config Release --target package

environment:
CFLAGS: /WX
CXXFLAGS: /WX
LDFLAGS: /WX

cd build
artifacts:
- path: build*/DDNet-*.zip
name: DDNet

cmake -Werror=dev ..
branches:
except:
- staging.tmp
3 changes: 2 additions & 1 deletion autoexec_server.cfg
Expand Up @@ -181,6 +181,7 @@ access_level bans 1
access_level bans_save 1
access_level kick 1
access_level force_vote 1
access_level moderate 1



Expand All @@ -194,7 +195,7 @@ sv_client_suggestion "Get DDNet client from DDNet.tw to use all features on DDNe
sv_client_suggestion_old "Your DDNet client is old, update it on DDNet.tw!"

# Broadcast to display for players with known botting client
sv_client_suggestion_bot "Your client has bots and can be remote controlled!\nPlease use another client like DDNet client from DDNet.tw"
sv_client_suggestion_bot "Your client has bots and can be remotely controlled!\nPlease use another client like DDNet client from DDNet.tw"



Expand Down
22 changes: 13 additions & 9 deletions bam.lua
Expand Up @@ -162,13 +162,8 @@ if family == "windows" then
table.insert(server_sql_depends, CopyToDirectory(".", "ddnet-libs/mysql/windows/mysqlcppconn.dll"))
table.insert(server_sql_depends, CopyToDirectory(".", "ddnet-libs/mysql/windows/libmysql.dll"))

if config.compiler.driver == "cl" then
client_link_other = {ResCompile("other/icons/DDNet_cl.rc")}
server_link_other = {ResCompile("other/icons/DDNet-Server_cl.rc")}
elseif config.compiler.driver == "gcc" then
client_link_other = {ResCompile("other/icons/DDNet_gcc.rc")}
server_link_other = {ResCompile("other/icons/DDNet-Server_gcc.rc")}
end
client_link_other = {ResCompile("other/icons/DDNet.rc")}
server_link_other = {ResCompile("other/icons/DDNet-Server.rc")}
end

function Intermediate_Output(settings, input)
Expand Down Expand Up @@ -210,6 +205,9 @@ function build(settings)
settings.cc.flags:Add("/EHsc")
else
settings.cc.flags:Add("-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
if config.compiler.driver == "gcc" then
settings.cc.flags_cxx:Add("-std=c++11")
end
if family == "windows" then
if config.compiler.driver == "gcc" then
settings.link.flags:Add("-static-libgcc")
Expand Down Expand Up @@ -240,7 +238,7 @@ function build(settings)
if platform == "macosx" then
settings.link.frameworks:Add("Carbon")
settings.link.frameworks:Add("AppKit")
settings.link.libs:Add("crypto")
-- settings.link.libs:Add("crypto")
else
settings.link.libs:Add("pthread")
end
Expand Down Expand Up @@ -291,6 +289,10 @@ function build(settings)
pnglite = Compile(external_settings, Collect("src/engine/external/pnglite/*.c"))
jsonparser = Compile(external_settings, Collect("src/engine/external/json-parser/*.c"))
md5 = Compile(external_settings, "src/engine/external/md5/md5.c")

external_settings.cc.flags:Add("-I src/engine/external/glew")
glew = Compile(external_settings, Collect("src/engine/external/glew/*.c"))

if config.websockets.value then
libwebsockets = Compile(external_settings, Collect("src/engine/external/libwebsockets/*.c"))
end
Expand All @@ -316,6 +318,8 @@ function build(settings)
end

elseif family == "windows" then
client_settings.cc.defines:Add("GLEW_STATIC")

if arch == "amd64" then
client_settings.link.libpath:Add("ddnet-libs/curl/windows/lib64")
else
Expand Down Expand Up @@ -378,7 +382,7 @@ function build(settings)

-- build client, server, version server and master server
client_exe = Link(client_settings, "DDNet", game_shared, game_client,
engine, client, game_editor, zlib, pnglite, wavpack,
engine, client, game_editor, zlib, pnglite, wavpack, glew,
client_link_other, client_osxlaunch, jsonparser, libwebsockets, md5, client_notification)

server_exe = Link(server_settings, "DDNet-Server", engine, server,
Expand Down
6 changes: 6 additions & 0 deletions bors.toml
@@ -0,0 +1,6 @@
status = [
"ci/circleci",
"continuous-integration/appveyor/branch",
"continuous-integration/travis-ci/push",
]
timeout_sec = 10800 # macOS on Travis CI has long waiting times
45 changes: 38 additions & 7 deletions circle.yml
@@ -1,27 +1,58 @@
dependencies:
pre:
override:
- |
sudo add-apt-repository -y ppa:zoogie/sdl2-snapshots
sudo apt-get update
sudo apt-get build-dep teeworlds
sudo apt-get install libsdl2-dev cmake
sudo apt-get install cmake libsdl2-dev xz-utils
- |
if [ ! -x ~/bam/bam ]; then
git clone https://github.com/matricks/bam ~/bam/
cd ~/bam; ./make_unix.sh
fi
cache_directories:
- "~/bam/"
- "~/cmake/"

checkout:
post:
- git submodule update --init

## Customize test commands
test:
compile:
override:
- ~/bam/bam release
- |
git submodule update --init
../bam/bam release
mkdir build
cd build
env CFLAGS="-Wdeclaration-after-statement -Werror" CXXFLAGS="-Werror" cmake ..
env CFLAGS="-Wdeclaration-after-statement -Werror" CXXFLAGS="-Werror" cmake -DDOWNLOAD_GTEST=ON ..
make everything
test:
override:
- |
cd build
make run_tests
./DDNet-Server shutdown
- |
cd build
make package
mv DDNet-*.tar.* ${CIRCLE_ARTIFACTS}
branches:
ignore:
- staging.tmp

##
##deployment:
## master:
## branch: master
## owner: ddnet
## commands:
## - |
## cd build
## make package
## mv DDNet-*.tar.*
16 changes: 16 additions & 0 deletions cmake/Download_GTest_CMakeLists.txt.in
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 2.8)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG "${DDNET_GTEST_VERSION}"
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
TLS_VERIFY ON
)
42 changes: 33 additions & 9 deletions cmake/FindCurl.cmake
@@ -1,27 +1,51 @@
find_package(PkgConfig QUIET)
pkg_check_modules(PC_CURL libcurl)
set_extra_dirs(CURL curl)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_CURL libcurl)
endif()

find_path(CURL_INCLUDEDIR curl/curl.h
HINTS ${HINTS_CURL_INCLUDEDIR} ${PC_CURL_INCLUDEDIR} ${PC_CURL_INCLUDE_DIRS}
PATHS ${PATHS_CURL_INCLUDEDIR}
)
set_extra_dirs_lib(CURL curl)
find_library(CURL_LIBRARY
NAMES curl
HINTS ${HINTS_CURL_LIBDIR} ${PC_CURL_LIBDIR} ${PC_CURL_LIBRARY_DIRS}
PATHS ${PATHS_CURL_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(CURL curl "${CURL_LIBRARY}")
find_path(CURL_INCLUDEDIR curl/curl.h
HINTS ${HINTS_CURL_INCLUDEDIR} ${PC_CURL_INCLUDEDIR} ${PC_CURL_INCLUDE_DIRS}
PATHS ${PATHS_CURL_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Curl DEFAULT_MSG CURL_LIBRARY CURL_INCLUDEDIR)

mark_as_advanced(CURL_LIBRARY CURL_INCLUDEDIR)

is_bundled(IS_BUNDLED "${CURL_LIBRARY}")

set(CURL_LIBRARIES ${CURL_LIBRARY})
set(CURL_INCLUDE_DIRS ${CURL_INCLUDEDIR})
if(IS_BUNDLED AND TARGET_OS STREQUAL "linux")
find_library(CURL_LIBRARY_SSL
NAMES ssl
HINTS ${EXTRA_CURL_LIBDIR}
)
find_library(CURL_LIBRARY_CRYPTO
NAMES crypto
HINTS ${EXTRA_CURL_LIBDIR}
)
# If we don't add `dl`, we get a missing reference to `dlclose`:
# ```
# /usr/bin/ld: ../ddnet-libs/curl/linux/lib64/libcrypto.a(dso_dlfcn.o): undefined reference to symbol 'dlclose@@GLIBC_2.2.5'
# ```
#
# Order matters, SSL needs to be linked before CRYPTO, otherwise we also get
# undefined symbols.
list(APPEND CURL_LIBRARIES ${CURL_LIBRARY_SSL} ${CURL_LIBRARY_CRYPTO} dl)
endif()

string(FIND "${CURL_LIBRARY}" "${PROJECT_SOURCE_DIR}" LOCAL_PATH_POS)
if(LOCAL_PATH_POS EQUAL 0 AND TARGET_OS STREQUAL "windows")
if(IS_BUNDLED AND TARGET_OS STREQUAL "windows")
set(CURL_COPY_FILES
"${EXTRA_CURL_LIBDIR}/libcurl.dll"
)
Expand Down
25 changes: 15 additions & 10 deletions cmake/FindFreetype.cmake
@@ -1,17 +1,22 @@
find_package(PkgConfig QUIET)
pkg_check_modules(PC_FREETYPE freetype2)
set_extra_dirs(FREETYPE freetype)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_FREETYPE freetype2)
endif()

set_extra_dirs_lib(FREETYPE freetype)
find_library(FREETYPE_LIBRARY
NAMES freetype freetype.6
HINTS ${HINTS_FREETYPE_LIBDIR} ${PC_FREETYPE_LIBDIR} ${PC_FREETYPE_LIBRARY_DIRS}
PATHS ${PATHS_FREETYPE_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(FREETYPE freetype "${FREETYPE_LIBRARY}")
find_path(FREETYPE_INCLUDEDIR
NAMES config/ftheader.h freetype/config/ftheader.h
PATH_SUFFIXES freetype2
HINTS ${HINTS_FREETYPE_INCLUDEDIR} ${PC_FREETYPE_INCLUDEDIR} ${PC_FREETYPE_INCLUDE_DIRS}
PATHS ${PATHS_FREETYPE_INCLUDEDIR}
)
find_library(FREETYPE_LIBRARY
NAMES freetype
HINTS ${HINTS_FREETYPE_LIBDIR} ${PC_FREETYPE_LIBDIR} ${PC_FREETYPE_LIBRARY_DIRS}
PATHS ${PATHS_FREETYPE_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
Expand All @@ -22,8 +27,8 @@ mark_as_advanced(FREETYPE_LIBRARY FREETYPE_INCLUDEDIR)
set(FREETYPE_LIBRARIES ${FREETYPE_LIBRARY})
set(FREETYPE_INCLUDE_DIRS ${FREETYPE_INCLUDEDIR})

string(FIND "${FREETYPE_LIBRARY}" "${PROJECT_SOURCE_DIR}" LOCAL_PATH_POS)
if(LOCAL_PATH_POS EQUAL 0 AND TARGET_OS STREQUAL "windows")
is_bundled(IS_BUNDLED "${FREETYPE_LIBRARY}")
if(IS_BUNDLED AND TARGET_OS STREQUAL "windows")
set(FREETYPE_COPY_FILES "${EXTRA_FREETYPE_LIBDIR}/libfreetype.dll")
else()
set(FREETYPE_COPY_FILES)
Expand Down
33 changes: 33 additions & 0 deletions cmake/FindGLEW.cmake
@@ -0,0 +1,33 @@
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_GLEW libglew)
endif()

set_extra_dirs_lib(GLEW glew)
find_library(GLEW_LIBRARY
NAMES GLEW glew32
HINTS ${HINTS_GLEW_LIBDIR} ${PC_GLEW_LIBDIR} ${PC_GLEW_LIBRARY_DIRS}
PATHS ${PATHS_GLEW_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(GLEW glew "${GLEW_LIBRARY}")
find_path(GLEW_INCLUDEDIR GL
HINTS ${HINTS_GLEW_INCLUDEDIR} ${PC_GLEW_INCLUDEDIR} ${PC_GLEW_INCLUDE_DIRS}
PATHS ${PATHS_GLEW_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_LIBRARY GLEW_INCLUDEDIR)

mark_as_advanced(GLEW_LIBRARY GLEW_INCLUDEDIR)

set(GLEW_LIBRARIES ${GLEW_LIBRARY})
set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDEDIR})

is_bundled(IS_BUNDLED "${GLEW_LIBRARY}")
if(IS_BUNDLED AND TARGET_OS STREQUAL "windows")
set(GLEW_COPY_FILES "${EXTRA_GLEW_LIBDIR}/glew32.dll")
else()
set(GLEW_COPY_FILES)
endif()
84 changes: 45 additions & 39 deletions cmake/FindMySQL.cmake
@@ -1,63 +1,69 @@
find_program(MYSQL_CONFIG
NAMES mysql_config
)

if(MYSQL_CONFIG)
exec_program(${MYSQL_CONFIG}
ARGS --include
OUTPUT_VARIABLE MY_TMP
if(NOT CMAKE_CROSSCOMPILING)
find_program(MYSQL_CONFIG
NAMES mysql_config
)

string(REGEX REPLACE "-I([^ ]*)( .*)?" "\\1" MY_TMP "${MY_TMP}")
if(MYSQL_CONFIG)
exec_program(${MYSQL_CONFIG}
ARGS --include
OUTPUT_VARIABLE MY_TMP
)

set(MYSQL_CONFIG_INCLUDE_DIR ${MY_TMP} CACHE FILEPATH INTERNAL)
string(REGEX REPLACE "-I([^ ]*)( .*)?" "\\1" MY_TMP "${MY_TMP}")

exec_program(${MYSQL_CONFIG}
ARGS --libs_r
OUTPUT_VARIABLE MY_TMP
)
set(MYSQL_CONFIG_INCLUDE_DIR ${MY_TMP} CACHE FILEPATH INTERNAL)

set(MYSQL_CONFIG_LIBRARIES "")
exec_program(${MYSQL_CONFIG}
ARGS --libs_r
OUTPUT_VARIABLE MY_TMP
)

string(REGEX MATCHALL "-l[^ ]*" MYSQL_LIB_LIST "${MY_TMP}")
foreach(LIB ${MYSQL_LIB_LIST})
string(REGEX REPLACE "[ ]*-l([^ ]*)" "\\1" LIB "${LIB}")
list(APPEND MYSQL_CONFIG_LIBRARIES "${LIB}")
endforeach(LIB ${MYSQL_LIBS})
set(MYSQL_CONFIG_LIBRARIES "")

set(MYSQL_CONFIG_LIBRARY_PATH "")
string(REGEX MATCHALL "-l[^ ]*" MYSQL_LIB_LIST "${MY_TMP}")
foreach(LIB ${MYSQL_LIB_LIST})
string(REGEX REPLACE "[ ]*-l([^ ]*)" "\\1" LIB "${LIB}")
list(APPEND MYSQL_CONFIG_LIBRARIES "${LIB}")
endforeach()

string(REGEX MATCHALL "-L[^ ]*" MYSQL_LIBDIR_LIST "${MY_TMP}")
foreach(LIB ${MYSQL_LIBDIR_LIST})
string(REGEX REPLACE "[ ]*-L([^ ]*)" "\\1" LIB "${LIB}")
list(APPEND MYSQL_CONFIG_LIBRARY_PATH "${LIB}")
endforeach(LIB ${MYSQL_LIBS})
endif(MYSQL_CONFIG)
set(MYSQL_CONFIG_LIBRARY_PATH "")

set_extra_dirs(MYSQL mysql)

find_path(MYSQL_INCLUDEDIR
NAMES "mysql.h"
HINTS ${HINTS_MYSQL_INCLUDEDIR} ${MYSQL_CONFIG_INCLUDE_DIR}
PATHS ${PATHS_MYSQL_INCLUDEDIR}
)
string(REGEX MATCHALL "-L[^ ]*" MYSQL_LIBDIR_LIST "${MY_TMP}")
foreach(LIB ${MYSQL_LIBDIR_LIST})
string(REGEX REPLACE "[ ]*-L([^ ]*)" "\\1" LIB "${LIB}")
list(APPEND MYSQL_CONFIG_LIBRARY_PATH "${LIB}")
endforeach()
endif()
endif()

set_extra_dirs_lib(MYSQL mysql)
find_library(MYSQL_LIBRARY
NAMES "mysqlclient" "mysqlclient_r" "mariadbclient"
HINTS ${HINTS_MYSQL_LIBDIR} ${MYSQL_CONFIG_LIBRARY_PATH}
PATHS ${PATHS_MYSQL_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

find_path(MYSQL_CPPCONN_INCLUDEDIR
NAMES "mysql_connection.h"
set_extra_dirs_include(MYSQL mysql "${MYSQL_LIBRARY}")
find_path(MYSQL_INCLUDEDIR
NAMES "mysql.h"
HINTS ${HINTS_MYSQL_INCLUDEDIR} ${MYSQL_CONFIG_INCLUDE_DIR}
PATHS ${PATHS_MYSQL_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

set_extra_dirs_lib(MYSQL_CPPCONN mysql)
find_library(MYSQL_CPPCONN_LIBRARY
NAMES "mysqlcppconn" "mysqlcppconn-static"
HINTS ${HINTS_MYSQL_LIBDIR} ${MYSQL_CONFIG_LIBRARY_PATH}
PATHS ${PATHS_MYSQL_LIBDIR}
HINTS ${HINTS_MYSQL_CPPCONN_LIBDIR} ${MYSQL_CONFIG_LIBRARY_PATH}
PATHS ${PATHS_MYSQL_CPPCONN_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(MYSQL_CPPCONN mysql "${MYSQL_CPPCONN_LIBRARY}")
find_path(MYSQL_CPPCONN_INCLUDEDIR
NAMES "mysql_connection.h"
HINTS ${HINTS_MYSQL_CPPCONN_INCLUDEDIR} ${MYSQL_CONFIG_INCLUDE_DIR}
PATHS ${PATHS_MYSQL_CPPCONN_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
Expand Down
22 changes: 13 additions & 9 deletions cmake/FindOgg.cmake
@@ -1,17 +1,21 @@
find_package(PkgConfig QUIET)
pkg_check_modules(PC_OGG ogg)

set_extra_dirs(OGG opus)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_OGG ogg)
endif()

find_path(OGG_INCLUDEDIR ogg.h
PATH_SUFFIXES ogg
HINTS ${HINTS_OGG_INCLUDEDIR} ${PC_OGG_INCLUDEDIR} ${PC_OGG_INCLUDE_DIRS}
PATHS ${PATHS_OGG_INCLUDEDIR}
)
set_extra_dirs_lib(OGG opus)
find_library(OGG_LIBRARY
NAMES ogg
HINTS ${HINTS_OGG_LIBDIR} ${PC_OGG_LIBDIR} ${PC_OGG_LIBRARY_DIRS}
PATHS ${PATHS_OGG_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(OGG opus "${OGG_LIBRARY}")
find_path(OGG_INCLUDEDIR ogg.h
PATH_SUFFIXES ogg
HINTS ${HINTS_OGG_INCLUDEDIR} ${PC_OGG_INCLUDEDIR} ${PC_OGG_INCLUDE_DIRS}
PATHS ${PATHS_OGG_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
Expand Down
21 changes: 13 additions & 8 deletions cmake/FindOpus.cmake
@@ -1,16 +1,21 @@
find_package(PkgConfig QUIET)
pkg_check_modules(PC_OPUS opus)
set_extra_dirs(OPUS opus)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_OPUS opus)
endif()

find_path(OPUS_INCLUDEDIR opus.h
PATH_SUFFIXES opus
HINTS ${HINTS_OPUS_INCLUDEDIR} ${PC_OPUS_INCLUDEDIR} ${PC_OPUS_INCLUDE_DIRS}
PATHS ${PATHS_OPUS_INCLUDEDIR}
)
set_extra_dirs_lib(OPUS opus)
find_library(OPUS_LIBRARY
NAMES opus
HINTS ${HINTS_OPUS_LIBDIR} ${PC_OPUS_LIBDIR} ${PC_OPUS_LIBRARY_DIRS}
PATHS ${PATHS_OPUS_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(OPUS opus "${OPUS_LIBRARY}")
find_path(OPUS_INCLUDEDIR opus.h
PATH_SUFFIXES opus
HINTS ${HINTS_OPUS_INCLUDEDIR} ${PC_OPUS_INCLUDEDIR} ${PC_OPUS_INCLUDE_DIRS}
PATHS ${PATHS_OPUS_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
Expand Down
27 changes: 16 additions & 11 deletions cmake/FindOpusfile.cmake
@@ -1,16 +1,21 @@
find_package(PkgConfig QUIET)
pkg_check_modules(PC_OPUSFILE opusfile)
set_extra_dirs(OPUSFILE opus)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_OPUSFILE opusfile)
endif()

find_path(OPUSFILE_INCLUDEDIR opusfile.h
PATH_SUFFIXES opus
HINTS ${HINTS_OPUSFILE_INCLUDEDIR} ${PC_OPUSFILE_INCLUDEDIR} ${PC_OPUSFILE_INCLUDE_DIRS}
PATHS ${PATHS_OPUSFILE_INCLUDEDIR}
)
set_extra_dirs_lib(OPUSFILE opus)
find_library(OPUSFILE_LIBRARY
NAMES opusfile
HINTS ${HINTS_OPUSFILE_LIBDIR} ${PC_OPUSFILE_LIBDIR} ${PC_OPUSFILE_LIBRARY_DIRS}
PATHS ${PATHS_OPUSFILE_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(OPUSFILE opus "${OPUSFILE_LIBRARY}")
find_path(OPUSFILE_INCLUDEDIR opusfile.h
PATH_SUFFIXES opus
HINTS ${HINTS_OPUSFILE_INCLUDEDIR} ${PC_OPUSFILE_INCLUDEDIR} ${PC_OPUSFILE_INCLUDE_DIRS}
PATHS ${PATHS_OPUSFILE_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)

include(FindPackageHandleStandardArgs)
Expand All @@ -21,15 +26,15 @@ mark_as_advanced(OPUSFILE_LIBRARY OPUSFILE_INCLUDEDIR)
set(OPUSFILE_LIBRARIES ${OPUSFILE_LIBRARY})
set(OPUSFILE_INCLUDE_DIRS ${OPUSFILE_INCLUDEDIR})

string(FIND "${OPUSFILE_LIBRARY}" "${PROJECT_SOURCE_DIR}" LOCAL_PATH_POS)
if(LOCAL_PATH_POS EQUAL 0 AND TARGET_OS STREQUAL "windows")
is_bundled(IS_BUNDLED "${OPUSFILE_LIBRARY}")
if(IS_BUNDLED AND TARGET_OS STREQUAL "windows")
set(OPUSFILE_COPY_FILES
"${EXTRA_OPUSFILE_LIBDIR}/libogg.dll"
"${EXTRA_OPUSFILE_LIBDIR}/libopus.dll"
"${EXTRA_OPUSFILE_LIBDIR}/libopusfile.dll"
"${EXTRA_OPUSFILE_LIBDIR}/libwinpthread-1.dll"
)
if (TARGET_BITS EQUAL 32)
if(TARGET_BITS EQUAL 32)
list(APPEND OPUSFILE_COPY_FILES
"${EXTRA_OPUSFILE_LIBDIR}/libgcc_s_sjlj-1.dll"
)
Expand Down
26 changes: 15 additions & 11 deletions cmake/FindSDL2.cmake
@@ -1,20 +1,24 @@
find_package(PkgConfig QUIET)
pkg_check_modules(PC_SDL2 sdl2)

set_extra_dirs(SDL2 sdl)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_SDL2 sdl2)
endif()

set_extra_dirs_lib(SDL2 sdl)
find_library(SDL2_LIBRARY
NAMES SDL2
HINTS ${HINTS_SDL2_LIBDIR} ${PC_SDL2_LIBDIR} ${PC_SDL2_LIBRARY_DIRS}
PATHS ${PATHS_SDL2_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set(CMAKE_FIND_FRAMEWORK FIRST)
set_extra_dirs_include(SDL2 sdl "${SDL2_LIBRARY}")
# Looking for 'SDL.h' directly might accidently find a SDL instead of SDL 2
# installation. Look for a header file only present in SDL 2 instead.
find_path(SDL2_INCLUDEDIR SDL_assert.h
PATH_SUFFIXES SDL2
HINTS ${HINTS_SDL2_INCLUDEDIR} ${PC_SDL2_INCLUDEDIR} ${PC_SDL2_INCLUDE_DIRS}
PATHS ${PATHS_SDL2_INCLUDEDIR}
)
find_library(SDL2_LIBRARY
NAMES SDL2
HINTS ${HINTS_SDL2_LIBDIR} ${PC_SDL2_LIBDIR} ${PC_SDL2_LIBRARY_DIRS}
PATHS ${PATHS_SDL2_LIBDIR}
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SDL2 DEFAULT_MSG SDL2_LIBRARY SDL2_INCLUDEDIR)
Expand All @@ -24,8 +28,8 @@ mark_as_advanced(SDL2_LIBRARY SDL2_INCLUDEDIR)
set(SDL2_LIBRARIES ${SDL2_LIBRARY})
set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDEDIR})

string(FIND "${SDL2_LIBRARY}" "${PROJECT_SOURCE_DIR}" LOCAL_PATH_POS)
if(LOCAL_PATH_POS EQUAL 0 AND TARGET_OS STREQUAL "windows")
is_bundled(IS_BUNDLED "${SDL2_LIBRARY}")
if(IS_BUNDLED AND TARGET_OS STREQUAL "windows")
set(SDL2_COPY_FILES "${EXTRA_SDL2_LIBDIR}/SDL2.dll")
else()
set(SDL2_COPY_FILES)
Expand Down
10 changes: 10 additions & 0 deletions cmake/toolchains/darwin.toolchain
@@ -0,0 +1,10 @@
set(CMAKE_SYSTEM_NAME Darwin)

set(CMAKE_C_COMPILER o64-clang)
set(CMAKE_CXX_COMPILER o64-clang++)
set(CMAKE_INSTALL_NAME_TOOL x86_64-apple-darwin15-install_name_tool)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
10 changes: 10 additions & 0 deletions cmake/toolchains/mingw32.toolchain
@@ -0,0 +1,10 @@
set(CMAKE_SYSTEM_NAME Windows)

set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
10 changes: 10 additions & 0 deletions cmake/toolchains/mingw64.toolchain
@@ -0,0 +1,10 @@
set(CMAKE_SYSTEM_NAME Windows)

set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
Binary file modified data/editor/entities_clear/fng.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/emojis/emojione.sprites.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9,653 changes: 9,653 additions & 0 deletions data/emojis/index.txt

Large diffs are not rendered by default.

241 changes: 138 additions & 103 deletions data/languages/german.txt

Large diffs are not rendered by default.

288 changes: 144 additions & 144 deletions data/languages/hungarian.txt

Large diffs are not rendered by default.

104 changes: 52 additions & 52 deletions data/languages/portuguese.txt
Expand Up @@ -688,22 +688,22 @@ Ratio
==

Dummy settings
==
== Configurações do Dummy

AntiPing
==
== Antiping

Show quads
==

Map sound volume
==
== Volume de som do mapa

âś—
==

Countries
==
== PaĂ­ses

\n\nReconnect in %d sec
==
Expand All @@ -715,25 +715,25 @@ Background (entities)
==

Show kill messages
==
== Mostrar mensagens de morte

Show ghost
==
== Mostrar fantasma

DDNet
==
== DDNet

No updates available
==
== Sem atualizações disponíveis

Enable server message sound
==
== Ativar o som de mensagem do servidor

second
==
== segundo

Show votes window after voting
==
== Mostrar janela de voto depois de votar

HUD
==
Expand All @@ -742,55 +742,55 @@ Look out!
==

Show names in chat in team colors
==
== Mostrar nomes no chat com cores da equipa

Select a name
==
== Escolha um nome

Enable team chat sound
==
== Ativar o som do chat de equipa

Show other players' hook collision lines
==

We will win
==
== NĂłs vamos ganhar

Hi o/
==
== Olá o/

Deaths
==
== Mortes

Enable game sounds
==
== Ativar os sons do jogo

DDNet Client needs to be restarted to complete update!
==
== O cliente DDNet precisa de ser reiniciado para completar a atualização

Spectator
==
== Espectador

Please use a different name
==
== Por favor usa um nome diferente

Wait before try for
==

Show console window
==
== Mostrar a janela da consola

Show others
==
== Mostrar os outros

Gameplay
==

Restart
==
== Reiniciar

Old mouse mode
==
== Modo de rato antigo

Browser
==
Expand All @@ -802,10 +802,10 @@ Enable gun sound
==

Show health + ammo
==
== Mostrar saúde + munição

Team message
==
== Mensagem de equipa

Manual %3d:%02d
==
Expand All @@ -814,22 +814,22 @@ Automatically create statboard csv
==

Save the best demo of each race
==
== Guardar a melhor demo de cada corrida

Show tiles layers from BG map
==

Are you sure that you want to disconnect?
==
== Tens a certeza que te queres desconectar?

Types
==
== Tipos

Ghost
==
== Fantasma

Remove chat
==
== Removar o chat

Threaded sound loading
==
Expand All @@ -853,7 +853,7 @@ Frags
==

AntiPing: predict other players
==
== Antiping: prever os outros jogadores

Show other players' key presses
==
Expand All @@ -862,7 +862,7 @@ Automatically take statboard screenshot
==

System message
==
== Mensagem do sistema

Enable long pain sound (used when shooting in freeze)
==
Expand All @@ -871,13 +871,13 @@ DDNet %s is available:
==

Updating...
==
== A atualizar...

Overlay entities
==

Messages
==
== Mensagens

Vanilla Skins only
==
Expand All @@ -889,16 +889,16 @@ Refresh Rate
==

New random timeout code
==
== Novo cĂłdigo aleatĂłrio de timeout

Suicides
==
== SuicĂ­dios

Net
==

Loading DDNet Client
==
== Carregando o cliente DDNet

FPM
==
Expand All @@ -919,7 +919,7 @@ Max CSVs
==

seconds
==
== segundos

%.2f KiB
==
Expand All @@ -931,25 +931,25 @@ Use DDRace Scoreboard
==

Best
==
== Melhor

Enable regular chat sound
==

Normal message
==
== Mensagem normal

Search
==
== Procurar

Connecting dummy
==
== A conectar o dummy

Clan plates size
==

Update now
==
== Atualizar agora

Auto %3d:%02d
==
Expand All @@ -958,22 +958,22 @@ Show clan above name plates
==

Save ghost
==
== Guardar fantasma

Exclude
==
== Excluir

Enable highlighted chat sound
==

Hello and welcome
==
== Olá e bem-vindo

AntiPing: predict weapons
==
== AntiPing: prever as armas

DDNet Client updated!
==
== O cliente DDNet foi atualizado!

High-DPI screen support (experimental)
==
Expand All @@ -985,10 +985,10 @@ Highlighted message
==

Friend
==
== Amigo

Switch weapon when out of ammo
==
== Mudar de arma quando ficar sem munição

AntiPing: predict grenade paths
==
Expand Down
10 changes: 10 additions & 0 deletions data/shader/bordertile.frag
@@ -0,0 +1,10 @@
#version 330

uniform vec4 vertColor;

out vec4 FragClr;

void main()
{
FragClr = vertColor;
}
20 changes: 20 additions & 0 deletions data/shader/bordertile.vert
@@ -0,0 +1,20 @@
#version 330

layout (location = 0) in vec2 inVertex;

uniform mat4x2 Pos;

uniform vec2 Offset;
uniform vec2 Dir;
uniform int JumpIndex;

void main()
{
vec4 VertPos = vec4(inVertex, 0.0, 1.0);
int XCount = gl_InstanceID - (int(gl_InstanceID/JumpIndex) * JumpIndex);
int YCount = (int(gl_InstanceID/JumpIndex));
VertPos.x += Offset.x + Dir.x * XCount;
VertPos.y += Offset.y + Dir.y * YCount;

gl_Position = vec4(Pos * VertPos, 0.0, 1.0);
}
9 changes: 9 additions & 0 deletions data/shader/bordertileline.frag
@@ -0,0 +1,9 @@
#version 330

uniform vec4 vertColor;

out vec4 FragClr;
void main()
{
FragClr = vertColor;
}
16 changes: 16 additions & 0 deletions data/shader/bordertileline.vert
@@ -0,0 +1,16 @@
#version 330

layout (location = 0) in vec2 inVertex;

uniform mat4x2 Pos;

uniform vec2 Dir;

void main()
{
vec4 VertPos = vec4(inVertex, 0.0, 1.0);
VertPos.x += Dir.x * (gl_InstanceID+1);
VertPos.y += Dir.y * (gl_InstanceID+1);

gl_Position = vec4(Pos * VertPos, 0.0, 1.0);
}
15 changes: 15 additions & 0 deletions data/shader/bordertilelinetex.frag
@@ -0,0 +1,15 @@
#version 330

uniform sampler2D textureSampler;

uniform vec4 vertColor;

noperspective in vec2 texCoord;
flat in float fragLOD;

out vec4 FragClr;
void main()
{
vec4 tex = textureLod(textureSampler, texCoord, fragLOD);
FragClr = tex * vertColor;
}
28 changes: 28 additions & 0 deletions data/shader/bordertilelinetex.vert
@@ -0,0 +1,28 @@
#version 330

layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in ivec2 inVertexTexRightOrBottom;

uniform mat4x2 Pos;
uniform float LOD;

uniform vec2 Dir;

noperspective out vec2 texCoord;
flat out float fragLOD;

void main()
{
vec4 VertPos = vec4(inVertex, 0.0, 1.0);
VertPos.x += Dir.x * (gl_InstanceID+1);
VertPos.y += Dir.y * (gl_InstanceID+1);

gl_Position = vec4(Pos * VertPos, 0.0, 1.0);
float F1 = -(0.5/(1024.0 * pow(0.5, LOD)));
float F2 = (0.5/(1024.0 * pow(0.5, LOD)));
float tx = (inVertexTexCoord.x/(16.0));
float ty = (inVertexTexCoord.y/(16.0));
texCoord = vec2(tx + (inVertexTexRightOrBottom.x == 0 ? F2 : F1), ty + (inVertexTexRightOrBottom.y == 0 ? F2 : F1));
fragLOD = LOD;
}
15 changes: 15 additions & 0 deletions data/shader/bordertiletex.frag
@@ -0,0 +1,15 @@
#version 330

uniform sampler2D textureSampler;

uniform vec4 vertColor;

noperspective in vec2 texCoord;
flat in float fragLOD;

out vec4 FragClr;
void main()
{
vec4 tex = textureLod(textureSampler, texCoord, fragLOD);
FragClr = tex * vertColor;
}
32 changes: 32 additions & 0 deletions data/shader/bordertiletex.vert
@@ -0,0 +1,32 @@
#version 330

layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in ivec2 inVertexTexRightOrBottom;

uniform mat4x2 Pos;
uniform float LOD;

uniform vec2 Offset;
uniform vec2 Dir;
uniform int JumpIndex;

noperspective out vec2 texCoord;
flat out float fragLOD;

void main()
{
vec4 VertPos = vec4(inVertex, 0.0, 1.0);
int XCount = gl_InstanceID - (int(gl_InstanceID/JumpIndex) * JumpIndex);
int YCount = (int(gl_InstanceID/JumpIndex));
VertPos.x += Offset.x + Dir.x * XCount;
VertPos.y += Offset.y + Dir.y * YCount;

gl_Position = vec4(Pos * VertPos, 0.0, 1.0);
float F1 = -(0.5/(1024.0 * pow(0.5, LOD)));
float F2 = (0.5/(1024.0 * pow(0.5, LOD)));
float tx = (inVertexTexCoord.x/(16.0));
float ty = (inVertexTexCoord.y/(16.0));
texCoord = vec2(tx + (inVertexTexRightOrBottom.x == 0 ? F2 : F1), ty + (inVertexTexRightOrBottom.y == 0 ? F2 : F1));
fragLOD = LOD;
}
17 changes: 17 additions & 0 deletions data/shader/prim.frag
@@ -0,0 +1,17 @@
#version 330

uniform int isTextured;
uniform sampler2D textureSampler;

noperspective in vec2 texCoord;
noperspective in vec4 vertColor;

out vec4 FragClr;
void main()
{
if(isTextured == 1) {
vec4 tex = texture(textureSampler, texCoord);
FragClr = tex * vertColor;
}
else FragClr = vertColor;
}
17 changes: 17 additions & 0 deletions data/shader/prim.vert
@@ -0,0 +1,17 @@
#version 330

layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in vec4 inVertexColor;

uniform mat4x2 Pos;

noperspective out vec2 texCoord;
noperspective out vec4 vertColor;

void main()
{
gl_Position = vec4(Pos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
texCoord = inVertexTexCoord;
vertColor = inVertexColor;
}
10 changes: 10 additions & 0 deletions data/shader/tile.frag
@@ -0,0 +1,10 @@
#version 330

uniform vec4 vertColor;

out vec4 FragClr;

void main()
{
FragClr = vertColor;
}
10 changes: 10 additions & 0 deletions data/shader/tile.vert
@@ -0,0 +1,10 @@
#version 330

layout (location = 0) in vec2 inVertex;

uniform mat4x2 Pos;

void main()
{
gl_Position = vec4(Pos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
}
15 changes: 15 additions & 0 deletions data/shader/tiletex.frag
@@ -0,0 +1,15 @@
#version 330

uniform sampler2D textureSampler;

uniform vec4 vertColor;

noperspective in vec2 texCoord;
flat in float fragLOD;

out vec4 FragClr;
void main()
{
vec4 tex = textureLod(textureSampler, texCoord, fragLOD);
FragClr = tex * vertColor;
}
22 changes: 22 additions & 0 deletions data/shader/tiletex.vert
@@ -0,0 +1,22 @@
#version 330

layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in ivec2 inVertexTexRightOrBottom;

uniform mat4x2 Pos;
uniform float LOD;

noperspective out vec2 texCoord;
flat out float fragLOD;

void main()
{
gl_Position = vec4(Pos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
float F1 = -(0.5/(1024.0 * pow(0.5, LOD)));
float F2 = (0.5/(1024.0 * pow(0.5, LOD)));
float tx = (inVertexTexCoord.x/(16.0));
float ty = (inVertexTexCoord.y/(16.0));
texCoord = vec2(tx + (inVertexTexRightOrBottom.x == 0 ? F2 : F1), ty + (inVertexTexRightOrBottom.y == 0 ? F2 : F1));
fragLOD = LOD;
}
17 changes: 17 additions & 0 deletions datasrc/compile.py
Expand Up @@ -150,6 +150,7 @@ class CNetObjHandler
const char *GetMsgName(int Type);
void *SecureUnpackMsg(int Type, CUnpacker *pUnpacker);
bool TeeHistorianRecordMsg(int Type);
const char *FailedMsgOn();
};
Expand Down Expand Up @@ -323,6 +324,22 @@ class CNetObjHandler
lines += ['};']
lines += ['']

lines += ['bool CNetObjHandler::TeeHistorianRecordMsg(int Type)']
lines += ['{']
lines += ['\tswitch(Type)']
lines += ['\t{']
empty = True
for msg in network.Messages:
if not msg.teehistorian:
lines += ['\tcase %s:' % msg.enum_name]
empty = False
if not empty:
lines += ['\t\treturn false;']
lines += ['\tdefault:']
lines += ['\t\treturn true;']
lines += ['\t}']
lines += ['}']
lines += ['']

lines += ['void RegisterGameUuids(CUuidManager *pManager)']
lines += ['{']
Expand Down
1,825 changes: 1,825 additions & 0 deletions datasrc/content.py

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions datasrc/datatypes.py
Expand Up @@ -237,11 +237,12 @@ def __init__(self, name, variables, ex=None):
self.enum_name = "NETEVENTTYPE_%s" % self.name.upper()

class NetMessage(NetObject):
def __init__(self, name, variables, ex=None):
def __init__(self, name, variables, ex=None, teehistorian=True):
NetObject.__init__(self, name, variables, ex=ex)
self.base_struct_name = "CNetMsg_%s" % self.base
self.struct_name = "CNetMsg_%s" % self.name
self.enum_name = "NETMSGTYPE_%s" % self.name.upper()
self.teehistorian = teehistorian
def emit_unpack(self):
lines = []
lines += ["case %s:" % self.enum_name]
Expand Down Expand Up @@ -280,7 +281,7 @@ def __init__(self, name, ex, variables):
NetEvent.__init__(self, name, variables, ex=ex)

class NetMessageEx(NetMessage):
def __init__(self, name, ex, variables):
def __init__(self, name, ex, variables, teehistorian=True):
NetMessage.__init__(self, name, variables, ex=ex)


Expand Down
6 changes: 3 additions & 3 deletions datasrc/network.py
Expand Up @@ -305,7 +305,7 @@
NetMessage("Cl_Say", [
NetBool("m_Team"),
NetStringHalfStrict("m_pMessage"),
]),
], teehistorian=False),

NetMessage("Cl_SetTeam", [
NetIntRange("m_Team", 'TEAM_SPECTATORS', 'TEAM_BLUE'),
Expand Down Expand Up @@ -343,13 +343,13 @@

NetMessage("Cl_Vote", [
NetIntRange("m_Vote", -1, 1),
]),
], teehistorian=False),

NetMessage("Cl_CallVote", [
NetStringStrict("m_Type"),
NetStringStrict("m_Value"),
NetStringStrict("m_Reason"),
]),
], teehistorian=False),

NetMessage("Cl_IsDDNet", []),

Expand Down
2 changes: 1 addition & 1 deletion ddnet-libs
Submodule ddnet-libs updated 138 files
24 changes: 24 additions & 0 deletions other/bundle/client/Info.plist.in
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${TARGET_CLIENT}</string>
<key>CFBundleIconFile</key>
<string>${TARGET_CLIENT}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${PROJECT_VERSION}</string>
<key>CFBundleIdentifier</key>
<string>org.DDNetClient.app</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
1 change: 1 addition & 0 deletions other/bundle/client/PkgInfo
@@ -0,0 +1 @@
APPL????
20 changes: 20 additions & 0 deletions other/bundle/server/Info.plist.in
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${TARGET_SERVER_LAUNCHER}</string>
<key>CFBundleIconFile</key>
<string>${TARGET_SERVER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${PROJECT_VERSION}</string>
</dict>
</plist>
1 change: 1 addition & 0 deletions other/bundle/server/PkgInfo
@@ -0,0 +1 @@
APPL????
File renamed without changes.
1 change: 0 additions & 1 deletion other/icons/DDNet-Server_cl.rc

This file was deleted.

7 changes: 0 additions & 7 deletions other/icons/DDNet.exe.manifest

This file was deleted.

1 change: 1 addition & 0 deletions other/icons/DDNet.rc
@@ -0,0 +1 @@
ID ICON "DDNet.ico"
1 change: 0 additions & 1 deletion other/icons/DDNet_cl.rc

This file was deleted.

2 changes: 0 additions & 2 deletions other/icons/DDNet_gcc.rc

This file was deleted.

28 changes: 28 additions & 0 deletions other/manifest/DDNet.manifest
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="DDNet.exe"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
</application>
</compatibility>

<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>

</asmv1:assembly>
2 changes: 2 additions & 0 deletions other/manifest/DDNet.rc
@@ -0,0 +1,2 @@
#include "winuser.h"
1 RT_MANIFEST DDNet.manifest
2 changes: 1 addition & 1 deletion other/sdl.lua
Expand Up @@ -39,7 +39,7 @@ SDL = {
end

if option.use_winlib > 0 then
settings.cc.includes:Add("ddnet-libs/sdl/include")
settings.cc.includes:Add("ddnet-libs/sdl/include/windows")
if option.use_winlib == 32 then
settings.link.libpath:Add("ddnet-libs/sdl/windows/lib32")
else
Expand Down
2 changes: 1 addition & 1 deletion scripts/cmd5.py
Expand Up @@ -33,4 +33,4 @@ def cstrip(lines):
if hash == "3dc531e4296de555":
hash = "626fce9a778df4d4"
print('#define GAME_NETVERSION_HASH "%s"' % hash)
print('#define GIT_SHORTREV_HASH "%s"' % os.popen('git rev-parse HEAD').readline(8))
print('#define GIT_SHORTREV_HASH "%s"' % os.popen('git rev-parse --short HEAD').readline().strip())
106 changes: 106 additions & 0 deletions scripts/dmg.py
@@ -0,0 +1,106 @@
from collections import namedtuple
import os
import shlex
import subprocess
import tempfile

ConfigDmgtools = namedtuple('Config', 'dmg hfsplus newfs_hfs verbose')
ConfigHdiutil = namedtuple('Config', 'hdiutil verbose')

def chunks(l, n):
"""
Yield successive n-sized chunks from l.
From https://stackoverflow.com/a/312464.
"""
for i in range(0, len(l), n):
yield l[i:i + n]

class Dmg:
def __init__(self, config):
self.config = config

def _check_call(self, process_args, *args, **kwargs):
if self.config.verbose >= 1:
print("EXECUTING {}".format(" ".join(shlex.quote(x) for x in process_args)))
if not (self.config.verbose >= 2 and "stdout" not in kwargs):
kwargs["stdout"] = open(os.devnull, 'wb')
subprocess.check_call(process_args, *args, **kwargs)

class Dmgtools(Dmg):
def _mkfs_hfs(self, *args):
self._check_call((self.config.newfs_hfs,) + args)
def _hfs(self, *args):
self._check_call((self.config.hfsplus,) + args)
def _dmg(self, *args):
self._check_call((self.config.dmg,) + args)

def _create_hfs(self, hfs, volume_name, size):
if self.config.verbose >= 1:
print("TRUNCATING {} to {} bytes".format(hfs, size))
with open(hfs, 'wb') as f:
f.truncate(size)
self._mkfs_hfs('-v', volume_name, hfs)

def _symlink(self, hfs, target, link_name):
self._hfs(hfs, 'symlink', link_name, target)

def _add(self, hfs, directory):
self._hfs(hfs, 'addall', directory)

def _finish(self, hfs, dmg):
self._dmg('build', hfs, dmg)

def create(self, dmg, volume_name, directory, symlinks):
input_size = sum(os.stat(os.path.join(path, f)).st_size for path, dirs, files in os.walk(directory) for f in files)
output_size = max(input_size * 2, 1024**2)
hfs = tempfile.mktemp(prefix=dmg + '.', suffix='.hfs')
self._create_hfs(hfs, volume_name, output_size)
self._add(hfs, directory)
for target, link_name in symlinks:
self._symlink(hfs, target, link_name)
self._finish(hfs, dmg)
if self.config.verbose >= 1:
print("REMOVING {}".format(hfs))
os.remove(hfs)

class Hdiutil(Dmg):
def _hdiutil(self, *args):
self._check_call((self.config.hdiutil,) + args)

def create(self, dmg, volume_name, directory, symlinks):
if symlinks:
raise NotImplementedError("symlinks are not yet implemented")
self._hdiutil('create', '-volname', volume_name, '-srcdir', directory, dmg)

def main():
import argparse
p = argparse.ArgumentParser(description="Manipulate dmg archives")

subcommands = p.add_subparsers(help="Subcommand", dest='command', metavar="COMMAND")
subcommands.required = True

create = subcommands.add_parser("create", help="Create a dmg archive from files or directories")
create.add_argument('-v', '--verbose', action='count', help="Verbose output")
createx = create.add_mutually_exclusive_group(required=True)
createx.add_argument('--dmgtools', nargs=3, help="Paths to the dmg and hfsplus executable (https://github.com/mozilla/libdmg-hfsplus) and the newfs_hfs executable (http://pkgs.fedoraproject.org/repo/pkgs/hfsplus-tools/diskdev_cmds-540.1.linux3.tar.gz/0435afc389b919027b69616ad1b05709/diskdev_cmds-540.1.linux3.tar.gz)")
createx.add_argument('--hdiutil', help="Path to the hdiutil (only exists for macOS at time of writing)")
create.add_argument('output', metavar="OUTPUT", help="Filename of the output dmg archive")
create.add_argument('volume_name', metavar="VOLUME_NAME", help="Name of the dmg archive")
create.add_argument('directory', metavar="DIR", help="Directory to create the archive from")
create.add_argument('--symlink', metavar="SYMLINK", nargs=2, action="append", help="Symlink the first argument under the second name")
args = p.parse_args()

verbose = args.verbose or 0
symlinks = args.symlink or []
if args.dmgtools:
dmg, hfsplus, newfs_hfs = args.dmgtools
dmg = Dmgtools(ConfigDmgtools(dmg=dmg, hfsplus=hfsplus, newfs_hfs=newfs_hfs, verbose=verbose))
elif args.hdiutil:
dmg = Hdiutil(ConfigHdiutil(hdiutil=args.hdiutil, verbose=verbose))
else:
raise RuntimeError("unreachable")
dmg.create(volume_name=args.volume_name, directory=args.directory, dmg=args.output, symlinks=symlinks)

if __name__ == '__main__':
main()
611 changes: 466 additions & 145 deletions src/base/system.c

Large diffs are not rendered by default.

233 changes: 218 additions & 15 deletions src/base/system.h
Expand Up @@ -12,6 +12,10 @@
#include "stddef.h"
#include <time.h>

#ifdef CONF_FAMILY_UNIX
#include <sys/un.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -336,6 +340,18 @@ int io_close(IOHANDLE io);
*/
int io_flush(IOHANDLE io);

/*
Function: io_error
Checks whether an error occured during I/O with the file.
Parameters:
io - Handle to the file.
Returns:
Returns nonzero on error, 0 otherwise.
*/
int io_error(IOHANDLE io);


/*
Function: io_stdin
Expand All @@ -355,6 +371,134 @@ IOHANDLE io_stdout();
*/
IOHANDLE io_stderr();

typedef struct ASYNCIO ASYNCIO;

/*
Function: aio_new
Wraps a <IOHANDLE> for asynchronous writing.
Parameters:
io - Handle to the file.
Returns:
Returns the handle for asynchronous writing.
*/
ASYNCIO *aio_new(IOHANDLE io);

/*
Function: aio_lock
Locks the ASYNCIO structure so it can't be written into by
other threads.
Parameters:
aio - Handle to the file.
*/
void aio_lock(ASYNCIO *aio);

/*
Function: aio_unlock
Unlocks the ASYNCIO structure after finishing the contiguous
write.
Parameters:
aio - Handle to the file.
*/
void aio_unlock(ASYNCIO *aio);

/*
Function: aio_write
Queues a chunk of data for writing.
Parameters:
aio - Handle to the file.
buffer - Pointer to the data that should be written.
size - Number of bytes to write.
*/
void aio_write(ASYNCIO *aio, const void *buffer, unsigned size);

/*
Function: aio_write_newline
Queues a newline for writing.
Parameters:
aio - Handle to the file.
*/
void aio_write_newline(ASYNCIO *aio);

/*
Function: aio_write_unlocked
Queues a chunk of data for writing. The ASYNCIO struct must be
locked using `aio_lock` first.
Parameters:
aio - Handle to the file.
buffer - Pointer to the data that should be written.
size - Number of bytes to write.
*/
void aio_write_unlocked(ASYNCIO *aio, const void *buffer, unsigned size);

/*
Function: aio_write_newline_unlocked
Queues a newline for writing. The ASYNCIO struct must be locked
using `aio_lock` first.
Parameters:
aio - Handle to the file.
*/
void aio_write_newline_unlocked(ASYNCIO *aio);

/*
Function: aio_error
Checks whether errors have occured during the asynchronous
writing.
Call this function regularly to see if there are errors. Call
this function after <aio_wait> to see if the process of writing
to the file succeeded.
Parameters:
aio - Handle to the file.
Returns:
Returns 0 if no error occured, and nonzero on error.
*/
int aio_error(ASYNCIO *aio);

/*
Function: aio_close
Queues file closing.
Parameters:
aio - Handle to the file.
*/
void aio_close(ASYNCIO *aio);

/*
Function: aio_wait
Wait for the asynchronous operations to complete.
Parameters:
aio - Handle to the file.
*/
void aio_wait(ASYNCIO *aio);

/*
Function: aio_free
Frees the resources associated to the asynchronous file handle.
Parameters:
aio - Handle to the file.
*/
void aio_free(ASYNCIO *aio);

/* Group: Threads */

Expand Down Expand Up @@ -388,7 +532,7 @@ void *thread_init(void (*threadfunc)(void *), void *user);
void thread_wait(void *thread);

/*
Function: thread_yeild
Function: thread_yield
Yield the current threads execution slice.
*/
void thread_yield();
Expand Down Expand Up @@ -505,6 +649,10 @@ typedef struct
unsigned short port;
} NETADDR;

#ifdef CONF_FAMILY_UNIX
typedef int UNIXSOCKET;
typedef struct sockaddr_un UNIXSOCKETADDR;
#endif
/*
Function: net_init
Initiates network functionallity.
Expand Down Expand Up @@ -729,6 +877,54 @@ int net_tcp_recv(NETSOCKET sock, void *data, int maxsize);
*/
int net_tcp_close(NETSOCKET sock);

#if defined(CONF_FAMILY_UNIX)
/* Group: Network Unix Sockets */

/*
Function: net_unix_create_unnamed
Creates an unnamed unix datagram socket.
Returns:
On success it returns a handle to the socket. On failure it returns -1.
*/
UNIXSOCKET net_unix_create_unnamed();

/*
Function: net_unix_send
Sends data to a Unix socket.
Parameters:
sock - Socket to use.
addr - Where to send the packet.
data - Pointer to the packet data to send.
size - Size of the packet.
Returns:
Number of bytes sent. Negative value on failure.
*/
int net_unix_send(UNIXSOCKET sock, UNIXSOCKETADDR *addr, void *data, int size);

/*
Function: net_unix_set_addr
Sets the unixsocketaddress for a path to a socket file.
Parameters:
addr - Pointer to the addressstruct to fill.
path - Path to the (named) unix socket.
*/
void net_unix_set_addr(UNIXSOCKETADDR *addr, const char *path);

/*
Function: net_unix_close
Closes a Unix socket.
Parameters:
sock - Socket to close.
*/
void net_unix_close(UNIXSOCKET sock);

#endif

/* Group: Strings */

/*
Expand Down Expand Up @@ -847,6 +1043,18 @@ void str_sanitize_cc(char *str);
*/
void str_sanitize(char *str);

/*
Function: str_sanitize_filename
Replaces all invalid filename characters with whitespace.
Parameters:
str - String to sanitize.
Remarks:
- The strings are treated as zero-termineted strings.
*/
void str_sanitize_filename(char *str);

/*
Function: str_skip_to_whitespace
Skips leading non-whitespace characters(all but ' ', '\t', '\n', '\r').
Expand Down Expand Up @@ -920,7 +1128,7 @@ int str_comp_nocase_num(const char *a, const char *b, const int num);

/*
Function: str_comp
Compares to strings case sensitive.
Compares two strings case sensitive.
Parameters:
a - String to compare.
Expand Down Expand Up @@ -1261,10 +1469,10 @@ void mem_debug_dump(IOHANDLE file);
void swap_endian(void *data, unsigned elem_size, unsigned num);


typedef void (*DBG_LOGGER)(const char *line);
void dbg_logger(DBG_LOGGER logger);
typedef void (*DBG_LOGGER)(const char *line, void *user);
typedef void (*DBG_LOGGER_FINISH)(void *user);
void dbg_logger(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, void *user);

void dbg_enable_threaded();
void dbg_logger_stdout();
void dbg_logger_debugger();
void dbg_logger_file(const char *filename);
Expand Down Expand Up @@ -1404,19 +1612,14 @@ int pid();
void shell_execute(const char *file);

/*
Function: os_compare_version
Compares the OS version to a given major and minor.
Parameters:
major - Major version to compare to.
minor - Minor version to compare to.
Function: os_is_winxp_or_lower
Checks whether the program runs on Windows XP or lower.
Returns:
1 - OS version higher.
0 - OS version same.
-1 - OS version lower.
1 - Windows XP or lower.
0 - Higher Windows version, Linux, macOS, etc.
*/
int os_compare_version(unsigned int major, unsigned int minor);
int os_is_winxp_or_lower();

/*
Function: generate_password
Expand Down
13 changes: 7 additions & 6 deletions src/engine/client.h
Expand Up @@ -175,12 +175,13 @@ class IClient : public IInterface

//DDRace

virtual const char* GetCurrentMap() = 0;
virtual int GetCurrentMapCrc() = 0;
virtual const char* GetCurrentMapPath() = 0;
virtual const char* RaceRecordStart(const char *pFilename) = 0;
virtual void RaceRecordStop() = 0;
virtual bool RaceRecordIsRecording() = 0;
virtual const char *GetCurrentMap() = 0;
virtual const char *GetCurrentMapPath() = 0;
virtual unsigned GetMapCrc() = 0;

virtual void RaceRecord_Start(const char *pFilename) = 0;
virtual void RaceRecord_Stop() = 0;
virtual bool RaceRecord_IsRecording() = 0;

virtual void DemoSliceBegin() = 0;
virtual void DemoSliceEnd() = 0;
Expand Down
1,179 changes: 1,148 additions & 31 deletions src/engine/client/backend_sdl.cpp

Large diffs are not rendered by default.

140 changes: 137 additions & 3 deletions src/engine/client/backend_sdl.h
Expand Up @@ -70,7 +70,7 @@ class CCommandProcessorFragment_General
void Cmd_Nop();
void Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand);
public:
bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand);
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};

// takes care of opengl related rendering
Expand Down Expand Up @@ -114,7 +114,122 @@ class CCommandProcessorFragment_OpenGL
public:
CCommandProcessorFragment_OpenGL();

bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand);
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};

class CGLSLProgram;
class CGLSLTWProgram;
class CGLSLPrimitiveProgram;
class CGLSLQuadProgram;
class CGLSLTileProgram;
class CGLSLBorderTileProgram;
class CGLSLBorderTileLineProgram;
// takes care of opengl 3.3 related rendering
class CCommandProcessorFragment_OpenGL3_3
{
bool m_UseMultipleTextureUnits;
bool m_UsePreinitializedVertexBuffer;

struct CTexture
{
GLuint m_Tex;
GLuint m_Sampler;
int m_LastWrapMode;
int m_MemSize;
};
CTexture m_aTextures[CCommandBuffer::MAX_TEXTURES];
volatile int *m_pTextureMemoryUsage;

CGLSLPrimitiveProgram *m_pPrimitiveProgram;
CGLSLTileProgram *m_pTileProgram;
CGLSLTileProgram *m_pTileProgramTextured;
CGLSLBorderTileProgram *m_pBorderTileProgram;
CGLSLBorderTileProgram *m_pBorderTileProgramTextured;
CGLSLBorderTileLineProgram *m_pBorderTileLineProgram;
CGLSLBorderTileLineProgram *m_pBorderTileLineProgramTextured;

GLuint m_PrimitiveDrawVertexID;
GLuint m_PrimitiveDrawBufferID;
GLuint m_LastIndexBufferBound;

GLuint m_QuadDrawIndexBufferID;
unsigned int m_CurrentIndicesInBuffer;

GLint m_MaxTextureUnits;
GLint m_MaxTexSize;

int m_LastBlendMode; //avoid all possible opengl state changes
bool m_LastClipEnable;

struct STextureBound{
int m_TextureSlot;
};
std::vector<STextureBound> m_TextureSlotBoundToUnit; //the texture index generated by loadtextureraw is stored in an index calculated by max texture units

bool IsAndUpdateTextureSlotBound(int IDX, int Slot);
void DestroyTexture(int Slot);
void DestroyVisualObjects(int Index);

void AppendIndices(unsigned int NewIndicesCount);

struct SVisualObject{
SVisualObject() : m_VertArrayID(0), m_VertBufferID(0), m_LastIndexBufferBound(0), m_NumElements(0), m_IsTextured(false) {}
GLuint m_VertArrayID;
GLuint m_VertBufferID;
GLuint m_LastIndexBufferBound;
int m_NumElements; //vertices and texture coordinates
bool m_IsTextured;
};
std::vector<SVisualObject> m_VisualObjects;

CCommandBuffer::SColorf m_ClearColor;
public:
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_OPENGL3_3,
CMD_SHUTDOWN,
};

struct SCommand_Init : public CCommandBuffer::SCommand
{
SCommand_Init() : SCommand(CMD_INIT) {}
class IStorage *m_pStorage;
volatile int *m_pTextureMemoryUsage;
};

struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {}
};

private:
static int TexFormatToOpenGLFormat(int TexFormat);
static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp);
static void *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData);

void SetState(const CCommandBuffer::SState &State, CGLSLTWProgram *pProgram);

void Cmd_Init(const SCommand_Init *pCommand);
void Cmd_Shutdown(const SCommand_Shutdown *pCommand);
void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand);
void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand);
void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand);
void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand);
void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand);
void Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand);

void Cmd_CreateVertBuffer(const CCommandBuffer::SCommand_CreateVertexBufferObject *pCommand);
void Cmd_AppendVertBuffer(const CCommandBuffer::SCommand_AppendVertexBufferObject *pCommand);
void Cmd_CreateVertArray(const CCommandBuffer::SCommand_CreateVertexArrayObject *pCommand);
void Cmd_RenderVertexArray(const CCommandBuffer::SCommand_RenderVertexArray *pCommand);
void Cmd_DestroyVertexArray(const CCommandBuffer::SCommand_DestroyVisual *pCommand);

void Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand);
void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand);
public:
CCommandProcessorFragment_OpenGL3_3();

bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};

// takes care of sdl related commands
Expand All @@ -127,6 +242,7 @@ class CCommandProcessorFragment_SDL
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_SDL,
CMD_UPDATE_VIEWPORT,
CMD_SHUTDOWN,
};

Expand All @@ -137,13 +253,23 @@ class CCommandProcessorFragment_SDL
SDL_GLContext m_GLContext;
};

struct SCommand_Update_Viewport : public CCommandBuffer::SCommand
{
SCommand_Update_Viewport() : SCommand(CMD_UPDATE_VIEWPORT) {}
int m_X;
int m_Y;
int m_Width;
int m_Height;
};

struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {}
};

private:
void Cmd_Init(const SCommand_Init *pCommand);
void Cmd_Update_Viewport(const SCommand_Update_Viewport *pCommand);
void Cmd_Shutdown(const SCommand_Shutdown *pCommand);
void Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand);
void Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand);
Expand All @@ -159,9 +285,13 @@ class CCommandProcessorFragment_SDL
class CCommandProcessor_SDL_OpenGL : public CGraphicsBackend_Threaded::ICommandProcessor
{
CCommandProcessorFragment_OpenGL m_OpenGL;
CCommandProcessorFragment_OpenGL3_3 m_OpenGL3_3;
CCommandProcessorFragment_SDL m_SDL;
CCommandProcessorFragment_General m_General;

bool m_UseOpenGL3_3;
public:
void UseOpenGL3_3(bool Use) { m_UseOpenGL3_3 = Use; }
virtual void RunBuffer(CCommandBuffer *pBuffer);
};

Expand All @@ -173,8 +303,10 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
ICommandProcessor *m_pProcessor;
volatile int m_TextureMemoryUsage;
int m_NumScreens;

bool m_UseOpenGL3_3;
public:
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight);
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage);
virtual int Shutdown();

virtual int MemoryUsage() const;
Expand All @@ -191,4 +323,6 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
virtual int WindowOpen();
virtual void SetWindowGrab(bool Grab);
virtual void NotifyWindow();

virtual bool IsOpenGL3_3() { return m_UseOpenGL3_3; }
};
83 changes: 39 additions & 44 deletions src/engine/client/client.cpp
Expand Up @@ -41,6 +41,7 @@
#include <engine/shared/datafile.h>
#include <engine/shared/demo.h>
#include <engine/shared/filecollection.h>
#include <engine/shared/ghost.h>
#include <engine/shared/network.h>
#include <engine/shared/packer.h>
#include <engine/shared/protocol.h>
Expand Down Expand Up @@ -478,7 +479,7 @@ void CClient::SendInput()
if(m_PredTick[g_Config.m_ClDummy] <= 0)
return;

if(m_LastDummy != g_Config.m_ClDummy)
if(m_LastDummy != (bool)g_Config.m_ClDummy)
{
m_LastDummy = g_Config.m_ClDummy;
GameClient()->OnDummySwap();
Expand Down Expand Up @@ -1505,7 +1506,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
char aEscaped[128];
str_format(aFilename, sizeof(aFilename), "%s_%08x.map", pMap, MapCrc);

Fetcher()->Escape(aEscaped, sizeof(aEscaped), aFilename);
EscapeUrl(aEscaped, sizeof(aEscaped), aFilename);
str_format(aUrl, sizeof(aUrl), "%s/", g_Config.m_ClDDNetMapDownloadUrl);

// We only trust our own custom-selected CAs for our own servers.
Expand All @@ -1517,8 +1518,8 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)

str_append(aUrl, aEscaped, sizeof(aUrl));

m_pMapdownloadTask = new CFetchTask(true, UseDDNetCA);
Fetcher()->QueueAdd(m_pMapdownloadTask, aUrl, m_aMapdownloadFilename, IStorage::TYPE_SAVE);
m_pMapdownloadTask = std::make_shared<CFetchTask>(Storage(), aUrl, m_aMapdownloadFilename, IStorage::TYPE_SAVE, UseDDNetCA, true);
Engine()->AddJob(m_pMapdownloadTask);
}
else
SendMapRequest();
Expand Down Expand Up @@ -1731,7 +1732,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)

if(CompleteSize)
{
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2);
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2, sizeof(aTmpBuffer2));

if(IntSize < 0) // failure during decompression, bail
return;
Expand Down Expand Up @@ -1991,7 +1992,7 @@ void CClient::ProcessServerPacketDummy(CNetChunk *pPacket)

if(CompleteSize)
{
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2);
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2, sizeof(aTmpBuffer2));

if(IntSize < 0) // failure during decompression, bail
return;
Expand Down Expand Up @@ -2089,7 +2090,6 @@ void CClient::ResetMapDownload()
if(m_pMapdownloadTask)
{
m_pMapdownloadTask->Abort();
delete m_pMapdownloadTask;
m_pMapdownloadTask = NULL;
}
m_MapdownloadFile = 0;
Expand Down Expand Up @@ -2135,7 +2135,6 @@ void CClient::ResetDDNetInfo()
if(m_pDDNetInfoTask)
{
m_pDDNetInfoTask->Abort();
delete m_pDDNetInfoTask;
m_pDDNetInfoTask = NULL;
}
}
Expand Down Expand Up @@ -2486,7 +2485,6 @@ void CClient::Update()
}
else if(m_pMapdownloadTask->State() == CFetchTask::STATE_ABORTED)
{
delete m_pMapdownloadTask;
m_pMapdownloadTask = NULL;
}
}
Expand All @@ -2502,7 +2500,6 @@ void CClient::Update()
}
else if(m_pDDNetInfoTask->State() == CFetchTask::STATE_ABORTED)
{
delete m_pDDNetInfoTask;
m_pDDNetInfoTask = NULL;
}
}
Expand All @@ -2529,8 +2526,9 @@ void CClient::RegisterInterfaces()
{
Kernel()->RegisterInterface(static_cast<IDemoRecorder*>(&m_DemoRecorder[RECORDER_MANUAL]), false);
Kernel()->RegisterInterface(static_cast<IDemoPlayer*>(&m_DemoPlayer), false);
Kernel()->RegisterInterface(static_cast<IGhostRecorder*>(&m_GhostRecorder), false);
Kernel()->RegisterInterface(static_cast<IGhostLoader*>(&m_GhostLoader), false);
Kernel()->RegisterInterface(static_cast<IServerBrowser*>(&m_ServerBrowser), false);
Kernel()->RegisterInterface(static_cast<IFetcher*>(&m_Fetcher), false);
#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__)
Kernel()->RegisterInterface(static_cast<IUpdater*>(&m_Updater), false);
#endif
Expand All @@ -2549,7 +2547,6 @@ void CClient::InitInterfaces()
m_pInput = Kernel()->RequestInterface<IEngineInput>();
m_pMap = Kernel()->RequestInterface<IEngineMap>();
m_pMasterServer = Kernel()->RequestInterface<IEngineMasterServer>();
m_pFetcher = Kernel()->RequestInterface<IFetcher>();
#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__)
m_pUpdater = Kernel()->RequestInterface<IUpdater>();
#endif
Expand All @@ -2559,14 +2556,17 @@ void CClient::InitInterfaces()

m_ServerBrowser.SetBaseInfo(&m_NetClient[2], m_pGameClient->NetVersion());

m_Fetcher.Init();
FetcherInit();

#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__)
m_Updater.Init();
#endif

m_Friends.Init();
m_Foes.Init(true);

m_GhostRecorder.Init();
m_GhostLoader.Init();
}

void CClient::Run()
Expand Down Expand Up @@ -2884,6 +2884,7 @@ void CClient::Run()
GameClient()->OnShutdown();
Disconnect();

delete m_pEditor;
m_pGraphics->Shutdown();

// shutdown SDL
Expand Down Expand Up @@ -3431,6 +3432,7 @@ int main(int argc, const char **argv) // ignore_convention
{
#endif
bool Silent = false;
bool RandInitFailed = false;

for(int i = 1; i < argc; i++) // ignore_convention
{
Expand All @@ -3444,14 +3446,9 @@ int main(int argc, const char **argv) // ignore_convention
}
}

#if !defined(CONF_PLATFORM_MACOSX)
dbg_enable_threaded();
#endif

if(secure_random_init() != 0)
{
dbg_msg("secure", "could not initialize secure RNG");
return -1;
RandInitFailed = true;
}

CClient *pClient = CreateClient();
Expand All @@ -3470,6 +3467,12 @@ int main(int argc, const char **argv) // ignore_convention
IEngineMap *pEngineMap = CreateEngineMap();
IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();

if(RandInitFailed)
{
dbg_msg("secure", "could not initialize secure RNG");
return -1;
}

{
bool RegisterFail = false;

Expand All @@ -3492,7 +3495,7 @@ int main(int argc, const char **argv) // ignore_convention
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer), false);

RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor());
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor(), false);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateGameClient(), false);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);

Expand Down Expand Up @@ -3583,53 +3586,45 @@ int main(int argc, const char **argv) // ignore_convention

// DDRace

const char* CClient::GetCurrentMap()
const char *CClient::GetCurrentMap()
{
return m_aCurrentMap;
}

int CClient::GetCurrentMapCrc()
const char *CClient::GetCurrentMapPath()
{
return m_pMap->Crc();
return m_aCurrentMapPath;
}

const char* CClient::GetCurrentMapPath()
unsigned CClient::GetMapCrc()
{
return m_aCurrentMapPath;
return m_pMap->Crc();
}

const char* CClient::RaceRecordStart(const char *pFilename)
void CClient::RaceRecord_Start(const char *pFilename)
{
char aFilename[128];
str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", m_aCurrentMap, pFilename);

if(State() != STATE_ONLINE)
dbg_msg("demorec/record", "client is not online");
if(State() != IClient::STATE_ONLINE)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online");
else
m_DemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());

return m_aCurrentMap;
m_DemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, pFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());
}

void CClient::RaceRecordStop()
void CClient::RaceRecord_Stop()
{
if(m_DemoRecorder[RECORDER_RACE].IsRecording())
m_DemoRecorder[RECORDER_RACE].Stop();
}

bool CClient::RaceRecordIsRecording()
bool CClient::RaceRecord_IsRecording()
{
return m_DemoRecorder[RECORDER_RACE].IsRecording();
}


void CClient::RequestDDNetInfo()
{
char aUrl[256];
#if defined(CONF_FAMILY_WINDOWS)
static bool s_IsWinXP = os_compare_version(5U, 1U) <= 0;
#else
static bool s_IsWinXP = false;
#endif
static bool s_IsWinXP = os_is_winxp_or_lower();
if(s_IsWinXP)
str_copy(aUrl, "http://info.ddnet.tw/info", sizeof(aUrl));
else
Expand All @@ -3638,13 +3633,13 @@ void CClient::RequestDDNetInfo()
if(g_Config.m_BrIndicateFinished)
{
char aEscaped[128];
Fetcher()->Escape(aEscaped, sizeof(aEscaped), g_Config.m_PlayerName);
EscapeUrl(aEscaped, sizeof(aEscaped), g_Config.m_PlayerName);
str_append(aUrl, "?name=", sizeof(aUrl));
str_append(aUrl, aEscaped, sizeof(aUrl));
}

m_pDDNetInfoTask = new CFetchTask(true, /*UseDDNetCA*/ true);
Fetcher()->QueueAdd(m_pDDNetInfoTask, aUrl, "ddnet-info.json.tmp", IStorage::TYPE_SAVE);
m_pDDNetInfoTask = std::make_shared<CFetchTask>(Storage(), aUrl, "ddnet-info.json.tmp", IStorage::TYPE_SAVE, true, true);
Engine()->AddJob(m_pDDNetInfoTask);
}

int CClient::GetPredictionTime()
Expand Down
27 changes: 15 additions & 12 deletions src/engine/client/client.h
Expand Up @@ -3,6 +3,10 @@
#ifndef ENGINE_CLIENT_CLIENT_H
#define ENGINE_CLIENT_CLIENT_H

#include <memory>

#include "fetcher.h"

class CGraph
{
public:
Expand Down Expand Up @@ -61,7 +65,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
IEngineMap *m_pMap;
IConsole *m_pConsole;
IStorage *m_pStorage;
IFetcher *m_pFetcher;
IUpdater *m_pUpdater;
IEngineMasterServer *m_pMasterServer;

Expand All @@ -75,8 +78,9 @@ class CClient : public IClient, public CDemoPlayer::IListener
class CDemoPlayer m_DemoPlayer;
class CDemoRecorder m_DemoRecorder[RECORDER_MAX];
class CDemoEditor m_DemoEditor;
class CGhostRecorder m_GhostRecorder;
class CGhostLoader m_GhostLoader;
class CServerBrowser m_ServerBrowser;
class CFetcher m_Fetcher;
class CUpdater m_Updater;
class CFriends m_Friends;
class CFriends m_Foes;
Expand Down Expand Up @@ -126,7 +130,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
char m_aCmdConnect[256];

// map download
CFetchTask *m_pMapdownloadTask;
std::shared_ptr<CFetchTask> m_pMapdownloadTask;
char m_aMapdownloadFilename[256];
char m_aMapdownloadName[256];
IOHANDLE m_MapdownloadFile;
Expand All @@ -135,7 +139,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
int m_MapdownloadAmount;
int m_MapdownloadTotalsize;

CFetchTask *m_pDDNetInfoTask;
std::shared_ptr<CFetchTask> m_pDDNetInfoTask;

// time
CSmoothTime m_GameTime[2];
Expand Down Expand Up @@ -207,7 +211,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
IGameClient *GameClient() { return m_pGameClient; }
IEngineMasterServer *MasterServer() { return m_pMasterServer; }
IStorage *Storage() { return m_pStorage; }
IFetcher *Fetcher() { return m_pFetcher; }
IUpdater *Updater() { return m_pUpdater; }

CClient();
Expand Down Expand Up @@ -300,7 +303,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
void FinishDDNetInfo();
void LoadDDNetInfo();

virtual CFetchTask *MapDownloadTask() { return m_pMapdownloadTask; }
virtual const char *MapDownloadName() { return m_aMapdownloadName; }
virtual int MapDownloadAmount() { return !m_pMapdownloadTask ? m_MapdownloadAmount : (int)m_pMapdownloadTask->Current(); }
virtual int MapDownloadTotalsize() { return !m_pMapdownloadTask ? m_MapdownloadTotalsize : (int)m_pMapdownloadTask->Size(); }
Expand Down Expand Up @@ -381,12 +383,13 @@ class CClient : public IClient, public CDemoPlayer::IListener
void GenerateTimeoutSeed();
void GenerateTimeoutCodes();

virtual const char* GetCurrentMap();
virtual int GetCurrentMapCrc();
virtual const char* GetCurrentMapPath();
virtual const char* RaceRecordStart(const char *pFilename);
virtual void RaceRecordStop();
virtual bool RaceRecordIsRecording();
const char *GetCurrentMap();
const char *GetCurrentMapPath();
unsigned GetMapCrc();

void RaceRecord_Start(const char *pFilename);
void RaceRecord_Stop();
bool RaceRecord_IsRecording();

virtual void DemoSliceBegin();
virtual void DemoSliceEnd();
Expand Down
204 changes: 76 additions & 128 deletions src/engine/client/fetcher.cpp
@@ -1,119 +1,64 @@
#include <base/system.h>
#include <engine/engine.h>
#include <engine/storage.h>
#include <engine/shared/config.h>
#include <game/version.h>
#include "fetcher.h"

CFetchTask::CFetchTask(bool canTimeout, bool useDDNetCA)
{
m_pNext = NULL;
m_CanTimeout = canTimeout;
m_UseDDNetCA = useDDNetCA;
}
#define WIN32_LEAN_AND_MEAN
#include "curl/curl.h"
#include "curl/easy.h"

CFetcher::CFetcher()
{
m_pStorage = NULL;
m_pHandle = NULL;
m_Lock = lock_create();
sphore_init(&m_Queued);
m_pFirst = NULL;
m_pLast = NULL;
m_Running = true;
}
#include "fetcher.h"

bool CFetcher::Init()
double CFetchTask::Current() const { return m_Current; }
double CFetchTask::Size() const { return m_Size; }
int CFetchTask::Progress() const { return m_Progress; }
int CFetchTask::State() const { return m_State; }
const char *CFetchTask::Dest() const { return m_aDest; }

void CFetchTask::Abort() { m_Abort = true; };

CFetchTask::CFetchTask(IStorage *pStorage, const char *pUrl, const char *pDest, int StorageType, bool UseDDNetCA, bool CanTimeout) :
m_pStorage(pStorage),
m_StorageType(StorageType),
m_UseDDNetCA(UseDDNetCA),
m_CanTimeout(CanTimeout),
m_Size(0),
m_Progress(0),
m_State(CFetchTask::STATE_QUEUED),
m_Abort(false)
{
m_pStorage = Kernel()->RequestInterface<IStorage>();
if(!curl_global_init(CURL_GLOBAL_DEFAULT) && (m_pHandle = curl_easy_init()))
return true;
return false;
str_copy(m_aUrl, pUrl, sizeof(m_aUrl));
str_copy(m_aDest, pDest, sizeof(m_aDest));
}

CFetcher::~CFetcher()
{
if(m_pThHandle)
{
m_Running = false;
sphore_signal(&m_Queued);
thread_wait(m_pThHandle);
}
lock_destroy(m_Lock);
sphore_destroy(&m_Queued);

if(m_pHandle)
curl_easy_cleanup(m_pHandle);
curl_global_cleanup();
}

void CFetcher::QueueAdd(CFetchTask *pTask, const char *pUrl, const char *pDest, int StorageType, void *pUser, COMPFUNC pfnCompCb, PROGFUNC pfnProgCb)
bool FetcherInit()
{
str_copy(pTask->m_aUrl, pUrl, sizeof(pTask->m_aUrl));
str_copy(pTask->m_aDest, pDest, sizeof(pTask->m_aDest));
pTask->m_StorageType = StorageType;
pTask->m_pfnProgressCallback = pfnProgCb;
pTask->m_pfnCompCallback = pfnCompCb;
pTask->m_pUser = pUser;
pTask->m_Size = pTask->m_Progress = 0;
pTask->m_Abort = false;

lock_wait(m_Lock);
if(!m_pThHandle)
{
m_pThHandle = thread_init(&FetcherThread, this);
}

if(!m_pFirst)
{
m_pFirst = pTask;
m_pLast = m_pFirst;
}
else
{
m_pLast->m_pNext = pTask;
m_pLast = pTask;
}
pTask->m_State = CFetchTask::STATE_QUEUED;
lock_unlock(m_Lock);
sphore_signal(&m_Queued);
return !curl_global_init(CURL_GLOBAL_DEFAULT);
}

void CFetcher::Escape(char *pBuf, size_t size, const char *pStr)
void EscapeUrl(char *pBuf, int Size, const char *pStr)
{
char *pEsc = curl_easy_escape(0, pStr, 0);
str_copy(pBuf, pEsc, size);
str_copy(pBuf, pEsc, Size);
curl_free(pEsc);
}

void CFetcher::FetcherThread(void *pUser)
void CFetchTask::Run()
{
CFetcher *pFetcher = (CFetcher *)pUser;
dbg_msg("fetcher", "thread started...");
while(pFetcher->m_Running)
CURL *pHandle = curl_easy_init();
if(!pHandle)
{
sphore_wait(&pFetcher->m_Queued);
lock_wait(pFetcher->m_Lock);
CFetchTask *pTask = pFetcher->m_pFirst;
if(pTask)
pFetcher->m_pFirst = pTask->m_pNext;
lock_unlock(pFetcher->m_Lock);
if(pTask)
{
dbg_msg("fetcher", "task got %s -> %s", pTask->m_aUrl, pTask->m_aDest);
pFetcher->FetchFile(pTask);
if(pTask->m_pfnCompCallback)
pTask->m_pfnCompCallback(pTask, pTask->m_pUser);
}
m_State = STATE_ERROR;
return;
}
}

void CFetcher::FetchFile(CFetchTask *pTask)
{
char aPath[512];
if(pTask->m_StorageType == -2)
m_pStorage->GetBinaryPath(pTask->m_aDest, aPath, sizeof(aPath));
if(m_StorageType == -2)
m_pStorage->GetBinaryPath(m_aDest, aPath, sizeof(aPath));
else
m_pStorage->GetCompletePath(pTask->m_StorageType, pTask->m_aDest, aPath, sizeof(aPath));
m_pStorage->GetCompletePath(m_StorageType, m_aDest, aPath, sizeof(aPath));

if(fs_makedir_rec_for(aPath) < 0)
dbg_msg("fetcher", "i/o error, cannot create folder for: %s", aPath);
Expand All @@ -122,73 +67,76 @@ void CFetcher::FetchFile(CFetchTask *pTask)

if(!File)
{
dbg_msg("fetcher", "i/o error, cannot open file: %s", pTask->m_aDest);
pTask->m_State = CFetchTask::STATE_ERROR;
dbg_msg("fetcher", "i/o error, cannot open file: %s", m_aDest);
m_State = CFetchTask::STATE_ERROR;
return;
}

char aErr[CURL_ERROR_SIZE];
curl_easy_setopt(m_pHandle, CURLOPT_ERRORBUFFER, aErr);
curl_easy_setopt(pHandle, CURLOPT_ERRORBUFFER, aErr);

//curl_easy_setopt(m_pHandle, CURLOPT_VERBOSE, 1L);
if(pTask->m_CanTimeout)
//curl_easy_setopt(pHandle, CURLOPT_VERBOSE, 1L);
if(m_CanTimeout)
{
curl_easy_setopt(m_pHandle, CURLOPT_CONNECTTIMEOUT_MS, (long)g_Config.m_ClHTTPConnectTimeoutMs);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_LIMIT, (long)g_Config.m_ClHTTPLowSpeedLimit);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_TIME, (long)g_Config.m_ClHTTPLowSpeedTime);
curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT_MS, (long)g_Config.m_ClHTTPConnectTimeoutMs);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_LIMIT, (long)g_Config.m_ClHTTPLowSpeedLimit);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_TIME, (long)g_Config.m_ClHTTPLowSpeedTime);
}
else
{
curl_easy_setopt(m_pHandle, CURLOPT_CONNECTTIMEOUT_MS, 0);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_LIMIT, 0);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_TIME, 0);
curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT_MS, 0);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_LIMIT, 0);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_TIME, 0);
}
curl_easy_setopt(m_pHandle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(m_pHandle, CURLOPT_MAXREDIRS, 4L);
curl_easy_setopt(m_pHandle, CURLOPT_FAILONERROR, 1L);
if(pTask->m_UseDDNetCA)
curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(pHandle, CURLOPT_MAXREDIRS, 4L);
curl_easy_setopt(pHandle, CURLOPT_FAILONERROR, 1L);
if(m_UseDDNetCA)
{
char aCAFile[512];
m_pStorage->GetBinaryPath("data/ca-ddnet.pem", aCAFile, sizeof aCAFile);
curl_easy_setopt(m_pHandle, CURLOPT_CAINFO, aCAFile);
curl_easy_setopt(pHandle, CURLOPT_CAINFO, aCAFile);
}
curl_easy_setopt(m_pHandle, CURLOPT_URL, pTask->m_aUrl);
curl_easy_setopt(m_pHandle, CURLOPT_WRITEDATA, File);
curl_easy_setopt(m_pHandle, CURLOPT_WRITEFUNCTION, &CFetcher::WriteToFile);
curl_easy_setopt(m_pHandle, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(m_pHandle, CURLOPT_PROGRESSDATA, pTask);
curl_easy_setopt(m_pHandle, CURLOPT_PROGRESSFUNCTION, &CFetcher::ProgressCallback);
curl_easy_setopt(m_pHandle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(m_pHandle, CURLOPT_USERAGENT, "DDNet " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");

dbg_msg("fetcher", "downloading %s", pTask->m_aDest);
pTask->m_State = CFetchTask::STATE_RUNNING;
int ret = curl_easy_perform(m_pHandle);
curl_easy_setopt(pHandle, CURLOPT_URL, m_aUrl);
curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, File);
curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
curl_easy_setopt(pHandle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(pHandle, CURLOPT_USERAGENT, "DDNet " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");

dbg_msg("fetcher", "downloading %s", m_aDest);
m_State = CFetchTask::STATE_RUNNING;
int Ret = curl_easy_perform(pHandle);
io_close(File);
if(ret != CURLE_OK)
if(Ret != CURLE_OK)
{
dbg_msg("fetcher", "task failed. libcurl error: %s", aErr);
pTask->m_State = (ret == CURLE_ABORTED_BY_CALLBACK) ? CFetchTask::STATE_ABORTED : CFetchTask::STATE_ERROR;
m_State = (Ret == CURLE_ABORTED_BY_CALLBACK) ? CFetchTask::STATE_ABORTED : CFetchTask::STATE_ERROR;
}
else
{
dbg_msg("fetcher", "task done %s", pTask->m_aDest);
pTask->m_State = CFetchTask::STATE_DONE;
dbg_msg("fetcher", "task done %s", m_aDest);
m_State = CFetchTask::STATE_DONE;
}

curl_easy_cleanup(pHandle);

OnCompletion();
}

size_t CFetcher::WriteToFile(char *pData, size_t size, size_t nmemb, void *pFile)
size_t CFetchTask::WriteCallback(char *pData, size_t Size, size_t Number, void *pFile)
{
return io_write((IOHANDLE)pFile, pData, size*nmemb);
return io_write((IOHANDLE)pFile, pData, Size * Number);
}

int CFetcher::ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
int CFetchTask::ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
{
CFetchTask *pTask = (CFetchTask *)pUser;
pTask->m_Current = DlCurr;
pTask->m_Size = DlTotal;
pTask->m_Progress = (100 * DlCurr) / (DlTotal ? DlTotal : 1);
if(pTask->m_pfnProgressCallback)
pTask->m_pfnProgressCallback(pTask, pTask->m_pUser);
pTask->OnProgress();
return pTask->m_Abort ? -1 : 0;
}
67 changes: 44 additions & 23 deletions src/engine/client/fetcher.h
@@ -1,35 +1,56 @@
#ifndef ENGINE_CLIENT_FETCHER_H
#define ENGINE_CLIENT_FETCHER_H

#define WIN32_LEAN_AND_MEAN
#include "curl/curl.h"
#include "curl/easy.h"
#include <engine/fetcher.h>
#include <engine/shared/jobs.h>
#include <engine/storage.h>
#include <engine/kernel.h>

class CFetcher : public IFetcher
class CFetchTask : public IJob
{
private:
CURL *m_pHandle;
IStorage *m_pStorage;

void *m_pThHandle;
char m_aUrl[256];
char m_aDest[128];
int m_StorageType;
bool m_UseDDNetCA;
bool m_CanTimeout;

double m_Size;
double m_Current;
int m_Progress;
int m_State;

std::atomic<bool> m_Abort;

virtual void OnProgress() { }
virtual void OnCompletion() { }

bool m_Running;
LOCK m_Lock;
SEMAPHORE m_Queued;
CFetchTask *m_pFirst;
CFetchTask *m_pLast;
class IStorage *m_pStorage;
public:
CFetcher();
virtual bool Init();
~CFetcher();

virtual void QueueAdd(CFetchTask *pTask, const char *pUrl, const char *pDest, int StorageType = -2, void *pUser = 0, COMPFUNC pfnCompCb = 0, PROGFUNC pfnProgCb = 0);
virtual void Escape(char *pBud, size_t size, const char *pStr);
static void FetcherThread(void *pUser);
void FetchFile(CFetchTask *pTask);
static size_t WriteToFile(char *pData, size_t size, size_t nmemb, void *pFile);
static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr);
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pFile);

void Run();

public:
enum
{
STATE_ERROR = -1,
STATE_QUEUED,
STATE_RUNNING,
STATE_DONE,
STATE_ABORTED,
};

CFetchTask(IStorage *pStorage, const char *pUrl, const char *pDest, int StorageType = -2, bool UseDDNetCA = false, bool CanTimeout = true);

double Current() const;
double Size() const;
int Progress() const;
int State() const;
const char *Dest() const;
void Abort();
};

bool FetcherInit();
void EscapeUrl(char *pBud, int Size, const char *pStr);
#endif