Merge pull request #6 from ddnet/master

MERGE
This commit is contained in:
kyle-bradley 2021-04-08 12:58:54 +02:00 committed by GitHub
commit 77f5826146
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 779 additions and 520 deletions

View file

@ -80,11 +80,11 @@ jobs:
cd debug cd debug
${{ matrix.cmake-path }}cmake --version ${{ 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 ${{ 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 - name: Test debug
run: | run: |
cd debug 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 ./DDNet-Server shutdown
- name: Build in release mode - name: Build in release mode
@ -93,11 +93,11 @@ jobs:
mkdir release mkdir release
cd 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 ${{ 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 - name: Test release
run: | run: |
cd release 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 ./DDNet-Server shutdown
- name: Build in release mode with debug info and all features on - name: Build in release mode with debug info and all features on
@ -107,18 +107,18 @@ jobs:
mkdir fancy mkdir fancy
cd fancy cd fancy
${{ matrix.cmake-path }}cmake ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DANTIBOT=ON -DMYSQL=ON -DWEBSOCKETS=ON .. ${{ 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 - name: Test fancy
if: matrix.fancy if: matrix.fancy
run: | run: |
cd release 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 ./DDNet-Server shutdown
- name: Package - name: Package
run: | run: |
cd release 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 mkdir artifacts
mv ${{ matrix.package-file }} artifacts mv ${{ matrix.package-file }} artifacts

View file

@ -2336,7 +2336,6 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION
set(CPACK_SOURCE_FILES set(CPACK_SOURCE_FILES
CMakeLists.txt CMakeLists.txt
README.md README.md
autoexec_server.cfg
cmake/ cmake/
data/ data/
datasrc/ datasrc/
@ -2380,7 +2379,6 @@ set(CPACK_DIRS data)
set(CPACK_FILES set(CPACK_FILES
license.txt license.txt
storage.cfg storage.cfg
autoexec_server.cfg
${COPY_FILES} ${COPY_FILES}
) )
if(TARGET_OS STREQUAL "windows") if(TARGET_OS STREQUAL "windows")

View file

@ -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: 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: 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). 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).

View file

@ -6,7 +6,7 @@ if(NOT CMAKE_CROSSCOMPILING)
pkg_check_modules(PC_SWSCALE libswscale) pkg_check_modules(PC_SWSCALE libswscale)
pkg_check_modules(PC_SWRESAMPLE libswresample) pkg_check_modules(PC_SWRESAMPLE libswresample)
if(TARGET_OS STREQUAL "linux") if(TARGET_OS STREQUAL "linux")
pkg_check_modules(PC_X264 libx264) pkg_search_module(PC_X264 libx264 x264)
endif() endif()
endif() endif()

View file

@ -37,6 +37,7 @@
<content_attribute id="social-chat">intense</content_attribute> <content_attribute id="social-chat">intense</content_attribute>
</content_rating> </content_rating>
<releases> <releases>
<release date="2021-03-25" version="15.4"/>
<release date="2021-02-20" version="15.3.2"/> <release date="2021-02-20" version="15.3.2"/>
<release date="2021-02-12" version="15.3.1"/> <release date="2021-02-12" version="15.3.1"/>
<release date="2021-02-11" version="15.3"/> <release date="2021-02-11" version="15.3"/>

View file

@ -4,6 +4,7 @@ import os
import shlex import shlex
import subprocess import subprocess
import tempfile import tempfile
import time
ConfigDmgtools = namedtuple('Config', 'dmg hfsplus newfs_hfs verbose') ConfigDmgtools = namedtuple('Config', 'dmg hfsplus newfs_hfs verbose')
ConfigHdiutil = namedtuple('Config', 'hdiutil verbose') ConfigHdiutil = namedtuple('Config', 'hdiutil verbose')
@ -72,7 +73,16 @@ class Hdiutil(Dmg):
def create(self, dmg, volume_name, directory, symlinks): def create(self, dmg, volume_name, directory, symlinks):
if symlinks: if symlinks:
raise NotImplementedError("symlinks are not yet implemented") raise NotImplementedError("symlinks are not yet implemented")
for i in range(5):
try:
self._hdiutil('create', '-volname', volume_name, '-srcdir', directory, dmg) 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(): def main():
p = argparse.ArgumentParser(description="Manipulate dmg archives") p = argparse.ArgumentParser(description="Manipulate dmg archives")

View file

@ -3017,8 +3017,8 @@ const char *str_utf8_find_nocase(const char *haystack, const char *needle)
int str_utf8_isspace(int code) int str_utf8_isspace(int code)
{ {
return code <= 0x0020 || code == 0x0085 || code == 0x00A0 || return code <= 0x0020 || code == 0x0085 || code == 0x00A0 || code == 0x034F ||
code == 0x034F || code == 0x1160 || code == 0x1680 || code == 0x180E || code == 0x115F || code == 0x1160 || code == 0x1680 || code == 0x180E ||
(code >= 0x2000 && code <= 0x200F) || (code >= 0x2028 && code <= 0x202F) || (code >= 0x2000 && code <= 0x200F) || (code >= 0x2028 && code <= 0x202F) ||
(code >= 0x205F && code <= 0x2064) || (code >= 0x206A && code <= 0x206F) || (code >= 0x205F && code <= 0x2064) || (code >= 0x206A && code <= 0x206F) ||
code == 0x2800 || code == 0x3000 || code == 0x3164 || code == 0x2800 || code == 0x3000 || code == 0x3164 ||

View file

@ -109,7 +109,7 @@ void sort_quick(R range)
template<class R> template<class R>
void sort(R range) void sort(R range)
{ {
std::sort(&range.front(), &range.back() + 1); std::stable_sort(&range.front(), &range.back() + 1);
} }
template<class R> template<class R>

View file

@ -125,8 +125,7 @@ public:
// gfx // gfx
virtual void SwitchWindowScreen(int Index) = 0; virtual void SwitchWindowScreen(int Index) = 0;
virtual void ToggleFullscreen() = 0; virtual void SetWindowParams(int FullscreenMode, bool IsBorderless) = 0;
virtual void ToggleWindowBordered() = 0;
virtual void ToggleWindowVSync() = 0; virtual void ToggleWindowVSync() = 0;
virtual void LoadFont() = 0; virtual void LoadFont() = 0;
virtual void Notify(const char *pTitle, const char *pMessage) = 0; virtual void Notify(const char *pTitle, const char *pMessage) = 0;
@ -257,6 +256,7 @@ public:
virtual const char *DDNetVersionStr() const = 0; virtual const char *DDNetVersionStr() const = 0;
virtual void OnDummyDisconnect() = 0; virtual void OnDummyDisconnect() = 0;
virtual void DummyResetInput() = 0;
virtual void Echo(const char *pString) = 0; virtual void Echo(const char *pString) = 0;
virtual bool CanDisplayWarning() = 0; virtual bool CanDisplayWarning() = 0;
virtual bool IsDisplayingWarning() = 0; virtual bool IsDisplayingWarning() = 0;

View file

@ -17,6 +17,8 @@
#include <cmath> #include <cmath>
#include <stdlib.h> #include <stdlib.h>
#include "SDL_hints.h"
#if defined(SDL_VIDEO_DRIVER_X11) #if defined(SDL_VIDEO_DRIVER_X11)
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
@ -3785,6 +3787,13 @@ void CCommandProcessorFragment_SDL::Cmd_Init(const SCommand_Init *pCommand)
// check what this context can do // check what this context can do
const char *pVersionString = (const char *)glGetString(GL_VERSION); const char *pVersionString = (const char *)glGetString(GL_VERSION);
dbg_msg("opengl", "Version string: %s", pVersionString); 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 // parse version string
ParseVersionString(pVersionString, pCommand->m_pCapabilities->m_ContextMajor, pCommand->m_pCapabilities->m_ContextMinor, pCommand->m_pCapabilities->m_ContextPatch); 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 else
SdlFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SdlFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} }
else if(Flags & (IGraphicsBackend::INITFLAG_DESKTOP_FULLSCREEN))
{
SdlFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
// set gl attributes // set gl attributes
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 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_GlewPatch = GlewPatch;
CmdSDL.m_pInitError = &InitError; CmdSDL.m_pInitError = &InitError;
CmdSDL.m_pErrStringPtr = &pErrorStr; 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); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); CmdBuffer.Reset();
@ -4626,7 +4642,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt
CmdOpenGL.m_pStorage = pStorage; CmdOpenGL.m_pStorage = pStorage;
CmdOpenGL.m_pCapabilities = &m_Capabilites; CmdOpenGL.m_pCapabilities = &m_Capabilites;
CmdOpenGL.m_pInitError = &InitError; CmdOpenGL.m_pInitError = &InitError;
CmdBuffer.AddCommand(CmdOpenGL); CmdBuffer.AddCommandUnsafe(CmdOpenGL);
RunBuffer(&CmdBuffer); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); CmdBuffer.Reset();
@ -4634,7 +4650,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt
if(InitError == -2) if(InitError == -2)
{ {
CCommandProcessorFragment_OpenGL::SCommand_Shutdown CmdGL; CCommandProcessorFragment_OpenGL::SCommand_Shutdown CmdGL;
CmdBuffer.AddCommand(CmdGL); CmdBuffer.AddCommandUnsafe(CmdGL);
RunBuffer(&CmdBuffer); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); CmdBuffer.Reset();
@ -4648,7 +4664,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Screen, int *pWidt
if(InitError != 0) if(InitError != 0)
{ {
CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd; CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd;
CmdBuffer.AddCommand(Cmd); CmdBuffer.AddCommandUnsafe(Cmd);
RunBuffer(&CmdBuffer); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); 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_Width = CurrentDisplayMode.w;
CmdSDL.m_Height = CurrentDisplayMode.h; CmdSDL.m_Height = CurrentDisplayMode.h;
CmdBuffer.AddCommand(CmdSDL); CmdBuffer.AddCommandUnsafe(CmdSDL);
RunBuffer(&CmdBuffer); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); CmdBuffer.Reset();
@ -4717,13 +4733,13 @@ int CGraphicsBackend_SDL_OpenGL::Shutdown()
// issue a shutdown command // issue a shutdown command
CCommandBuffer CmdBuffer(1024, 512); CCommandBuffer CmdBuffer(1024, 512);
CCommandProcessorFragment_OpenGL::SCommand_Shutdown CmdGL; CCommandProcessorFragment_OpenGL::SCommand_Shutdown CmdGL;
CmdBuffer.AddCommand(CmdGL); CmdBuffer.AddCommandUnsafe(CmdGL);
RunBuffer(&CmdBuffer); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); CmdBuffer.Reset();
CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd; CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd;
CmdBuffer.AddCommand(Cmd); CmdBuffer.AddCommandUnsafe(Cmd);
RunBuffer(&CmdBuffer); RunBuffer(&CmdBuffer);
WaitForIdle(); WaitForIdle();
CmdBuffer.Reset(); CmdBuffer.Reset();
@ -4755,18 +4771,28 @@ void CGraphicsBackend_SDL_OpenGL::Maximize()
// TODO: SDL // 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) #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 #else
return SDL_SetWindowFullscreen(m_pWindow, State ? SDL_WINDOW_FULLSCREEN : 0) == 0; SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN);
#endif #endif
} }
else if(FullscreenMode == 2)
void CGraphicsBackend_SDL_OpenGL::SetWindowBordered(bool State) {
{ SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_SetWindowBordered(m_pWindow, SDL_bool(State)); }
}
else
{
SDL_SetWindowFullscreen(m_pWindow, 0);
SDL_SetWindowBordered(m_pWindow, SDL_bool(!IsBorderless));
}
} }
bool CGraphicsBackend_SDL_OpenGL::SetWindowScreen(int Index) bool CGraphicsBackend_SDL_OpenGL::SetWindowScreen(int Index)

View file

@ -433,6 +433,10 @@ public:
int *m_pInitError; int *m_pInitError;
char *m_pVendorString;
char *m_pVersionString;
char *m_pRendererString;
int m_RequestedMajor; int m_RequestedMajor;
int m_RequestedMinor; int m_RequestedMinor;
int m_RequestedPatch; int m_RequestedPatch;
@ -486,6 +490,8 @@ public:
virtual void RunBuffer(CCommandBuffer *pBuffer); virtual void RunBuffer(CCommandBuffer *pBuffer);
}; };
static constexpr size_t gs_GPUInfoStringSize = 256;
// graphics backend implemented with SDL and OpenGL // graphics backend implemented with SDL and OpenGL
class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
{ {
@ -497,6 +503,10 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
SBackendCapabilites m_Capabilites; SBackendCapabilites m_Capabilites;
char m_aVendorString[gs_GPUInfoStringSize] = {};
char m_aVersionString[gs_GPUInfoStringSize] = {};
char m_aRendererString[gs_GPUInfoStringSize] = {};
bool m_UseNewOpenGL; bool m_UseNewOpenGL;
char m_aErrorString[256]; char m_aErrorString[256];
@ -511,8 +521,7 @@ public:
virtual void Minimize(); virtual void Minimize();
virtual void Maximize(); virtual void Maximize();
virtual bool Fullscreen(bool State); virtual void SetWindowParams(int FullscreenMode, bool IsBorderless);
virtual void SetWindowBordered(bool State); // on=true/off=false
virtual bool SetWindowScreen(int Index); virtual bool SetWindowScreen(int Index);
virtual int GetWindowScreen(); virtual int GetWindowScreen();
virtual int WindowActive(); virtual int WindowActive();
@ -536,6 +545,21 @@ public:
return NULL; 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 #endif // ENGINE_CLIENT_BACKEND_SDL_H

View file

@ -3486,6 +3486,12 @@ void CClient::Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData)
pSelf->DummyDisconnect(0); 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) void CClient::Con_Quit(IConsole::IResult *pResult, void *pUserData)
{ {
CClient *pSelf = (CClient *)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) // Todo SDL: remove this when fixed (changing screen when in fullscreen is bugged)
if(g_Config.m_GfxFullscreen) if(g_Config.m_GfxFullscreen)
{ {
ToggleFullscreen(); SetWindowParams(0, g_Config.m_GfxBorderless);
if(Graphics()->SetWindowScreen(Index)) if(Graphics()->SetWindowScreen(Index))
g_Config.m_GfxScreen = Index; g_Config.m_GfxScreen = Index;
ToggleFullscreen(); SetWindowParams(g_Config.m_GfxFullscreen, g_Config.m_GfxBorderless);
} }
else else
{ {
@ -4014,10 +4020,11 @@ void CClient::ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData,
pfnCallback(pResult, pCallbackUserData); 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 = clamp(FullscreenMode, 0, 2);
g_Config.m_GfxFullscreen ^= 1; g_Config.m_GfxBorderless = (int)IsBorderless;
Graphics()->SetWindowParams(FullscreenMode, IsBorderless);
} }
void CClient::ConchainFullscreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) 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(pSelf->Graphics() && pResult->NumArguments())
{ {
if(g_Config.m_GfxFullscreen != pResult->GetInteger(0)) if(g_Config.m_GfxFullscreen != pResult->GetInteger(0))
pSelf->ToggleFullscreen(); pSelf->SetWindowParams(pResult->GetInteger(0), g_Config.m_GfxBorderless);
} }
else else
pfnCallback(pResult, pCallbackUserData); 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) void CClient::ConchainWindowBordered(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{ {
CClient *pSelf = (CClient *)pUserData; CClient *pSelf = (CClient *)pUserData;
if(pSelf->Graphics() && pResult->NumArguments()) if(pSelf->Graphics() && pResult->NumArguments())
{ {
if(!g_Config.m_GfxFullscreen && (g_Config.m_GfxBorderless != pResult->GetInteger(0))) 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 else
pfnCallback(pResult, pCallbackUserData); pfnCallback(pResult, pCallbackUserData);
@ -4175,8 +4176,9 @@ void CClient::RegisterCommands()
m_pConsole->Register("stoprecord", "", CFGFLAG_SERVER, 0, 0, "Stop recording"); m_pConsole->Register("stoprecord", "", CFGFLAG_SERVER, 0, 0, "Stop recording");
m_pConsole->Register("reload", "", CFGFLAG_SERVER, 0, 0, "Reload the map"); 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_connect", "", CFGFLAG_CLIENT, Con_DummyConnect, this, "Connect dummy");
m_pConsole->Register("dummy_disconnect", "", CFGFLAG_CLIENT, Con_DummyDisconnect, this, "disconnect 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("quit", "", CFGFLAG_CLIENT | CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds");
m_pConsole->Register("exit", "", CFGFLAG_CLIENT | CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds"); m_pConsole->Register("exit", "", CFGFLAG_CLIENT | CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds");

View file

@ -400,6 +400,7 @@ public:
static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData); static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData);
static void Con_DummyDisconnect(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_Quit(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoPlay(IConsole::IResult *pResult, void *pUserData); static void Con_DemoPlay(IConsole::IResult *pResult, void *pUserData);
@ -465,12 +466,11 @@ public:
void HandleMapPath(const char *pPath); void HandleMapPath(const char *pPath);
// gfx // gfx
void SwitchWindowScreen(int Index); virtual void SwitchWindowScreen(int Index);
void ToggleFullscreen(); virtual void SetWindowParams(int FullscreenMode, bool IsBorderless);
void ToggleWindowBordered(); virtual void ToggleWindowVSync();
void ToggleWindowVSync(); virtual void LoadFont();
void LoadFont(); virtual void Notify(const char *pTitle, const char *pMessage);
void Notify(const char *pTitle, const char *pMessage);
void BenchmarkQuit(int Seconds, const char *pFilename); void BenchmarkQuit(int Seconds, const char *pFilename);
// DDRace // DDRace

View file

@ -272,7 +272,8 @@ int CGraphics_Threaded::UnloadTexture(CTextureHandle Index)
CCommandBuffer::SCommand_Texture_Destroy Cmd; CCommandBuffer::SCommand_Texture_Destroy Cmd;
Cmd.m_Slot = Index; Cmd.m_Slot = Index;
m_pCommandBuffer->AddCommand(Cmd); AddCmd(
Cmd, [] { return true; }, "failed to unload texture.");
m_TextureIndices[Index] = m_FirstFreeTexture; m_TextureIndices[Index] = m_FirstFreeTexture;
m_FirstFreeTexture = Index; m_FirstFreeTexture = Index;
@ -325,13 +326,8 @@ int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y
mem_copy(pTmpData, pData, MemSize); mem_copy(pTmpData, pData, MemSize);
Cmd.m_pData = pTmpData; Cmd.m_pData = pTmpData;
// check if we have enough free memory in the commandbuffer AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [] { return true; }, "failed to load raw sub texture.");
{
// kick command buffer and try again
KickCommandBuffer();
m_pCommandBuffer->AddCommand(Cmd);
}
return 0; return 0;
} }
@ -481,13 +477,8 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Heig
mem_copy(pTmpData, pData, MemSize); mem_copy(pTmpData, pData, MemSize);
Cmd.m_pData = pTmpData; Cmd.m_pData = pTmpData;
// check if we have enough free memory in the commandbuffer AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [] { return true; }, "failed to load raw texture.");
{
// kick command buffer and try again
KickCommandBuffer();
m_pCommandBuffer->AddCommand(Cmd);
}
return CreateTextureHandle(Tex); return CreateTextureHandle(Tex);
} }
@ -669,7 +660,8 @@ void CGraphics_Threaded::ScreenshotDirect()
CCommandBuffer::SCommand_Screenshot Cmd; CCommandBuffer::SCommand_Screenshot Cmd;
Cmd.m_pImage = &Image; Cmd.m_pImage = &Image;
m_pCommandBuffer->AddCommand(Cmd); AddCmd(
Cmd, [] { return true; }, "failed to take screenshot.");
// kick the buffer and wait for the result // kick the buffer and wait for the result
KickCommandBuffer(); KickCommandBuffer();
@ -688,7 +680,7 @@ void CGraphics_Threaded::ScreenshotDirect()
// save png // save png
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "saved screenshot to '%s'", aWholePath); 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_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_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 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.g = g;
Cmd.m_Color.b = b; Cmd.m_Color.b = b;
Cmd.m_Color.a = 0; Cmd.m_Color.a = 0;
m_pCommandBuffer->AddCommand(Cmd); AddCmd(
Cmd, [] { return true; }, "failed to clear graphics.");
} }
void CGraphics_Threaded::QuadsBegin() void CGraphics_Threaded::QuadsBegin()
@ -1126,27 +1119,22 @@ void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, float *pColor
Cmd.m_pIndicesOffsets = (char **)Data; Cmd.m_pIndicesOffsets = (char **)Data;
Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet)); Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet));
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet); Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet);
if(Data == 0x0) if(Data == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for vertices"); dbg_msg("graphics", "failed to allocate data for vertices");
return; return false;
} }
Cmd.m_pIndicesOffsets = (char **)Data; Cmd.m_pIndicesOffsets = (char **)Data;
Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet)); Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet));
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for render command"))
{ {
dbg_msg("graphics", "failed to allocate memory for render command");
return; return;
} }
}
mem_copy(Cmd.m_pIndicesOffsets, pOffsets, sizeof(char *) * NumIndicesOffet); mem_copy(Cmd.m_pIndicesOffsets, pOffsets, sizeof(char *) * NumIndicesOffet);
mem_copy(Cmd.m_pDrawCount, IndicedVertexDrawNum, sizeof(unsigned int) * NumIndicesOffet); mem_copy(Cmd.m_pDrawCount, IndicedVertexDrawNum, sizeof(unsigned int) * NumIndicesOffet);
@ -1174,17 +1162,11 @@ void CGraphics_Threaded::RenderBorderTiles(int BufferContainerIndex, float *pCol
Cmd.m_Dir[1] = pDir[1]; Cmd.m_Dir[1] = pDir[1];
// check if we have enough free memory in the commandbuffer // 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;
} }
}
} }
void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, unsigned int IndexDrawNum, unsigned int RedrawNum) void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, unsigned int IndexDrawNum, unsigned int RedrawNum)
@ -1207,17 +1189,11 @@ void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, float *
Cmd.m_Dir[1] = pDir[1]; Cmd.m_Dir[1] = pDir[1];
// check if we have enough free memory in the commandbuffer // 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;
} }
}
} }
void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset) void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset)
@ -1237,24 +1213,20 @@ void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderIn
return; return;
// check if we have enough free memory in the commandbuffer // check if we have enough free memory in the commandbuffer
if(!m_pCommandBuffer->AddCommand(Cmd)) if(!AddCmd(
{ Cmd, [&] {
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_pQuadInfo = (SQuadRenderInfo *)m_pCommandBuffer->AllocData(QuadNum * sizeof(SQuadRenderInfo)); Cmd.m_pQuadInfo = (SQuadRenderInfo *)m_pCommandBuffer->AllocData(QuadNum * sizeof(SQuadRenderInfo));
if(Cmd.m_pQuadInfo == 0x0) if(Cmd.m_pQuadInfo == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for the quad info"); dbg_msg("graphics", "failed to allocate data for the quad info");
return; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for render quad command"))
{ {
dbg_msg("graphics", "failed to allocate memory for render quad command");
return; return;
} }
}
mem_copy(Cmd.m_pQuadInfo, pQuadInfo, sizeof(SQuadRenderInfo) * QuadNum); mem_copy(Cmd.m_pQuadInfo, pQuadInfo, sizeof(SQuadRenderInfo) * QuadNum);
} }
@ -1274,18 +1246,11 @@ void CGraphics_Threaded::RenderText(int BufferContainerIndex, int TextQuadNum, i
mem_copy(Cmd.m_aTextColor, pTextColor, sizeof(Cmd.m_aTextColor)); mem_copy(Cmd.m_aTextColor, pTextColor, sizeof(Cmd.m_aTextColor));
mem_copy(Cmd.m_aTextOutlineColor, pTextoutlineColor, sizeof(Cmd.m_aTextOutlineColor)); mem_copy(Cmd.m_aTextOutlineColor, pTextoutlineColor, sizeof(Cmd.m_aTextOutlineColor));
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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;
} }
}
} }
int CGraphics_Threaded::CreateQuadContainer() int CGraphics_Threaded::CreateQuadContainer()
@ -1490,19 +1455,12 @@ void CGraphics_Threaded::RenderQuadContainer(int ContainerIndex, int QuadOffset,
Cmd.m_pOffset = (void *)(QuadOffset * 6 * sizeof(unsigned int)); Cmd.m_pOffset = (void *)(QuadOffset * 6 * sizeof(unsigned int));
Cmd.m_BufferContainerIndex = Container.m_QuadBufferContainerIndex; Cmd.m_BufferContainerIndex = Container.m_QuadBufferContainerIndex;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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 else
{ {
if(g_Config.m_GfxQuadAsTriangle) if(g_Config.m_GfxQuadAsTriangle)
@ -1573,19 +1531,12 @@ 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.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; 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(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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 else
{ {
if(g_Config.m_GfxQuadAsTriangle) if(g_Config.m_GfxQuadAsTriangle)
@ -1713,25 +1664,20 @@ void CGraphics_Threaded::RenderQuadContainerAsSpriteMultiple(int ContainerIndex,
} }
} }
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount); Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
if(Cmd.m_pRenderInfo == 0x0) if(Cmd.m_pRenderInfo == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for render info"); dbg_msg("graphics", "failed to allocate data for render info");
return; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for render quad container sprite"))
{ {
dbg_msg("graphics", "failed to allocate memory for render quad container sprite");
return; return;
} }
}
mem_copy(Cmd.m_pRenderInfo, pRenderInfo, sizeof(IGraphics::SRenderSpriteInfo) * DrawCount); mem_copy(Cmd.m_pRenderInfo, pRenderInfo, sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
WrapNormal(); WrapNormal();
@ -1788,18 +1734,12 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD
{ {
Cmd.m_pUploadData = pUploadData; 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 else
{ {
if(UploadDataSize <= CMD_BUFFER_DATA_BUFFER_SIZE) if(UploadDataSize <= CMD_BUFFER_DATA_BUFFER_SIZE)
@ -1808,41 +1748,32 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD
if(Cmd.m_pUploadData == NULL) if(Cmd.m_pUploadData == NULL)
return -1; return -1;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize);
if(Cmd.m_pUploadData == 0x0) if(Cmd.m_pUploadData == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for upload data"); dbg_msg("graphics", "failed to allocate data for upload data");
return false;
}
return true;
},
"failed to allocate memory for create buffer object command"))
{
return -1; return -1;
} }
if(!m_pCommandBuffer->AddCommand(Cmd))
{
dbg_msg("graphics", "failed to allocate memory for create buffer object command");
return -1;
}
}
mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize); mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize);
} }
else else
{ {
Cmd.m_pUploadData = NULL; 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 // update the buffer instead
size_t UploadDataOffset = 0; size_t UploadDataOffset = 0;
@ -1872,18 +1803,12 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData
{ {
Cmd.m_pUploadData = pUploadData; 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 else
{ {
if(UploadDataSize <= CMD_BUFFER_DATA_BUFFER_SIZE) if(UploadDataSize <= CMD_BUFFER_DATA_BUFFER_SIZE)
@ -1892,42 +1817,32 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData
if(Cmd.m_pUploadData == NULL) if(Cmd.m_pUploadData == NULL)
return; return;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize);
if(Cmd.m_pUploadData == 0x0) if(Cmd.m_pUploadData == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for upload data"); dbg_msg("graphics", "failed to allocate data for upload data");
return; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for recreate buffer object command"))
{ {
dbg_msg("graphics", "failed to allocate memory for recreate buffer object command");
return; return;
} }
}
mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize); mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize);
} }
else else
{ {
Cmd.m_pUploadData = NULL; 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 // update the buffer instead
size_t UploadDataOffset = 0; size_t UploadDataOffset = 0;
@ -1956,43 +1871,32 @@ void CGraphics_Threaded::UpdateBufferObject(int BufferIndex, size_t UploadDataSi
{ {
Cmd.m_pUploadData = pUploadData; 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 else
{ {
Cmd.m_pUploadData = AllocCommandBufferData(UploadDataSize); Cmd.m_pUploadData = AllocCommandBufferData(UploadDataSize);
if(Cmd.m_pUploadData == NULL) if(Cmd.m_pUploadData == NULL)
return; return;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize); Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize);
if(Cmd.m_pUploadData == 0x0) if(Cmd.m_pUploadData == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for upload data"); dbg_msg("graphics", "failed to allocate data for upload data");
return; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for update buffer object command"))
{ {
dbg_msg("graphics", "failed to allocate memory for update buffer object command");
return; return;
} }
}
mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize); mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize);
} }
@ -2007,18 +1911,11 @@ void CGraphics_Threaded::CopyBufferObject(int WriteBufferIndex, int ReadBufferIn
Cmd.m_pReadOffset = ReadOffset; Cmd.m_pReadOffset = ReadOffset;
Cmd.m_CopySize = CopyDataSize; Cmd.m_CopySize = CopyDataSize;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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;
} }
}
} }
void CGraphics_Threaded::DeleteBufferObject(int BufferIndex) void CGraphics_Threaded::DeleteBufferObject(int BufferIndex)
@ -2029,18 +1926,11 @@ void CGraphics_Threaded::DeleteBufferObject(int BufferIndex)
CCommandBuffer::SCommand_DeleteBufferObject Cmd; CCommandBuffer::SCommand_DeleteBufferObject Cmd;
Cmd.m_BufferIndex = BufferIndex; Cmd.m_BufferIndex = BufferIndex;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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 // also clear the buffer object index
m_BufferObjectIndices[BufferIndex] = m_FirstFreeBufferObjectIndex; m_BufferObjectIndices[BufferIndex] = m_FirstFreeBufferObjectIndex;
@ -2071,25 +1961,20 @@ int CGraphics_Threaded::CreateBufferContainer(SBufferContainerInfo *pContainerIn
if(Cmd.m_Attributes == NULL) if(Cmd.m_Attributes == NULL)
return -1; return -1;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
if(Cmd.m_Attributes == 0x0) if(Cmd.m_Attributes == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for upload data"); dbg_msg("graphics", "failed to allocate data for upload data");
return -1; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for create buffer container command"))
{ {
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)); mem_copy(Cmd.m_Attributes, &pContainerInfo->m_Attributes[0], Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
@ -2107,18 +1992,11 @@ void CGraphics_Threaded::DeleteBufferContainer(int ContainerIndex, bool DestroyA
Cmd.m_BufferContainerIndex = ContainerIndex; Cmd.m_BufferContainerIndex = ContainerIndex;
Cmd.m_DestroyAllBO = DestroyAllBO; Cmd.m_DestroyAllBO = DestroyAllBO;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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) if(DestroyAllBO)
{ {
@ -2158,25 +2036,20 @@ void CGraphics_Threaded::UpdateBufferContainer(int ContainerIndex, SBufferContai
if(Cmd.m_Attributes == NULL) if(Cmd.m_Attributes == NULL)
return; return;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) Cmd, [&] {
{
// kick command buffer and try again
KickCommandBuffer();
Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute)); Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
if(Cmd.m_Attributes == 0x0) if(Cmd.m_Attributes == 0x0)
{ {
dbg_msg("graphics", "failed to allocate data for upload data"); dbg_msg("graphics", "failed to allocate data for upload data");
return; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Cmd)) },
"failed to allocate memory for update buffer container command"))
{ {
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)); mem_copy(Cmd.m_Attributes, &pContainerInfo->m_Attributes[0], Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
@ -2190,18 +2063,11 @@ void CGraphics_Threaded::IndicesNumRequiredNotify(unsigned int RequiredIndicesCo
CCommandBuffer::SCommand_IndicesRequiredNumNotify Cmd; CCommandBuffer::SCommand_IndicesRequiredNumNotify Cmd;
Cmd.m_RequiredIndicesNum = RequiredIndicesCount; Cmd.m_RequiredIndicesNum = RequiredIndicesCount;
// check if we have enough free memory in the commandbuffer if(!AddCmd(
if(!m_pCommandBuffer->AddCommand(Cmd)) 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;
} }
}
} }
int CGraphics_Threaded::IssueInit() int CGraphics_Threaded::IssueInit()
@ -2210,8 +2076,10 @@ int CGraphics_Threaded::IssueInit()
if(g_Config.m_GfxBorderless) if(g_Config.m_GfxBorderless)
Flags |= IGraphicsBackend::INITFLAG_BORDERLESS; Flags |= IGraphicsBackend::INITFLAG_BORDERLESS;
if(g_Config.m_GfxFullscreen) if(g_Config.m_GfxFullscreen == 1)
Flags |= IGraphicsBackend::INITFLAG_FULLSCREEN; Flags |= IGraphicsBackend::INITFLAG_FULLSCREEN;
else if(g_Config.m_GfxFullscreen == 2)
Flags |= IGraphicsBackend::INITFLAG_DESKTOP_FULLSCREEN;
if(g_Config.m_GfxVsync) if(g_Config.m_GfxVsync)
Flags |= IGraphicsBackend::INITFLAG_VSYNC; Flags |= IGraphicsBackend::INITFLAG_VSYNC;
if(g_Config.m_GfxHighdpi) 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}; 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); 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; return 0;
} }
@ -2419,14 +2300,9 @@ void CGraphics_Threaded::Maximize()
m_pBackend->Maximize(); m_pBackend->Maximize();
} }
bool CGraphics_Threaded::Fullscreen(bool State) void CGraphics_Threaded::SetWindowParams(int FullscreenMode, bool IsBorderless)
{ {
return m_pBackend->Fullscreen(State); m_pBackend->SetWindowParams(FullscreenMode, IsBorderless);
}
void CGraphics_Threaded::SetWindowBordered(bool State)
{
m_pBackend->SetWindowBordered(State);
} }
bool CGraphics_Threaded::SetWindowScreen(int Index) bool CGraphics_Threaded::SetWindowScreen(int Index)
@ -2461,7 +2337,12 @@ void CGraphics_Threaded::Resize(int w, int h, bool SetWindowSize)
CCommandBuffer::SCommand_Resize Cmd; CCommandBuffer::SCommand_Resize Cmd;
Cmd.m_Width = m_ScreenWidth; Cmd.m_Width = m_ScreenWidth;
Cmd.m_Height = m_ScreenHeight; Cmd.m_Height = m_ScreenHeight;
m_pCommandBuffer->AddCommand(Cmd);
if(!AddCmd(
Cmd, [] { return true; }, "failed to add resize command"))
{
return;
}
// kick the command buffer // kick the command buffer
KickCommandBuffer(); KickCommandBuffer();
@ -2538,7 +2419,11 @@ void CGraphics_Threaded::Swap()
// add swap command // add swap command
CCommandBuffer::SCommand_Swap Cmd; CCommandBuffer::SCommand_Swap Cmd;
Cmd.m_Finish = g_Config.m_GfxFinish; 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 // kick the command buffer
KickCommandBuffer(); KickCommandBuffer();
@ -2554,7 +2439,12 @@ bool CGraphics_Threaded::SetVSync(bool State)
CCommandBuffer::SCommand_VSync Cmd; CCommandBuffer::SCommand_VSync Cmd;
Cmd.m_VSync = State ? 1 : 0; Cmd.m_VSync = State ? 1 : 0;
Cmd.m_pRetOk = &RetOk; Cmd.m_pRetOk = &RetOk;
m_pCommandBuffer->AddCommand(Cmd);
if(!AddCmd(
Cmd, [] { return true; }, "failed to add vsync command"))
{
return false;
}
// kick the command buffer // kick the command buffer
KickCommandBuffer(); KickCommandBuffer();
@ -2567,7 +2457,12 @@ void CGraphics_Threaded::InsertSignal(CSemaphore *pSemaphore)
{ {
CCommandBuffer::SCommand_Signal Cmd; CCommandBuffer::SCommand_Signal Cmd;
Cmd.m_pSemaphore = pSemaphore; Cmd.m_pSemaphore = pSemaphore;
m_pCommandBuffer->AddCommand(Cmd);
if(!AddCmd(
Cmd, [] { return true; }, "failed to add signal command"))
{
return;
}
} }
bool CGraphics_Threaded::IsIdle() const 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) int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes, int Screen)
{ {
if(g_Config.m_GfxDisplayAllModes) 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_MaxModes = MaxModes;
Cmd.m_pNumModes = &NumModes; Cmd.m_pNumModes = &NumModes;
Cmd.m_Screen = Screen; 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 // kick the buffer and wait for the result and return it
KickCommandBuffer(); KickCommandBuffer();

View file

@ -581,7 +581,7 @@ public:
} }
template<class T> template<class T>
bool AddCommand(const T &Command) bool AddCommandUnsafe(const T &Command)
{ {
// make sure that we don't do something stupid like ->AddCommand(&Cmd); // make sure that we don't do something stupid like ->AddCommand(&Cmd);
(void)static_cast<const SCommand *>(&Command); (void)static_cast<const SCommand *>(&Command);
@ -640,6 +640,7 @@ public:
INITFLAG_RESIZABLE = 1 << 2, INITFLAG_RESIZABLE = 1 << 2,
INITFLAG_BORDERLESS = 1 << 3, INITFLAG_BORDERLESS = 1 << 3,
INITFLAG_HIGHDPI = 1 << 4, INITFLAG_HIGHDPI = 1 << 4,
INITFLAG_DESKTOP_FULLSCREEN = 1 << 5,
}; };
virtual ~IGraphicsBackend() {} virtual ~IGraphicsBackend() {}
@ -653,8 +654,7 @@ public:
virtual void Minimize() = 0; virtual void Minimize() = 0;
virtual void Maximize() = 0; virtual void Maximize() = 0;
virtual bool Fullscreen(bool State) = 0; virtual void SetWindowParams(int FullscreenMode, bool IsBorderless) = 0;
virtual void SetWindowBordered(bool State) = 0;
virtual bool SetWindowScreen(int Index) = 0; virtual bool SetWindowScreen(int Index) = 0;
virtual int GetWindowScreen() = 0; virtual int GetWindowScreen() = 0;
virtual int WindowActive() = 0; virtual int WindowActive() = 0;
@ -675,6 +675,10 @@ public:
virtual bool HasQuadContainerBuffering() { return false; } virtual bool HasQuadContainerBuffering() { return false; }
virtual bool Has2DTextureArrays() { return false; } virtual bool Has2DTextureArrays() { return false; }
virtual const char *GetErrorString() { return NULL; } 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 class CGraphics_Threaded : public IEngineGraphics
@ -803,6 +807,26 @@ class CGraphics_Threaded : public IEngineGraphics
} }
} }
template<typename TName, typename TFunc>
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 KickCommandBuffer();
void AddBackEndWarningIfExists(); void AddBackEndWarningIfExists();
@ -1065,26 +1089,22 @@ public:
Command.m_PrimType = PrimType; Command.m_PrimType = PrimType;
Command.m_PrimCount = PrimCount; Command.m_PrimCount = PrimCount;
// check if we have enough free memory in the commandbuffer if(
if(!m_pCommandBuffer->AddCommand(Command)) !AddCmd(
{ Command, [&] {
// kick command buffer and try again
KickCommandBuffer();
Command.m_pVertices = (decltype(Command.m_pVertices))m_pCommandBuffer->AllocData(VertSize * NumVerts); Command.m_pVertices = (decltype(Command.m_pVertices))m_pCommandBuffer->AllocData(VertSize * NumVerts);
if(Command.m_pVertices == NULL) if(Command.m_pVertices == NULL)
{ {
dbg_msg("graphics", "failed to allocate data for vertices"); dbg_msg("graphics", "failed to allocate data for vertices");
return; return false;
} }
return true;
if(!m_pCommandBuffer->AddCommand(Command)) },
"failed to allocate memory for render command"))
{ {
dbg_msg("graphics", "failed to allocate memory for render command");
return; return;
} }
} }
}
void FlushVertices(bool KeepVertices = false) override; void FlushVertices(bool KeepVertices = false) override;
void FlushTextVertices(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) override; void FlushTextVertices(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) override;
@ -1112,8 +1132,7 @@ public:
int GetNumScreens() const override; int GetNumScreens() const override;
void Minimize() override; void Minimize() override;
void Maximize() override; void Maximize() override;
bool Fullscreen(bool State) override; void SetWindowParams(int FullscreenMode, bool IsBorderless) override;
void SetWindowBordered(bool State) override;
bool SetWindowScreen(int Index) override; bool SetWindowScreen(int Index) override;
void Resize(int w, int h, bool SetWindowSize = false) override; void Resize(int w, int h, bool SetWindowSize = false) override;
void AddWindowResizeListener(WINDOW_RESIZE_FUNC pFunc, void *pUser) override; void AddWindowResizeListener(WINDOW_RESIZE_FUNC pFunc, void *pUser) override;
@ -1150,6 +1169,10 @@ public:
bool IsTextBufferingEnabled() override { return m_OpenGLTextBufferingEnabled; } bool IsTextBufferingEnabled() override { return m_OpenGLTextBufferingEnabled; }
bool IsQuadContainerBufferingEnabled() override { return m_OpenGLQuadContainerBufferingEnabled; } bool IsQuadContainerBufferingEnabled() override { return m_OpenGLQuadContainerBufferingEnabled; }
bool HasTextureArrays() override { return m_OpenGLHasTextureArrays; } bool HasTextureArrays() override { return m_OpenGLHasTextureArrays; }
const char *GetVendorString() override;
const char *GetVersionString() override;
const char *GetRendererString() override;
}; };
extern IGraphicsBackend *CreateGraphicsBackend(); extern IGraphicsBackend *CreateGraphicsBackend();

