diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5a73a6873..335153e0b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -80,11 +80,11 @@ jobs: cd debug ${{ matrix.cmake-path }}cmake --version ${{ matrix.cmake-path }}cmake ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=Debug -Werror=dev -DDOWNLOAD_GTEST=ON -DDEV=ON -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=. .. - ${{ matrix.cmake-path }}cmake --build . --config Debug ${{ matrix.build-args }} --target everything + ${{ matrix.cmake-path }}cmake --build . --config Debug --target everything ${{ matrix.build-args }} - name: Test debug run: | cd debug - ${{ matrix.cmake-path }}cmake --build . --config Debug ${{ matrix.build-args }} --target run_tests + ${{ matrix.cmake-path }}cmake --build . --config Debug --target run_tests ${{ matrix.build-args }} ./DDNet-Server shutdown - name: Build in release mode @@ -93,11 +93,11 @@ jobs: mkdir release cd release ${{ matrix.cmake-path }}cmake ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=Release -Werror=dev -DDOWNLOAD_GTEST=ON -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE=. .. - ${{ matrix.cmake-path }}cmake --build . --config Release ${{ matrix.build-args }} --target everything + ${{ matrix.cmake-path }}cmake --build . --config Release --target everything ${{ matrix.build-args }} - name: Test release run: | cd release - ${{ matrix.cmake-path }}cmake --build . --config Release ${{ matrix.build-args }} --target run_tests + ${{ matrix.cmake-path }}cmake --build . --config Release --target run_tests ${{ matrix.build-args }} ./DDNet-Server shutdown - name: Build in release mode with debug info and all features on @@ -107,18 +107,18 @@ jobs: mkdir fancy cd fancy ${{ matrix.cmake-path }}cmake ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DANTIBOT=ON -DMYSQL=ON -DWEBSOCKETS=ON .. - ${{ matrix.cmake-path }}cmake --build . --config RelWithDebInfo ${{ matrix.build-args }} --target everything + ${{ matrix.cmake-path }}cmake --build . --config RelWithDebInfo --target everything ${{ matrix.build-args }} - name: Test fancy if: matrix.fancy run: | cd release - ${{ matrix.cmake-path }}cmake --build . --config RelWithDebInfo ${{ matrix.build-args }} --target run_tests + ${{ matrix.cmake-path }}cmake --build . --config RelWithDebInfo --target run_tests ${{ matrix.build-args }} ./DDNet-Server shutdown - name: Package run: | cd release - ${{ matrix.cmake-path }}cmake --build . --config Release ${{ matrix.build-args }} --target package_default + ${{ matrix.cmake-path }}cmake --build . --config Release --target package_default ${{ matrix.build-args }} mkdir artifacts mv ${{ matrix.package-file }} artifacts diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f86af688..a5fad44ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2336,7 +2336,6 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION set(CPACK_SOURCE_FILES CMakeLists.txt README.md - autoexec_server.cfg cmake/ data/ datasrc/ @@ -2380,7 +2379,6 @@ set(CPACK_DIRS data) set(CPACK_FILES license.txt storage.cfg - autoexec_server.cfg ${COPY_FILES} ) if(TARGET_OS STREQUAL "windows") diff --git a/README.md b/README.md index 4399e1679..3e48447ab 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,11 @@ Dependencies on Linux You can install the required libraries on your system, `touch CMakeLists.txt` and CMake will use the system-wide libraries by default. You can install all required dependencies and CMake on Debian or Ubuntu like this: - sudo apt install build-essential cmake git libcurl4-openssl-dev libssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpnglite-dev libsdl2-dev libsqlite3-dev libwavpack-dev python + sudo apt install build-essential cmake git libcurl4-openssl-dev libssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpnglite-dev libsdl2-dev libsqlite3-dev libwavpack-dev python google-mock Or on Arch Linux like this: - sudo pacman -S --needed base-devel cmake curl freetype2 git glew libnotify opusfile python sdl2 sqlite wavpack + sudo pacman -S --needed base-devel cmake curl freetype2 git glew libnotify opusfile python sdl2 sqlite wavpack gmock There is an [AUR package for pnglite](https://aur.archlinux.org/packages/pnglite/). For instructions on installing it, see [AUR packages installation instructions on ArchWiki](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages). diff --git a/cmake/FindFFMPEG.cmake b/cmake/FindFFMPEG.cmake index 058402fae..77da309ff 100644 --- a/cmake/FindFFMPEG.cmake +++ b/cmake/FindFFMPEG.cmake @@ -6,7 +6,7 @@ if(NOT CMAKE_CROSSCOMPILING) pkg_check_modules(PC_SWSCALE libswscale) pkg_check_modules(PC_SWRESAMPLE libswresample) if(TARGET_OS STREQUAL "linux") - pkg_check_modules(PC_X264 libx264) + pkg_search_module(PC_X264 libx264 x264) endif() endif() diff --git a/autoexec_server.cfg b/data/autoexec_server.cfg similarity index 100% rename from autoexec_server.cfg rename to data/autoexec_server.cfg diff --git a/other/ddnet.appdata.xml b/other/ddnet.appdata.xml index a12afeee2..1bc4113f2 100644 --- a/other/ddnet.appdata.xml +++ b/other/ddnet.appdata.xml @@ -37,6 +37,7 @@ intense + diff --git a/scripts/dmg.py b/scripts/dmg.py index 4babfa9ad..517814fee 100644 --- a/scripts/dmg.py +++ b/scripts/dmg.py @@ -4,6 +4,7 @@ import os import shlex import subprocess import tempfile +import time ConfigDmgtools = namedtuple('Config', 'dmg hfsplus newfs_hfs verbose') ConfigHdiutil = namedtuple('Config', 'hdiutil verbose') @@ -72,7 +73,16 @@ class Hdiutil(Dmg): def create(self, dmg, volume_name, directory, symlinks): if symlinks: raise NotImplementedError("symlinks are not yet implemented") - self._hdiutil('create', '-volname', volume_name, '-srcdir', directory, dmg) + for i in range(5): + try: + self._hdiutil('create', '-volname', volume_name, '-srcdir', directory, dmg) + except subprocess.CalledProcessError as e: + if i == 4: + raise e + print("Retrying hdiutil create") + time.sleep(5) + else: + break def main(): p = argparse.ArgumentParser(description="Manipulate dmg archives") diff --git a/src/base/system.c b/src/base/system.c index 543b68020..37fddb190 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -3017,8 +3017,8 @@ const char *str_utf8_find_nocase(const char *haystack, const char *needle) int str_utf8_isspace(int code) { - return code <= 0x0020 || code == 0x0085 || code == 0x00A0 || - code == 0x034F || code == 0x1160 || code == 0x1680 || code == 0x180E || + return code <= 0x0020 || code == 0x0085 || code == 0x00A0 || code == 0x034F || + code == 0x115F || code == 0x1160 || code == 0x1680 || code == 0x180E || (code >= 0x2000 && code <= 0x200F) || (code >= 0x2028 && code <= 0x202F) || (code >= 0x205F && code <= 0x2064) || (code >= 0x206A && code <= 0x206F) || code == 0x2800 || code == 0x3000 || code == 0x3164 || diff --git a/src/base/tl/algorithm.h b/src/base/tl/algorithm.h index b2fe29ea3..9d84c3b3a 100644 --- a/src/base/tl/algorithm.h +++ b/src/base/tl/algorithm.h @@ -109,7 +109,7 @@ void sort_quick(R range) template void sort(R range) { - std::sort(&range.front(), &range.back() + 1); + std::stable_sort(&range.front(), &range.back() + 1); } template diff --git a/src/engine/client.h b/src/engine/client.h index 56e8104d9..1d0454880 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -125,8 +125,7 @@ public: // gfx virtual void SwitchWindowScreen(int Index) = 0; - virtual void ToggleFullscreen() = 0; - virtual void ToggleWindowBordered() = 0; + virtual void SetWindowParams(int FullscreenMode, bool IsBorderless) = 0; virtual void ToggleWindowVSync() = 0; virtual void LoadFont() = 0; virtual void Notify(const char *pTitle, const char *pMessage) = 0; @@ -257,6 +256,7 @@ public: virtual const char *DDNetVersionStr() const = 0; virtual void OnDummyDisconnect() = 0; + virtual void DummyResetInput() = 0; virtual void Echo(const char *pString) = 0; virtual bool CanDisplayWarning() = 0; virtual bool IsDisplayingWarning() = 0; diff --git a/src/engine/client/backend_sdl.cpp b/src/engine/client/backend_sdl.cpp index 855366a41..0042bd0cb 100644 --- a/src/engine/client/backend_sdl.cpp +++ b/src/engine/client/backend_sdl.cpp @@ -17,6 +17,8 @@ #include #include +#include "SDL_hints.h" + #if defined(SDL_VIDEO_DRIVER_X11) #include #include @@ -3785,6 +3787,13 @@ void CCommandProcessorFragment_SDL::Cmd_Init(const SCommand_Init *pCommand) // check what this context can do const char *pVersionString = (const char *)glGetString(GL_VERSION); dbg_msg("opengl", "Version string: %s", pVersionString); + + const char *pRendererString = (const char *)glGetString(GL_RENDERER); + + str_copy(pCommand->m_pVendorString, pVendorString, gs_GPUInfoStringSize); + str_copy(pCommand->m_pVersionString, pVersionString, gs_GPUInfoStringSize); + str_copy(pCommand->m_pRendererString, pRendererString, gs_GPUInfoStringSize); + // parse version string ParseVersionString(pVersionString, pCommand->m_pCapabilities->m_ContextMajor, pCommand->m_pCapabilities->m_ContextMinor, pCommand->m_pCapabilities->m_ContextPatch); @@ -4520,6 +4529,10 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt else SdlFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } + else if(Flags & (IGraphicsBackend::INITFLAG_DESKTOP_FULLSCREEN)) + { + SdlFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } // set gl attributes SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -4614,7 +4627,10 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt CmdSDL.m_GlewPatch = GlewPatch; CmdSDL.m_pInitError = &InitError; CmdSDL.m_pErrStringPtr = &pErrorStr; - CmdBuffer.AddCommand(CmdSDL); + CmdSDL.m_pVendorString = m_aVendorString; + CmdSDL.m_pVersionString = m_aVersionString; + CmdSDL.m_pRendererString = m_aRendererString; + CmdBuffer.AddCommandUnsafe(CmdSDL); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); @@ -4626,7 +4642,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt CmdOpenGL.m_pStorage = pStorage; CmdOpenGL.m_pCapabilities = &m_Capabilites; CmdOpenGL.m_pInitError = &InitError; - CmdBuffer.AddCommand(CmdOpenGL); + CmdBuffer.AddCommandUnsafe(CmdOpenGL); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); @@ -4634,7 +4650,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt if(InitError == -2) { CCommandProcessorFragment_OpenGL::SCommand_Shutdown CmdGL; - CmdBuffer.AddCommand(CmdGL); + CmdBuffer.AddCommandUnsafe(CmdGL); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); @@ -4648,7 +4664,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt if(InitError != 0) { CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd; - CmdBuffer.AddCommand(Cmd); + CmdBuffer.AddCommandUnsafe(Cmd); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); @@ -4701,7 +4717,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt CmdSDL.m_Width = CurrentDisplayMode.w; CmdSDL.m_Height = CurrentDisplayMode.h; - CmdBuffer.AddCommand(CmdSDL); + CmdBuffer.AddCommandUnsafe(CmdSDL); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); @@ -4717,13 +4733,13 @@ int CGraphicsBackend_SDL_OpenGL::Shutdown() // issue a shutdown command CCommandBuffer CmdBuffer(1024, 512); CCommandProcessorFragment_OpenGL::SCommand_Shutdown CmdGL; - CmdBuffer.AddCommand(CmdGL); + CmdBuffer.AddCommandUnsafe(CmdGL); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd; - CmdBuffer.AddCommand(Cmd); + CmdBuffer.AddCommandUnsafe(Cmd); RunBuffer(&CmdBuffer); WaitForIdle(); CmdBuffer.Reset(); @@ -4755,18 +4771,28 @@ void CGraphicsBackend_SDL_OpenGL::Maximize() // TODO: SDL } -bool CGraphicsBackend_SDL_OpenGL::Fullscreen(bool State) +void CGraphicsBackend_SDL_OpenGL::SetWindowParams(int FullscreenMode, bool IsBorderless) { + if(FullscreenMode > 0) + { + if(FullscreenMode == 1) + { #if defined(CONF_PLATFORM_MACOS) // Todo SDL: remove this when fixed (game freezes when losing focus in fullscreen) - return SDL_SetWindowFullscreen(m_pWindow, State ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) == 0; + SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); #else - return SDL_SetWindowFullscreen(m_pWindow, State ? SDL_WINDOW_FULLSCREEN : 0) == 0; + SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN); #endif -} - -void CGraphicsBackend_SDL_OpenGL::SetWindowBordered(bool State) -{ - SDL_SetWindowBordered(m_pWindow, SDL_bool(State)); + } + else if(FullscreenMode == 2) + { + SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); + } + } + else + { + SDL_SetWindowFullscreen(m_pWindow, 0); + SDL_SetWindowBordered(m_pWindow, SDL_bool(!IsBorderless)); + } } bool CGraphicsBackend_SDL_OpenGL::SetWindowScreen(int Index) diff --git a/src/engine/client/backend_sdl.h b/src/engine/client/backend_sdl.h index a4231018a..4d383c0f9 100644 --- a/src/engine/client/backend_sdl.h +++ b/src/engine/client/backend_sdl.h @@ -433,6 +433,10 @@ public: int *m_pInitError; + char *m_pVendorString; + char *m_pVersionString; + char *m_pRendererString; + int m_RequestedMajor; int m_RequestedMinor; int m_RequestedPatch; @@ -486,6 +490,8 @@ public: virtual void RunBuffer(CCommandBuffer *pBuffer); }; +static constexpr size_t gs_GPUInfoStringSize = 256; + // graphics backend implemented with SDL and OpenGL class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded { @@ -497,6 +503,10 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded SBackendCapabilites m_Capabilites; + char m_aVendorString[gs_GPUInfoStringSize] = {}; + char m_aVersionString[gs_GPUInfoStringSize] = {}; + char m_aRendererString[gs_GPUInfoStringSize] = {}; + bool m_UseNewOpenGL; char m_aErrorString[256]; @@ -511,8 +521,7 @@ public: virtual void Minimize(); virtual void Maximize(); - virtual bool Fullscreen(bool State); - virtual void SetWindowBordered(bool State); // on=true/off=false + virtual void SetWindowParams(int FullscreenMode, bool IsBorderless); virtual bool SetWindowScreen(int Index); virtual int GetWindowScreen(); virtual int WindowActive(); @@ -536,6 +545,21 @@ public: return NULL; } + + virtual const char *GetVendorString() + { + return m_aVendorString; + } + + virtual const char *GetVersionString() + { + return m_aVersionString; + } + + virtual const char *GetRendererString() + { + return m_aRendererString; + } }; #endif // ENGINE_CLIENT_BACKEND_SDL_H diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 915663a47..eac33d87b 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3486,6 +3486,12 @@ void CClient::Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData) pSelf->DummyDisconnect(0); } +void CClient::Con_DummyResetInput(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->GameClient()->DummyResetInput(); +} + void CClient::Con_Quit(IConsole::IResult *pResult, void *pUserData) { CClient *pSelf = (CClient *)pUserData; @@ -3990,10 +3996,10 @@ void CClient::SwitchWindowScreen(int Index) // Todo SDL: remove this when fixed (changing screen when in fullscreen is bugged) if(g_Config.m_GfxFullscreen) { - ToggleFullscreen(); + SetWindowParams(0, g_Config.m_GfxBorderless); if(Graphics()->SetWindowScreen(Index)) g_Config.m_GfxScreen = Index; - ToggleFullscreen(); + SetWindowParams(g_Config.m_GfxFullscreen, g_Config.m_GfxBorderless); } else { @@ -4014,10 +4020,11 @@ void CClient::ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData, pfnCallback(pResult, pCallbackUserData); } -void CClient::ToggleFullscreen() +void CClient::SetWindowParams(int FullscreenMode, bool IsBorderless) { - if(Graphics()->Fullscreen(g_Config.m_GfxFullscreen ^ 1)) - g_Config.m_GfxFullscreen ^= 1; + g_Config.m_GfxFullscreen = clamp(FullscreenMode, 0, 2); + g_Config.m_GfxBorderless = (int)IsBorderless; + Graphics()->SetWindowParams(FullscreenMode, IsBorderless); } void CClient::ConchainFullscreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) @@ -4026,25 +4033,19 @@ void CClient::ConchainFullscreen(IConsole::IResult *pResult, void *pUserData, IC if(pSelf->Graphics() && pResult->NumArguments()) { if(g_Config.m_GfxFullscreen != pResult->GetInteger(0)) - pSelf->ToggleFullscreen(); + pSelf->SetWindowParams(pResult->GetInteger(0), g_Config.m_GfxBorderless); } else pfnCallback(pResult, pCallbackUserData); } -void CClient::ToggleWindowBordered() -{ - g_Config.m_GfxBorderless ^= 1; - Graphics()->SetWindowBordered(!g_Config.m_GfxBorderless); -} - void CClient::ConchainWindowBordered(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { CClient *pSelf = (CClient *)pUserData; if(pSelf->Graphics() && pResult->NumArguments()) { if(!g_Config.m_GfxFullscreen && (g_Config.m_GfxBorderless != pResult->GetInteger(0))) - pSelf->ToggleWindowBordered(); + pSelf->SetWindowParams(g_Config.m_GfxFullscreen, !g_Config.m_GfxBorderless); } else pfnCallback(pResult, pCallbackUserData); @@ -4175,8 +4176,9 @@ void CClient::RegisterCommands() m_pConsole->Register("stoprecord", "", CFGFLAG_SERVER, 0, 0, "Stop recording"); m_pConsole->Register("reload", "", CFGFLAG_SERVER, 0, 0, "Reload the map"); - m_pConsole->Register("dummy_connect", "", CFGFLAG_CLIENT, Con_DummyConnect, this, "connect dummy"); - m_pConsole->Register("dummy_disconnect", "", CFGFLAG_CLIENT, Con_DummyDisconnect, this, "disconnect dummy"); + m_pConsole->Register("dummy_connect", "", CFGFLAG_CLIENT, Con_DummyConnect, this, "Connect dummy"); + m_pConsole->Register("dummy_disconnect", "", CFGFLAG_CLIENT, Con_DummyDisconnect, this, "Disconnect dummy"); + m_pConsole->Register("dummy_reset", "", CFGFLAG_CLIENT, Con_DummyResetInput, this, "Reset dummy"); m_pConsole->Register("quit", "", CFGFLAG_CLIENT | CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds"); m_pConsole->Register("exit", "", CFGFLAG_CLIENT | CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds"); diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 84c33aa6d..3b110a3b2 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -400,6 +400,7 @@ public: static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData); static void Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData); + static void Con_DummyResetInput(IConsole::IResult *pResult, void *pUserData); static void Con_Quit(IConsole::IResult *pResult, void *pUserData); static void Con_DemoPlay(IConsole::IResult *pResult, void *pUserData); @@ -465,12 +466,11 @@ public: void HandleMapPath(const char *pPath); // gfx - void SwitchWindowScreen(int Index); - void ToggleFullscreen(); - void ToggleWindowBordered(); - void ToggleWindowVSync(); - void LoadFont(); - void Notify(const char *pTitle, const char *pMessage); + virtual void SwitchWindowScreen(int Index); + virtual void SetWindowParams(int FullscreenMode, bool IsBorderless); + virtual void ToggleWindowVSync(); + virtual void LoadFont(); + virtual void Notify(const char *pTitle, const char *pMessage); void BenchmarkQuit(int Seconds, const char *pFilename); // DDRace diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 94686eaf0..582d7fc6a 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -272,7 +272,8 @@ int CGraphics_Threaded::UnloadTexture(CTextureHandle Index) CCommandBuffer::SCommand_Texture_Destroy Cmd; Cmd.m_Slot = Index; - m_pCommandBuffer->AddCommand(Cmd); + AddCmd( + Cmd, [] { return true; }, "failed to unload texture."); m_TextureIndices[Index] = m_FirstFreeTexture; m_FirstFreeTexture = Index; @@ -325,13 +326,8 @@ int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y mem_copy(pTmpData, pData, MemSize); Cmd.m_pData = pTmpData; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - // kick command buffer and try again - KickCommandBuffer(); - m_pCommandBuffer->AddCommand(Cmd); - } + AddCmd( + Cmd, [] { return true; }, "failed to load raw sub texture."); return 0; } @@ -481,13 +477,8 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Heig mem_copy(pTmpData, pData, MemSize); Cmd.m_pData = pTmpData; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - // kick command buffer and try again - KickCommandBuffer(); - m_pCommandBuffer->AddCommand(Cmd); - } + AddCmd( + Cmd, [] { return true; }, "failed to load raw texture."); return CreateTextureHandle(Tex); } @@ -669,7 +660,8 @@ void CGraphics_Threaded::ScreenshotDirect() CCommandBuffer::SCommand_Screenshot Cmd; Cmd.m_pImage = &Image; - m_pCommandBuffer->AddCommand(Cmd); + AddCmd( + Cmd, [] { return true; }, "failed to take screenshot."); // kick the buffer and wait for the result KickCommandBuffer(); @@ -688,7 +680,7 @@ void CGraphics_Threaded::ScreenshotDirect() // save png char aBuf[256]; str_format(aBuf, sizeof(aBuf), "saved screenshot to '%s'", aWholePath); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, ColorRGBA{0.75f, 0.4f, 0.0f, 1.0f}); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, ColorRGBA{1.0f, 0.6f, 0.3f, 1.0f}); png_open_file_write(&Png, aWholePath); // ignore_convention png_set_data(&Png, Image.m_Width, Image.m_Height, 8, PNG_TRUECOLOR, (unsigned char *)Image.m_pData); // ignore_convention png_close_file(&Png); // ignore_convention @@ -710,7 +702,8 @@ void CGraphics_Threaded::Clear(float r, float g, float b) Cmd.m_Color.g = g; Cmd.m_Color.b = b; Cmd.m_Color.a = 0; - m_pCommandBuffer->AddCommand(Cmd); + AddCmd( + Cmd, [] { return true; }, "failed to clear graphics."); } void CGraphics_Threaded::QuadsBegin() @@ -1126,26 +1119,21 @@ void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, float *pColor Cmd.m_pIndicesOffsets = (char **)Data; Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet)); - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet); + if(Data == 0x0) + { + dbg_msg("graphics", "failed to allocate data for vertices"); + return false; + } + Cmd.m_pIndicesOffsets = (char **)Data; + Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet)); + return true; + }, + "failed to allocate memory for render command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet); - if(Data == 0x0) - { - dbg_msg("graphics", "failed to allocate data for vertices"); - return; - } - Cmd.m_pIndicesOffsets = (char **)Data; - Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet)); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render command"); - return; - } + return; } mem_copy(Cmd.m_pIndicesOffsets, pOffsets, sizeof(char *) * NumIndicesOffet); @@ -1174,16 +1162,10 @@ void CGraphics_Threaded::RenderBorderTiles(int BufferContainerIndex, float *pCol Cmd.m_Dir[1] = pDir[1]; // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for render command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render command"); - return; - } + return; } } @@ -1207,16 +1189,10 @@ void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, float * Cmd.m_Dir[1] = pDir[1]; // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for render command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render command"); - return; - } + return; } } @@ -1237,23 +1213,19 @@ void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderIn return; // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_pQuadInfo = (SQuadRenderInfo *)m_pCommandBuffer->AllocData(QuadNum * sizeof(SQuadRenderInfo)); + if(Cmd.m_pQuadInfo == 0x0) + { + dbg_msg("graphics", "failed to allocate data for the quad info"); + return false; + } + return true; + }, + "failed to allocate memory for render quad command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_pQuadInfo = (SQuadRenderInfo *)m_pCommandBuffer->AllocData(QuadNum * sizeof(SQuadRenderInfo)); - if(Cmd.m_pQuadInfo == 0x0) - { - dbg_msg("graphics", "failed to allocate data for the quad info"); - return; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render quad command"); - return; - } + return; } mem_copy(Cmd.m_pQuadInfo, pQuadInfo, sizeof(SQuadRenderInfo) * QuadNum); @@ -1274,17 +1246,10 @@ void CGraphics_Threaded::RenderText(int BufferContainerIndex, int TextQuadNum, i mem_copy(Cmd.m_aTextColor, pTextColor, sizeof(Cmd.m_aTextColor)); mem_copy(Cmd.m_aTextOutlineColor, pTextoutlineColor, sizeof(Cmd.m_aTextOutlineColor)); - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for render text command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render text command"); - return; - } + return; } } @@ -1490,17 +1455,10 @@ void CGraphics_Threaded::RenderQuadContainer(int ContainerIndex, int QuadOffset, Cmd.m_pOffset = (void *)(QuadOffset * 6 * sizeof(unsigned int)); Cmd.m_BufferContainerIndex = Container.m_QuadBufferContainerIndex; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for render quad container")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render quad container"); - return; - } + return; } } else @@ -1573,17 +1531,10 @@ void CGraphics_Threaded::RenderQuadContainerEx(int ContainerIndex, int QuadOffse Cmd.m_Center.x = Quad.m_aVertices[0].m_Pos.x + (Quad.m_aVertices[1].m_Pos.x - Quad.m_aVertices[0].m_Pos.x) / 2.f; Cmd.m_Center.y = Quad.m_aVertices[0].m_Pos.y + (Quad.m_aVertices[2].m_Pos.y - Quad.m_aVertices[0].m_Pos.y) / 2.f; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for render quad container extended")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render quad container extended"); - return; - } + return; } } else @@ -1713,24 +1664,19 @@ void CGraphics_Threaded::RenderQuadContainerAsSpriteMultiple(int ContainerIndex, } } - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount); + if(Cmd.m_pRenderInfo == 0x0) + { + dbg_msg("graphics", "failed to allocate data for render info"); + return false; + } + return true; + }, + "failed to allocate memory for render quad container sprite")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount); - if(Cmd.m_pRenderInfo == 0x0) - { - dbg_msg("graphics", "failed to allocate data for render info"); - return; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for render quad container sprite"); - return; - } + return; } mem_copy(Cmd.m_pRenderInfo, pRenderInfo, sizeof(IGraphics::SRenderSpriteInfo) * DrawCount); @@ -1788,16 +1734,10 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD { Cmd.m_pUploadData = pUploadData; - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for update buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for update buffer object command"); - return -1; - } + return -1; } } else @@ -1808,40 +1748,31 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD if(Cmd.m_pUploadData == NULL) return -1; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); + if(Cmd.m_pUploadData == 0x0) + { + dbg_msg("graphics", "failed to allocate data for upload data"); + return false; + } + return true; + }, + "failed to allocate memory for create buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); - if(Cmd.m_pUploadData == 0x0) - { - dbg_msg("graphics", "failed to allocate data for upload data"); - return -1; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for create buffer object command"); - return -1; - } + return -1; } + mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize); } else { Cmd.m_pUploadData = NULL; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for create buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for create buffer object command"); - return -1; - } + return -1; } // update the buffer instead @@ -1872,16 +1803,10 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData { Cmd.m_pUploadData = pUploadData; - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for recreate buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for recreate buffer object command"); - return; - } + return; } } else @@ -1892,24 +1817,19 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData if(Cmd.m_pUploadData == NULL) return; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); + if(Cmd.m_pUploadData == 0x0) + { + dbg_msg("graphics", "failed to allocate data for upload data"); + return false; + } + return true; + }, + "failed to allocate memory for recreate buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); - if(Cmd.m_pUploadData == 0x0) - { - dbg_msg("graphics", "failed to allocate data for upload data"); - return; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for recreate buffer object command"); - return; - } + return; } mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize); @@ -1917,16 +1837,11 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData else { Cmd.m_pUploadData = NULL; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for update buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for update buffer object command"); - return; - } + return; } // update the buffer instead @@ -1956,16 +1871,10 @@ void CGraphics_Threaded::UpdateBufferObject(int BufferIndex, size_t UploadDataSi { Cmd.m_pUploadData = pUploadData; - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for update buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for update buffer object command"); - return; - } + return; } } else @@ -1974,24 +1883,19 @@ void CGraphics_Threaded::UpdateBufferObject(int BufferIndex, size_t UploadDataSi if(Cmd.m_pUploadData == NULL) return; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); + if(Cmd.m_pUploadData == 0x0) + { + dbg_msg("graphics", "failed to allocate data for upload data"); + return false; + } + return true; + }, + "failed to allocate memory for update buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); - if(Cmd.m_pUploadData == 0x0) - { - dbg_msg("graphics", "failed to allocate data for upload data"); - return; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for update buffer object command"); - return; - } + return; } mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize); @@ -2007,17 +1911,10 @@ void CGraphics_Threaded::CopyBufferObject(int WriteBufferIndex, int ReadBufferIn Cmd.m_pReadOffset = ReadOffset; Cmd.m_CopySize = CopyDataSize; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for copy buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for copy buffer object command"); - return; - } + return; } } @@ -2029,17 +1926,10 @@ void CGraphics_Threaded::DeleteBufferObject(int BufferIndex) CCommandBuffer::SCommand_DeleteBufferObject Cmd; Cmd.m_BufferIndex = BufferIndex; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for delete buffer object command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for delete buffer object command"); - return; - } + return; } // also clear the buffer object index @@ -2071,24 +1961,19 @@ int CGraphics_Threaded::CreateBufferContainer(SBufferContainerInfo *pContainerIn if(Cmd.m_Attributes == NULL) return -1; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); + if(Cmd.m_Attributes == 0x0) + { + dbg_msg("graphics", "failed to allocate data for upload data"); + return false; + } + return true; + }, + "failed to allocate memory for create buffer container command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); - if(Cmd.m_Attributes == 0x0) - { - dbg_msg("graphics", "failed to allocate data for upload data"); - return -1; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for create buffer container command"); - return -1; - } + return -1; } mem_copy(Cmd.m_Attributes, &pContainerInfo->m_Attributes[0], Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); @@ -2107,17 +1992,10 @@ void CGraphics_Threaded::DeleteBufferContainer(int ContainerIndex, bool DestroyA Cmd.m_BufferContainerIndex = ContainerIndex; Cmd.m_DestroyAllBO = DestroyAllBO; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for delete buffer container command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for delete buffer container command"); - return; - } + return; } if(DestroyAllBO) @@ -2158,24 +2036,19 @@ void CGraphics_Threaded::UpdateBufferContainer(int ContainerIndex, SBufferContai if(Cmd.m_Attributes == NULL) return; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [&] { + Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); + if(Cmd.m_Attributes == 0x0) + { + dbg_msg("graphics", "failed to allocate data for upload data"); + return false; + } + return true; + }, + "failed to allocate memory for update buffer container command")) { - // kick command buffer and try again - KickCommandBuffer(); - - Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); - if(Cmd.m_Attributes == 0x0) - { - dbg_msg("graphics", "failed to allocate data for upload data"); - return; - } - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for update buffer container command"); - return; - } + return; } mem_copy(Cmd.m_Attributes, &pContainerInfo->m_Attributes[0], Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); @@ -2190,17 +2063,10 @@ void CGraphics_Threaded::IndicesNumRequiredNotify(unsigned int RequiredIndicesCo CCommandBuffer::SCommand_IndicesRequiredNumNotify Cmd; Cmd.m_RequiredIndicesNum = RequiredIndicesCount; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Cmd)) + if(!AddCmd( + Cmd, [] { return true; }, "failed to allocate memory for indcies required count notify command")) { - // kick command buffer and try again - KickCommandBuffer(); - - if(!m_pCommandBuffer->AddCommand(Cmd)) - { - dbg_msg("graphics", "failed to allocate memory for indcies required count notify command"); - return; - } + return; } } @@ -2210,8 +2076,10 @@ int CGraphics_Threaded::IssueInit() if(g_Config.m_GfxBorderless) Flags |= IGraphicsBackend::INITFLAG_BORDERLESS; - if(g_Config.m_GfxFullscreen) + if(g_Config.m_GfxFullscreen == 1) Flags |= IGraphicsBackend::INITFLAG_FULLSCREEN; + else if(g_Config.m_GfxFullscreen == 2) + Flags |= IGraphicsBackend::INITFLAG_DESKTOP_FULLSCREEN; if(g_Config.m_GfxVsync) Flags |= IGraphicsBackend::INITFLAG_VSYNC; if(g_Config.m_GfxHighdpi) @@ -2388,6 +2256,19 @@ int CGraphics_Threaded::Init() 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff}; m_InvalidTexture = LoadTextureRaw(4, 4, CImageInfo::FORMAT_RGBA, s_aNullTextureData, CImageInfo::FORMAT_RGBA, TEXLOAD_NORESAMPLE); + + ColorRGBA GPUInfoPrintColor{0.6f, 0.5f, 1.0f, 1.0f}; + + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "GPU vendor: %s", GetVendorString()); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor); + + str_format(aBuf, sizeof(aBuf), "GPU renderer: %s", GetRendererString()); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor); + + str_format(aBuf, sizeof(aBuf), "GPU version: %s", GetVersionString()); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor); + return 0; } @@ -2419,14 +2300,9 @@ void CGraphics_Threaded::Maximize() m_pBackend->Maximize(); } -bool CGraphics_Threaded::Fullscreen(bool State) +void CGraphics_Threaded::SetWindowParams(int FullscreenMode, bool IsBorderless) { - return m_pBackend->Fullscreen(State); -} - -void CGraphics_Threaded::SetWindowBordered(bool State) -{ - m_pBackend->SetWindowBordered(State); + m_pBackend->SetWindowParams(FullscreenMode, IsBorderless); } bool CGraphics_Threaded::SetWindowScreen(int Index) @@ -2461,7 +2337,12 @@ void CGraphics_Threaded::Resize(int w, int h, bool SetWindowSize) CCommandBuffer::SCommand_Resize Cmd; Cmd.m_Width = m_ScreenWidth; Cmd.m_Height = m_ScreenHeight; - m_pCommandBuffer->AddCommand(Cmd); + + if(!AddCmd( + Cmd, [] { return true; }, "failed to add resize command")) + { + return; + } // kick the command buffer KickCommandBuffer(); @@ -2538,7 +2419,11 @@ void CGraphics_Threaded::Swap() // add swap command CCommandBuffer::SCommand_Swap Cmd; Cmd.m_Finish = g_Config.m_GfxFinish; - m_pCommandBuffer->AddCommand(Cmd); + if(!AddCmd( + Cmd, [] { return true; }, "failed to add swap command")) + { + return; + } // kick the command buffer KickCommandBuffer(); @@ -2554,7 +2439,12 @@ bool CGraphics_Threaded::SetVSync(bool State) CCommandBuffer::SCommand_VSync Cmd; Cmd.m_VSync = State ? 1 : 0; Cmd.m_pRetOk = &RetOk; - m_pCommandBuffer->AddCommand(Cmd); + + if(!AddCmd( + Cmd, [] { return true; }, "failed to add vsync command")) + { + return false; + } // kick the command buffer KickCommandBuffer(); @@ -2567,7 +2457,12 @@ void CGraphics_Threaded::InsertSignal(CSemaphore *pSemaphore) { CCommandBuffer::SCommand_Signal Cmd; Cmd.m_pSemaphore = pSemaphore; - m_pCommandBuffer->AddCommand(Cmd); + + if(!AddCmd( + Cmd, [] { return true; }, "failed to add signal command")) + { + return; + } } bool CGraphics_Threaded::IsIdle() const @@ -2591,6 +2486,21 @@ SWarning *CGraphics_Threaded::GetCurWarning() } } +const char *CGraphics_Threaded::GetVendorString() +{ + return m_pBackend->GetVendorString(); +} + +const char *CGraphics_Threaded::GetVersionString() +{ + return m_pBackend->GetVersionString(); +} + +const char *CGraphics_Threaded::GetRendererString() +{ + return m_pBackend->GetRendererString(); +} + int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes, int Screen) { if(g_Config.m_GfxDisplayAllModes) @@ -2612,7 +2522,12 @@ int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes, int Scre Cmd.m_MaxModes = MaxModes; Cmd.m_pNumModes = &NumModes; Cmd.m_Screen = Screen; - m_pCommandBuffer->AddCommand(Cmd); + + if(!AddCmd( + Cmd, [] { return true; }, "failed to add video mode command")) + { + return 0; + } // kick the buffer and wait for the result and return it KickCommandBuffer(); diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index 83d4abfe0..5abccc871 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -581,7 +581,7 @@ public: } template - bool AddCommand(const T &Command) + bool AddCommandUnsafe(const T &Command) { // make sure that we don't do something stupid like ->AddCommand(&Cmd); (void)static_cast(&Command); @@ -640,6 +640,7 @@ public: INITFLAG_RESIZABLE = 1 << 2, INITFLAG_BORDERLESS = 1 << 3, INITFLAG_HIGHDPI = 1 << 4, + INITFLAG_DESKTOP_FULLSCREEN = 1 << 5, }; virtual ~IGraphicsBackend() {} @@ -653,8 +654,7 @@ public: virtual void Minimize() = 0; virtual void Maximize() = 0; - virtual bool Fullscreen(bool State) = 0; - virtual void SetWindowBordered(bool State) = 0; + virtual void SetWindowParams(int FullscreenMode, bool IsBorderless) = 0; virtual bool SetWindowScreen(int Index) = 0; virtual int GetWindowScreen() = 0; virtual int WindowActive() = 0; @@ -675,6 +675,10 @@ public: virtual bool HasQuadContainerBuffering() { return false; } virtual bool Has2DTextureArrays() { return false; } virtual const char *GetErrorString() { return NULL; } + + virtual const char *GetVendorString() = 0; + virtual const char *GetVersionString() = 0; + virtual const char *GetRendererString() = 0; }; class CGraphics_Threaded : public IEngineGraphics @@ -803,6 +807,26 @@ class CGraphics_Threaded : public IEngineGraphics } } + template + bool AddCmd(TName &Cmd, TFunc &&FailFunc, const char *pFailStr) + { + if(!m_pCommandBuffer->AddCommandUnsafe(Cmd)) + { + // kick command buffer and try again + KickCommandBuffer(); + + if(!FailFunc()) + return false; + + if(!m_pCommandBuffer->AddCommandUnsafe(Cmd)) + { + dbg_msg("graphics", "%s", pFailStr); + return false; + } + } + return true; + } + void KickCommandBuffer(); void AddBackEndWarningIfExists(); @@ -1065,24 +1089,20 @@ public: Command.m_PrimType = PrimType; Command.m_PrimCount = PrimCount; - // check if we have enough free memory in the commandbuffer - if(!m_pCommandBuffer->AddCommand(Command)) + 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")) { - // 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; - } - - if(!m_pCommandBuffer->AddCommand(Command)) - { - dbg_msg("graphics", "failed to allocate memory for render command"); - return; - } + return; } } @@ -1112,8 +1132,7 @@ public: int GetNumScreens() const override; void Minimize() override; void Maximize() override; - bool Fullscreen(bool State) override; - void SetWindowBordered(bool State) override; + void SetWindowParams(int FullscreenMode, bool IsBorderless) override; bool SetWindowScreen(int Index) override; void Resize(int w, int h, bool SetWindowSize = false) override; void AddWindowResizeListener(WINDOW_RESIZE_FUNC pFunc, void *pUser) override; @@ -1150,6 +1169,10 @@ public: bool IsTextBufferingEnabled() override { return m_OpenGLTextBufferingEnabled; } bool IsQuadContainerBufferingEnabled() override { return m_OpenGLQuadContainerBufferingEnabled; } bool HasTextureArrays() override { return m_OpenGLHasTextureArrays; } + + const char *GetVendorString() override; + const char *GetVersionString() override; + const char *GetRendererString() override; }; extern IGraphicsBackend *CreateGraphicsBackend(); diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index c369c2890..df253c5d4 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -89,8 +89,6 @@ void CInput::MouseModeRelative() m_InputGrabbed = 1; SDL_SetRelativeMouseMode(SDL_TRUE); Graphics()->SetWindowGrab(true); - // We should do this call to reset relative mouse position after alt+tab - SDL_GetRelativeMouseState(0x0, 0x0); } int CInput::MouseDoubleClick() @@ -336,6 +334,8 @@ int CInput::Update() MouseModeRelative(); m_MouseFocus = true; IgnoreKeys = true; + // We should do this call to reset relative mouse position after alt+tab + SDL_GetRelativeMouseState(0x0, 0x0); break; case SDL_WINDOWEVENT_FOCUS_LOST: m_MouseFocus = false; diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index a7e992118..6c8a73c01 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -330,15 +330,12 @@ int CServerBrowser::SortHash() const void SetFilteredPlayers(const CServerInfo &Item) { - if(g_Config.m_BrFilterSpectators) - Item.m_NumFilteredPlayers = Item.m_NumPlayers; - else - Item.m_NumFilteredPlayers = Item.m_NumClients; + Item.m_NumFilteredPlayers = g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients; if(g_Config.m_BrFilterConnectingPlayers) { for(const auto &Client : Item.m_aClients) { - if(str_comp(Client.m_aName, "(connecting)") == 0 && Client.m_aClan[0] == '\0' && Client.m_Country == -1 && Client.m_Score == 0) + if((!g_Config.m_BrFilterSpectators || Client.m_Player) && str_comp(Client.m_aName, "(connecting)") == 0 && Client.m_aClan[0] == '\0') Item.m_NumFilteredPlayers--; } } diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 0838ad6a3..9532915f9 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -196,8 +196,7 @@ public: int ScreenHeight() const { return m_ScreenHeight; } float ScreenAspect() const { return (float)ScreenWidth() / (float)ScreenHeight(); } - virtual bool Fullscreen(bool State) = 0; - virtual void SetWindowBordered(bool State) = 0; + virtual void SetWindowParams(int FullscreenMode, bool IsBorderless) = 0; virtual bool SetWindowScreen(int Index) = 0; virtual bool SetVSync(bool State) = 0; virtual int GetWindowScreen() = 0; @@ -276,6 +275,10 @@ public: virtual bool IsQuadContainerBufferingEnabled() = 0; virtual bool HasTextureArrays() = 0; + virtual const char *GetVendorString() = 0; + virtual const char *GetVersionString() = 0; + virtual const char *GetRendererString() = 0; + struct CLineItem { float m_X0, m_Y0, m_X1, m_Y1; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 03db2ea9d..c193f9113 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -100,10 +100,10 @@ MACRO_CONFIG_INT(GfxScreenWidth, gfx_screen_width, 0, 0, 0, CFGFLAG_SAVE | CFGFL MACRO_CONFIG_INT(GfxScreenHeight, gfx_screen_height, 0, 0, 0, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Screen resolution height") #if !defined(CONF_PLATFORM_MACOS) MACRO_CONFIG_INT(GfxBorderless, gfx_borderless, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Borderless window (not to be used with fullscreen)") -MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Fullscreen") +MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 1, 0, 2, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Set fullscreen mode: 0=no fullscreen, 1=pure fullscreen, 2=desktop fullscreen") #else MACRO_CONFIG_INT(GfxBorderless, gfx_borderless, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Borderless window (not to be used with fullscreen)") -MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Fullscreen") +MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 0, 0, 2, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Set fullscreen mode: 0=no fullscreen, 1=pure fullscreen, 2=desktop fullscreen") #endif MACRO_CONFIG_INT(GfxHighdpi, gfx_highdpi, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable high-dpi") MACRO_CONFIG_INT(GfxAlphabits, gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Alpha bits for framebuffer (fullscreen only)") @@ -230,7 +230,9 @@ MACRO_CONFIG_STR(SvScoreFolder, sv_score_folder, 32, "records", CFGFLAG_SERVER, MACRO_CONFIG_STR(SvRegionName, sv_region_name, 5, "UNK", CFGFLAG_SERVER, "Server region. Used for regional bans") MACRO_CONFIG_STR(SvSqlServerName, sv_sql_servername, 5, "UNK", CFGFLAG_SERVER, "SQL Server name that is inserted into record table") MACRO_CONFIG_INT(SvSaveGames, sv_savegames, 1, 0, 1, CFGFLAG_SERVER, "Enables savegames (/save and /load)") -MACRO_CONFIG_INT(SvSaveGamesDelay, sv_savegames_delay, 60, 0, 10000, CFGFLAG_SERVER, "Delay in seconds for loading a savegame") +MACRO_CONFIG_INT(SvSaveSwapGamesDelay, sv_saveswapgames_delay, 30, 0, 10000, CFGFLAG_SERVER, "Delay in seconds for loading a savegame or before swapping") +MACRO_CONFIG_INT(SvSaveSwapGamesPenalty, sv_saveswapgames_penalty, 60, 0, 10000, CFGFLAG_SERVER, "Penalty in seconds for saving or swapping position") +MACRO_CONFIG_INT(SvSwapTimeout, sv_swap_timeout, 30, 0, 10000, CFGFLAG_SERVER, "Timeout in seconds before option to swap expires") MACRO_CONFIG_INT(SvUseSQL, sv_use_sql, 0, 0, 1, CFGFLAG_SERVER, "Enables MySQL backend instead of SQLite backend (sv_sqlite_file is still used as fallback write server when no MySQL server is reachable)") MACRO_CONFIG_INT(SvSqlQueriesDelay, sv_sql_queries_delay, 1, 0, 20, CFGFLAG_SERVER, "Delay in seconds between SQL queries of a single player") MACRO_CONFIG_STR(SvSqliteFile, sv_sqlite_file, 64, "ddnet-server.sqlite", CFGFLAG_SERVER, "File to store ranks in case sv_use_sql is turned off or used as backup sql server") diff --git a/src/engine/shared/netban.h b/src/engine/shared/netban.h index 576ccd456..9d3d1b317 100644 --- a/src/engine/shared/netban.h +++ b/src/engine/shared/netban.h @@ -235,7 +235,7 @@ void CNetBan::MakeBanInfo(const CBan *pBan, char *pBuf, unsigned BuffSize, in str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason); } else - str_format(pBuf, BuffSize, "%s for life (%s)", aBuf, pBan->m_Info.m_aReason); + str_format(pBuf, BuffSize, "%s (%s)", aBuf, pBan->m_Info.m_aReason); } #endif diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index 569529057..c34d1b7a5 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -198,10 +198,6 @@ void CControls::OnConsoleInit() static CInputState s_State = {this, &m_ShowHookColl[0], &m_ShowHookColl[1]}; Console()->Register("+showhookcoll", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Show Hook Collision"); } - { - static CInputState s_State = {this, &m_ResetDummy[0], &m_ResetDummy[1]}; - Console()->Register("+resetdummy", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Reset Dummy"); - } { static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 1}; @@ -341,19 +337,6 @@ int CControls::SnapInput(int *pData) pDummyInput->m_Hook = g_Config.m_ClDummyHook; } - if(m_ResetDummy[g_Config.m_ClDummy]) - { - ResetInput(!g_Config.m_ClDummy); - m_InputData[!g_Config.m_ClDummy].m_Hook = 0; - - CNetObj_PlayerInput *pDummyInput = &m_pClient->m_DummyInput; - pDummyInput->m_Hook = m_InputData[!g_Config.m_ClDummy].m_Hook; - pDummyInput->m_Jump = m_InputData[!g_Config.m_ClDummy].m_Jump; - pDummyInput->m_Direction = m_InputData[!g_Config.m_ClDummy].m_Jump; - - pDummyInput->m_Fire = m_InputData[!g_Config.m_ClDummy].m_Fire; - } - // stress testing #ifdef CONF_DEBUG if(g_Config.m_DbgStress) diff --git a/src/game/client/components/controls.h b/src/game/client/components/controls.h index f23ae12f9..0c7ca2bbf 100644 --- a/src/game/client/components/controls.h +++ b/src/game/client/components/controls.h @@ -29,7 +29,6 @@ public: int m_InputDirectionLeft[NUM_DUMMIES]; int m_InputDirectionRight[NUM_DUMMIES]; int m_ShowHookColl[NUM_DUMMIES]; - int m_ResetDummy[NUM_DUMMIES]; int m_LastDummy; int m_OtherFire; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 156223f03..46702fca9 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1253,7 +1253,10 @@ int CMenus::RenderMenubar(CUIRect r) Box.VSplitLeft(4.0f, 0, &Box); static int s_CallVoteButton = 0; if(DoButton_MenuTab(&s_CallVoteButton, Localize("Call vote"), m_ActivePage == PAGE_CALLVOTE, &Button, CUI::CORNER_TR)) + { NewPage = PAGE_CALLVOTE; + m_ControlPageOpening = true; + } } TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT)); diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index fb10107ec..638e53044 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -220,6 +220,8 @@ class CMenus : public CComponent CListboxItem UiDoListboxNextRow(); int UiDoListboxEnd(float *pScrollValue, bool *pItemActivated, bool *pListBoxActive = 0); + int UiLogicGetCurrentClickedItem(); + //static void demolist_listdir_callback(const char *name, int is_dir, void *user); //static void demolist_list_callback(const CUIRect *rect, int index, void *user); @@ -349,6 +351,7 @@ protected: int m_CallvoteSelectedPlayer; char m_aCallvoteReason[VOTE_REASON_LENGTH]; char m_aFilterString[25]; + bool m_ControlPageOpening; // demo enum @@ -689,6 +692,8 @@ private: ColorHSLA RenderHSLColorPicker(const CUIRect *pRect, unsigned int *pColor, bool Alpha); ColorHSLA RenderHSLScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha = false); + int RenderDropDown(int &CurDropDownState, CUIRect *pRect, int CurSelection, const void **pIDs, const char **pStr, int PickNum, const void *pID, float &ScrollVal); + CServerProcess m_ServerProcess; }; #endif diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 691bb1583..5f13a81d5 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1066,14 +1066,14 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++) { - CListboxItem Item = UiDoListboxNextItem(&i); + CListboxItem Item = UiDoListboxNextItem(&pSelectedServer->m_aClients[i]); if(!Item.m_Visible) continue; CUIRect Name, Clan, Score, Flag; Item.m_Rect.HSplitTop(25.0f, &Name, &Item.m_Rect); - if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name)) + if(UiLogicGetCurrentClickedItem() == i) { if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER) m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index b8fbb780e..fa1b36934 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -620,7 +620,18 @@ CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected, CListboxItem Item = UiDoListboxNextRow(); - if(Item.m_Visible && UI()->DoButtonLogic(pId, "", gs_ListBoxSelectedIndex == gs_ListBoxItemIndex, &Item.m_HitRect)) + CUIRect HitRect = Item.m_HitRect; + + if(HitRect.y < gs_ListBoxOriginalView.y) + { + float TmpDiff = gs_ListBoxOriginalView.y - HitRect.y; + HitRect.y = gs_ListBoxOriginalView.y; + HitRect.h -= TmpDiff; + } + + HitRect.h = minimum(HitRect.h, (gs_ListBoxOriginalView.y + gs_ListBoxOriginalView.h) - HitRect.y); + + if(Item.m_Visible && UI()->DoButtonLogic(pId, "", gs_ListBoxSelectedIndex == gs_ListBoxItemIndex, &HitRect)) { gs_ListBoxClicked = true; gs_ListBoxNewSelected = ThisItemIndex; @@ -696,7 +707,7 @@ CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected, r.Margin(1.5f, &r); RenderTools()->DrawUIRect(&r, ColorRGBA(1, 1, 1, 0.5f), CUI::CORNER_ALL, 4.0f); } - else if(UI()->MouseInside(&Item.m_Rect) && !NoHoverEffects) + else if(UI()->MouseInside(&HitRect) && !NoHoverEffects) { CUIRect r = Item.m_Rect; r.Margin(1.5f, &r); @@ -718,6 +729,14 @@ int CMenus::UiDoListboxEnd(float *pScrollValue, bool *pItemActivated, bool *pLis return gs_ListBoxNewSelected; } +int CMenus::UiLogicGetCurrentClickedItem() +{ + if(gs_ListBoxClicked) + return gs_ListBoxNewSelected; + else + return -1; +} + int CMenus::DemolistFetchCallback(const char *pName, time_t Date, int IsDir, int StorageType, void *pUser) { CMenus *pSelf = (CMenus *)pUser; diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 2fc902fd8..6f0cf005b 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -667,8 +667,11 @@ void CMenus::RenderServerControl(CUIRect MainView) static int s_ClearButton = 0; static float Offset = 0.0f; //static char aFilterString[25]; - if(Input()->KeyPress(KEY_F) && (Input()->KeyIsPressed(KEY_LCTRL) || Input()->KeyIsPressed(KEY_RCTRL))) + if(m_ControlPageOpening || (Input()->KeyPress(KEY_F) && (Input()->KeyIsPressed(KEY_LCTRL) || Input()->KeyIsPressed(KEY_RCTRL)))) + { UI()->SetActiveItem(&m_aFilterString); + m_ControlPageOpening = false; + } if(DoClearableEditBox(&m_aFilterString, &s_ClearButton, &QuickSearch, m_aFilterString, sizeof(m_aFilterString), 14.0f, &Offset, false, CUI::CORNER_ALL, Localize("Search"))) { // TODO: Implement here diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index c29ba7a4c..f6080a07e 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -442,6 +442,21 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) } } +struct CUISkin +{ + const CSkin *m_pSkin; + + CUISkin() : + m_pSkin(nullptr) {} + CUISkin(const CSkin *pSkin) : + m_pSkin(pSkin) {} + + bool operator<(const CUISkin &Other) const { return str_comp_nocase(m_pSkin->m_aName, Other.m_pSkin->m_aName) < 0; } + + bool operator<(const char *pOther) const { return str_comp_nocase(m_pSkin->m_aName, pOther) < 0; } + bool operator==(const char *pOther) const { return !str_comp_nocase(m_pSkin->m_aName, pOther); } +}; + void CMenus::RenderSettingsTee(CUIRect MainView) { CUIRect Button, Label, Button2, Dummy, DummyLabel, SkinList, QuickSearch, QuickSearchClearButton, SkinDB, SkinPrefix, SkinPrefixLabel, DirectoryButton, RefreshButton; @@ -606,7 +621,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) // skin selector MainView.HSplitTop(20.0f, 0, &MainView); MainView.HSplitTop(230.0f, &SkinList, &MainView); - static sorted_array s_paSkinList; + static sorted_array s_paSkinList; static int s_SkinCount = 0; static float s_ScrollValue = 0.0f; if(s_InitSkinlist || m_pClient->m_pSkins->Num() != s_SkinCount) @@ -631,7 +646,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) if(s == 0) continue; - s_paSkinList.add_unsorted(s); + s_paSkinList.add(CUISkin(s)); } s_InitSkinlist = false; s_SkinCount = m_pClient->m_pSkins->Num(); @@ -641,12 +656,12 @@ void CMenus::RenderSettingsTee(CUIRect MainView) UiDoListboxStart(&s_InitSkinlist, &SkinList, 50.0f, Localize("Skins"), "", s_paSkinList.size(), 4, OldSelected, s_ScrollValue); for(int i = 0; i < s_paSkinList.size(); ++i) { - const CSkin *s = s_paSkinList[i]; + const CSkin *s = s_paSkinList[i].m_pSkin; if(str_comp(s->m_aName, Skin) == 0) OldSelected = i; - CListboxItem Item = UiDoListboxNextItem(s_paSkinList[i], OldSelected == i); + CListboxItem Item = UiDoListboxNextItem(s_paSkinList[i].m_pSkin, OldSelected == i); char aBuf[128]; if(Item.m_Visible) { @@ -680,7 +695,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); if(OldSelected != NewSelected) { - mem_copy(Skin, s_paSkinList[NewSelected]->m_aName, sizeof(g_Config.m_ClPlayerSkin)); + mem_copy(Skin, s_paSkinList[NewSelected].m_pSkin->m_aName, sizeof(g_Config.m_ClPlayerSkin)); SetNeedSendInfo(); } @@ -884,7 +899,9 @@ void CMenus::RenderSettingsControls(CUIRect MainView) static int s_SelectedControl = -1; static float s_ScrollValue = 0; int OldSelected = s_SelectedControl; - UiDoListboxStart(&s_ControlsList, &MainView, 500.0f, Localize("Controls"), "", 1, 1, s_SelectedControl, s_ScrollValue); + // Hacky values: Size of 10.0f per item for smoother scrolling, 72 elements + // fits the current size of controls settings + UiDoListboxStart(&s_ControlsList, &MainView, 10.0f, Localize("Controls"), "", 72, 1, s_SelectedControl, s_ScrollValue); CUIRect MovementSettings, WeaponSettings, VotingSettings, ChatSettings, DummySettings, MiscSettings, ResetButton; CListboxItem Item = UiDoListboxNextItem(&OldSelected, false, false, true); @@ -1012,6 +1029,43 @@ void CMenus::RenderSettingsControls(CUIRect MainView) UiDoListboxEnd(&s_ScrollValue, 0); } +int CMenus::RenderDropDown(int &CurDropDownState, CUIRect *pRect, int CurSelection, const void **pIDs, const char **pStr, int PickNum, const void *pID, float &ScrollVal) +{ + if(CurDropDownState != 0) + { + CUIRect ListRect; + pRect->HSplitTop(24.0f * PickNum, &ListRect, pRect); + char aBuf[1024]; + UiDoListboxStart(&pID, &ListRect, 24.0f, "", aBuf, PickNum, 1, CurSelection, ScrollVal); + for(int i = 0; i < PickNum; ++i) + { + CListboxItem Item = UiDoListboxNextItem(pIDs[i], CurSelection == i); + if(Item.m_Visible) + { + str_format(aBuf, sizeof(aBuf), "%s", pStr[i]); + UI()->DoLabelScaled(&Item.m_Rect, aBuf, 16.0f, 0); + } + } + bool ClickedItem = false; + int NewIndex = UiDoListboxEnd(&ScrollVal, NULL, &ClickedItem); + if(ClickedItem) + { + CurDropDownState = 0; + return NewIndex; + } + else + return CurSelection; + } + else + { + CUIRect Button; + pRect->HSplitTop(24.0f, &Button, pRect); + if(DoButton_MenuTab(&pID, CurSelection > -1 ? pStr[CurSelection] : "", 0, &Button, CUI::CORNER_ALL, NULL, NULL, NULL, NULL, 4.0f)) + CurDropDownState = 1; + return CurSelection; + } +} + void CMenus::RenderSettingsGraphics(CUIRect MainView) { CUIRect Button, Label; @@ -1080,16 +1134,28 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) } // switches - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_GfxBorderless, Localize("Borderless window"), g_Config.m_GfxBorderless, &Button)) - { - Client()->ToggleWindowBordered(); - } + static float s_ScrollValueDrop = 0; + static const int s_NumWindowMode = 4; + static int s_aWindowModeIDs[s_NumWindowMode]; + const void *aWindowModeIDs[s_NumWindowMode]; + for(int i = 0; i < s_NumWindowMode; ++i) + aWindowModeIDs[i] = &s_aWindowModeIDs[i]; + static int s_WindowModeDropDownState = 0; + const char *pWindowModes[] = {Localize("Windowed"), Localize("Windowed borderless"), Localize("Desktop fullscreen"), Localize("Fullscreen")}; - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_GfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button)) + OldSelected = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 3 : 2) : (g_Config.m_GfxBorderless ? 1 : 0)); + + const int NewWindowMode = RenderDropDown(s_WindowModeDropDownState, &MainView, OldSelected, aWindowModeIDs, pWindowModes, s_NumWindowMode, &s_NumWindowMode, s_ScrollValueDrop); + if(OldSelected != NewWindowMode) { - Client()->ToggleFullscreen(); + if(NewWindowMode == 0) + Client()->SetWindowParams(0, false); + else if(NewWindowMode == 1) + Client()->SetWindowParams(0, true); + else if(NewWindowMode == 2) + Client()->SetWindowParams(2, false); + else if(NewWindowMode == 3) + Client()->SetWindowParams(1, false); } MainView.HSplitTop(20.0f, &Button, &MainView); diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 4554540bb..3607f3feb 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -2992,6 +2992,21 @@ void CGameClient::ConchainMenuMap(IConsole::IResult *pResult, void *pUserData, I pfnCallback(pResult, pCallbackUserData); } +void CGameClient::DummyResetInput() +{ + if(!Client()->DummyConnected()) + return; + + if((m_DummyInput.m_Fire & 1) != 0) + m_DummyInput.m_Fire++; + + m_pControls->ResetInput(!g_Config.m_ClDummy); + m_pControls->m_InputData[!g_Config.m_ClDummy].m_Hook = 0; + m_pControls->m_InputData[!g_Config.m_ClDummy].m_Fire = m_DummyInput.m_Fire; + + m_DummyInput = m_pControls->m_InputData[!g_Config.m_ClDummy]; +} + bool CGameClient::CanDisplayWarning() { return m_pMenus->CanDisplayWarning(); diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 81ab6231b..dabc65565 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -451,6 +451,7 @@ public: CGameWorld m_PredictedWorld; CGameWorld m_PrevPredictedWorld; + void DummyResetInput(); void Echo(const char *pString); bool IsOtherTeam(int ClientID); bool CanDisplayWarning(); diff --git a/src/game/client/skin.h b/src/game/client/skin.h index 200b76b61..5b3050553 100644 --- a/src/game/client/skin.h +++ b/src/game/client/skin.h @@ -131,10 +131,10 @@ struct CSkin }; SSkinMetrics m_Metrics; - bool operator<(const CSkin &Other) const { return str_comp_nocase(m_aName, Other.m_aName) < 0; } + bool operator<(const CSkin &Other) const { return str_comp(m_aName, Other.m_aName) < 0; } - bool operator<(const char *pOther) const { return str_comp_nocase(m_aName, pOther) < 0; } - bool operator==(const char *pOther) const { return !str_comp_nocase(m_aName, pOther); } + bool operator<(const char *pOther) const { return str_comp(m_aName, pOther) < 0; } + bool operator==(const char *pOther) const { return !str_comp(m_aName, pOther); } }; #endif diff --git a/src/game/ddracechat.h b/src/game/ddracechat.h index 1d9999777..575db196e 100644 --- a/src/game/ddracechat.h +++ b/src/game/ddracechat.h @@ -27,11 +27,14 @@ CHAT_COMMAND("dnd", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, CHAT_COMMAND("mapinfo", "?r[map]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConMapInfo, this, "Show info about the map with name r gives (current map by default)") CHAT_COMMAND("timeout", "?s[code]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTimeout, this, "Set timeout protection code s") CHAT_COMMAND("practice", "?i['0'|'1']", CFGFLAG_CHAT | CFGFLAG_SERVER, ConPractice, this, "Enable cheats (currently only /rescue) for your current team's run, but you can't earn a rank") +CHAT_COMMAND("swap", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConSwap, this, "Request to swap your tee with another team member") CHAT_COMMAND("save", "?r[code]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConSave, this, "Save team with code r.") CHAT_COMMAND("load", "?r[code]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConLoad, this, "Load with code r. /load to check your existing saves") CHAT_COMMAND("map", "?r[map]", CFGFLAG_CHAT | CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, ConMap, this, "Vote a map by name") + CHAT_COMMAND("rankteam", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)") CHAT_COMMAND("teamrank", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)") + CHAT_COMMAND("rank", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConRank, this, "Shows the rank of player with name r (your rank by default)") CHAT_COMMAND("top5team", "?s[player name] ?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTeamTop5, this, "Shows five team ranks of the ladder or of a player beginning with rank i (1 by default, -1 for worst)") CHAT_COMMAND("teamtop5", "?s[player name] ?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTeamTop5, this, "Shows five team ranks of the ladder or of a player beginning with rank i (1 by default, -1 for worst)") diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp index 7e77ad179..a3f366cc7 100644 --- a/src/game/editor/auto_map.cpp +++ b/src/game/editor/auto_map.cpp @@ -397,25 +397,17 @@ void CAutoMapper::ProceedLocalized(CLayerTiles *pLayer, int ConfigID, int Seed, int UpdateToX = clamp(X + Width + 3 * pConf->m_EndX, 0, pLayer->m_Width); int UpdateToY = clamp(Y + Height + 3 * pConf->m_EndY, 0, pLayer->m_Height); - CLayerTiles *pUpdateLayer; - if(UpdateFromX != 0 || UpdateFromY != 0 || UpdateToX != pLayer->m_Width || UpdateToY != pLayer->m_Width) - { // Needs a layer to work on - pUpdateLayer = new CLayerTiles(UpdateToX - UpdateFromX, UpdateToY - UpdateFromY); + CLayerTiles *pUpdateLayer = new CLayerTiles(UpdateToX - UpdateFromX, UpdateToY - UpdateFromY); - for(int y = UpdateFromY; y < UpdateToY; y++) - { - for(int x = UpdateFromX; x < UpdateToX; x++) - { - CTile *in = &pLayer->m_pTiles[y * pLayer->m_Width + x]; - CTile *out = &pUpdateLayer->m_pTiles[(y - UpdateFromY) * pUpdateLayer->m_Width + x - UpdateFromX]; - out->m_Index = in->m_Index; - out->m_Flags = in->m_Flags; - } - } - } - else + for(int y = UpdateFromY; y < UpdateToY; y++) { - pUpdateLayer = pLayer; + for(int x = UpdateFromX; x < UpdateToX; x++) + { + CTile *in = &pLayer->m_pTiles[y * pLayer->m_Width + x]; + CTile *out = &pUpdateLayer->m_pTiles[(y - UpdateFromY) * pUpdateLayer->m_Width + x - UpdateFromX]; + out->m_Index = in->m_Index; + out->m_Flags = in->m_Flags; + } } Proceed(pUpdateLayer, ConfigID, Seed, UpdateFromX, UpdateFromY); @@ -431,8 +423,7 @@ void CAutoMapper::ProceedLocalized(CLayerTiles *pLayer, int ConfigID, int Seed, } } - if(pUpdateLayer) - delete pUpdateLayer; + delete pUpdateLayer; } void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID, int Seed, int SeedOffsetX, int SeedOffsetY) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 16c0f0c95..ffe49bb8e 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -3811,18 +3811,20 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View, void *pContext) pImg->m_External = 0; return 1; } + View.HSplitTop(5.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); } - else + else if(IsVanillaImage(pImg->m_aName)) { if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file.")) { pImg->m_External = 1; return 1; } + View.HSplitTop(5.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); } - View.HSplitTop(5.0f, &Slot, &View); - View.HSplitTop(12.0f, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one")) { pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace Image", "Replace", "mapres", "", ReplaceImage, pEditor); @@ -4038,7 +4040,15 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect View) static int s_PopupImageID = 0; if(Result == 2) - UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, 60, PopupImage); + { + CEditorImage *pImg = m_Map.m_lImages[m_SelectedImage]; + int Height; + if(pImg->m_External || IsVanillaImage(pImg->m_aName)) + Height = 60; + else + Height = 43; + UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, Height, PopupImage); + } } ToolBox.HSplitTop(2.0f, 0, &ToolBox); diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 8852d7297..58d23f75c 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -674,6 +674,81 @@ void CGameContext::ConPractice(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConSwap(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + const char *pName = pResult->GetString(0); + + if(!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if(!pPlayer) + return; + + if(pSelf->ProcessSpamProtection(pResult->m_ClientID)) + return; + + CGameTeams &Teams = ((CGameControllerDDRace *)pSelf->m_pController)->m_Teams; + + int Team = Teams.m_Core.Team(pResult->m_ClientID); + + if(Team < TEAM_FLOCK || (Team == TEAM_FLOCK && g_Config.m_SvTeam != 3) || Team >= TEAM_SUPER) + { + pSelf->Console()->Print( + IConsole::OUTPUT_LEVEL_STANDARD, + "print", + "Join a team to use swap feature, which means you can swap positions with each other."); + return; + } + + int TargetClientId = -1; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(pSelf->m_apPlayers[i] && !str_comp(pName, pSelf->Server()->ClientName(i))) + { + TargetClientId = i; + break; + } + } + + if(TargetClientId < 0) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "swap", "Player not found"); + return; + } + + if(TargetClientId == pResult->m_ClientID) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "swap", "Can't swap with yourself"); + return; + } + + int TargetTeam = Teams.m_Core.Team(TargetClientId); + if(TargetTeam != Team) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "swap", "Player is on a different team"); + return; + } + + if(!Teams.IsStarted(Team)) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "swap", "Need to have started the map to swap with a player."); + return; + } + + CPlayer *pSwapPlayer = pSelf->m_apPlayers[TargetClientId]; + + bool SwapPending = pSwapPlayer->m_SwapTargetsClientID != pResult->m_ClientID; + if(SwapPending) + { + Teams.RequestTeamSwap(pPlayer, pSwapPlayer, Team); + return; + } + + Teams.SwapTeamCharacters(pPlayer, pSwapPlayer, Team); +} + void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 8cc37ed1a..5c189552a 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -356,6 +356,7 @@ private: static void ConMapInfo(IConsole::IResult *pResult, void *pUserData); static void ConTimeout(IConsole::IResult *pResult, void *pUserData); static void ConPractice(IConsole::IResult *pResult, void *pUserData); + static void ConSwap(IConsole::IResult *pResult, void *pUserData); static void ConSave(IConsole::IResult *pResult, void *pUserData); static void ConLoad(IConsole::IResult *pResult, void *pUserData); static void ConMap(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 8ed2992de..3a851f3b9 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -138,6 +138,7 @@ void CPlayer::Reset() m_NotEligibleForFinish = false; m_EligibleForFinishCheck = 0; m_VotedForPractice = false; + m_SwapTargetsClientID = -1; } static int PlayerFlags_SevenToSix(int Flags) diff --git a/src/game/server/player.h b/src/game/server/player.h index c02178a9d..b579584ee 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -210,6 +210,7 @@ public: bool m_NotEligibleForFinish; int64 m_EligibleForFinishCheck; bool m_VotedForPractice; + int m_SwapTargetsClientID; //Client ID of the swap target for the given player }; #endif diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index 4d67152b5..d549ce92d 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -55,7 +55,7 @@ void CSaveTee::Save(CCharacter *pChr) m_TuneZoneOld = pChr->m_TuneZoneOld; if(pChr->m_StartTime) - m_Time = pChr->Server()->Tick() - pChr->m_StartTime; + m_Time = pChr->Server()->Tick() - pChr->m_StartTime + g_Config.m_SvSaveSwapGamesPenalty * pChr->Server()->TickSpeed(); else m_Time = 0; @@ -209,9 +209,6 @@ void CSaveTee::Load(CCharacter *pChr, int Team) char *CSaveTee::GetString(const CSaveTeam *pTeam) { - // Add time penalty of 60 seconds (only to the database) - int Time = m_Time + 60 * SERVER_TICK_SPEED; - int HookedPlayer = -1; if(m_HookedPlayer != -1) { @@ -268,7 +265,7 @@ char *CSaveTee::GetString(const CSaveTeam *pTeam) m_LastWeapon, m_QueuedWeapon, // tee states m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook, - m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, Time, + m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time, (int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y, m_TeleCheckpoint, m_LastPenalty, (int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y, diff --git a/src/game/server/score.cpp b/src/game/server/score.cpp index 0e01c9ec0..b73ff4928 100644 --- a/src/game/server/score.cpp +++ b/src/game/server/score.cpp @@ -718,7 +718,8 @@ bool CScore::SaveTeamScoreThread(IDbConnection *pSqlServer, const ISqlData *pGam pSqlServer->BindString(2, pData->m_GameUuid); pSqlServer->BindBlob(3, Teamrank.m_TeamID.m_aData, sizeof(Teamrank.m_TeamID.m_aData)); pSqlServer->Print(); - if(pSqlServer->Step(&End, pError, ErrorSize)) + int NumUpdated; + if(pSqlServer->ExecuteUpdate(&NumUpdated, pError, ErrorSize)) { return true; } @@ -843,18 +844,18 @@ bool CScore::ShowRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData if(str_comp_nocase(pData->m_RequestingPlayer, pData->m_Name) == 0) { str_format(pResult->m_Data.m_aaMessages[0], sizeof(pResult->m_Data.m_aaMessages[0]), - "%s Time: %s, better than %d%%", + "%s - %s - better than %d%%", pData->m_Name, aBuf, BetterThanPercent); } else { str_format(pResult->m_Data.m_aaMessages[0], sizeof(pResult->m_Data.m_aaMessages[0]), - "%s Time: %s, better than %d%%, requested by %s", + "%s - %s - better than %d%% - requested by %s", pData->m_Name, aBuf, BetterThanPercent, pData->m_RequestingPlayer); } str_format(pResult->m_Data.m_aaMessages[1], sizeof(pResult->m_Data.m_aaMessages[1]), - "Global rank %d || %s %s", + "Global rank %d - %s %s", Rank, pData->m_Server, aRegionalRank); } } @@ -996,7 +997,7 @@ bool CScore::ShowTopThread(IDbConnection *pSqlServer, const ISqlData *pGameData, } pSqlServer->BindString(1, pData->m_Map); pSqlServer->BindString(2, pAny); - pSqlServer->BindInt(3, 6); + pSqlServer->BindInt(3, 5); // show top int Line = 0; @@ -1023,11 +1024,6 @@ bool CScore::ShowTopThread(IDbConnection *pSqlServer, const ISqlData *pGameData, HasLocal = HasLocal || str_comp(aRecordServer, pData->m_Server) == 0; Line++; - - if(!HasLocal && Line == 4) - { - break; - } } if(!HasLocal) @@ -1873,7 +1869,7 @@ bool CScore::LoadTeamThread(IDbConnection *pSqlServer, const ISqlData *pGameData bool Found = false; for(int i = 0; i < pResult->m_SavedTeam.GetMembersCount(); i++) { - if(str_comp(pResult->m_SavedTeam.m_pSavedTees->GetName(), pData->m_RequestingPlayer) == 0) + if(str_comp(pResult->m_SavedTeam.m_pSavedTees[i].GetName(), pData->m_RequestingPlayer) == 0) { Found = true; break; @@ -1886,11 +1882,11 @@ bool CScore::LoadTeamThread(IDbConnection *pSqlServer, const ISqlData *pGameData } int Since = pSqlServer->GetInt(2); - if(Since < g_Config.m_SvSaveGamesDelay) + if(Since < g_Config.m_SvSaveSwapGamesDelay) { str_format(pResult->m_aMessage, sizeof(pResult->m_aMessage), "You have to wait %d seconds until you can load this savegame", - g_Config.m_SvSaveGamesDelay - Since); + g_Config.m_SvSaveSwapGamesDelay - Since); return false; } diff --git a/src/game/server/score.h b/src/game/server/score.h index f009c8805..8aa300162 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -31,7 +31,7 @@ struct CScorePlayerResult : ISqlResult enum { - MAX_MESSAGES = 8, + MAX_MESSAGES = 10, }; enum Variant diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index 27ceb70cb..e63dbb689 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -19,12 +19,31 @@ void CGameTeams::Reset() for(int i = 0; i < MAX_CLIENTS; ++i) { m_TeamState[i] = TEAMSTATE_EMPTY; + m_TeamLocked[i] = false; m_TeeFinished[i] = false; m_LastChat[i] = 0; - m_TeamLocked[i] = false; + m_pSaveTeamResult[i] = nullptr; + m_Invited[i] = 0; m_Practice[i] = false; - m_pSaveTeamResult[i] = nullptr; + m_LastSwap[i] = 0; + } +} + +void CGameTeams::ResetRoundState(int Team) +{ + ResetInvited(Team); + ResetSwitchers(Team); + m_LastSwap[Team] = 0; + + m_Practice[Team] = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) + { + GameServer()->m_apPlayers[i]->m_VotedForPractice = false; + GameServer()->m_apPlayers[i]->m_SwapTargetsClientID = -1; + } } } @@ -268,13 +287,23 @@ const char *CGameTeams::SetCharacterTeam(int ClientID, int Team) void CGameTeams::SetForceCharacterTeam(int ClientID, int Team) { - if(Team != m_Core.Team(ClientID)) - ForceLeaveTeam(ClientID); - else - m_TeeFinished[ClientID] = false; - + m_TeeFinished[ClientID] = false; int OldTeam = m_Core.Team(ClientID); + if(Team != OldTeam && (OldTeam != TEAM_FLOCK || g_Config.m_SvTeam == 3) && OldTeam != TEAM_SUPER && m_TeamState[OldTeam] != TEAMSTATE_EMPTY) + { + bool NoElseInOldTeam = Count(OldTeam) <= 1; + if(NoElseInOldTeam) + { + m_TeamState[OldTeam] = TEAMSTATE_EMPTY; + + // unlock team when last player leaves + SetTeamLock(OldTeam, false); + ResetRoundState(OldTeam); + // do not reset SaveTeamResult, because it should be logged into teehistorian even if the team leaves + } + } + m_Core.Team(ClientID, Team); if(OldTeam != Team) @@ -284,7 +313,10 @@ void CGameTeams::SetForceCharacterTeam(int ClientID, int Team) SendTeamsState(LoopClientID); if(GetPlayer(ClientID)) + { GetPlayer(ClientID)->m_VotedForPractice = false; + GetPlayer(ClientID)->m_SwapTargetsClientID = -1; + } } if(Team != TEAM_SUPER && (m_TeamState[Team] == TEAMSTATE_EMPTY || m_TeamLocked[Team])) @@ -296,32 +328,6 @@ void CGameTeams::SetForceCharacterTeam(int ClientID, int Team) } } -void CGameTeams::ForceLeaveTeam(int ClientID) -{ - m_TeeFinished[ClientID] = false; - - if((m_Core.Team(ClientID) != TEAM_FLOCK || g_Config.m_SvTeam == 3) && m_Core.Team(ClientID) != TEAM_SUPER && m_TeamState[m_Core.Team(ClientID)] != TEAMSTATE_EMPTY) - { - bool NoOneInOldTeam = true; - for(int i = 0; i < MAX_CLIENTS; ++i) - if(i != ClientID && m_Core.Team(ClientID) == m_Core.Team(i)) - { - NoOneInOldTeam = false; // all good exists someone in old team - break; - } - if(NoOneInOldTeam) - { - m_TeamState[m_Core.Team(ClientID)] = TEAMSTATE_EMPTY; - - // unlock team when last player leaves - SetTeamLock(m_Core.Team(ClientID), false); - ResetInvited(m_Core.Team(ClientID)); - m_Practice[m_Core.Team(ClientID)] = false; - // do not reset SaveTeamResult, because it should be logged into teehistorian even if the team leaves - } - } -} - int CGameTeams::Count(int Team) const { if(Team == TEAM_SUPER) @@ -675,6 +681,85 @@ void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp) } } +void CGameTeams::RequestTeamSwap(CPlayer *pPlayer, CPlayer *pTargetPlayer, int Team) +{ + if(!pPlayer || !pTargetPlayer) + return; + + char aBuf[512]; + if(pPlayer->m_SwapTargetsClientID == pTargetPlayer->GetCID()) + { + str_format(aBuf, sizeof(aBuf), + "%s has already requested to swap with %s.", + Server()->ClientName(pPlayer->GetCID()), Server()->ClientName(pTargetPlayer->GetCID())); + + GameServer()->SendChatTeam(Team, aBuf); + return; + } + + str_format(aBuf, sizeof(aBuf), + "%s has requested to swap with %s. Please wait %d seconds then type /swap %s.", + Server()->ClientName(pPlayer->GetCID()), Server()->ClientName(pTargetPlayer->GetCID()), g_Config.m_SvSaveSwapGamesDelay, Server()->ClientName(pPlayer->GetCID())); + + GameServer()->SendChatTeam(Team, aBuf); + + pPlayer->m_SwapTargetsClientID = pTargetPlayer->GetCID(); + m_LastSwap[Team] = Server()->Tick(); +} + +void CGameTeams::SwapTeamCharacters(CPlayer *pPlayer, CPlayer *pTargetPlayer, int Team) +{ + if(!pPlayer || !pTargetPlayer) + return; + + char aBuf[128]; + + int Since = (Server()->Tick() - m_LastSwap[Team]) / Server()->TickSpeed(); + if(Since < g_Config.m_SvSaveSwapGamesDelay) + { + str_format(aBuf, sizeof(aBuf), + "You have to wait %d seconds until you can swap.", + g_Config.m_SvSaveSwapGamesDelay - Since); + + GameServer()->SendChatTeam(Team, aBuf); + + return; + } + + int TimeoutAfterDelay = g_Config.m_SvSaveSwapGamesDelay + g_Config.m_SvSwapTimeout; + if(Since >= TimeoutAfterDelay) + { + str_format(aBuf, sizeof(aBuf), + "Your swap request timed out %d seconds ago. Use /swap again to re-initiate it.", + Since - g_Config.m_SvSwapTimeout); + + GameServer()->SendChatTeam(Team, aBuf); + + pPlayer->m_SwapTargetsClientID = -1; + pTargetPlayer->m_SwapTargetsClientID = -1; + + return; + } + + CSaveTee PrimarySavedTee; + PrimarySavedTee.Save(pPlayer->GetCharacter()); + + CSaveTee SecondarySavedTee; + SecondarySavedTee.Save(pTargetPlayer->GetCharacter()); + + PrimarySavedTee.Load(pTargetPlayer->GetCharacter(), Team); + SecondarySavedTee.Load(pPlayer->GetCharacter(), Team); + + pPlayer->m_SwapTargetsClientID = -1; + pTargetPlayer->m_SwapTargetsClientID = -1; + + str_format(aBuf, sizeof(aBuf), + "%s has swapped with %s.", + Server()->ClientName(pPlayer->GetCID()), Server()->ClientName(pTargetPlayer->GetCID())); + + GameServer()->SendChatTeam(Team, aBuf); +} + void CGameTeams::ProcessSaveTeam() { for(int Team = 0; Team < MAX_CLIENTS; Team++) @@ -780,9 +865,7 @@ void CGameTeams::OnCharacterDeath(int ClientID, int Weapon) if(g_Config.m_SvTeam == 3) { ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); - ResetSwitchers(Team); - m_Practice[Team] = false; - GameServer()->m_apPlayers[ClientID]->m_VotedForPractice = false; + ResetRoundState(Team); } else if(Locked) { @@ -859,8 +942,7 @@ void CGameTeams::ResetSavedTeam(int ClientID, int Team) if(g_Config.m_SvTeam == 3) { ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); - ResetSwitchers(Team); - m_Practice[Team] = false; + ResetRoundState(Team); } else { diff --git a/src/game/server/teams.h b/src/game/server/teams.h index cceda0354..cf1474954 100644 --- a/src/game/server/teams.h +++ b/src/game/server/teams.h @@ -17,6 +17,7 @@ class CGameTeams uint64 m_Invited[MAX_CLIENTS]; bool m_Practice[MAX_CLIENTS]; std::shared_ptr m_pSaveTeamResult[MAX_CLIENTS]; + uint64 m_LastSwap[MAX_CLIENTS]; class CGameContext *m_pGameContext; @@ -73,10 +74,9 @@ public: // need to be very careful using this method. SERIOUSLY... void SetForceCharacterTeam(int ClientID, int Team); - void SetForceCharacterNewTeam(int ClientID, int Team); - void ForceLeaveTeam(int ClientID); void Reset(); + void ResetRoundState(int Team); void ResetSwitchers(int Team); void SendTeamsState(int ClientID); @@ -94,6 +94,8 @@ public: void SetCpActive(CPlayer *Player, int CpActive); void KillSavedTeam(int ClientID, int Team); void ResetSavedTeam(int ClientID, int Team); + void RequestTeamSwap(CPlayer *pPlayer, CPlayer *pTargetPlayer, int Team); + void SwapTeamCharacters(CPlayer *pPlayer, CPlayer *pTargetPlayer, int Team); void ProcessSaveTeam(); bool TeeFinished(int ClientID) @@ -119,6 +121,11 @@ public: return m_Invited[Team] & 1LL << ClientID; } + bool IsStarted(int Team) + { + return m_TeamState[Team] == CGameTeams::TEAMSTATE_STARTED; + } + void SetFinished(int ClientID, bool finished) { m_TeeFinished[ClientID] = finished; diff --git a/src/game/variables.h b/src/game/variables.h index 36375c930..00e55826e 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -138,10 +138,10 @@ MACRO_CONFIG_INT(ClDummyRestoreWeapon, cl_dummy_restore_weapon, 1, 0, 1, CFGFLAG MACRO_CONFIG_INT(ClDummyCopyMoves, cl_dummy_copy_moves, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy should copy your moves") // more controlable dummy command -MACRO_CONFIG_INT(ClDummyControl, cl_dummy_control, 0, 0, 1, CFGFLAG_CLIENT, "Whether can you control dummy at the same time") -MACRO_CONFIG_INT(ClDummyJump, cl_dummy_jump, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is jumping") -MACRO_CONFIG_INT(ClDummyFire, cl_dummy_fire, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is firing") -MACRO_CONFIG_INT(ClDummyHook, cl_dummy_hook, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is hooking") +MACRO_CONFIG_INT(ClDummyControl, cl_dummy_control, 0, 0, 1, CFGFLAG_CLIENT, "Whether can you control dummy at the same time (cl_dummy_jump, cl_dummy_fire, cl_dummy_hook)") +MACRO_CONFIG_INT(ClDummyJump, cl_dummy_jump, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is jumping (requires cl_dummy_control 1)") +MACRO_CONFIG_INT(ClDummyFire, cl_dummy_fire, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is firing (requires cl_dummy_control 1)") +MACRO_CONFIG_INT(ClDummyHook, cl_dummy_hook, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is hooking (requires cl_dummy_control 1)") // start menu MACRO_CONFIG_INT(ClShowStartMenuImages, cl_show_start_menu_images, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show start menu images") diff --git a/src/game/version.h b/src/game/version.h index adf37c79c..a8f7628a2 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -3,11 +3,11 @@ #ifndef GAME_VERSION_H #define GAME_VERSION_H #ifndef GAME_RELEASE_VERSION -#define GAME_RELEASE_VERSION "15.3.2" +#define GAME_RELEASE_VERSION "15.4" #endif #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_NETVERSION "0.6 626fce9a778df4d4" #define GAME_NAME "DDNet" -#define CLIENT_VERSIONNR 15032 +#define CLIENT_VERSIONNR 15040 extern const char *GIT_SHORTREV_HASH; #endif