On macOS, semaphore names are global and processes can open semaphores of other processes by using their name. It was possible that multiple client/server processes randomly tried to create semaphores with the same memory address, which would cause them to have the same name. In that case, `sem_open` would fail because `O_EXCL` prevents creating semaphores if the name is already used.
This is made more unlikely by also including the PID in the semaphore name. Additionally, the semaphore name is prefixed with `/` to more closely follow the construction rules for semaphore names. In particular, the behavior is implementation-defined if the name is not prefixed with a slash.
See https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.htmlCloses#8773.
More details are added to the assertion error message in case this does not end up fixing the issue.
Previously, the `net_udp_create` function returned whether the IPv6 socket in particular was created successfully. If an IPv4 socket or Websocket was created successfully before this, it was not used and also not closed properly, if the IPv6 socket could not be created.
Now, the `net_udp_create` function succeeds if the socket for at least one network type (IPv4, IPv6 or Websocket) could be created successfully.
Avoid reusing local variables and remove redundant comments. Also apply the same refactoring to `net_tcp_create`, which already has the behavior of succeeding if the socket for any network type (IPv4 or IPv6) was created successfully.
skins7: Extract the skins dir to a macro
skins7: Look for the skins at 'skins7' dir
Data: Add 0.7 skins (diliated)
CMenus::RenderSkinSelection7: Fix the skins number (use 0.7 getter)
CSkins7::GetColorV3: Fix missing UnclampLighting
CRenderTools: Use stronger type for 0.7 colors
CSkins7::RandomizeSkin: Use enum value instead of hardcode+comment
CMenus::RenderSettingsTee7: Enable team skins validation
system: Add str_utf8_copy_num()
GameClient: Extract ApplySkin7InfoFromGameMsg()
GameClient: Add skin7 to CClientData (also process and validate it)
While keeping 0.6 fully working and untouched
This adds the option to connect via 0.7
0.7 servers are now mixed into the server browser list.
And users can also manually connect via 0.7
using a connection url like so:
./DDNet "connect tw-0.7+udp://127.0.0.1:8303"
This also adds a "Tee 0.7" section in the menu to pick a 0.7 skin.
The config variables are prefixed with player7_ like for example
player7_color_body
Starting the server on Android is currently not possible because a) the server executable is currently not compiled for Android b) launching the server executable with `fork` and `exec` is not supported on Android and may result in strange bugs like broken networking.
Running the server in a background service or in a separate process by using `Runtime.getRuntime().exec` from Java code might be feasible, but this is currently too much effort and may be done in the future.
For now, the "Run server" button as well as the process related system functions are disabled via conditional compilation on Android, to prevent the networking bug that would otherwise be triggered by pressing the "Run server" button.
Opening links and files with the `open_link` and `open_file` functions does not work on Android, as the `open_link` function uses `fork` which is not supported on Android. This also seems to cause a strange bug where client networking partially breaks. Currently, after trying to open any link, connecting to servers is not possible anymore but the server browser still works, with the connection getting stuck randomly in the connecting/loading state.
SDL implements URL opening, including of file URIs, with the `SDL_OpenURL` function for most systems including Android. However, using `SDL_OpenURL` for all systems has several downsides:
1. The `SDL_OpenURL` function is only available since SDL 2.0.14, in particular not for the Ubuntu 20 CI runner. Hence, we would either have to conditionally compile the link opening function to a null-implementation or fallback to using the existing `open_link` function.
2. We would be undoing some additional fixes in the `open_link` function for Windows, which are not included in the Windows implementation of `SDL_OpenURL`.
3. This would also replace the use of `open` on UNIX with `xdg-open`.
4. This would move the functionality to open links and files from the base to the engine client, so we could not have tools or the server potentially making use of this functionality in the future (e.g. open a folder for convenience).
Implementing link and file opening for Android ourselves is too much effort and potentially made even harder by SDL already managing all the unique JVM resources in the `SDLActivity`.
Therefore, the `SDL_OpenURL` function is only used for Android, which is always based on the latest SDL2 version. The original `open_link` functionality is kept for the other systems. For this purpose, the `IClient::ViewLink` and `ViewFile` functions are added to wrap `open_link` and `open_file` for the client and also reduce some duplicate code for error logging.
Unfortunately, testing also revealed that `SDL_OpenURL` does not currently support opening file URIs, at least not of files the internal storage location, which all the DDNet client's files would be located in. At least opening URLs works and neither breaks networking anymore.
Read the entire file into memory immediately when the line reader is initialized instead of using a fixed size buffer of size 32769, which leads to broken line reading for larger files (closes#8431).
Replace the `CLineReader::Init` function with the `CLineReader::OpenFile` function, which additionally checks whether the file contains any null bytes (indicates that the file is likely not a text file).
As the file will be read into memory entirely in the `OpenFile` function, is can also be closed immediately, since using the `io_read_all_str` function should ensure that nothing more can be read from the file. This also simplifies the usage of the `CLineReader` class, as manually closing the file is inconvenient and error-prone. In fact, the file handle for the `ddnet-serverlist-urls.cfg` file was previously not closed properly.
Benchmarking on Windows did not show any noticeable performance impact of this change both for smaller files (config and language files) and larger files (synthetic ~10 MiB files).
Let the `CLineReader::Get` function return `const char *` instead of `char *`, since users of this class should not modify the internal buffer. Also, since the entire file is read into memory now, the returned strings are now valid until the respective line reader object is destructed, instead of only until the `Get` function is called again. This simplifies the usage in some cases where all lines are handled, since additional temporary buffers are now unnecessary.
Remove the `IOFLAG_SKIP_BOM` flag from the `io_open` function, as this flag was only used together with the line reader. This was inconvenient to use, as any use of the `io_open` function specifically for line readers had to be used with `IOFLAG_SKIP_BOM`. Now, the line reader transparently skips the UTF-8 BOM internally. Skipping the UTF-8 BOM never worked with the `IStorage::ReadFileStr` function, because `io_length` is used in the `io_read_all` function, which rewinds the file position to before the UTF-8 BOM that was skipped by using `IOFLAG_SKIP_BOM`. In any case, as the `ReadFileStr` function is currently unused, this has/had no further effect. The respective test cases for `IOFLAG_SKIP_BOM` are removed.
Add more test cases for the `CLineReader` class. All test cases are checked both with and without the UTF-8 BOM. Additional tests with mixed new lines, empty lines with different new lines, and internal null bytes are added.
Consistently use the construct `while(const char *pLine = LineReader.Get())` to iterate over all lines of a line reader. In this case, the assignment inside the `while`-loop seems acceptable over the alternatives, e.g. `while(true)` with `break` or adding a function `CLineReader::ForAll(std::function<const char *> Consumer)`.
```
/home/chiller/Desktop/git/ddnet/src/base/system.cpp:1989:10: error: The 1st argument to 'connect' is < 0 but should be >= 0 [clang-analyzer-unix.StdCLibraryFunctions,-warnings-as-errors]
1989 | return connect(sock->ipv4sock, (struct sockaddr *)&addr, sizeof(addr));
| ^
```
Remove unnecessary `fs_is_dir` check in the `fs_chdir` function. The `SetCurrentDirectoryW` and `chdir` functions would already include such necessary checks anyway.
Assert that `GetCurrentDirectoryW` was successful instead of logging an error message. This function should only potentially fail if the current working directory was changed by another thread between the two calls, which should not happen in our code.
This would make the function `str_from_int` unnecessary, at least
user-facing.
Advantage: User doesn't have to care about `str_from_int`/`str_format`
distinction.
Disadvantage: We're adding some template programming to `system.h`,
potentially slowing all compilation.
Fix leak of pending future logger log messages if the future logger is not set, in particular when the `logfile` config variable is not set or the file could not be opened, by setting a logger that discards all log messages in this case.
Closes#8265.
`GetConsoleMode` can fail with `ERROR_INVALID_HANDLE` when redirecting output to `nul`, which is considered a character file but cannot be used as a console.
The function `fs_makedir_rec_for` for recursively creating folders did not handle paths containing backslashes correctly and only created folders after every occurrence of a regular slash. This could cause the recursive folder creation to fail when a parent folder is not created before the child folder. For example when recursively creating folders for the path `D:\Games/DDNet\downloadedskins`, the `DDNet` folder was not being created because this path segment does not end in a forward slash. Now, backslashes are handled the same as regular slashes, which is consistent with the other filesystem functions.
Additionally, the function tried to create folders for drive letters on Windows (e.g. `C:` and `D:`). Trying to create a system drive as a folder will fail due to access being denied, whereas it seems to work for already existing non-system drives. For example when recursively creating folders for the path `C:/Games/DDNet/downloadedskins`, the `C:` "folder" could not be created which caused the entire operation to fail. Now, the function will not try to create folders for drive letters anymore, i.e. if the name of the to be created folder ends with `:`.
Closes#8148.
Instead of defining the macro `WIN32_LEAN_AND_MEAN` and sometimes also the macro `_WIN32_WINNT` in each file that directly or indirectly includes `<windows.h>`, only define these macros once consistently in `CMakeLists.txt`.
Also define `NTDDI_VERSION`, which is the new macro to specify the minimum Windows version starting with Windows Vista. This macro needs to be defined in addition to old `_WIN32_WINNT` macro, according to the documentation.
See https://learn.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers
Assert on failures in all `thread_*` and `sphore_*` functions on all operating systems instead of only printing log messages on Unix, as these functions are only expected to fail when used with incorrect arguments or in some cases when a dead-lock is detected.
On macOS, `sphore_wait` was not correctly calling `sem_wait` in a loop to repeat the wait operation if it is interrupted by a signal.
On Windows, the AIO tests were failing with the additional assertions. The maximum count that semaphores on Windows could be incremented to was previously, arbitrarily limited to 10000, which was causing the `ReleaseSemaphore` call to fail as the AIO semaphore is signaled 65536 times (for each write operation) in multiple of the AIO tests.
Add/update doxygen comments for all `shell_*`, `os_*`, `secure_*`, `cmdline_*`, `windows_*` and related functions.
Move `open_link` and `open_file` declarations to shell group, to the same relative position as their function definitions.
Add parameter to `shell_execute` to either start the process in the foreground or background on Windows. Previously, all processes were started in the background, because this is desired when starting the server from the client. However, this causes the graphics initialization to fail when restarting the client after updating or with the `restart` command when using Vulkan with windowed and windowed fullscreen mode.
Closes#6578.
Use [virtual terminal processing](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for Windows console output when possible, using the existing async logger. Fallback to the old Windows logger when virtual terminal processing is not available. Do not set any logger when there is no console/file/pipe to output to.
This adds support for 24 bit RGB colors using ANSI escape codes (when Terminal emulator supports it) instead of only supporting 16 fixed colors.
Use the existing async logger also on Windows by converting the console standard output handle to an IOHANDLE. Avoid converting console output to UTF-16 by changing the console output codepage to UTF-8 and outputting that directly. Overall, this reduces the time until the loading screen first appears when launching the client from the console on Windows by around 70%, from 80-100ms down to 25ms.
Add support for `NO_COLOR` environment variable also on Windows.
Use the async logger also for writing output to files and pipes instead of using a Windows specific implementation for this, to reduce lags when redirecting the standard output.
Closes#3925. The client/server will not freeze anymore when entering selection mode in the console because we are using the async logger on Windows now.
Closes#1108. We were already using AIO on Windows to write the logfile, which was not causing any known issues. We are now also using AIO with the async loggers for console/files/pipes on Windows.
On Windows, the result of the `localtime` function is thread-local, hence our uses of this function are already thread-safe.
On non-Windows, the `localtime` function is potentially not thread-safe, so the reentrant `localtime_r` function is used instead with a thread-local buffer for the result.
Closes#7788.
Files were not being truncated on Windows anymore when using `io_open` with `IOFLAG_WRITE` due to a regression from #7254. Instead, the existing file contents were kept and the file pointer was set to the beginning of the file.
This caused broken demo files to be created (#7349) when recording a shorter demo with the same filename as an existing longer demo. It also caused the map tools to produce maps with additional junk data at the end, if an existing map is overridden by a smaller map.
This is fixed by using the creation disposition `CREATE_ALWAYS` instead of `OPEN_EXISTING` with [`CreateFileW`](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew), which ensures that a file is always created and truncated.
A regression test case is added, which fails without this change.
We support little and big endian but not PDP endian (Middle-endian).
Define endianness as string `CONF_ARCH_ENDIAN_STRING` to avoid conditional compilation when printing endianness.