View file

@ -89,8 +89,6 @@ void CInput::MouseModeRelative()
m_InputGrabbed = 1; m_InputGrabbed = 1;
SDL_SetRelativeMouseMode(SDL_TRUE); SDL_SetRelativeMouseMode(SDL_TRUE);
Graphics()->SetWindowGrab(true); Graphics()->SetWindowGrab(true);
// We should do this call to reset relative mouse position after alt+tab
SDL_GetRelativeMouseState(0x0, 0x0);
} }
int CInput::MouseDoubleClick() int CInput::MouseDoubleClick()
@ -336,6 +334,8 @@ int CInput::Update()
MouseModeRelative(); MouseModeRelative();
m_MouseFocus = true; m_MouseFocus = true;
IgnoreKeys = true; IgnoreKeys = true;
// We should do this call to reset relative mouse position after alt+tab
SDL_GetRelativeMouseState(0x0, 0x0);
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_LOST:
m_MouseFocus = false; m_MouseFocus = false;

View file

@ -330,15 +330,12 @@ int CServerBrowser::SortHash() const
void SetFilteredPlayers(const CServerInfo &Item) void SetFilteredPlayers(const CServerInfo &Item)
{ {
if(g_Config.m_BrFilterSpectators) Item.m_NumFilteredPlayers = g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients;
Item.m_NumFilteredPlayers = Item.m_NumPlayers;
else
Item.m_NumFilteredPlayers = Item.m_NumClients;
if(g_Config.m_BrFilterConnectingPlayers) if(g_Config.m_BrFilterConnectingPlayers)
{ {
for(const auto &Client : Item.m_aClients) 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--; Item.m_NumFilteredPlayers--;
} }
} }

