63 changes: 51 additions & 12 deletions data/languages/spanish.txt
Expand Up @@ -21,6 +21,7 @@
# Deëivid! 2023-01-05 21:05:00
# Deëivid! 2023-02-26 00:35:00
# Deëivid! 2023-05-22 19:54:00
# Deëivid! 2023-07-01 22:52:00
##### /authors #####

##### translated strings #####
Expand Down Expand Up @@ -1334,6 +1335,7 @@ Download community skins
Enable controller
== Habilitar mando

[Ingame controller mode]
Relative
== Relativo

Expand Down Expand Up @@ -1596,8 +1598,6 @@ Reset controls
Are you sure that you want to reset the controls to their defaults?
== ¿Estás seguro de que quieres restablecer a los controles predeterminados?

[Graphics error]

[Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== Error durante la iniciación. Intenta cambiar gfx_backend a OpenGL o Vulkan desde settings_ddnet.cfg en la carpeta de configuraciones e inténtalo otra vez.
Expand Down Expand Up @@ -1696,31 +1696,70 @@ Add Clan
== Agregar clan

A demo with this name already exists
==
== Ya existe una demo con este nombre

No server selected
==
== Ningún servidor seleccionado

Mark the beginning of a cut (right click to reset)
==
== Marcar el inicio de un corte (clic derecho para restablecer)

Mark the end of a cut (right click to reset)
==
== Marcar el final de un corte (clic derecho para restablecer)

Close the demo player
==
== Cerrar el reproductor de demos

Export demo cut
==
== Exportar corte de la demo

Cut interval
==
== Intervalo del corte

Cut length
==
== Largo del corte

Axis
==
== Eje

Graphics card
==
== Tarjeta gráfica

Quitting. Please wait…
== Saliendo. Por favor, espera…

Restarting. Please wait…
== Reiniciando. Por favor, espera…

Multi-View
== Vista Múltiple

Rename folder
== Renombrar carpeta

A folder with this name already exists
== Ya existe una carpeta con este nombre

Unable to rename the folder
== No se pudo renombrar la carpeta

(paused)
== (en pausa)

All combined
== Todo combinado

Folder Link
== Enlace de la carpeta

Are you sure that you want to delete the folder '%s'?
== ¿Seguro que quieres eliminar la carpeta '%s'?

Delete folder
== Eliminar carpeta

Unable to delete the folder '%s'. Make sure it's empty first.
== No se pudo eliminar la carpeta '%s'. Asegúrate de que esté vacía primero.

Moved ingame
== Movido dentro del juego
41 changes: 39 additions & 2 deletions data/languages/swedish.txt
Expand Up @@ -1237,8 +1237,6 @@ Discord
https://ddnet.org/discord
== https://ddnet.org/discord

[Graphics error]

[Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
==
Expand Down Expand Up @@ -1305,6 +1303,12 @@ Initializing map logic
Sending initial client info
==

Quitting. Please wait…
==

Restarting. Please wait…
==

Position:
==

Expand All @@ -1314,6 +1318,9 @@ Speed:
Angle:
==

Multi-View
==

Team %d
==

Expand All @@ -1338,12 +1345,24 @@ Getting game info
Requesting to join the game
==

Rename folder
==

A demo with this name already exists
==

A folder with this name already exists
==

Unable to rename the folder
==

File '%s' already exists, do you want to overwrite it?
==

(paused)
==

Join Tutorial Server
==

Expand Down Expand Up @@ -1444,15 +1463,30 @@ Cut length
Loading demo files
==

All combined
==

Folder Link
==

Open the directory that contains the demo files
==

Are you sure that you want to delete the folder '%s'?
==

Are you sure that you want to delete the demo '%s'?
==

Delete folder
==

Unable to delete the demo '%s'
==

Unable to delete the folder '%s'. Make sure it's empty first.
==

Loading ghost files
==

Expand Down Expand Up @@ -1709,3 +1743,6 @@ Super

Loading sound files
==

Moved ingame
==
66 changes: 53 additions & 13 deletions data/languages/traditional_chinese.txt
Expand Up @@ -22,6 +22,8 @@
# 2023-01-11 cheeser0613
# 2023-04-01 cheeser0613
# 2023-05-25 cheeser0613
# 2023-07-20 By
# 2023-08-11 By
##### /authors #####

##### translated strings #####
Expand Down Expand Up @@ -1342,6 +1344,7 @@ Download community skins
Enable controller
== 啓用控制器

[Ingame controller mode]
Relative
== 相對

Expand Down Expand Up @@ -1416,7 +1419,7 @@ Loading map file from storage
== 正在從硬盤中加載地圖檔案

Why are you slowmo replaying to read this?
== 所以你爲什麽要慢速回放來看這個
== 所以你爲什麽要慢速回放來看這個

Initializing components
== 正在初始化游戲引擎
Expand Down Expand Up @@ -1601,8 +1604,6 @@ Reset controls
Are you sure that you want to reset the controls to their defaults?
== 你確定要將當前的控制恢復至預設設定

[Graphics error]

[Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== 初始化失敗。請嘗試打開配置目錄中的設定檔案 (settings_ddnet.cfg) 并將“gfx_backend OpenGL”修改為“gfx_backend Vulkan” (若沒有前者則可直接輸入後者) 再重試。
Expand Down Expand Up @@ -1701,31 +1702,70 @@ Add Clan
== 新增戰隊

A demo with this name already exists
==
== 已經存在同名回放

No server selected
==
== 未選擇伺服器

Mark the beginning of a cut (right click to reset)
==
== 標記裁剪起點 (右鍵重置)

Mark the end of a cut (right click to reset)
==
== 標記裁剪終點 (右鍵重置)

Close the demo player
==
== 關閉回放

Export demo cut
==
== 另存裁剪部分

Cut interval
==
== 裁剪區間

Cut length
==
== 裁剪長度

Axis
==
==

Graphics card
==
== 顯示卡

Quitting. Please wait…
== 正在退出,請稍等

Restarting. Please wait…
== 正在重啟,請稍等

Multi-View
== 多人同框視角

Rename folder
== 重新命名資料夾

A folder with this name already exists
== 已經存在同名資料夾

Unable to rename the folder
== 無法重新命名這個資料夾

(paused)
== (暫停)

All combined
== 全部檔案

Folder Link
== 資料夾連結

Are you sure that you want to delete the folder '%s'?
== 你確定要刪除資料夾 "%s" ?

Delete folder
== 刪除資料夾

Unable to delete the folder '%s'. Make sure it's empty first.
== 無法刪除 "%s" ,請確保你已清空該資料夾

Moved ingame
== 遊戲內移動
41 changes: 39 additions & 2 deletions data/languages/turkish.txt
Expand Up @@ -1044,8 +1044,6 @@ Replay
https://wiki.ddnet.org/
== https://wiki.ddnet.org/wiki/Main_Page/tr

[Graphics error]

[Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
==
Expand Down Expand Up @@ -1121,6 +1119,12 @@ Sending initial client info
Warning
==

Quitting. Please wait…
==

Restarting. Please wait…
==

Debug mode enabled. Press Ctrl+Shift+D to disable debug mode.
==

Expand All @@ -1133,6 +1137,9 @@ Speed:
Angle:
==

Multi-View
==

Team %d
==

Expand All @@ -1157,6 +1164,9 @@ Getting game info
Requesting to join the game
==

Rename folder
==

Existing Player
==

Expand All @@ -1169,9 +1179,18 @@ Checking for existing player with your name
A demo with this name already exists
==

A folder with this name already exists
==

Unable to rename the folder
==

File '%s' already exists, do you want to overwrite it?
==

(paused)
==

Speed
==

Expand Down Expand Up @@ -1320,18 +1339,33 @@ Cut length
Loading demo files
==

All combined
==

Folder Link
==

Demos directory
==

Open the directory that contains the demo files
==

Are you sure that you want to delete the folder '%s'?
==

Are you sure that you want to delete the demo '%s'?
==

Delete folder
==

Unable to delete the demo '%s'
==

Unable to delete the folder '%s'. Make sure it's empty first.
==

Loading ghost files
==

Expand Down Expand Up @@ -1712,3 +1746,6 @@ Super

Loading sound files
==

Moved ingame
==
78 changes: 58 additions & 20 deletions data/languages/ukrainian.txt
Expand Up @@ -101,7 +101,7 @@ Fire
== Постріл

Folder
== Папка
== Папку

Force vote
== Форсувати
Expand Down Expand Up @@ -656,7 +656,7 @@ Fetch Info
== Отримати інформацію

Rename
== Перейменуватися
== Перейменувати

Render
== Почати рендер
Expand Down Expand Up @@ -746,7 +746,7 @@ Max CSVs
== Максимум CSV

Dummy settings
== Налаштування dummy
== Налаштування даммі

Vanilla skins only
== Тільки базові скіни
Expand Down Expand Up @@ -1094,7 +1094,7 @@ Getting server list from master server
== %d З %d сервера

%d players
== %dГравців
== %d Гравців

%d player
== %d Гравець
Expand All @@ -1112,7 +1112,7 @@ Smooth Dynamic Camera
== Гладка динамічна камера

Skip the main menu
== пропускати головне меняю
== Пропускати головне меню

Themes directory
== Каталог тем
Expand Down Expand Up @@ -1169,10 +1169,10 @@ Background
== Задній фон

Entities Background color
== колір текстури заднього фону
== Колір текстури заднього фону

Use current map as background
== використовувати поточну карту як задній фон
== Використовувати поточну карту як задній фон

Regular Background Color
== Звичайний колір заднього фону
Expand All @@ -1184,7 +1184,7 @@ Emoticons
== Емоції

Particles
== частинки
== Частинки

Assets directory
== Каталог текстур
Expand Down Expand Up @@ -1220,8 +1220,6 @@ Editor
Play
== Грати

[Graphics error]

[Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== Помилка під час ініціалізації. Спробуйте змінити gfx_backend на OpenGL або Vulkan у settings_ddnet.cfg в каталозі config і спробуйте ще раз.
Expand Down Expand Up @@ -1460,6 +1458,7 @@ Open the directory to add custom skins
Enable controller
== Увімкнути контролер

[Ingame controller mode]
Relative
== Relative

Expand Down Expand Up @@ -1677,32 +1676,71 @@ Super
Loading sound files
== Завантаження звукових файлів

Quitting. Please wait…
== Припинення гри. Зачекайте будь ласка...

Restarting. Please wait…
== Перезавантаження. Зачекайте будь ласка...

Multi-View
== Дінамічний перегляд

Rename folder
== Перейменувати папку

A demo with this name already exists
==
== Демо з такою назвою вже існує

A folder with this name already exists
== Папка с такою назвою вже існує

Unable to rename the folder
== Неможливо перейменувати папку

(paused)
== (пауза)

No server selected
==
== Жоден сервер не вибран

Mark the beginning of a cut (right click to reset)
==
== Позначте початок відрізка (натисніть праву кнопку миші, щоб скинути позначку)

Mark the end of a cut (right click to reset)
==
== Позначте кінець відрізка (натисніть праву кнопку миші, щоб скинути позначку)

Close the demo player
==
== Закрити програвач демо

Export demo cut
==
== Експорт відрізку з демо

Cut interval
==
== Інтервал відрізку

Cut length
==
== Довжина відрізку

All combined
== Все разом

Folder Link
== Посилання папки

Are you sure that you want to delete the folder '%s'?
== Ви впевнені, що хочете видалити папку '%s'?

Delete folder
== Видалити папку

Unable to delete the folder '%s'. Make sure it's empty first.
== Неможливо видалити папку '%s'. Спочатку переконайтеся, що вона порожня.

Axis
==
== Вісь

Graphics card
==
== Відеокарта

Moved ingame
== Перемістився в грі
Binary file modified data/mapres/grass_doodads_0.7.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion data/skins/license.txt
Expand Up @@ -27,10 +27,14 @@ CC-BY-SA license
bomb, kitty_bluekitty, kitty_bluestripe, kitty_brownbear, kitty_cammo,
kitty_coala, kitty_default, kitty_limekitty, kitty_pinky, kitty_redbopp,
kitty_redstripe, kitty_saddo, kitty_toptri, kitty_twinbop, kitty_twintri,
kitty_warpaint, kitty_x_ninja:
kitty_warpaint:
Copyright Ravie
CC0 license

kitty_x_ninja:
Copyright patwo.*
CC0 license

demonlimekitty, nanas, nersif:
Copyright Miper
CC-BY-SA license
Expand Down
2 changes: 1 addition & 1 deletion ddnet-libs
221 changes: 216 additions & 5 deletions license.txt
Expand Up @@ -23,19 +23,44 @@ freely, subject to the following restrictions:
All content under 'data' except the assets, font, language & skin files,
(which have their own licenses) are released under
CC-BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/).
SIL OFL 1.1 License for the 'Icon.otf' ('Font Awesome 6 Free-Solid-900.otf') file:

Copyright (c) 2022 Fonticons, Inc. (https://fontawesome.com)
DejaVuSans.ttf:
---------------

Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)

Font_Awesome_6_Free-Solid-900.otf:
----------------------------------

Copyright (c) 2023 Fonticons, Inc. (https://fontawesome.com)
with Reserved Font Name: "Font Awesome".

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL

SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
GlowSansJ-Compressed-Book.otf:
------------------------------

© 2020 Project Wêlai

Developer: Celestial Phineas

The other fonts are released under CC-BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/).
Glow Sans fonts are released under SIL Open Font License 1.1.

SourceHanSans.ttc:
------------------

Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font
Name 'Source'. Source is a trademark of Adobe in the United States
and/or other countries.

This Font Software is licensed under the SIL Open Font License,
Version 1.1.

This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL

------------------------------------------------------------------------

Expand All @@ -47,3 +72,189 @@ check the individual libraries.

With that being said, contact us if there is anything you want to do
that the license does not permit.

------------------------------------------------------------------------


SIL OPEN FONT LICENSE
---------------------
Version 1.1 - 26 February 2007

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting — in part or in whole — any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.


Bitstream Vera Fonts Copyright
------------------------------

Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
a trademark of Bitstream, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute the
Font Software, including without limitation the rights to use, copy, merge,
publish, distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to the
following conditions:

The above copyright and trademark notices and this permission notice shall
be included in all copies of one or more of the Font Software typefaces.

The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional glyphs or characters may be added to the Fonts, only if the fonts
are renamed to names not containing either the words "Bitstream" or the word
"Vera".

This License becomes null and void to the extent applicable to Fonts or Font
Software that has been modified and is distributed under the "Bitstream
Vera" names.

The Font Software may be sold as part of a larger software package but no
copy of one or more of the Font Software typefaces may be sold by itself.

THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
FONT SOFTWARE.

Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or Bitstream
Inc., respectively. For further information, contact: fonts at gnome dot
org.

Arev Fonts Copyright
------------------------------

Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and
associated documentation files (the "Font Software"), to reproduce
and distribute the modifications to the Bitstream Vera Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to
the following conditions:

The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.

The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Tavmjong Bah" or the word "Arev".

This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Tavmjong Bah Arev" names.

The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.

THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Except as contained in this notice, the name of Tavmjong Bah shall not
be used in advertising or otherwise to promote the sale, use or other
dealings in this Font Software without prior written authorization
from Tavmjong Bah. For further information, contact: tavmjong @ free
. fr.
1 change: 0 additions & 1 deletion lsan.supp
@@ -1 +0,0 @@
leak:CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferObject
12 changes: 6 additions & 6 deletions scripts/integration_test.sh
Expand Up @@ -182,7 +182,7 @@ $tool ../DDNet \

if [ "$arg_valgrind_memcheck" == "1" ]; then
wait_for_fifo client1.fifo 180
sleep 20
sleep 40
else
wait_for_fifo client1.fifo 50
sleep 1
Expand All @@ -206,7 +206,7 @@ $tool ../DDNet \

if [ "$arg_valgrind_memcheck" == "1" ]; then
wait_for_fifo client2.fifo 180
sleep 20
sleep 40
else
wait_for_fifo client2.fifo 50
sleep 2
Expand Down Expand Up @@ -255,7 +255,7 @@ sleep 1
echo "[*] test map change"
echo "rcon sv_map Tutorial" > client1.fifo
if [ "$arg_valgrind_memcheck" == "1" ]; then
sleep 30
sleep 60
else
sleep 15
fi
Expand All @@ -264,9 +264,9 @@ echo "[*] play demos"
echo "play demos/server.demo" > client1.fifo
echo "play demos/client1.demo" > client2.fifo
if [ "$arg_valgrind_memcheck" == "1" ]; then
sleep 20
sleep 40
else
sleep 5
sleep 10
fi

# Kill all processes first so all outputs are fully written
Expand Down Expand Up @@ -335,7 +335,7 @@ do
touch fail_logs.txt
continue
fi
logdiff="$(diff -u <(sort "$logfile") <(sort "stdout_$(basename "$logfile" .log).txt"))"
logdiff="$(diff -u <(grep -v "console: .* access for .* is now .*abled" "$logfile" | sort) <(sort "stdout_$(basename "$logfile" .log).txt"))"
if [ "$logdiff" != "" ]
then
echo "[-] Error: logfile '$logfile' differs from stdout"
Expand Down
54 changes: 45 additions & 9 deletions src/base/log.cpp
Expand Up @@ -20,16 +20,10 @@
#include <android/log.h>
#endif

std::atomic<LEVEL> loglevel = LEVEL_INFO;
std::atomic<ILogger *> global_logger = nullptr;
thread_local ILogger *scope_logger = nullptr;
thread_local bool in_logger = false;

void log_set_loglevel(LEVEL level)
{
loglevel.store(level, std::memory_order_release);
}

void log_set_global_logger(ILogger *logger)
{
ILogger *null = nullptr;
Expand Down Expand Up @@ -80,9 +74,6 @@ void log_log_impl(LEVEL level, bool have_color, LOG_COLOR color, const char *sys

void log_log_impl(LEVEL level, bool have_color, LOG_COLOR color, const char *sys, const char *fmt, va_list args)
{
if(level > loglevel.load(std::memory_order_acquire))
return;

// Make sure we're not logging recursively.
if(in_logger)
{
Expand Down Expand Up @@ -146,12 +137,21 @@ void log_log_color(LEVEL level, LOG_COLOR color, const char *sys, const char *fm
va_end(args);
}

bool CLogFilter::Filters(const CLogMessage *pMessage)
{
return pMessage->m_Level > m_MaxLevel.load(std::memory_order_relaxed);
}

#if defined(CONF_PLATFORM_ANDROID)
class CLoggerAndroid : public ILogger
{
public:
void Log(const CLogMessage *pMessage) override
{
if(m_Filter.Filters(pMessage))
{
return;
}
int AndroidLevel;
switch(pMessage->m_Level)
{
Expand Down Expand Up @@ -184,9 +184,14 @@ class CLoggerCollection : public ILogger
CLoggerCollection(std::vector<std::shared_ptr<ILogger>> &&vpLoggers) :
m_vpLoggers(std::move(vpLoggers))
{
m_Filter.m_MaxLevel.store(LEVEL_TRACE, std::memory_order_relaxed);
}
void Log(const CLogMessage *pMessage) override
{
if(m_Filter.Filters(pMessage))
{
return;
}
for(auto &pLogger : m_vpLoggers)
{
pLogger->Log(pMessage);
Expand Down Expand Up @@ -221,6 +226,10 @@ class CLoggerAsync : public ILogger
}
void Log(const CLogMessage *pMessage) override
{
if(m_Filter.Filters(pMessage))
{
return;
}
aio_lock(m_pAio);
if(m_AnsiTruecolor)
{
Expand Down Expand Up @@ -329,6 +338,10 @@ class CWindowsConsoleLogger : public ILogger
}
void Log(const CLogMessage *pMessage) override
{
if(m_Filter.Filters(pMessage))
{
return;
}
const std::wstring WideMessage = windows_utf8_to_wide(pMessage->m_aLine) + L"\r\n";

int Color = m_BackgroundColor;
Expand Down Expand Up @@ -372,6 +385,10 @@ class CWindowsFileLogger : public ILogger
}
void Log(const CLogMessage *pMessage) override
{
if(m_Filter.Filters(pMessage))
{
return;
}
m_OutputLock.lock();
DWORD Written; // we don't care about the value, but Windows 7 crashes if we pass NULL
WriteFile(m_pFile, pMessage->m_aLine, pMessage->m_LineLength, &Written, NULL);
Expand Down Expand Up @@ -408,6 +425,10 @@ class CLoggerWindowsDebugger : public ILogger
public:
void Log(const CLogMessage *pMessage) override
{
if(m_Filter.Filters(pMessage))
{
return;
}
const std::wstring WideMessage = windows_utf8_to_wide(pMessage->m_aLine);
OutputDebugStringW(WideMessage.c_str());
}
Expand Down Expand Up @@ -452,6 +473,12 @@ void CFutureLogger::Log(const CLogMessage *pMessage)
return;
}
m_PendingLock.lock();
pLogger = std::atomic_load_explicit(&m_pLogger, std::memory_order_relaxed);
if(pLogger)
{
pLogger->Log(pMessage);
return;
}
m_vPending.push_back(*pMessage);
m_PendingLock.unlock();
}
Expand All @@ -464,3 +491,12 @@ void CFutureLogger::GlobalFinish()
pLogger->GlobalFinish();
}
}

void CFutureLogger::OnFilterChange()
{
auto pLogger = std::atomic_load_explicit(&m_pLogger, std::memory_order_acquire);
if(pLogger)
{
pLogger->SetFilter(m_Filter);
}
}
45 changes: 34 additions & 11 deletions src/base/logger.h
Expand Up @@ -7,7 +7,7 @@
#include <mutex>
#include <vector>

typedef struct IOINTERNAL *IOHANDLE;
typedef void *IOHANDLE;

/**
* @ingroup Log
Expand Down Expand Up @@ -50,11 +50,36 @@ class CLogMessage
}
};

class CLogFilter
{
public:
/**
* The highest `LEVEL` that is still logged, -1 corresponds to no
* printing at all.
*/
std::atomic_int m_MaxLevel{LEVEL_INFO};

bool Filters(const CLogMessage *pMessage);
};

class ILogger
{
protected:
CLogFilter m_Filter;

public:
virtual ~ILogger() {}

/**
* Set a new filter. It's up to the logger implementation to actually
* use the filter.
*/
void SetFilter(const CLogFilter &Filter)
{
m_Filter.m_MaxLevel.store(Filter.m_MaxLevel.load(std::memory_order_relaxed), std::memory_order_relaxed);
OnFilterChange();
}

/**
* Send the specified message to the logging backend.
*
Expand All @@ -77,18 +102,12 @@ class ILogger
* @see log_global_logger_finish
*/
virtual void GlobalFinish() {}
/**
* Notifies thte logger of a changed `m_Filter`.
*/
virtual void OnFilterChange() {}
};

/**
* @ingroup Log
*
* Sets the global loglevel to the given value. Log messages with a less severe
* loglevel will not be forwarded to loggers.
*
* @param level The loglevel.
*/
void log_set_loglevel(LEVEL level);

/**
* @ingroup Log
*
Expand Down Expand Up @@ -198,6 +217,9 @@ std::unique_ptr<ILogger> log_logger_windows_debugger();
*
* Useful when you want to set a global logger without all logging targets
* being configured.
*
* This logger forwards `SetFilter` calls, `SetFilter` calls before a logger is
* set have no effect.
*/
class CFutureLogger : public ILogger
{
Expand All @@ -214,6 +236,7 @@ class CFutureLogger : public ILogger
void Set(std::shared_ptr<ILogger> pLogger);
void Log(const CLogMessage *pMessage) override;
void GlobalFinish() override;
void OnFilterChange() override;
};

/**
Expand Down
14 changes: 14 additions & 0 deletions src/base/math.h
Expand Up @@ -27,6 +27,20 @@ constexpr inline T mix(const T a, const T b, TB amount)
return a + (b - a) * amount;
}

template<typename T, typename TB>
inline T bezier(const T p0, const T p1, const T p2, const T p3, TB amount)
{
// De-Casteljau Algorithm
const T c10 = mix(p0, p1, amount);
const T c11 = mix(p1, p2, amount);
const T c12 = mix(p2, p3, amount);

const T c20 = mix(c10, c11, amount);
const T c21 = mix(c11, c12, amount);

return mix(c20, c21, amount); // c30
}

inline float random_float()
{
return rand() / (float)(RAND_MAX);
Expand Down
191 changes: 169 additions & 22 deletions src/base/system.cpp
Expand Up @@ -91,10 +91,30 @@

IOHANDLE io_stdin()
{
return (IOHANDLE)stdin;
#if defined(CONF_FAMILY_WINDOWS)
return GetStdHandle(STD_INPUT_HANDLE);
#else
return stdin;
#endif
}

IOHANDLE io_stdout()
{
#if defined(CONF_FAMILY_WINDOWS)
return GetStdHandle(STD_OUTPUT_HANDLE);
#else
return stdout;
#endif
}

IOHANDLE io_stderr()
{
#if defined(CONF_FAMILY_WINDOWS)
return GetStdHandle(STD_ERROR_HANDLE);
#else
return stderr;
#endif
}
IOHANDLE io_stdout() { return (IOHANDLE)stdout; }
IOHANDLE io_stderr() { return (IOHANDLE)stderr; }

IOHANDLE io_current_exe()
{
Expand Down Expand Up @@ -258,21 +278,52 @@ IOHANDLE io_open_impl(const char *filename, int flags)
dbg_assert(flags == (IOFLAG_READ | IOFLAG_SKIP_BOM) || flags == IOFLAG_READ || flags == IOFLAG_WRITE || flags == IOFLAG_APPEND, "flags must be read, read+skipbom, write or append");
#if defined(CONF_FAMILY_WINDOWS)
const std::wstring wide_filename = windows_utf8_to_wide(filename);
DWORD desired_access;
DWORD creation_disposition;
if((flags & IOFLAG_READ) != 0)
return (IOHANDLE)_wfsopen(wide_filename.c_str(), L"rb", _SH_DENYNO);
if(flags == IOFLAG_WRITE)
return (IOHANDLE)_wfsopen(wide_filename.c_str(), L"wb", _SH_DENYNO);
if(flags == IOFLAG_APPEND)
return (IOHANDLE)_wfsopen(wide_filename.c_str(), L"ab", _SH_DENYNO);
return 0x0;
{
desired_access = FILE_READ_DATA;
creation_disposition = OPEN_EXISTING;
}
else if(flags == IOFLAG_WRITE)
{
desired_access = FILE_WRITE_DATA;
creation_disposition = OPEN_ALWAYS;
}
else if(flags == IOFLAG_APPEND)
{
desired_access = FILE_APPEND_DATA;
creation_disposition = OPEN_ALWAYS;
}
else
{
dbg_assert(false, "logic error");
return nullptr;
}
HANDLE handle = CreateFileW(wide_filename.c_str(), desired_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if(handle == INVALID_HANDLE_VALUE)
return nullptr; // otherwise all existing checks don't work for the invalid handle
return handle;
#else
const char *open_mode;
if((flags & IOFLAG_READ) != 0)
return (IOHANDLE)fopen(filename, "rb");
if(flags == IOFLAG_WRITE)
return (IOHANDLE)fopen(filename, "wb");
if(flags == IOFLAG_APPEND)
return (IOHANDLE)fopen(filename, "ab");
return 0x0;
{
open_mode = "rb";
}
else if(flags == IOFLAG_WRITE)
{
open_mode = "wb";
}
else if(flags == IOFLAG_APPEND)
{
open_mode = "ab";
}
else
{
dbg_assert(false, "logic error");
return nullptr;
}
return fopen(filename, open_mode);
#endif
}

Expand All @@ -293,7 +344,13 @@ IOHANDLE io_open(const char *filename, int flags)

unsigned io_read(IOHANDLE io, void *buffer, unsigned size)
{
#if defined(CONF_FAMILY_WINDOWS)
DWORD actual_size;
ReadFile((HANDLE)io, buffer, size, &actual_size, nullptr);
return actual_size;
#else
return fread(buffer, 1, size, (FILE *)io);
#endif
}

void io_read_all(IOHANDLE io, void **result, unsigned *result_len)
Expand Down Expand Up @@ -343,14 +400,31 @@ char *io_read_all_str(IOHANDLE io)

unsigned io_skip(IOHANDLE io, int size)
{
fseek((FILE *)io, size, SEEK_CUR);
return size;
return io_seek(io, size, IOSEEK_CUR);
}

int io_seek(IOHANDLE io, int offset, int origin)
{
#if defined(CONF_FAMILY_WINDOWS)
DWORD move_method;
switch(origin)
{
case IOSEEK_START:
move_method = FILE_BEGIN;
break;
case IOSEEK_CUR:
move_method = FILE_CURRENT;
break;
case IOSEEK_END:
move_method = FILE_END;
break;
default:
dbg_assert(false, "origin invalid");
return -1;
}
return SetFilePointer((HANDLE)io, offset, nullptr, move_method) == INVALID_SET_FILE_POINTER ? -1 : 0;
#else
int real_origin;

switch(origin)
{
case IOSEEK_START:
Expand All @@ -363,15 +437,21 @@ int io_seek(IOHANDLE io, int offset, int origin)
real_origin = SEEK_END;
break;
default:
dbg_assert(false, "origin invalid");
return -1;
}

return fseek((FILE *)io, offset, real_origin);
#endif
}

long int io_tell(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
const DWORD position = SetFilePointer((HANDLE)io, 0, nullptr, FILE_CURRENT);
return position == INVALID_SET_FILE_POINTER ? -1 : position;
#else
return ftell((FILE *)io);
#endif
}

long int io_length(IOHANDLE io)
Expand All @@ -385,12 +465,23 @@ long int io_length(IOHANDLE io)

int io_error(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
// Only works when called directly after the operation that failed
return GetLastError();
#else
return ferror((FILE *)io);
#endif
}

unsigned io_write(IOHANDLE io, const void *buffer, unsigned size)
{
#if defined(CONF_FAMILY_WINDOWS)
DWORD actual_size;
WriteFile((HANDLE)io, buffer, size, &actual_size, nullptr);
return actual_size;
#else
return fwrite(buffer, 1, size, (FILE *)io);
#endif
}

bool io_write_newline(IOHANDLE io)
Expand All @@ -404,12 +495,20 @@ bool io_write_newline(IOHANDLE io)

int io_close(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
return CloseHandle((HANDLE)io) == 0;
#else
return fclose((FILE *)io) != 0;
#endif
}

int io_flush(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
return FlushFileBuffers((HANDLE)io) == FALSE;
#else
return fflush((FILE *)io);
#endif
}

int io_sync(IOHANDLE io)
Expand All @@ -419,7 +518,7 @@ int io_sync(IOHANDLE io)
return 1;
}
#if defined(CONF_FAMILY_WINDOWS)
return FlushFileBuffers((HANDLE)_get_osfhandle(_fileno((FILE *)io))) == 0;
return FlushFileBuffers((HANDLE)io) == 0;
#else
return fsync(fileno((FILE *)io)) != 0;
#endif
Expand Down Expand Up @@ -1344,6 +1443,48 @@ static int parse_uint16(unsigned short *out, const char **str)
return 0;
}

int net_addr_from_url(NETADDR *addr, const char *string, char *host_buf, size_t host_buf_size)
{
char host[128];
int length;
int start = 0;
int end;
int failure;
const char *str = str_startswith(string, "tw-0.6+udp://");
if(!str)
return 1;

mem_zero(addr, sizeof(*addr));

length = str_length(str);
end = length;
for(int i = 0; i < length; i++)
{
if(str[i] == '@')
{
if(start != 0)
{
// Two at signs.
return true;
}
start = i + 1;
}
else if(str[i] == '/' || str[i] == '?' || str[i] == '#')
{
end = i;
break;
}
}
str_truncate(host, sizeof(host), str + start, end - start);
if(host_buf)
str_copy(host_buf, host, host_buf_size);

if((failure = net_addr_from_str(addr, host)))
return failure;

return failure;
}

int net_addr_from_str(NETADDR *addr, const char *string)
{
const char *str = string;
Expand Down Expand Up @@ -3539,7 +3680,7 @@ int str_utf8_comp_nocase_num(const char *a, const char *b, int num)
return (unsigned char)*a - (unsigned char)*b;
}

const char *str_utf8_find_nocase(const char *haystack, const char *needle)
const char *str_utf8_find_nocase(const char *haystack, const char *needle, const char **end)
{
while(*haystack) /* native implementation */
{
Expand All @@ -3553,11 +3694,17 @@ const char *str_utf8_find_nocase(const char *haystack, const char *needle)
b = b_next;
}
if(!(*b))
{
if(end != nullptr)
*end = a_next;
return haystack;
}
str_utf8_decode(&haystack);
}

return 0;
if(end != nullptr)
*end = nullptr;
return nullptr;
}

int str_utf8_isspace(int code)
Expand Down
32 changes: 30 additions & 2 deletions src/base/system.h
Expand Up @@ -223,7 +223,7 @@ enum
IO_MAX_PATH_LENGTH = 512,
};

typedef struct IOINTERNAL *IOHANDLE;
typedef void *IOHANDLE;

/**
* Opens a file.
Expand Down Expand Up @@ -937,6 +937,31 @@ int net_addr_comp_noport(const NETADDR *a, const NETADDR *b);
*/
void net_addr_str(const NETADDR *addr, char *string, int max_length, int add_port);

/**
* Turns url string into a network address struct.
* The url format is tw-0.6+udp://{ipaddr}[:{port}]
* ipaddr: can be ipv4 or ipv6
* port: is a optional internet protocol port
*
* This format is used for parsing the master server, be careful before changing it.
*
* Examples:
* tw-0.6+udp://127.0.0.1
* tw-0.6+udp://127.0.0.1:8303
*
* @param addr Address to fill in.
* @param string String to parse.
* @param host_buf Pointer to a buffer to write the host to
* It will include the port if one is included in the url
* It can also be set to NULL then it will be ignored
* @param host_buf_size Size of the host buffer or 0 if no host_buf pointer is given
*
* @return 0 on success,
* positive if the input wasn't a valid DDNet URL,
* negative if the input is a valid DDNet URL but the host part was not a valid IPv4/IPv6 address
*/
int net_addr_from_url(NETADDR *addr, const char *string, char *host_buf, size_t host_buf_size);

/**
* Turns string into a network address.
*
Expand Down Expand Up @@ -2300,6 +2325,9 @@ int str_utf8_comp_nocase_num(const char *a, const char *b, int num);
Parameters:
haystack - String to search in
needle - String to search for
end - A pointer that will be set to a pointer into haystack directly behind the
last character where the needle was found. Will be set to nullptr if needle
could not be found. Optional parameter.
Returns:
A pointer into haystack where the needle was found.
Expand All @@ -2308,7 +2336,7 @@ int str_utf8_comp_nocase_num(const char *a, const char *b, int num);
Remarks:
- The strings are treated as zero-terminated strings.
*/
const char *str_utf8_find_nocase(const char *haystack, const char *needle);
const char *str_utf8_find_nocase(const char *haystack, const char *needle, const char **end = nullptr);

/*
Function: str_utf8_isspace
Expand Down
5 changes: 5 additions & 0 deletions src/base/vmath.h
Expand Up @@ -109,6 +109,11 @@ inline float length(const vector2_base<float> &a)
return std::sqrt(dot(a, a));
}

inline float length(const vector2_base<int> &a)
{
return std::sqrt(dot(a, a));
}

inline float length_squared(const vector2_base<float> &a)
{
return dot(a, a);
Expand Down
1 change: 0 additions & 1 deletion src/engine/client.h
Expand Up @@ -180,7 +180,6 @@ class IClient : public IInterface
virtual void SwitchWindowScreen(int Index) = 0;
virtual void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) = 0;
virtual void ToggleWindowVSync() = 0;
virtual void LoadFont() = 0;
virtual void Notify(const char *pTitle, const char *pMessage) = 0;

virtual void UpdateAndSwap() = 0;
Expand Down
10 changes: 4 additions & 6 deletions src/engine/client/backend/opengl/backend_opengl.cpp
Expand Up @@ -1964,10 +1964,9 @@ void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferContainer(const CCommand

SBufferContainer &BufferContainer = m_vBufferContainers[Index];

for(int i = 0; i < pCommand->m_AttrCount; ++i)
for(size_t i = 0; i < pCommand->m_AttrCount; ++i)
{
SBufferContainerInfo::SAttribute &Attr = pCommand->m_pAttributes[i];
BufferContainer.m_ContainerInfo.m_vAttributes.push_back(Attr);
BufferContainer.m_ContainerInfo.m_vAttributes.push_back(pCommand->m_pAttributes[i]);
}

BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
Expand All @@ -1980,10 +1979,9 @@ void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferContainer(const CCommand

BufferContainer.m_ContainerInfo.m_vAttributes.clear();

for(int i = 0; i < pCommand->m_AttrCount; ++i)
for(size_t i = 0; i < pCommand->m_AttrCount; ++i)
{
SBufferContainerInfo::SAttribute &Attr = pCommand->m_pAttributes[i];
BufferContainer.m_ContainerInfo.m_vAttributes.push_back(Attr);
BufferContainer.m_ContainerInfo.m_vAttributes.push_back(pCommand->m_pAttributes[i]);
}

BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
Expand Down
4 changes: 2 additions & 2 deletions src/engine/client/backend/opengl/backend_opengl3.cpp
Expand Up @@ -1008,7 +1008,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_CreateBufferContainer(const CComma

BufferContainer.m_LastIndexBufferBound = 0;

for(int i = 0; i < pCommand->m_AttrCount; ++i)
for(size_t i = 0; i < pCommand->m_AttrCount; ++i)
{
glEnableVertexAttribArray((GLuint)i);

Expand Down Expand Up @@ -1041,7 +1041,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_UpdateBufferContainer(const CComma
}
BufferContainer.m_ContainerInfo.m_vAttributes.clear();

for(int i = 0; i < pCommand->m_AttrCount; ++i)
for(size_t i = 0; i < pCommand->m_AttrCount; ++i)
{
glEnableVertexAttribArray((GLuint)i);

Expand Down
162 changes: 82 additions & 80 deletions src/engine/client/client.cpp
Expand Up @@ -770,14 +770,19 @@ void CClient::Connect(const char *pAddress, const char *pPassword)
while((pNextAddr = str_next_token(pNextAddr, ",", aBuffer, sizeof(aBuffer))))
{
NETADDR NextAddr;
if(net_host_lookup(aBuffer, &NextAddr, m_aNetClient[CONN_MAIN].NetType()) != 0)
char aHost[128];
int url = net_addr_from_url(&NextAddr, aBuffer, aHost, sizeof(aHost));
if(url > 0)
str_copy(aHost, aBuffer);

if(net_host_lookup(aHost, &NextAddr, m_aNetClient[CONN_MAIN].NetType()) != 0)
{
log_error("client", "could not find address of %s", aBuffer);
log_error("client", "could not find address of %s", aHost);
continue;
}
if(NumConnectAddrs == (int)std::size(aConnectAddrs))
{
log_warn("client", "too many connect addresses, ignoring %s", aBuffer);
log_warn("client", "too many connect addresses, ignoring %s", aHost);
continue;
}
if(NextAddr.port == 0)
Expand Down Expand Up @@ -1866,6 +1871,16 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
SendMsg(Conn, &MsgP, MSGFLAG_VITAL);
}
}
else if(Msg == NETMSG_REDIRECT)
{
int RedirectPort = Unpacker.GetInt();
char aAddr[128];
char aIP[64];
NETADDR ServerAddr = ServerAddress();
net_addr_str(&ServerAddr, aIP, sizeof(aIP), 0);
str_format(aAddr, sizeof(aAddr), "%s:%d", aIP, RedirectPort);
Connect(aAddr);
}
else if(Conn == CONN_MAIN && (pPacket->m_Flags & NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_RCON_CMD_ADD)
{
const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC);
Expand Down Expand Up @@ -2879,18 +2894,18 @@ void CClient::Update()

if(State() == IClient::STATE_ONLINE)
{
if(!m_lpEditJobs.empty())
if(!m_EditJobs.empty())
{
std::shared_ptr<CDemoEdit> e = m_lpEditJobs.front();
if(e->Status() == IJob::STATE_DONE)
std::shared_ptr<CDemoEdit> pJob = m_EditJobs.front();
if(pJob->Status() == IJob::STATE_DONE)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Successfully saved the replay to %s!", e->Destination());
char aBuf[IO_MAX_PATH_LENGTH + 64];
str_format(aBuf, sizeof(aBuf), "Successfully saved the replay to %s!", pJob->Destination());
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "replay", aBuf);

GameClient()->Echo(Localize("Successfully saved the replay!"));

m_lpEditJobs.pop_front();
m_EditJobs.pop_front();
}
}
}
Expand Down Expand Up @@ -3035,8 +3050,9 @@ void CClient::Run()
}
#endif

// init font rendering
Kernel()->RequestInterface<IEngineTextRender>()->Init();
// init text render
IEngineTextRender *pTextRender = Kernel()->RequestInterface<IEngineTextRender>();
pTextRender->Init();

// init the input
Input()->Init();
Expand Down Expand Up @@ -3200,7 +3216,7 @@ void CClient::Run()
{
Input()->MouseModeRelative();
GameClient()->OnActivateEditor();
m_pEditor->ResetMentions();
m_pEditor->OnActivate();
m_EditorActive = true;
}
}
Expand Down Expand Up @@ -3393,6 +3409,9 @@ void CClient::Run()
m_aNetClient[i].Close();

delete m_pEditor;

// shutdown text render while graphics are still available
pTextRender->Shutdown();
}

bool CClient::InitNetworkClient(char *pError, size_t ErrorSize)
Expand Down Expand Up @@ -3818,7 +3837,7 @@ void CClient::SaveReplay(const int Length, const char *pFilename)
// Create a job to do this slicing in background because it can be a bit long depending on the file size
std::shared_ptr<CDemoEdit> pDemoEditTask = std::make_shared<CDemoEdit>(GameClient()->NetVersion(), &m_SnapshotDelta, m_pStorage, pSrc, aFilename, StartTick, EndTick);
Engine()->AddJob(pDemoEditTask);
m_lpEditJobs.push_back(pDemoEditTask);
m_EditJobs.push_back(pDemoEditTask);

// And we restart the recorder
DemoRecorder_StartReplayRecorder();
Expand Down Expand Up @@ -3912,7 +3931,11 @@ const char *CClient::DemoPlayer_Render(const char *pFilename, int StorageType, c

this->CClient::StartVideo(NULL, this, pVideoName);
m_DemoPlayer.Play();
m_DemoPlayer.SetSpeed(g_aSpeeds[SpeedIndex]);
m_DemoPlayer.SetSpeedIndex(SpeedIndex);
if(Config()->m_ClVideoPauseOnStart)
{
m_DemoPlayer.Pause();
}
//m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "demo_recorder", "demo eof");
return 0;
}
Expand Down Expand Up @@ -4268,61 +4291,6 @@ void CClient::ToggleWindowVSync()
g_Config.m_GfxVsync ^= 1;
}

void CClient::LoadFont()
{
static CFont *pDefaultFont = 0;
char aFilename[IO_MAX_PATH_LENGTH];
char aBuff[1024];
const char *pFontFile = "fonts/DejaVuSans.ttf";
const char *apFallbackFontFiles[] =
{
"fonts/GlowSansJCompressed-Book.otf",
"fonts/SourceHanSansSC-Regular.otf",
};
IOHANDLE File = Storage()->OpenFile(pFontFile, IOFLAG_READ, IStorage::TYPE_ALL, aFilename, sizeof(aFilename));
if(File)
{
IEngineTextRender *pTextRender = Kernel()->RequestInterface<IEngineTextRender>();
pDefaultFont = pTextRender->GetFont(aFilename);
if(pDefaultFont == NULL)
{
void *pBuf;
unsigned Size;
io_read_all(File, &pBuf, &Size);
pDefaultFont = pTextRender->LoadFont(aFilename, (unsigned char *)pBuf, Size);
}
io_close(File);

for(auto &pFallbackFontFile : apFallbackFontFiles)
{
bool FontLoaded = false;
File = Storage()->OpenFile(pFallbackFontFile, IOFLAG_READ, IStorage::TYPE_ALL, aFilename, sizeof(aFilename));
if(File)
{
void *pBuf;
unsigned Size;
io_read_all(File, &pBuf, &Size);
io_close(File);
FontLoaded = pTextRender->LoadFallbackFont(pDefaultFont, aFilename, (unsigned char *)pBuf, Size);
}

if(!FontLoaded)
{
str_format(aBuff, std::size(aBuff), "failed to load the fallback font. filename='%s'", pFallbackFontFile);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", aBuff);
}
}

pTextRender->SetDefaultFont(pDefaultFont);
}

if(!pDefaultFont)
{
str_format(aBuff, std::size(aBuff), "failed to load font. filename='%s'", pFontFile);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", aBuff);
}
}

void CClient::Notify(const char *pTitle, const char *pMessage)
{
if(m_pGraphics->WindowActive() || !g_Config.m_ClShowNotifications)
Expand Down Expand Up @@ -4352,12 +4320,6 @@ void CClient::ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, I
pSelf->m_GenerateTimeoutSeed = false;
}

void CClient::ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
log_set_loglevel((LEVEL)g_Config.m_Loglevel);
}

void CClient::ConchainPassword(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CClient *pSelf = (CClient *)pUserData;
Expand Down Expand Up @@ -4386,6 +4348,26 @@ void CClient::ConchainReplays(IConsole::IResult *pResult, void *pUserData, ICons
}
}

void CClient::ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CClient *pSelf = (CClient *)pUserData;
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments())
{
pSelf->m_pFileLogger->SetFilter(CLogFilter{IConsole::ToLogLevelFilter(g_Config.m_Loglevel)});
}
}

void CClient::ConchainStdoutOutputLevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CClient *pSelf = (CClient *)pUserData;
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() && pSelf->m_pStdoutLogger)
{
pSelf->m_pStdoutLogger->SetFilter(CLogFilter{IConsole::ToLogLevelFilter(g_Config.m_StdoutOutputLevel)});
}
}

void CClient::RegisterCommands()
{
m_pConsole = Kernel()->RequestInterface<IConsole>();
Expand Down Expand Up @@ -4442,7 +4424,6 @@ void CClient::RegisterCommands()
m_pConsole->Chain("cl_timeout_seed", ConchainTimeoutSeed, this);
m_pConsole->Chain("cl_replays", ConchainReplays, this);

m_pConsole->Chain("loglevel", ConchainLoglevel, this);
m_pConsole->Chain("password", ConchainPassword, this);

// used for server browser update
Expand All @@ -4458,6 +4439,9 @@ void CClient::RegisterCommands()
m_pConsole->Chain("gfx_borderless", ConchainWindowBordered, this);
m_pConsole->Chain("gfx_vsync", ConchainWindowVSync, this);

m_pConsole->Chain("loglevel", ConchainLoglevel, this);
m_pConsole->Chain("stdout_output_level", ConchainStdoutOutputLevel, this);

// DDRace

#define CONSOLE_COMMAND(name, params, flags, callback, userdata, help) m_pConsole->Register(name, params, flags, 0, 0, help);
Expand Down Expand Up @@ -4573,14 +4557,19 @@ int main(int argc, const char **argv)
#endif

std::vector<std::shared_ptr<ILogger>> vpLoggers;
std::shared_ptr<ILogger> pStdoutLogger = nullptr;
#if defined(CONF_PLATFORM_ANDROID)
vpLoggers.push_back(std::shared_ptr<ILogger>(log_logger_android()));
pStdoutLogger = std::shared_ptr<ILogger>(log_logger_android());
#else
if(!Silent)
{
vpLoggers.push_back(std::shared_ptr<ILogger>(log_logger_stdout()));
pStdoutLogger = std::shared_ptr<ILogger>(log_logger_stdout());
}
#endif
if(pStdoutLogger)
{
vpLoggers.push_back(pStdoutLogger);
}
std::shared_ptr<CFutureLogger> pFutureFileLogger = std::make_shared<CFutureLogger>();
vpLoggers.push_back(pFutureFileLogger);
std::shared_ptr<CFutureLogger> pFutureConsoleLogger = std::make_shared<CFutureLogger>();
Expand Down Expand Up @@ -4621,6 +4610,8 @@ int main(int argc, const char **argv)
CleanerFunctions.push([]() { SDL_Quit(); });

CClient *pClient = CreateClient();
pClient->SetLoggers(pFutureFileLogger, std::move(pStdoutLogger));

IKernel *pKernel = IKernel::Create();
pKernel->RegisterInterface(pClient, false);
pClient->RegisterInterfaces();
Expand Down Expand Up @@ -4690,7 +4681,13 @@ int main(int argc, const char **argv)
{
bool RegisterFail = false;

RegisterFail = RegisterFail || !pKernel->RegisterInterface(pEngine);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pEngine, false);

CleanerFunctions.push([pEngine]() {
// Has to be before destroying graphics so that skin download thread can finish
delete pEngine;
});

RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConsole);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConfigManager);

Expand Down Expand Up @@ -4780,7 +4777,6 @@ int main(int argc, const char **argv)
pSteam->ClearConnectAddress();
}

log_set_loglevel((LEVEL)g_Config.m_Loglevel);
const int Mode = g_Config.m_Logappend ? IOFLAG_APPEND : IOFLAG_WRITE;
if(g_Config.m_Logfile[0])
{
Expand Down Expand Up @@ -5077,3 +5073,9 @@ void CClient::GetGPUInfoString(char (&aGPUInfo)[256])
str_copy(aGPUInfo, "Graphics backend was not yet initialized.");
}
}

void CClient::SetLoggers(std::shared_ptr<ILogger> &&pFileLogger, std::shared_ptr<ILogger> &&pStdoutLogger)
{
m_pFileLogger = pFileLogger;
m_pStdoutLogger = pStdoutLogger;
}
15 changes: 8 additions & 7 deletions src/engine/client/client.h
Expand Up @@ -3,7 +3,7 @@
#ifndef ENGINE_CLIENT_CLIENT_H
#define ENGINE_CLIENT_CLIENT_H

#include <list>
#include <deque>
#include <memory>

#include <base/hash.h>
Expand Down Expand Up @@ -246,7 +246,7 @@ class CClient : public IClient, public CDemoPlayer::IListener

CSnapshotDelta m_SnapshotDelta;

std::list<std::shared_ptr<CDemoEdit>> m_lpEditJobs;
std::deque<std::shared_ptr<CDemoEdit>> m_EditJobs;

//
bool m_CanReceiveServerCapabilities;
Expand Down Expand Up @@ -277,9 +277,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
class CHostLookup m_VersionServeraddr;
} m_VersionInfo;

static void GraphicsThreadProxy(void *pThis) { ((CClient *)pThis)->GraphicsThread(); }
void GraphicsThread();

std::vector<SWarning> m_vWarnings;

CFifo m_Fifo;
Expand All @@ -301,6 +298,9 @@ class CClient : public IClient, public CDemoPlayer::IListener
int MaxLatencyTicks() const;
int PredictionMargin() const;

std::shared_ptr<ILogger> m_pFileLogger = nullptr;
std::shared_ptr<ILogger> m_pStdoutLogger = nullptr;

public:
IEngine *Engine() { return m_pEngine; }
IEngineGraphics *Graphics() { return m_pGraphics; }
Expand Down Expand Up @@ -468,9 +468,10 @@ class CClient : public IClient, public CDemoPlayer::IListener
static void ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainWindowVSync(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainPassword(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainReplays(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainStdoutOutputLevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);

static void Con_DemoSlice(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoSliceBegin(IConsole::IResult *pResult, void *pUserData);
Expand Down Expand Up @@ -509,7 +510,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
void SwitchWindowScreen(int Index) override;
void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) override;
void ToggleWindowVSync() override;
void LoadFont() override;
void Notify(const char *pTitle, const char *pMessage) override;
void BenchmarkQuit(int Seconds, const char *pFilename);

Expand Down Expand Up @@ -554,6 +554,7 @@ class CClient : public IClient, public CDemoPlayer::IListener

void ShowMessageBox(const char *pTitle, const char *pMessage, EMessageBoxType Type = MESSAGE_BOX_TYPE_ERROR) override;
void GetGPUInfoString(char (&aGPUInfo)[256]) override;
void SetLoggers(std::shared_ptr<ILogger> &&pFileLogger, std::shared_ptr<ILogger> &&pStdoutLogger);
};

#endif
642 changes: 189 additions & 453 deletions src/engine/client/graphics_threaded.cpp

Large diffs are not rendered by default.

114 changes: 49 additions & 65 deletions src/engine/client/graphics_threaded.h
Expand Up @@ -325,7 +325,7 @@ class CCommandBuffer
int m_Stride;
int m_VertBufferBindingIndex;

int m_AttrCount;
size_t m_AttrCount;
SBufferContainerInfo::SAttribute *m_pAttributes;
};

Expand All @@ -339,7 +339,7 @@ class CCommandBuffer
int m_Stride;
int m_VertBufferBindingIndex;

int m_AttrCount;
size_t m_AttrCount;
SBufferContainerInfo::SAttribute *m_pAttributes;
};

Expand Down Expand Up @@ -413,7 +413,7 @@ class CCommandBuffer

int m_BufferContainerIndex;
SQuadRenderInfo *m_pQuadInfo;
int m_QuadNum;
size_t m_QuadNum;
int m_QuadOffset;
};

Expand Down Expand Up @@ -540,8 +540,8 @@ class CCommandBuffer
// texture information
int m_Slot;

int m_Width;
int m_Height;
size_t m_Width;
size_t m_Height;
int m_PixelSize;
int m_Format;
int m_StoreFormat;
Expand All @@ -559,8 +559,8 @@ class CCommandBuffer

int m_X;
int m_Y;
int m_Width;
int m_Height;
size_t m_Width;
size_t m_Height;
int m_Format;
void *m_pData; // will be freed by the command processor
};
Expand All @@ -583,8 +583,8 @@ class CCommandBuffer
int m_Slot;
int m_SlotOutline;

int m_Width;
int m_Height;
size_t m_Width;
size_t m_Height;

void *m_pTextData;
void *m_pTextOutlineData;
Expand All @@ -610,8 +610,8 @@ class CCommandBuffer

int m_X;
int m_Y;
int m_Width;
int m_Height;
size_t m_Width;
size_t m_Height;
void *m_pData; // will be freed by the command processor
};

Expand Down Expand Up @@ -886,7 +886,7 @@ class CGraphics_Threaded : public IEngineGraphics
std::vector<WINDOW_RESIZE_FUNC> m_vResizeListeners;
std::vector<WINDOW_PROPS_CHANGED_FUNC> m_vPropChangeListeners;

void *AllocCommandBufferData(unsigned AllocSize);
void *AllocCommandBufferData(size_t AllocSize);

void AddVertices(int Count);
void AddVertices(int Count, CCommandBuffer::SVertex *pVertices);
Expand All @@ -910,24 +910,29 @@ class CGraphics_Threaded : public IEngineGraphics
}
}

template<typename TName, typename TFunc>
bool AddCmd(TName &Cmd, TFunc &&FailFunc, const char *pFailStr)
template<typename TName>
void AddCmd(
TName &Cmd, std::function<bool()> FailFunc = [] { return true; })
{
if(!m_pCommandBuffer->AddCommandUnsafe(Cmd))
{
// kick command buffer and try again
KickCommandBuffer();
if(m_pCommandBuffer->AddCommandUnsafe(Cmd))
return;

if(!FailFunc())
return false;
// kick command buffer and try again
KickCommandBuffer();

if(!m_pCommandBuffer->AddCommandUnsafe(Cmd))
{
dbg_msg("graphics", "%s", pFailStr);
return false;
}
if(!FailFunc())
{
char aError[256];
str_format(aError, sizeof(aError), "graphics: failed to run fail handler for command '%s'", typeid(TName).name());
dbg_assert(false, aError);
}

if(!m_pCommandBuffer->AddCommandUnsafe(Cmd))
{
char aError[256];
str_format(aError, sizeof(aError), "graphics: failed to add command '%s' to command buffer", typeid(TName).name());
dbg_assert(false, aError);
}
return true;
}

void KickCommandBuffer();
Expand Down Expand Up @@ -966,15 +971,18 @@ class CGraphics_Threaded : public IEngineGraphics
void LinesEnd() override;
void LinesDraw(const CLineItem *pArray, int Num) override;

IGraphics::CTextureHandle FindFreeTextureIndex();
void FreeTextureIndex(CTextureHandle *pIndex);
int UnloadTexture(IGraphics::CTextureHandle *pIndex) override;
IGraphics::CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL) override;
int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData) override;
IGraphics::CTextureHandle LoadTextureRaw(size_t Width, size_t Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL) override;
int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, int Format, const void *pData) override;
IGraphics::CTextureHandle InvalidTexture() const override;

bool LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) override;
bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) override;
bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) override;
bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData) override;
bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, const void *pData) override;

CTextureHandle LoadSpriteTextureImpl(CImageInfo &FromImageInfo, int x, int y, int w, int h);
CTextureHandle LoadSpriteTextureImpl(CImageInfo &FromImageInfo, int x, int y, size_t w, size_t h);
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct CDataSprite *pSprite) override;
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) override;

Expand All @@ -989,8 +997,8 @@ class CGraphics_Threaded : public IEngineGraphics
bool CheckImageDivisibility(const char *pFileName, CImageInfo &Img, int DivX, int DivY, bool AllowResize) override;
bool IsImageFormatRGBA(const char *pFileName, CImageInfo &Img) override;

void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight) override;
void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, int DestWidth, int DestHeight, uint8_t *pSourceBuffer, int SrcWidth, int SrcHeight, int ColorChannelCount, int SrcSubOffsetX, int SrcSubOffsetY, int SrcSubCopyWidth, int SrcSubCopyHeight) override;
void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t ColorChannelCount, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) override;
void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t ColorChannelCount, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) override;

bool ScreenshotDirect();

Expand Down Expand Up @@ -1160,9 +1168,9 @@ class CGraphics_Threaded : public IEngineGraphics
void RenderQuadContainerAsSpriteMultiple(int ContainerIndex, int QuadOffset, int DrawCount, SRenderSpriteInfo *pRenderInfo) override;

template<typename TName>
void FlushVerticesImpl(bool KeepVertices, int &PrimType, int &PrimCount, int &NumVerts, TName &Command, size_t VertSize)
void FlushVerticesImpl(bool KeepVertices, int &PrimType, size_t &PrimCount, size_t &NumVerts, TName &Command, size_t VertSize)
{
Command.m_pVertices = NULL;
Command.m_pVertices = nullptr;
if(m_NumVertices == 0)
return;

Expand Down Expand Up @@ -1197,40 +1205,16 @@ class CGraphics_Threaded : public IEngineGraphics
else
return;

Command.m_pVertices = (decltype(Command.m_pVertices))m_pCommandBuffer->AllocData(VertSize * NumVerts);
if(Command.m_pVertices == NULL)
{
// kick command buffer and try again
KickCommandBuffer();

Command.m_pVertices = (decltype(Command.m_pVertices))m_pCommandBuffer->AllocData(VertSize * NumVerts);
if(Command.m_pVertices == NULL)
{
dbg_msg("graphics", "failed to allocate data for vertices");
return;
}
}

Command.m_pVertices = (decltype(Command.m_pVertices))AllocCommandBufferData(VertSize * NumVerts);
Command.m_State = m_State;

Command.m_PrimType = PrimType;
Command.m_PrimCount = PrimCount;

if(
!AddCmd(
Command, [&] {
Command.m_pVertices = (decltype(Command.m_pVertices))m_pCommandBuffer->AllocData(VertSize * NumVerts);
if(Command.m_pVertices == NULL)
{
dbg_msg("graphics", "failed to allocate data for vertices");
return false;
}
return true;
},
"failed to allocate memory for render command"))
{
return;
}
AddCmd(Command, [&] {
Command.m_pVertices = (decltype(Command.m_pVertices))m_pCommandBuffer->AllocData(VertSize * NumVerts);
return Command.m_pVertices != nullptr;
});

m_pCommandBuffer->AddRenderCalls(1);
}
Expand All @@ -1241,7 +1225,7 @@ class CGraphics_Threaded : public IEngineGraphics
void RenderTileLayer(int BufferContainerIndex, const ColorRGBA &Color, char **pOffsets, unsigned int *pIndicedVertexDrawNum, size_t NumIndicesOffset) override;
void RenderBorderTiles(int BufferContainerIndex, const ColorRGBA &Color, char *pIndexBufferOffset, const vec2 &Offset, const vec2 &Dir, int JumpIndex, unsigned int DrawNum) override;
void RenderBorderTileLines(int BufferContainerIndex, const ColorRGBA &Color, char *pIndexBufferOffset, const vec2 &Offset, const vec2 &Dir, unsigned int IndexDrawNum, unsigned int RedrawNum) override;
void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset) override;
void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, size_t QuadNum, int QuadOffset) override;
void RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor) override;

// modern GL functions
Expand Down
29 changes: 1 addition & 28 deletions src/engine/client/serverbrowser_http.cpp
Expand Up @@ -369,34 +369,7 @@ void CServerBrowserHttp::Refresh()
}
bool ServerbrowserParseUrl(NETADDR *pOut, const char *pUrl)
{
char aHost[128];
const char *pRest = str_startswith(pUrl, "tw-0.6+udp://");
if(!pRest)
{
return true;
}
int Length = str_length(pRest);
int Start = 0;
int End = Length;
for(int i = 0; i < Length; i++)
{
if(pRest[i] == '@')
{
if(Start != 0)
{
// Two at signs.
return true;
}
Start = i + 1;
}
else if(pRest[i] == '/' || pRest[i] == '?' || pRest[i] == '#')
{
End = i;
break;
}
}
str_truncate(aHost, sizeof(aHost), pRest + Start, End - Start);
return net_addr_from_str(pOut, aHost) != 0;
return net_addr_from_url(pOut, pUrl, nullptr, 0) != 0;
}
bool CServerBrowserHttp::Validate(json_value *pJson)
{
Expand Down
5 changes: 5 additions & 0 deletions src/engine/client/sound.cpp
Expand Up @@ -275,6 +275,11 @@ static void SdlCallback(void *pUnused, Uint8 *pStream, int Len)
#endif
}

CSound::CSound() :
m_SoundEnabled(false), m_Device(0), m_pGraphics(nullptr), m_pStorage(nullptr)
{
}

int CSound::Init()
{
m_SoundEnabled = false;
Expand Down
1 change: 1 addition & 0 deletions src/engine/client/sound.h
Expand Up @@ -28,6 +28,7 @@ class CSound : public IEngineSound
static int DecodeOpus(int SampleID, const void *pData, unsigned DataSize);

public:
CSound();
int Init() override;
int Update() override;
void Shutdown() override;
Expand Down
1,848 changes: 1,090 additions & 758 deletions src/engine/client/text.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/engine/console.h
Expand Up @@ -119,9 +119,10 @@ class IConsole : public IInterface

virtual void SetAccessLevel(int AccessLevel) = 0;

virtual void ResetServerGameSettings() = 0;
virtual void ResetGameSettings() = 0;

static LEVEL ToLogLevel(int ConsoleLevel);
static int ToLogLevelFilter(int ConsoleLevel);

// DDRace

Expand Down
3 changes: 2 additions & 1 deletion src/engine/demo.h
Expand Up @@ -84,7 +84,8 @@ class IDemoPlayer : public IInterface

~IDemoPlayer() {}
virtual void SetSpeed(float Speed) = 0;
virtual void SetSpeedIndex(int Offset) = 0;
virtual void SetSpeedIndex(int SpeedIndex) = 0;
virtual void AdjustSpeedIndex(int Offset) = 0;
virtual int SeekPercent(float Percent) = 0;
virtual int SeekTime(float Seconds) = 0;
virtual int SeekTick(ETickOffset TickOffset) = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/engine/editor.h
Expand Up @@ -12,11 +12,14 @@ class IEditor : public IInterface
virtual void Init() = 0;
virtual void OnUpdate() = 0;
virtual void OnRender() = 0;
virtual void OnActivate() = 0;
virtual bool HasUnsavedData() const = 0;
virtual bool Load(const char *pFilename, int StorageType) = 0;
virtual bool Save(const char *pFilename) = 0;
virtual void UpdateMentions() = 0;
virtual void ResetMentions() = 0;
virtual void OnIngameMoved() = 0;
virtual void ResetIngameMoved() = 0;
};

extern IEditor *CreateEditor();
Expand Down
25 changes: 8 additions & 17 deletions src/engine/graphics.h
Expand Up @@ -307,22 +307,23 @@ class IGraphics : public IInterface
virtual bool IsImageFormatRGBA(const char *pFileName, CImageInfo &Img) = 0;

// destination and source buffer require to have the same width and height
virtual void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight) = 0;
virtual void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t ColorChannelCount, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) = 0;

// destination width must be equal to the subwidth of the source
virtual void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, int DestWidth, int DestHeight, uint8_t *pSourceBuffer, int SrcWidth, int SrcHeight, int ColorChannelCount, int SrcSubOffsetX, int SrcSubOffsetY, int SrcSubCopyWidth, int SrcSubCopyHeight) = 0;
virtual void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t ColorChannelCount, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) = 0;

virtual int UnloadTexture(CTextureHandle *pIndex) = 0;
virtual CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = nullptr) = 0;
virtual int LoadTextureRawSub(CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData) = 0;
virtual CTextureHandle LoadTextureRaw(size_t Width, size_t Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = nullptr) = 0;
virtual int LoadTextureRawSub(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, int Format, const void *pData) = 0;
virtual CTextureHandle LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) = 0;
virtual CTextureHandle InvalidTexture() const = 0;
virtual void TextureSet(CTextureHandle Texture) = 0;
void TextureClear() { TextureSet(CTextureHandle()); }

// pTextData & pTextOutlineData are automatically free'd
virtual bool LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) = 0;
virtual bool LoadTextTextures(size_t Width, size_t Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) = 0;
virtual bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) = 0;
virtual bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData) = 0;
virtual bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, const void *pData) = 0;

virtual CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct CDataSprite *pSprite) = 0;
virtual CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) = 0;
Expand All @@ -337,7 +338,7 @@ class IGraphics : public IInterface
virtual void RenderTileLayer(int BufferContainerIndex, const ColorRGBA &Color, char **pOffsets, unsigned int *pIndicedVertexDrawNum, size_t NumIndicesOffset) = 0;
virtual void RenderBorderTiles(int BufferContainerIndex, const ColorRGBA &Color, char *pIndexBufferOffset, const vec2 &Offset, const vec2 &Dir, int JumpIndex, unsigned int DrawNum) = 0;
virtual void RenderBorderTileLines(int BufferContainerIndex, const ColorRGBA &Color, char *pIndexBufferOffset, const vec2 &Offset, const vec2 &Dir, unsigned int IndexDrawNum, unsigned int RedrawNum) = 0;
virtual void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset) = 0;
virtual void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, size_t QuadNum, int QuadOffset) = 0;
virtual void RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor) = 0;

// opengl 3.3 functions
Expand Down Expand Up @@ -460,23 +461,13 @@ class IGraphics : public IInterface
CORNER_TR = 2,
CORNER_BL = 4,
CORNER_BR = 8,
CORNER_ITL = 16,
CORNER_ITR = 32,
CORNER_IBL = 64,
CORNER_IBR = 128,

CORNER_T = CORNER_TL | CORNER_TR,
CORNER_B = CORNER_BL | CORNER_BR,
CORNER_R = CORNER_TR | CORNER_BR,
CORNER_L = CORNER_TL | CORNER_BL,

CORNER_IT = CORNER_ITL | CORNER_ITR,
CORNER_IB = CORNER_IBL | CORNER_IBR,
CORNER_IR = CORNER_ITR | CORNER_IBR,
CORNER_IL = CORNER_ITL | CORNER_IBL,

CORNER_ALL = CORNER_T | CORNER_B,
CORNER_INV_ALL = CORNER_IT | CORNER_IB
};
virtual void DrawRectExt(float x, float y, float w, float h, float r, int Corners) = 0;
virtual void DrawRectExt4(float x, float y, float w, float h, ColorRGBA ColorTopLeft, ColorRGBA ColorTopRight, ColorRGBA ColorBottomLeft, ColorRGBA ColorBottomRight, float r, int Corners) = 0;
Expand Down
1 change: 1 addition & 0 deletions src/engine/server.h
Expand Up @@ -234,6 +234,7 @@ class IServer : public IInterface
virtual const char *GetAuthName(int ClientID) const = 0;
virtual void Kick(int ClientID, const char *pReason) = 0;
virtual void Ban(int ClientID, int Seconds, const char *pReason) = 0;
virtual void RedirectClient(int ClientID, int Port, bool Verbose = false) = 0;
virtual void ChangeMap(const char *pMap) = 0;

virtual void DemoRecorder_HandleAutoStart() = 0;
Expand Down
12 changes: 9 additions & 3 deletions src/engine/server/main.cpp
Expand Up @@ -67,14 +67,19 @@ int main(int argc, const char **argv)
#endif

std::vector<std::shared_ptr<ILogger>> vpLoggers;
std::shared_ptr<ILogger> pStdoutLogger;
#if defined(CONF_PLATFORM_ANDROID)
vpLoggers.push_back(std::shared_ptr<ILogger>(log_logger_android()));
pStdoutLogger = std::shared_ptr<ILogger>(log_logger_android());
#else
if(!Silent)
{
vpLoggers.push_back(std::shared_ptr<ILogger>(log_logger_stdout()));
pStdoutLogger = std::shared_ptr<ILogger>(log_logger_stdout());
}
#endif
if(pStdoutLogger)
{
vpLoggers.push_back(pStdoutLogger);
}
std::shared_ptr<CFutureLogger> pFutureFileLogger = std::make_shared<CFutureLogger>();
vpLoggers.push_back(pFutureFileLogger);
std::shared_ptr<CFutureLogger> pFutureConsoleLogger = std::make_shared<CFutureLogger>();
Expand Down Expand Up @@ -102,6 +107,8 @@ int main(int argc, const char **argv)
#endif

CServer *pServer = CreateServer();
pServer->SetLoggers(pFutureFileLogger, std::move(pStdoutLogger));

IKernel *pKernel = IKernel::Create();

// create the components
Expand Down Expand Up @@ -169,7 +176,6 @@ int main(int argc, const char **argv)
pConsole->Register("sv_test_cmds", "", CFGFLAG_SERVER, CServer::ConTestingCommands, pConsole, "Turns testing commands aka cheats on/off (setting only works in initial config)");
pConsole->Register("sv_rescue", "", CFGFLAG_SERVER, CServer::ConRescue, pConsole, "Allow /rescue command so players can teleport themselves out of freeze (setting only works in initial config)");

log_set_loglevel((LEVEL)g_Config.m_Loglevel);
const int Mode = g_Config.m_Logappend ? IOFLAG_APPEND : IOFLAG_WRITE;
if(g_Config.m_Logfile[0])
{
Expand Down
98 changes: 81 additions & 17 deletions src/engine/server/server.cpp
Expand Up @@ -284,6 +284,10 @@ class CRconClientLogger : public ILogger

void CRconClientLogger::Log(const CLogMessage *pMessage)
{
if(m_Filter.Filters(pMessage))
{
return;
}
m_pServer->SendRconLogLine(m_ClientID, pMessage);
}

Expand All @@ -302,6 +306,7 @@ void CServer::CClient::Reset()
m_Score = -1;
m_NextMapChunk = 0;
m_Flags = 0;
m_RedirectDropTime = 0;
}

CServer::CServer()
Expand Down Expand Up @@ -520,6 +525,35 @@ void CServer::Ban(int ClientID, int Seconds, const char *pReason)
m_NetServer.NetBan()->BanAddr(&Addr, Seconds, pReason);
}

void CServer::RedirectClient(int ClientID, int Port, bool Verbose)
{
if(ClientID < 0 || ClientID >= MAX_CLIENTS)
return;

char aBuf[512];
bool SupportsRedirect = GetClientVersion(ClientID) >= VERSION_DDNET_REDIRECT;
if(Verbose)
{
str_format(aBuf, sizeof(aBuf), "redirecting '%s' to port %d supported=%d", ClientName(ClientID), Port, SupportsRedirect);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "redirect", aBuf);
}

if(!SupportsRedirect)
{
bool SamePort = Port == m_NetServer.Address().port;
str_format(aBuf, sizeof(aBuf), "Redirect unsupported: please connect to port %d", Port);
Kick(ClientID, SamePort ? "Redirect unsupported: please reconnect" : aBuf);
return;
}

CMsgPacker Msg(NETMSG_REDIRECT, true);
Msg.AddInt(Port);
SendMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_FLUSH, ClientID);

m_aClients[ClientID].m_RedirectDropTime = time_get() + time_freq() * 10;
m_aClients[ClientID].m_State = CClient::STATE_REDIRECTED;
}

int64_t CServer::TickStartTime(int Tick)
{
return m_GameStartTime + (time_freq() * Tick) / SERVER_TICK_SPEED;
Expand All @@ -540,6 +574,7 @@ int CServer::Init()
Client.m_AuthKey = -1;
Client.m_Latency = 0;
Client.m_Sixup = false;
Client.m_RedirectDropTime = 0;
}

m_CurrentGameTick = MIN_TICK;
Expand All @@ -553,11 +588,11 @@ int CServer::Init()

void CServer::SendLogLine(const CLogMessage *pMessage)
{
if(pMessage->m_Level <= IConsole::ToLogLevel(g_Config.m_ConsoleOutputLevel))
if(pMessage->m_Level <= IConsole::ToLogLevelFilter(g_Config.m_ConsoleOutputLevel))
{
SendRconLogLine(-1, pMessage);
}
if(pMessage->m_Level <= IConsole::ToLogLevel(g_Config.m_EcOutputLevel))
if(pMessage->m_Level <= IConsole::ToLogLevelFilter(g_Config.m_EcOutputLevel))
{
m_Econ.Send(-1, pMessage->m_aLine);
}
Expand Down Expand Up @@ -1164,6 +1199,7 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
pThis->m_aPrevStates[ClientID] = CClient::STATE_EMPTY;
pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
pThis->m_aClients[ClientID].m_Sixup = false;
pThis->m_aClients[ClientID].m_RedirectDropTime = 0;

pThis->GameServer()->OnClientEngineDrop(ClientID, pReason);
pThis->Antibot()->OnEngineClientDrop(ClientID, pReason);
Expand Down Expand Up @@ -1858,7 +1894,7 @@ static inline int GetCacheIndex(int Type, bool SendClient)

CServer::CCache::CCache()
{
m_Cache.clear();
m_vCache.clear();
}

CServer::CCache::~CCache()
Expand All @@ -1873,12 +1909,12 @@ CServer::CCache::CCacheChunk::CCacheChunk(const void *pData, int Size)

void CServer::CCache::AddChunk(const void *pData, int Size)
{
m_Cache.emplace_back(pData, Size);
m_vCache.emplace_back(pData, Size);
}

void CServer::CCache::Clear()
{
m_Cache.clear();
m_vCache.clear();
}

void CServer::CacheServerInfo(CCache *pCache, int Type, bool SendClients)
Expand Down Expand Up @@ -2178,12 +2214,12 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token, int Type, bool Sen
Packet.m_Address = *pAddr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;

for(const auto &Chunk : pCache->m_Cache)
for(const auto &Chunk : pCache->m_vCache)
{
p.Reset();
if(Type == SERVERINFO_EXTENDED)
{
if(&Chunk == &pCache->m_Cache.front())
if(&Chunk == &pCache->m_vCache.front())
p.AddRaw(SERVERBROWSE_INFO_EXTENDED, sizeof(SERVERBROWSE_INFO_EXTENDED));
else
p.AddRaw(SERVERBROWSE_INFO_EXTENDED_MORE, sizeof(SERVERBROWSE_INFO_EXTENDED_MORE));
Expand Down Expand Up @@ -2222,7 +2258,7 @@ void CServer::GetServerInfoSixup(CPacker *pPacker, int Token, bool SendClients)

SendClients = SendClients && Token != -1;

CCache::CCacheChunk &FirstChunk = m_aSixupServerInfoCache[SendClients].m_Cache.front();
CCache::CCacheChunk &FirstChunk = m_aSixupServerInfoCache[SendClients].m_vCache.front();
pPacker->AddRaw(FirstChunk.m_vData.data(), FirstChunk.m_vData.size());
}

Expand Down Expand Up @@ -2430,6 +2466,9 @@ void CServer::PumpNetwork(bool PacketWaiting)
}
else
{
if(m_aClients[Packet.m_ClientID].m_State == CClient::STATE_REDIRECTED)
continue;

int GameFlags = 0;
if(Packet.m_Flags & NET_CHUNKFLAG_VITAL)
{
Expand Down Expand Up @@ -2867,9 +2906,12 @@ int CServer::Run()

NonActive = true;

for(const auto &Client : m_aClients)
for(int i = 0; i < MAX_CLIENTS; ++i)
{
if(Client.m_State != CClient::STATE_EMPTY)
if(m_aClients[i].m_State == CClient::STATE_REDIRECTED)
if(time_get() > m_aClients[i].m_RedirectDropTime)
m_NetServer.Drop(i, "redirected");
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
{
NonActive = false;
break;
Expand Down Expand Up @@ -3499,12 +3541,6 @@ void CServer::ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData)
}
}

void CServer::ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
log_set_loglevel((LEVEL)g_Config.m_Loglevel);
}

void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
Expand Down Expand Up @@ -3669,6 +3705,26 @@ void CServer::ConchainSixupUpdate(IConsole::IResult *pResult, void *pUserData, I
pThis->m_MapReload |= (pThis->m_apCurrentMapData[MAP_TYPE_SIXUP] != 0) != (pResult->GetInteger(0) != 0);
}

void CServer::ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CServer *pSelf = (CServer *)pUserData;
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments())
{
pSelf->m_pFileLogger->SetFilter(CLogFilter{IConsole::ToLogLevelFilter(g_Config.m_Loglevel)});
}
}

void CServer::ConchainStdoutOutputLevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CServer *pSelf = (CServer *)pUserData;
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() && pSelf->m_pStdoutLogger)
{
pSelf->m_pStdoutLogger->SetFilter(CLogFilter{IConsole::ToLogLevelFilter(g_Config.m_StdoutOutputLevel)});
}
}

#if defined(CONF_FAMILY_UNIX)
void CServer::ConchainConnLoggingServerChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
Expand Down Expand Up @@ -3736,7 +3792,6 @@ void CServer::RegisterCommands()
RustVersionRegister(*Console());

Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
Console()->Chain("loglevel", ConchainLoglevel, this);
Console()->Chain("password", ConchainSpecialInfoupdate, this);

Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this);
Expand All @@ -3748,6 +3803,9 @@ void CServer::RegisterCommands()
Console()->Chain("sv_map", ConchainMapUpdate, this);
Console()->Chain("sv_sixup", ConchainSixupUpdate, this);

Console()->Chain("loglevel", ConchainLoglevel, this);
Console()->Chain("stdout_output_level", ConchainStdoutOutputLevel, this);

#if defined(CONF_FAMILY_UNIX)
Console()->Chain("sv_conn_logging_server", ConchainConnLoggingServerChange, this);
#endif
Expand Down Expand Up @@ -3868,3 +3926,9 @@ void CServer::SetErrorShutdown(const char *pReason)
{
str_copy(m_aErrorShutdownReason, pReason);
}

void CServer::SetLoggers(std::shared_ptr<ILogger> &&pFileLogger, std::shared_ptr<ILogger> &&pStdoutLogger)
{
m_pFileLogger = pFileLogger;
m_pStdoutLogger = pStdoutLogger;
}
15 changes: 13 additions & 2 deletions src/engine/server/server.h
Expand Up @@ -36,6 +36,7 @@ class CLogMessage;
class CMsgPacker;
class CPacker;
class IEngineMap;
class ILogger;

class CSnapIDPool
{
Expand Down Expand Up @@ -146,6 +147,7 @@ class CServer : public IServer
STATE_CONNECTING,
STATE_READY,
STATE_INGAME,
STATE_REDIRECTED,

SNAPRATE_INIT = 0,
SNAPRATE_FULL,
Expand Down Expand Up @@ -206,6 +208,7 @@ class CServer : public IServer
int m_DDNetVersion;
char m_aDDNetVersionStr[64];
CUuid m_ConnectionID;
int64_t m_RedirectDropTime;

// DNSBL
int m_DnsblState;
Expand Down Expand Up @@ -273,6 +276,9 @@ class CServer : public IServer
std::vector<std::string> m_vAnnouncements;
char m_aAnnouncementFile[IO_MAX_PATH_LENGTH];

std::shared_ptr<ILogger> m_pFileLogger = nullptr;
std::shared_ptr<ILogger> m_pStdoutLogger = nullptr;

CServer();
~CServer();

Expand All @@ -288,6 +294,7 @@ class CServer : public IServer

void Kick(int ClientID, const char *pReason) override;
void Ban(int ClientID, int Seconds, const char *pReason) override;
void RedirectClient(int ClientID, int Port, bool Verbose = false) override;

void DemoRecorder_HandleAutoStart() override;

Expand Down Expand Up @@ -350,11 +357,12 @@ class CServer : public IServer
public:
CCacheChunk(const void *pData, int Size);
CCacheChunk(const CCacheChunk &) = delete;
CCacheChunk(CCacheChunk &&) = default;

std::vector<uint8_t> m_vData;
};

std::list<CCacheChunk> m_Cache;
std::vector<CCacheChunk> m_vCache;

CCache();
~CCache();
Expand Down Expand Up @@ -417,7 +425,6 @@ class CServer : public IServer
static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData);
static void ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData);

static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainCommandAccessUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
Expand All @@ -431,6 +438,8 @@ class CServer : public IServer
static void ConchainRconHelperPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainMapUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainSixupUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainStdoutOutputLevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);

#if defined(CONF_FAMILY_UNIX)
static void ConchainConnLoggingServerChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
Expand Down Expand Up @@ -480,6 +489,8 @@ class CServer : public IServer

bool IsSixup(int ClientID) const override { return ClientID != SERVER_DEMO_CLIENT && m_aClients[ClientID].m_Sixup; }

void SetLoggers(std::shared_ptr<ILogger> &&pFileLogger, std::shared_ptr<ILogger> &&pStdoutLogger);

#ifdef CONF_FAMILY_UNIX
enum CONN_LOGGING_CMD
{
Expand Down
4 changes: 4 additions & 0 deletions src/engine/server/server_logger.cpp
Expand Up @@ -11,6 +11,10 @@ CServerLogger::CServerLogger(CServer *pServer) :

void CServerLogger::Log(const CLogMessage *pMessage)
{
if(m_Filter.Filters(pMessage))
{
return;
}
m_PendingLock.lock();
if(m_MainThread == std::this_thread::get_id())
{
Expand Down
4 changes: 4 additions & 0 deletions src/engine/shared/assertion_logger.cpp
Expand Up @@ -30,6 +30,10 @@ class CAssertionLogger : public ILogger

void CAssertionLogger::Log(const CLogMessage *pMessage)
{
if(m_Filter.Filters(pMessage))
{
return;
}
std::unique_lock<std::mutex> Lock(m_DbgMessageMutex);
SDebugMessageItem *pMsgItem = (SDebugMessageItem *)m_DbgMessages.Allocate(sizeof(SDebugMessageItem));
str_copy(pMsgItem->m_aMessage, pMessage->m_aLine);
Expand Down
7 changes: 4 additions & 3 deletions src/engine/shared/config_variables.h
Expand Up @@ -11,10 +11,11 @@ MACRO_CONFIG_STR(PlayerClan, player_clan, 12, "", CFGFLAG_SAVE | CFGFLAG_CLIENT
MACRO_CONFIG_INT(PlayerCountry, player_country, -1, -1, 1000, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_INSENSITIVE, "Country of the player")
MACRO_CONFIG_STR(Password, password, 32, "", CFGFLAG_CLIENT | CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, "Password to the server")
MACRO_CONFIG_STR(Logfile, logfile, 128, "", CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Filename to log all output to")
MACRO_CONFIG_INT(Loglevel, loglevel, 2, 0, 4, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Log level (0 = Error, 1 = Warn, 2 = Info, 3 = Debug, 4 = Trace)")
MACRO_CONFIG_INT(Loglevel, loglevel, 0, -3, 2, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Log level (-3 = none, -2 = error, 1 = Warn, 2 = Info, 3 = Debug, 4 = Trace)")
MACRO_CONFIG_INT(Logappend, logappend, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Append to logfile instead of overwriting it every time")
MACRO_CONFIG_INT(ConsoleOutputLevel, console_output_level, 0, 0, 2, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Adjusts the amount of information in the console")
MACRO_CONFIG_INT(ConsoleEnableColors, console_enable_colors, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Enable colors in console output")
MACRO_CONFIG_INT(StdoutOutputLevel, stdout_output_level, 0, -3, 2, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Adjusts the amount of information in the console (-3 = none, -2 = error only, -1 = warn, 0 = info, 1 = debug, 2 = trace)")
MACRO_CONFIG_INT(ConsoleOutputLevel, console_output_level, 0, -3, 2, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Adjusts the amount of information in the console (-3 = none, -2 = error only, -1 = warn, 0 = info, 1 = debug, 2 = trace)")
MACRO_CONFIG_INT(ConsoleEnableColors, console_enable_colors, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Enable colors in console output")
MACRO_CONFIG_INT(Events, events, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT | CFGFLAG_SERVER, "Enable triggering of events, (eye emotes on some holidays in server, christmas skins in client).")

MACRO_CONFIG_STR(SteamName, steam_name, 16, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Last seen name of the Steam profile")
Expand Down
25 changes: 19 additions & 6 deletions src/engine/shared/console.cpp
Expand Up @@ -293,6 +293,15 @@ LEVEL IConsole::ToLogLevel(int Level)
return LEVEL_INFO;
}

int IConsole::ToLogLevelFilter(int Level)
{
if(!(-3 <= Level && Level <= 2))
{
dbg_assert(0, "invalid log level filter");
}
return Level + 2;
}

LOG_COLOR ColorToLogColor(ColorRGBA Color)
{
return LOG_COLOR{
Expand Down Expand Up @@ -438,7 +447,11 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, bo
if(!*Result.m_pCommand)
return;

CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask);
CCommand *pCommand;
if(ClientID == IConsole::CLIENT_ID_GAME)
pCommand = FindCommand(Result.m_pCommand, m_FlagMask | CFGFLAG_GAME);
else
pCommand = FindCommand(Result.m_pCommand, m_FlagMask);

if(pCommand)
{
Expand Down Expand Up @@ -1259,13 +1272,13 @@ const IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName, int Fl

std::unique_ptr<IConsole> CreateConsole(int FlagMask) { return std::make_unique<CConsole>(FlagMask); }

void CConsole::ResetServerGameSettings()
void CConsole::ResetGameSettings()
{
#define MACRO_CONFIG_INT(Name, ScriptName, Def, Min, Max, Flags, Desc) \
{ \
if(((Flags) & (CFGFLAG_SERVER | CFGFLAG_GAME)) == (CFGFLAG_SERVER | CFGFLAG_GAME)) \
if(((Flags)&CFGFLAG_GAME) == CFGFLAG_GAME) \
{ \
CCommand *pCommand = FindCommand(#ScriptName, CFGFLAG_SERVER); \
CCommand *pCommand = FindCommand(#ScriptName, CFGFLAG_GAME); \
void *pUserData = pCommand->m_pUserData; \
FCommandCallback pfnCallback = pCommand->m_pfnCallback; \
TraverseChain(&pfnCallback, &pUserData); \
Expand All @@ -1278,9 +1291,9 @@ void CConsole::ResetServerGameSettings()

#define MACRO_CONFIG_STR(Name, ScriptName, Len, Def, Flags, Desc) \
{ \
if(((Flags) & (CFGFLAG_SERVER | CFGFLAG_GAME)) == (CFGFLAG_SERVER | CFGFLAG_GAME)) \
if(((Flags)&CFGFLAG_GAME) == CFGFLAG_GAME) \
{ \
CCommand *pCommand = FindCommand(#ScriptName, CFGFLAG_SERVER); \
CCommand *pCommand = FindCommand(#ScriptName, CFGFLAG_GAME); \
void *pUserData = pCommand->m_pUserData; \
FCommandCallback pfnCallback = pCommand->m_pfnCallback; \
TraverseChain(&pfnCallback, &pUserData); \
Expand Down
2 changes: 1 addition & 1 deletion src/engine/shared/console.h
Expand Up @@ -223,7 +223,7 @@ class CConsole : public IConsole
void InitChecksum(CChecksumData *pData) const override;

void SetAccessLevel(int AccessLevel) override { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_USER)); }
void ResetServerGameSettings() override;
void ResetGameSettings() override;
// DDRace

static void ConUserCommandStatus(IConsole::IResult *pResult, void *pUser);
Expand Down