View file

@ -196,8 +196,7 @@ public:
int ScreenHeight() const { return m_ScreenHeight; } int ScreenHeight() const { return m_ScreenHeight; }
float ScreenAspect() const { return (float)ScreenWidth() / (float)ScreenHeight(); } float ScreenAspect() const { return (float)ScreenWidth() / (float)ScreenHeight(); }
virtual bool Fullscreen(bool State) = 0; virtual void SetWindowParams(int FullscreenMode, bool IsBorderless) = 0;
virtual void SetWindowBordered(bool State) = 0;
virtual bool SetWindowScreen(int Index) = 0; virtual bool SetWindowScreen(int Index) = 0;
virtual bool SetVSync(bool State) = 0; virtual bool SetVSync(bool State) = 0;
virtual int GetWindowScreen() = 0; virtual int GetWindowScreen() = 0;
@ -276,6 +275,10 @@ public:
virtual bool IsQuadContainerBufferingEnabled() = 0; virtual bool IsQuadContainerBufferingEnabled() = 0;
virtual bool HasTextureArrays() = 0; virtual bool HasTextureArrays() = 0;
virtual const char *GetVendorString() = 0;
virtual const char *GetVersionString() = 0;
virtual const char *GetRendererString() = 0;
struct CLineItem struct CLineItem
{ {
float m_X0, m_Y0, m_X1, m_Y1; float m_X0, m_Y0, m_X1, m_Y1;

View file

@ -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") MACRO_CONFIG_INT(GfxScreenHeight, gfx_screen_height, 0, 0, 0, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Screen resolution height")
#if !defined(CONF_PLATFORM_MACOS) #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(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 #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(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 #endif
MACRO_CONFIG_INT(GfxHighdpi, gfx_highdpi, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable high-dpi") 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)") 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(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_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(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(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_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") 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")

View file

@ -235,7 +235,7 @@ void CNetBan::MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, in
str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason); str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason);
} }
else 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 #endif

View file

@ -198,10 +198,6 @@ void CControls::OnConsoleInit()
static CInputState s_State = {this, &m_ShowHookColl[0], &m_ShowHookColl[1]}; static CInputState s_State = {this, &m_ShowHookColl[0], &m_ShowHookColl[1]};
Console()->Register("+showhookcoll", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Show Hook Collision"); 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}; 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; 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 // stress testing
#ifdef CONF_DEBUG #ifdef CONF_DEBUG
if(g_Config.m_DbgStress) if(g_Config.m_DbgStress)

View file

@ -29,7 +29,6 @@ public:
int m_InputDirectionLeft[NUM_DUMMIES]; int m_InputDirectionLeft[NUM_DUMMIES];
int m_InputDirectionRight[NUM_DUMMIES]; int m_InputDirectionRight[NUM_DUMMIES];
int m_ShowHookColl[NUM_DUMMIES]; int m_ShowHookColl[NUM_DUMMIES];
int m_ResetDummy[NUM_DUMMIES];
int m_LastDummy; int m_LastDummy;
int m_OtherFire; int m_OtherFire;

View file

@ -1253,7 +1253,10 @@ int CMenus::RenderMenubar(CUIRect r)
Box.VSplitLeft(4.0f, 0, &Box); Box.VSplitLeft(4.0f, 0, &Box);
static int s_CallVoteButton = 0; static int s_CallVoteButton = 0;
if(DoButton_MenuTab(&s_CallVoteButton, Localize("Call vote"), m_ActivePage == PAGE_CALLVOTE, &Button, CUI::CORNER_TR)) if(DoButton_MenuTab(&s_CallVoteButton, Localize("Call vote"), m_ActivePage == PAGE_CALLVOTE, &Button, CUI::CORNER_TR))
{
NewPage = PAGE_CALLVOTE; NewPage = PAGE_CALLVOTE;
m_ControlPageOpening = true;
}
} }
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT)); TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));

View file

@ -220,6 +220,8 @@ class CMenus : public CComponent
CListboxItem UiDoListboxNextRow(); CListboxItem UiDoListboxNextRow();
int UiDoListboxEnd(float *pScrollValue, bool *pItemActivated, bool *pListBoxActive = 0); 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_listdir_callback(const char *name, int is_dir, void *user);
//static void demolist_list_callback(const CUIRect *rect, int index, void *user); //static void demolist_list_callback(const CUIRect *rect, int index, void *user);
@ -349,6 +351,7 @@ protected:
int m_CallvoteSelectedPlayer; int m_CallvoteSelectedPlayer;
char m_aCallvoteReason[VOTE_REASON_LENGTH]; char m_aCallvoteReason[VOTE_REASON_LENGTH];
char m_aFilterString[25]; char m_aFilterString[25];
bool m_ControlPageOpening;
// demo // demo
enum enum
@ -689,6 +692,8 @@ private:
ColorHSLA RenderHSLColorPicker(const CUIRect *pRect, unsigned int *pColor, bool Alpha); ColorHSLA RenderHSLColorPicker(const CUIRect *pRect, unsigned int *pColor, bool Alpha);
ColorHSLA RenderHSLScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha = false); 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; CServerProcess m_ServerProcess;
}; };
#endif #endif

View file

@ -1066,14 +1066,14 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++) for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++)
{ {
CListboxItem Item = UiDoListboxNextItem(&i); CListboxItem Item = UiDoListboxNextItem(&pSelectedServer->m_aClients[i]);
if(!Item.m_Visible) if(!Item.m_Visible)
continue; continue;
CUIRect Name, Clan, Score, Flag; CUIRect Name, Clan, Score, Flag;
Item.m_Rect.HSplitTop(25.0f, &Name, &Item.m_Rect); 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) 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); m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan);

View file

@ -620,7 +620,18 @@ CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected,
CListboxItem Item = UiDoListboxNextRow(); 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_ListBoxClicked = true;
gs_ListBoxNewSelected = ThisItemIndex; gs_ListBoxNewSelected = ThisItemIndex;
@ -696,7 +707,7 @@ CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected,
r.Margin(1.5f, &r); r.Margin(1.5f, &r);
RenderTools()->DrawUIRect(&r, ColorRGBA(1, 1, 1, 0.5f), CUI::CORNER_ALL, 4.0f); 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; CUIRect r = Item.m_Rect;
r.Margin(1.5f, &r); r.Margin(1.5f, &r);
@ -718,6 +729,14 @@ int CMenus::UiDoListboxEnd(float *pScrollValue, bool *pItemActivated, bool *pLis
return gs_ListBoxNewSelected; 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) int CMenus::DemolistFetchCallback(const char *pName, time_t Date, int IsDir, int StorageType, void *pUser)
{ {
CMenus *pSelf = (CMenus *)pUser; CMenus *pSelf = (CMenus *)pUser;

View file

@ -667,8 +667,11 @@ void CMenus::RenderServerControl(CUIRect MainView)
static int s_ClearButton = 0; static int s_ClearButton = 0;
static float Offset = 0.0f; static float Offset = 0.0f;
//static char aFilterString[25]; //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); 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"))) if(DoClearableEditBox(&m_aFilterString, &s_ClearButton, &QuickSearch, m_aFilterString, sizeof(m_aFilterString), 14.0f, &Offset, false, CUI::CORNER_ALL, Localize("Search")))
{ {
// TODO: Implement here // TODO: Implement here

View file

@ -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) void CMenus::RenderSettingsTee(CUIRect MainView)
{ {
CUIRect Button, Label, Button2, Dummy, DummyLabel, SkinList, QuickSearch, QuickSearchClearButton, SkinDB, SkinPrefix, SkinPrefixLabel, DirectoryButton, RefreshButton; 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 // skin selector
MainView.HSplitTop(20.0f, 0, &MainView); MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(230.0f, &SkinList, &MainView); MainView.HSplitTop(230.0f, &SkinList, &MainView);
static sorted_array<const CSkin *> s_paSkinList; static sorted_array<CUISkin> s_paSkinList;
static int s_SkinCount = 0; static int s_SkinCount = 0;
static float s_ScrollValue = 0.0f; static float s_ScrollValue = 0.0f;
if(s_InitSkinlist || m_pClient->m_pSkins->Num() != s_SkinCount) if(s_InitSkinlist || m_pClient->m_pSkins->Num() != s_SkinCount)
@ -631,7 +646,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
if(s == 0) if(s == 0)
continue; continue;
s_paSkinList.add_unsorted(s); s_paSkinList.add(CUISkin(s));
} }
s_InitSkinlist = false; s_InitSkinlist = false;
s_SkinCount = m_pClient->m_pSkins->Num(); 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); UiDoListboxStart(&s_InitSkinlist, &SkinList, 50.0f, Localize("Skins"), "", s_paSkinList.size(), 4, OldSelected, s_ScrollValue);
for(int i = 0; i < s_paSkinList.size(); ++i) 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) if(str_comp(s->m_aName, Skin) == 0)
OldSelected = i; OldSelected = i;
CListboxItem Item = UiDoListboxNextItem(s_paSkinList[i], OldSelected == i); CListboxItem Item = UiDoListboxNextItem(s_paSkinList[i].m_pSkin, OldSelected == i);
char aBuf[128]; char aBuf[128];
if(Item.m_Visible) if(Item.m_Visible)
{ {
@ -680,7 +695,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0);
if(OldSelected != NewSelected) 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(); SetNeedSendInfo();
} }
@ -884,7 +899,9 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
static int s_SelectedControl = -1; static int s_SelectedControl = -1;
static float s_ScrollValue = 0; static float s_ScrollValue = 0;
int OldSelected = s_SelectedControl; 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; CUIRect MovementSettings, WeaponSettings, VotingSettings, ChatSettings, DummySettings, MiscSettings, ResetButton;
CListboxItem Item = UiDoListboxNextItem(&OldSelected, false, false, true); CListboxItem Item = UiDoListboxNextItem(&OldSelected, false, false, true);
@ -1012,6 +1029,43 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
UiDoListboxEnd(&s_ScrollValue, 0); 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) void CMenus::RenderSettingsGraphics(CUIRect MainView)
{ {
CUIRect Button, Label; CUIRect Button, Label;
@ -1080,16 +1134,28 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
} }
// switches // switches
MainView.HSplitTop(20.0f, &Button, &MainView); static float s_ScrollValueDrop = 0;
if(DoButton_CheckBox(&g_Config.m_GfxBorderless, Localize("Borderless window"), g_Config.m_GfxBorderless, &Button)) static const int s_NumWindowMode = 4;
{ static int s_aWindowModeIDs[s_NumWindowMode];
Client()->ToggleWindowBordered(); 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); OldSelected = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 3 : 2) : (g_Config.m_GfxBorderless ? 1 : 0));
if(DoButton_CheckBox(&g_Config.m_GfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button))
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); MainView.HSplitTop(20.0f, &Button, &MainView);

View file

@ -2992,6 +2992,21 @@ void CGameClient::ConchainMenuMap(IConsole::IResult *pResult, void *pUserData, I
pfnCallback(pResult, pCallbackUserData); 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() bool CGameClient::CanDisplayWarning()
{ {
return m_pMenus->CanDisplayWarning(); return m_pMenus->CanDisplayWarning();

View file

@ -451,6 +451,7 @@ public:
CGameWorld m_PredictedWorld; CGameWorld m_PredictedWorld;
CGameWorld m_PrevPredictedWorld; CGameWorld m_PrevPredictedWorld;
void DummyResetInput();
void Echo(const char *pString); void Echo(const char *pString);
bool IsOtherTeam(int ClientID); bool IsOtherTeam(int ClientID);
bool CanDisplayWarning(); bool CanDisplayWarning();

View file

@ -131,10 +131,10 @@ struct CSkin
}; };
SSkinMetrics m_Metrics; 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(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); }
}; };
#endif #endif

View file

@ -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("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("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("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("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("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("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("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("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("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("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)") 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)")

View file

@ -397,10 +397,7 @@ void CAutoMapper::ProceedLocalized(CLayerTiles *pLayer, int ConfigID, int Seed,
int UpdateToX = clamp(X + Width + 3 * pConf->m_EndX, 0, pLayer->m_Width); 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); int UpdateToY = clamp(Y + Height + 3 * pConf->m_EndY, 0, pLayer->m_Height);
CLayerTiles *pUpdateLayer; CLayerTiles *pUpdateLayer = new CLayerTiles(UpdateToX - UpdateFromX, UpdateToY - UpdateFromY);
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);
for(int y = UpdateFromY; y < UpdateToY; y++) for(int y = UpdateFromY; y < UpdateToY; y++)
{ {
@ -412,11 +409,6 @@ void CAutoMapper::ProceedLocalized(CLayerTiles *pLayer, int ConfigID, int Seed,
out->m_Flags = in->m_Flags; out->m_Flags = in->m_Flags;
} }
} }
}
else
{
pUpdateLayer = pLayer;
}
Proceed(pUpdateLayer, ConfigID, Seed, UpdateFromX, UpdateFromY); Proceed(pUpdateLayer, ConfigID, Seed, UpdateFromX, UpdateFromY);
@ -431,7 +423,6 @@ void CAutoMapper::ProceedLocalized(CLayerTiles *pLayer, int ConfigID, int Seed,
} }
} }
if(pUpdateLayer)
delete pUpdateLayer; delete pUpdateLayer;
} }

View file

@ -3811,18 +3811,20 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View, void *pContext)
pImg->m_External = 0; pImg->m_External = 0;
return 1; 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.")) if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file."))
{ {
pImg->m_External = 1; pImg->m_External = 1;
return 1; return 1;
} }
}
View.HSplitTop(5.0f, &Slot, &View); View.HSplitTop(5.0f, &Slot, &View);
View.HSplitTop(12.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")) 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); 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; static int s_PopupImageID = 0;
if(Result == 2) 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); ToolBox.HSplitTop(2.0f, 0, &ToolBox);

View file

@ -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) void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData)
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;

View file

@ -356,6 +356,7 @@ private:
static void ConMapInfo(IConsole::IResult *pResult, void *pUserData); static void ConMapInfo(IConsole::IResult *pResult, void *pUserData);
static void ConTimeout(IConsole::IResult *pResult, void *pUserData); static void ConTimeout(IConsole::IResult *pResult, void *pUserData);
static void ConPractice(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 ConSave(IConsole::IResult *pResult, void *pUserData);
static void ConLoad(IConsole::IResult *pResult, void *pUserData); static void ConLoad(IConsole::IResult *pResult, void *pUserData);
static void ConMap(IConsole::IResult *pResult, void *pUserData); static void ConMap(IConsole::IResult *pResult, void *pUserData);

View file

@ -138,6 +138,7 @@ void CPlayer::Reset()
m_NotEligibleForFinish = false; m_NotEligibleForFinish = false;
m_EligibleForFinishCheck = 0; m_EligibleForFinishCheck = 0;
m_VotedForPractice = false; m_VotedForPractice = false;
m_SwapTargetsClientID = -1;
} }
static int PlayerFlags_SevenToSix(int Flags) static int PlayerFlags_SevenToSix(int Flags)

View file

@ -210,6 +210,7 @@ public:
bool m_NotEligibleForFinish; bool m_NotEligibleForFinish;
int64 m_EligibleForFinishCheck; int64 m_EligibleForFinishCheck;
bool m_VotedForPractice; bool m_VotedForPractice;
int m_SwapTargetsClientID; //Client ID of the swap target for the given player
}; };
#endif #endif

View file

@ -55,7 +55,7 @@ void CSaveTee::Save(CCharacter *pChr)
m_TuneZoneOld = pChr->m_TuneZoneOld; m_TuneZoneOld = pChr->m_TuneZoneOld;
if(pChr->m_StartTime) 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 else
m_Time = 0; m_Time = 0;
@ -209,9 +209,6 @@ void CSaveTee::Load(CCharacter *pChr, int Team)
char *CSaveTee::GetString(const CSaveTeam *pTeam) 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; int HookedPlayer = -1;
if(m_HookedPlayer != -1) if(m_HookedPlayer != -1)
{ {
@ -268,7 +265,7 @@ char *CSaveTee::GetString(const CSaveTeam *pTeam)
m_LastWeapon, m_QueuedWeapon, m_LastWeapon, m_QueuedWeapon,
// tee states // tee states
m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook, 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, (int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y,
m_TeleCheckpoint, m_LastPenalty, m_TeleCheckpoint, m_LastPenalty,
(int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y, (int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y,

View file

@ -718,7 +718,8 @@ bool CScore::SaveTeamScoreThread(IDbConnection *pSqlServer, const ISqlData *pGam
pSqlServer->BindString(2, pData->m_GameUuid); pSqlServer->BindString(2, pData->m_GameUuid);
pSqlServer->BindBlob(3, Teamrank.m_TeamID.m_aData, sizeof(Teamrank.m_TeamID.m_aData)); pSqlServer->BindBlob(3, Teamrank.m_TeamID.m_aData, sizeof(Teamrank.m_TeamID.m_aData));
pSqlServer->Print(); pSqlServer->Print();
if(pSqlServer->Step(&End, pError, ErrorSize)) int NumUpdated;
if(pSqlServer->ExecuteUpdate(&NumUpdated, pError, ErrorSize))
{ {
return true; 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) 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]), 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); pData->m_Name, aBuf, BetterThanPercent);
} }
else else
{ {
str_format(pResult->m_Data.m_aaMessages[0], sizeof(pResult->m_Data.m_aaMessages[0]), 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); pData->m_Name, aBuf, BetterThanPercent, pData->m_RequestingPlayer);
} }
str_format(pResult->m_Data.m_aaMessages[1], sizeof(pResult->m_Data.m_aaMessages[1]), 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); 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(1, pData->m_Map);
pSqlServer->BindString(2, pAny); pSqlServer->BindString(2, pAny);
pSqlServer->BindInt(3, 6); pSqlServer->BindInt(3, 5);
// show top // show top
int Line = 0; int Line = 0;
@ -1023,11 +1024,6 @@ bool CScore::ShowTopThread(IDbConnection *pSqlServer, const ISqlData *pGameData,
HasLocal = HasLocal || str_comp(aRecordServer, pData->m_Server) == 0; HasLocal = HasLocal || str_comp(aRecordServer, pData->m_Server) == 0;
Line++; Line++;
if(!HasLocal && Line == 4)
{
break;
}
} }
if(!HasLocal) if(!HasLocal)
@ -1873,7 +1869,7 @@ bool CScore::LoadTeamThread(IDbConnection *pSqlServer, const ISqlData *pGameData
bool Found = false; bool Found = false;
for(int i = 0; i < pResult->m_SavedTeam.GetMembersCount(); i++) 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; Found = true;
break; break;
@ -1886,11 +1882,11 @@ bool CScore::LoadTeamThread(IDbConnection *pSqlServer, const ISqlData *pGameData
} }
int Since = pSqlServer->GetInt(2); 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), str_format(pResult->m_aMessage, sizeof(pResult->m_aMessage),
"You have to wait %d seconds until you can load this savegame", "You have to wait %d seconds until you can load this savegame",
g_Config.m_SvSaveGamesDelay - Since); g_Config.m_SvSaveSwapGamesDelay - Since);
return false; return false;
} }

View file

@ -31,7 +31,7 @@ struct CScorePlayerResult : ISqlResult
enum enum
{ {
MAX_MESSAGES = 8, MAX_MESSAGES = 10,
}; };
enum Variant enum Variant

View file

@ -19,12 +19,31 @@ void CGameTeams::Reset()
for(int i = 0; i < MAX_CLIENTS; ++i) for(int i = 0; i < MAX_CLIENTS; ++i)
{ {
m_TeamState[i] = TEAMSTATE_EMPTY; m_TeamState[i] = TEAMSTATE_EMPTY;
m_TeamLocked[i] = false;
m_TeeFinished[i] = false; m_TeeFinished[i] = false;
m_LastChat[i] = 0; m_LastChat[i] = 0;
m_TeamLocked[i] = false; m_pSaveTeamResult[i] = nullptr;
m_Invited[i] = 0; m_Invited[i] = 0;
m_Practice[i] = false; 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) 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); 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); m_Core.Team(ClientID, Team);
if(OldTeam != Team) if(OldTeam != Team)
@ -284,7 +313,10 @@ void CGameTeams::SetForceCharacterTeam(int ClientID, int Team)
SendTeamsState(LoopClientID); SendTeamsState(LoopClientID);
if(GetPlayer(ClientID)) if(GetPlayer(ClientID))
{
GetPlayer(ClientID)->m_VotedForPractice = false; GetPlayer(ClientID)->m_VotedForPractice = false;
GetPlayer(ClientID)->m_SwapTargetsClientID = -1;
}
} }
if(Team != TEAM_SUPER && (m_TeamState[Team] == TEAMSTATE_EMPTY || m_TeamLocked[Team])) 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 int CGameTeams::Count(int Team) const
{ {
if(Team == TEAM_SUPER) 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() void CGameTeams::ProcessSaveTeam()
{ {
for(int Team = 0; Team < MAX_CLIENTS; Team++) 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) if(g_Config.m_SvTeam == 3)
{ {
ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN);
ResetSwitchers(Team); ResetRoundState(Team);
m_Practice[Team] = false;
GameServer()->m_apPlayers[ClientID]->m_VotedForPractice = false;
} }
else if(Locked) else if(Locked)
{ {
@ -859,8 +942,7 @@ void CGameTeams::ResetSavedTeam(int ClientID, int Team)
if(g_Config.m_SvTeam == 3) if(g_Config.m_SvTeam == 3)
{ {
ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN);
ResetSwitchers(Team); ResetRoundState(Team);
m_Practice[Team] = false;
} }
else else
{ {

View file

@ -17,6 +17,7 @@ class CGameTeams
uint64 m_Invited[MAX_CLIENTS]; uint64 m_Invited[MAX_CLIENTS];
bool m_Practice[MAX_CLIENTS]; bool m_Practice[MAX_CLIENTS];
std::shared_ptr<CScoreSaveResult> m_pSaveTeamResult[MAX_CLIENTS]; std::shared_ptr<CScoreSaveResult> m_pSaveTeamResult[MAX_CLIENTS];
uint64 m_LastSwap[MAX_CLIENTS];
class CGameContext *m_pGameContext; class CGameContext *m_pGameContext;
@ -73,10 +74,9 @@ public:
// need to be very careful using this method. SERIOUSLY... // need to be very careful using this method. SERIOUSLY...
void SetForceCharacterTeam(int ClientID, int Team); void SetForceCharacterTeam(int ClientID, int Team);
void SetForceCharacterNewTeam(int ClientID, int Team);
void ForceLeaveTeam(int ClientID);
void Reset(); void Reset();
void ResetRoundState(int Team);
void ResetSwitchers(int Team); void ResetSwitchers(int Team);
void SendTeamsState(int ClientID); void SendTeamsState(int ClientID);
@ -94,6 +94,8 @@ public:
void SetCpActive(CPlayer *Player, int CpActive); void SetCpActive(CPlayer *Player, int CpActive);
void KillSavedTeam(int ClientID, int Team); void KillSavedTeam(int ClientID, int Team);
void ResetSavedTeam(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(); void ProcessSaveTeam();
bool TeeFinished(int ClientID) bool TeeFinished(int ClientID)
@ -119,6 +121,11 @@ public:
return m_Invited[Team] & 1LL << ClientID; return m_Invited[Team] & 1LL << ClientID;
} }
bool IsStarted(int Team)
{
return m_TeamState[Team] == CGameTeams::TEAMSTATE_STARTED;
}
void SetFinished(int ClientID, bool finished) void SetFinished(int ClientID, bool finished)
{ {
m_TeeFinished[ClientID] = finished; m_TeeFinished[ClientID] = finished;

View file

@ -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") MACRO_CONFIG_INT(ClDummyCopyMoves, cl_dummy_copy_moves, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy should copy your moves")
// more controlable dummy command // 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(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") 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") 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") MACRO_CONFIG_INT(ClDummyHook, cl_dummy_hook, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is hooking (requires cl_dummy_control 1)")
// start menu // start menu
MACRO_CONFIG_INT(ClShowStartMenuImages, cl_show_start_menu_images, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show start menu images") MACRO_CONFIG_INT(ClShowStartMenuImages, cl_show_start_menu_images, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show start menu images")

View file

@ -3,11 +3,11 @@
#ifndef GAME_VERSION_H #ifndef GAME_VERSION_H
#define GAME_VERSION_H #define GAME_VERSION_H
#ifndef GAME_RELEASE_VERSION #ifndef GAME_RELEASE_VERSION
#define GAME_RELEASE_VERSION "15.3.2" #define GAME_RELEASE_VERSION "15.4"
#endif #endif
#define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION
#define GAME_NETVERSION "0.6 626fce9a778df4d4" #define GAME_NETVERSION "0.6 626fce9a778df4d4"
#define GAME_NAME "DDNet" #define GAME_NAME "DDNet"
#define CLIENT_VERSIONNR 15032 #define CLIENT_VERSIONNR 15040
extern const char *GIT_SHORTREV_HASH; extern const char *GIT_SHORTREV_HASH;
#endif #endif