From 8f8c9a20c0f64900e44553580c3496640a685d71 Mon Sep 17 00:00:00 2001 From: furo Date: Tue, 12 Sep 2023 17:19:24 +0200 Subject: [PATCH 01/70] Add a popup for when video render is done. --- src/game/client/components/menus.cpp | 48 +++++++++++++++++++++++ src/game/client/components/menus.h | 1 + src/game/client/components/menus_demo.cpp | 7 ++++ 3 files changed, 56 insertions(+) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 5d074e5d2ad..90de9ab6b94 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1222,6 +1222,10 @@ int CMenus::Render() { pTitle = Localize("Render demo"); } + else if(m_Popup == POPUP_RENDER_DONE) + { + pTitle = Localize("Render complete"); + } #endif else if(m_Popup == POPUP_PASSWORD) { @@ -1677,6 +1681,50 @@ int CMenus::Render() UI()->DoLabel(&Label, Localize("Video name:"), 18.0f, TEXTALIGN_ML); UI()->DoEditBox(&m_DemoRenderInput, &TextBox, 12.0f); } + else if(m_Popup == POPUP_RENDER_DONE) + { + CUIRect TextBox, Ok, OpenFolder; + + char aFilePath[IO_MAX_PATH_LENGTH]; + char aSaveFolder[IO_MAX_PATH_LENGTH]; + Storage()->GetCompletePath(Storage()->TYPE_SAVE, "videos", aSaveFolder, sizeof(aSaveFolder)); + str_format(aFilePath, sizeof(aFilePath), "%s/%s.mp4", aSaveFolder, m_DemoRenderInput.GetString()); + + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Part.VMargin(80.0f, &Part); + + Part.VSplitMid(&OpenFolder, &Ok); + + Ok.VMargin(20.0f, &Ok); + OpenFolder.VMargin(20.0f, &OpenFolder); + + static CButtonContainer s_ButtonOpenFolder; + if(DoButton_Menu(&s_ButtonOpenFolder, Localize("Videos directory"), 0, &OpenFolder)) + { + if(!open_file(aSaveFolder)) + { + dbg_msg("menus", "couldn't open file '%s'", aBuf); + } + } + + static CButtonContainer s_ButtonOk; + if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &Ok) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER)) + { + m_Popup = POPUP_NONE; + m_DemoRenderInput.Clear(); + } + + Box.HSplitBottom(160.f, &Box, &Part); + + Part.VSplitLeft(60.0f, 0, &TextBox); + TextBox.VSplitLeft(20.0f, 0, &TextBox); + TextBox.VSplitRight(60.0f, &TextBox, 0); + + str_format(aBuf, sizeof(aBuf), Localize("Video was saved to '%s'"), aFilePath); + + UI()->DoLabel(&TextBox, aBuf, 18.0f, TEXTALIGN_LEFT); + } #endif else if(m_Popup == POPUP_FIRST_LAUNCH) { diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index dcf154d654c..ee0a28f73c8 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -652,6 +652,7 @@ class CMenus : public CComponent POPUP_LANGUAGE, POPUP_RENAME_DEMO, POPUP_RENDER_DEMO, + POPUP_RENDER_DONE, POPUP_PASSWORD, POPUP_QUIT, POPUP_WARNING, diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 87bb6caf403..1025308826e 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -986,6 +986,13 @@ void CMenus::RenderDemoList(CUIRect MainView) s_Inited = 1; } +#if defined(CONF_VIDEORECORDER) + if(m_DemoRenderInput.GetLength() > 0) + { + m_Popup = POPUP_RENDER_DONE; + } +#endif + char aFooterLabel[128] = {0}; if(m_DemolistSelectedIndex >= 0) { From 79c503ec0fb0c32aeddecc3cc2f22cfa6c1eb7bd Mon Sep 17 00:00:00 2001 From: furo Date: Tue, 12 Sep 2023 17:50:06 +0200 Subject: [PATCH 02/70] Fix requested changes --- src/game/client/components/menus.cpp | 8 ++++---- src/game/client/components/menus_demo.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 90de9ab6b94..8861d7cabae 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1717,13 +1717,13 @@ int CMenus::Render() Box.HSplitBottom(160.f, &Box, &Part); - Part.VSplitLeft(60.0f, 0, &TextBox); - TextBox.VSplitLeft(20.0f, 0, &TextBox); - TextBox.VSplitRight(60.0f, &TextBox, 0); + Part.VSplitLeft(60.0f, nullptr, &TextBox); + TextBox.VSplitLeft(20.0f, nullptr, &TextBox); + TextBox.VSplitRight(60.0f, &TextBox, nullptr); str_format(aBuf, sizeof(aBuf), Localize("Video was saved to '%s'"), aFilePath); - UI()->DoLabel(&TextBox, aBuf, 18.0f, TEXTALIGN_LEFT); + UI()->DoLabel(&TextBox, aBuf, 18.0f, TEXTALIGN_TL); } #endif else if(m_Popup == POPUP_FIRST_LAUNCH) diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 1025308826e..56926c4af06 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -987,7 +987,7 @@ void CMenus::RenderDemoList(CUIRect MainView) } #if defined(CONF_VIDEORECORDER) - if(m_DemoRenderInput.GetLength() > 0) + if(!m_DemoRenderInput.IsEmpty()) { m_Popup = POPUP_RENDER_DONE; } From e4ddbaff49706b04c80be7fbbebeff9a46629548 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Thu, 14 Sep 2023 17:50:31 +0200 Subject: [PATCH 03/70] add export image button --- src/game/editor/editor.cpp | 65 ++++++++++++++++++++++++++++++++++++-- src/game/editor/editor.h | 2 ++ src/game/editor/popups.cpp | 22 +++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d8b4022c062..2b9a97717c0 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -785,6 +786,61 @@ bool CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void * } } +bool CEditor::CallbackSaveImage(const char *pFileName, int StorageType, void *pUser) +{ + dbg_assert(StorageType == IStorage::TYPE_SAVE, "Saving only allowed for IStorage::TYPE_SAVE"); + + CEditor *pEditor = static_cast(pUser); + char aBuf[IO_MAX_PATH_LENGTH]; + + // add file extension + if(!str_endswith(pFileName, ".png")) + { + str_format(aBuf, sizeof(aBuf), "%s.png", pFileName); + pFileName = aBuf; + } + + std::shared_ptr pImg = pEditor->m_Map.m_vpImages[pEditor->m_SelectedImage]; + + EImageFormat OutputFormat; + switch(pImg->m_Format) + { + case CImageInfo::FORMAT_RGB: + OutputFormat = IMAGE_FORMAT_RGB; + break; + case CImageInfo::FORMAT_RGBA: + OutputFormat = IMAGE_FORMAT_RGBA; + break; + case CImageInfo::FORMAT_SINGLE_COMPONENT: + OutputFormat = IMAGE_FORMAT_R; + break; + default: + pEditor->ShowFileDialogError("Image has invalid format."); + return false; + }; + + TImageByteBuffer ByteBuffer; + SImageByteBuffer ImageByteBuffer(&ByteBuffer); + if(SavePNG(OutputFormat, static_cast(pImg->m_pData), ImageByteBuffer, pImg->m_Width, pImg->m_Height)) + { + IOHANDLE File = pEditor->Storage()->OpenFile(pFileName, IOFLAG_WRITE, StorageType); + if(File) + { + io_write(File, &ByteBuffer.front(), ByteBuffer.size()); + io_close(File); + pEditor->m_Dialog = DIALOG_NONE; + return true; + } + pEditor->ShowFileDialogError("Failed to open file '%s'.", pFileName); + return false; + } + else + { + pEditor->ShowFileDialogError("Failed to write image to file."); + return false; + } +} + void CEditor::DoToolbarLayers(CUIRect ToolBar) { const bool ModPressed = Input()->ModifierIsPressed(); @@ -4119,7 +4175,7 @@ void CEditor::RenderImagesList(CUIRect ToolBox) if(Result == 2) { const std::shared_ptr pImg = m_Map.m_vpImages[m_SelectedImage]; - const int Height = pImg->m_External || IsVanillaImage(pImg->m_aName) ? 73 : 56; + const int Height = !pImg->m_External && IsVanillaImage(pImg->m_aName) ? 90 : 73; static SPopupMenuId s_PopupImageId; UI()->DoPopupMenu(&s_PopupImageId, UI()->MouseX(), UI()->MouseY(), 120, Height, this, PopupImage); } @@ -4743,7 +4799,12 @@ void CEditor::RenderFileDialog() str_append(m_aFileSaveName, FILETYPE_EXTENSIONS[m_FileDialogFileType]); if(!str_comp(m_pFileDialogButtonText, "Save") && Storage()->FileExists(m_aFileSaveName, StorageType)) { - m_PopupEventType = m_pfnFileDialogFunc == &CallbackSaveCopyMap ? POPEVENT_SAVE_COPY : POPEVENT_SAVE; + if(m_pfnFileDialogFunc == &CallbackSaveMap) + m_PopupEventType = POPEVENT_SAVE; + else if(m_pfnFileDialogFunc == &CallbackSaveCopyMap) + m_PopupEventType = POPEVENT_SAVE_COPY; + else + m_PopupEventType = POPEVENT_SAVE_IMG; m_PopupEventActivated = true; } else if(m_pfnFileDialogFunc) diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 2ca9712cf47..9f08c4b7853 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -994,6 +994,7 @@ class CEditor : public IEditor POPEVENT_NEW, POPEVENT_SAVE, POPEVENT_SAVE_COPY, + POPEVENT_SAVE_IMG, POPEVENT_LARGELAYER, POPEVENT_PREVENTUNUSEDTILES, POPEVENT_IMAGEDIV16, @@ -1286,6 +1287,7 @@ class CEditor : public IEditor static bool CallbackSaveMap(const char *pFileName, int StorageType, void *pUser); static bool CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser); static bool CallbackAddTileart(const char *pFilepath, int StorageType, void *pUser); + static bool CallbackSaveImage(const char *pFileName, int StorageType, void *pUser); void PopupSelectImageInvoke(int Current, float x, float y); int PopupSelectImageResult(); diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index e99d24c6d1a..ed7744890b7 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1459,6 +1459,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View, static int s_ReaddButton = 0; static int s_ReplaceButton = 0; static int s_RemoveButton = 0; + static int s_ExportButton = 0; CUIRect Slot; View.HSplitTop(12.0f, &Slot, &View); @@ -1538,6 +1539,17 @@ CUI::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View, return CUI::POPUP_CLOSE_CURRENT; } + if(!pImg->m_External) + { + View.HSplitTop(5.0f, nullptr, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_ExportButton, "Export", 0, &Slot, 0, "Export the image")) + { + pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_IMG, "Save image", "Save", "mapres", false, CallbackSaveImage, pEditor); + return CUI::POPUP_CLOSE_CURRENT; + } + } + return CUI::POPUP_KEEP_OPEN; } @@ -1750,6 +1762,11 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, pTitle = "Save map"; pMessage = "The file already exists.\n\nDo you want to overwrite the map?"; } + else if(pEditor->m_PopupEventType == POPEVENT_SAVE_IMG) + { + pTitle = "Save image"; + pMessage = "The file already exists.\n\nDo you want to overwrite the image?"; + } else if(pEditor->m_PopupEventType == POPEVENT_LARGELAYER) { pTitle = "Large layer"; @@ -1869,6 +1886,11 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, CallbackSaveCopyMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); return CUI::POPUP_CLOSE_CURRENT; } + else if(pEditor->m_PopupEventType == POPEVENT_SAVE_IMG) + { + CallbackSaveImage(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); + return CUI::POPUP_CLOSE_CURRENT; + } else if(pEditor->m_PopupEventType == POPEVENT_PLACE_BORDER_TILES) { pEditor->PlaceBorderTiles(); From 74567a1a62f2c8635b7798298c1404c8cb2be8bc Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:34:23 +0200 Subject: [PATCH 04/70] ability to show local keypresses only --- src/engine/shared/config_variables.h | 3 ++- src/game/client/components/menus_settings.cpp | 15 ++------------- src/game/client/components/nameplates.cpp | 14 +++++++++----- src/game/variables.h | 3 ++- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index a45ea0de61f..6d6824a6a81 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -401,7 +401,8 @@ MACRO_CONFIG_INT(ClChatTeamColors, cl_chat_teamcolors, 0, 0, 1, CFGFLAG_CLIENT | MACRO_CONFIG_INT(ClChatReset, cl_chat_reset, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Reset chat when pressing escape") MACRO_CONFIG_INT(ClChatOld, cl_chat_old, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Old chat style: No tee, no background"); -MACRO_CONFIG_INT(ClShowDirection, cl_show_direction, 1, 0, 2, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show key presses (1 = other players', 2 = also your own)") +MACRO_CONFIG_INT(ClShowDirectionOther, cl_show_direction_other, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show other players' key presses") +MACRO_CONFIG_INT(ClShowDirectionOwn, cl_show_direction_own, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show local player's key presses") MACRO_CONFIG_INT(ClOldGunPosition, cl_old_gun_position, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Tees hold gun a bit higher like in TW 0.6.1 and older") MACRO_CONFIG_INT(ClConfirmDisconnectTime, cl_confirm_disconnect_time, 20, -1, 1440, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Confirmation popup before disconnecting after game time (in minutes, -1 to turn off, 0 to always turn on)") MACRO_CONFIG_INT(ClConfirmQuitTime, cl_confirm_quit_time, 20, -1, 1440, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Confirmation popup before quitting after game time (in minutes, -1 to turn off, 0 to always turn on)") diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index bd0e8e7c924..d5d0cd98d66 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -2798,19 +2798,8 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesTeamcolors, Localize("Use team colors for name plates"), &g_Config.m_ClNameplatesTeamcolors, &Section, LineSize); DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesStrong, Localize("Show hook strength indicator"), &g_Config.m_ClNameplatesStrong, &Section, LineSize); - - Section.HSplitTop(LineSize, &Button, &Section); - if(DoButton_CheckBox(&g_Config.m_ClShowDirection, Localize("Show other players' key presses"), g_Config.m_ClShowDirection >= 1, &Button)) - { - g_Config.m_ClShowDirection = g_Config.m_ClShowDirection >= 1 ? 0 : 1; - } - - Section.HSplitTop(LineSize, &Button, &Section); - static int s_ShowLocalPlayer = 0; - if(DoButton_CheckBox(&s_ShowLocalPlayer, Localize("Show local player's key presses"), g_Config.m_ClShowDirection == 2, &Button)) - { - g_Config.m_ClShowDirection = g_Config.m_ClShowDirection != 2 ? 2 : 1; - } + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowDirectionOther, Localize("Show other players' key presses"), &g_Config.m_ClShowDirectionOther, &Section, LineSize); + DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowDirectionOwn, Localize("Show local player's key presses"), &g_Config.m_ClShowDirectionOwn, &Section, LineSize); } else if(s_CurTab == APPEARANCE_TAB_HOOK_COLLISION) { diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index ca08c694669..d164e369c02 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -44,12 +44,16 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP ColorRGBA rgb = ColorRGBA(1.0f, 1.0f, 1.0f); // render players' key presses - int ShowDirection = g_Config.m_ClShowDirection; + int ShowDirectionOther = g_Config.m_ClShowDirectionOther; + int ShowDirectionOwn = g_Config.m_ClShowDirectionOwn; #if defined(CONF_VIDEORECORDER) if(IVideo::Current()) - ShowDirection = g_Config.m_ClVideoShowDirection; + { + ShowDirectionOther = g_Config.m_ClVideoShowDirectionOther; + ShowDirectionOwn = g_Config.m_ClVideoShowDirectionOwn; + } #endif - if((pPlayerInfo->m_Local && ShowDirection == 2) || (!pPlayerInfo->m_Local && ShowDirection >= 1)) + if((ShowDirectionOther && !pPlayerInfo->m_Local) || (ShowDirectionOwn && pPlayerInfo->m_Local) || (ShowDirectionOwn && Client()->DummyConnected() && Client()->State() != IClient::STATE_DEMOPLAYBACK && ClientID == m_pClient->m_aLocalIDs[!g_Config.m_ClDummy])) { Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); Graphics()->QuadsSetRotation(0); @@ -282,10 +286,10 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP void CNamePlates::OnRender() { - int ShowDirection = g_Config.m_ClShowDirection; + int ShowDirection = (g_Config.m_ClShowDirectionOther || g_Config.m_ClShowDirectionOwn); #if defined(CONF_VIDEORECORDER) if(IVideo::Current()) - ShowDirection = g_Config.m_ClVideoShowDirection; + ShowDirection = (g_Config.m_ClVideoShowDirectionOther || g_Config.m_ClVideoShowDirectionOwn); #endif if(!g_Config.m_ClNameplates && ShowDirection == 0) return; diff --git a/src/game/variables.h b/src/game/variables.h index f65cea0abeb..6ebf172602d 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -213,7 +213,8 @@ MACRO_CONFIG_INT(ClVideoShowhud, cl_video_showhud, 0, 0, 1, CFGFLAG_CLIENT | CFG MACRO_CONFIG_INT(ClVideoShowChat, cl_video_showchat, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show chat when rendering video") MACRO_CONFIG_INT(ClVideoSndEnable, cl_video_sound_enable, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Use sound when rendering video") MACRO_CONFIG_INT(ClVideoShowHookCollOther, cl_video_show_hook_coll_other, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show other players' hook collision lines when rendering video") -MACRO_CONFIG_INT(ClVideoShowDirection, cl_video_show_direction, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show players' key presses when rendering video (1 = other players', 2 = also your own)") +MACRO_CONFIG_INT(ClVideoShowDirectionOther, cl_video_show_direction_other, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show other players' key presses when rendering video") +MACRO_CONFIG_INT(ClVideoShowDirectionOwn, cl_video_show_direction_own, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show local player's key presses when rendering video") MACRO_CONFIG_INT(ClVideoX264Crf, cl_video_crf, 18, 0, 51, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Set crf when encode video with libx264 (0 for highest quality, 51 for lowest)") MACRO_CONFIG_INT(ClVideoX264Preset, cl_video_preset, 5, 0, 9, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Set preset when encode video with libx264, default is 5 (medium), 0 is ultrafast, 9 is placebo (the slowest, not recommend)") From 7c710ddb96302749aacbcc72a090f69703dee0da Mon Sep 17 00:00:00 2001 From: dobrykafe <121701317+dobrykafe@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:47:09 +0200 Subject: [PATCH 05/70] add another value instead of new variable --- src/engine/shared/config_variables.h | 5 +-- src/game/client/components/menus_settings.cpp | 43 ++++++++++++++++++- src/game/client/components/nameplates.cpp | 14 +++--- src/game/variables.h | 3 +- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 6d6824a6a81..1668f8a4a56 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -399,10 +399,9 @@ MACRO_CONFIG_COL(ClHookCollColorTeeColl, cl_hook_coll_color_tee_coll, 2817919, C MACRO_CONFIG_INT(ClChatTeamColors, cl_chat_teamcolors, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show names in chat in team colors") MACRO_CONFIG_INT(ClChatReset, cl_chat_reset, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Reset chat when pressing escape") -MACRO_CONFIG_INT(ClChatOld, cl_chat_old, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Old chat style: No tee, no background"); +MACRO_CONFIG_INT(ClChatOld, cl_chat_old, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Old chat style: No tee, no background") -MACRO_CONFIG_INT(ClShowDirectionOther, cl_show_direction_other, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show other players' key presses") -MACRO_CONFIG_INT(ClShowDirectionOwn, cl_show_direction_own, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show local player's key presses") +MACRO_CONFIG_INT(ClShowDirection, cl_show_direction, 1, 0, 3, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show key presses (1 = other players', 2 = also your own, 3 = only your own") MACRO_CONFIG_INT(ClOldGunPosition, cl_old_gun_position, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Tees hold gun a bit higher like in TW 0.6.1 and older") MACRO_CONFIG_INT(ClConfirmDisconnectTime, cl_confirm_disconnect_time, 20, -1, 1440, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Confirmation popup before disconnecting after game time (in minutes, -1 to turn off, 0 to always turn on)") MACRO_CONFIG_INT(ClConfirmQuitTime, cl_confirm_quit_time, 20, -1, 1440, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Confirmation popup before quitting after game time (in minutes, -1 to turn off, 0 to always turn on)") diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index d5d0cd98d66..fe60db0ab2f 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -2798,8 +2798,47 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesTeamcolors, Localize("Use team colors for name plates"), &g_Config.m_ClNameplatesTeamcolors, &Section, LineSize); DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClNameplatesStrong, Localize("Show hook strength indicator"), &g_Config.m_ClNameplatesStrong, &Section, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowDirectionOther, Localize("Show other players' key presses"), &g_Config.m_ClShowDirectionOther, &Section, LineSize); - DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClShowDirectionOwn, Localize("Show local player's key presses"), &g_Config.m_ClShowDirectionOwn, &Section, LineSize); + + Section.HSplitTop(LineSize, &Button, &Section); + if(DoButton_CheckBox(&g_Config.m_ClShowDirection, Localize("Show other players' key presses"), g_Config.m_ClShowDirection >= 1 && g_Config.m_ClShowDirection != 3, &Button)) + { + switch(g_Config.m_ClShowDirection) + { + case 0: + g_Config.m_ClShowDirection = 1; + break; + case 1: + g_Config.m_ClShowDirection = 0; + break; + case 2: + g_Config.m_ClShowDirection = 3; + break; + case 3: + g_Config.m_ClShowDirection = 2; + break; + } + } + + Section.HSplitTop(LineSize, &Button, &Section); + static int s_ShowLocalPlayer = 0; + if(DoButton_CheckBox(&s_ShowLocalPlayer, Localize("Show local player's key presses"), g_Config.m_ClShowDirection >= 2, &Button)) + { + switch(g_Config.m_ClShowDirection) + { + case 0: + g_Config.m_ClShowDirection = 3; + break; + case 1: + g_Config.m_ClShowDirection = 2; + break; + case 2: + g_Config.m_ClShowDirection = 1; + break; + case 3: + g_Config.m_ClShowDirection = 0; + break; + } + } } else if(s_CurTab == APPEARANCE_TAB_HOOK_COLLISION) { diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index d164e369c02..153b9780135 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -44,16 +44,12 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP ColorRGBA rgb = ColorRGBA(1.0f, 1.0f, 1.0f); // render players' key presses - int ShowDirectionOther = g_Config.m_ClShowDirectionOther; - int ShowDirectionOwn = g_Config.m_ClShowDirectionOwn; + int ShowDirection = g_Config.m_ClShowDirection; #if defined(CONF_VIDEORECORDER) if(IVideo::Current()) - { - ShowDirectionOther = g_Config.m_ClVideoShowDirectionOther; - ShowDirectionOwn = g_Config.m_ClVideoShowDirectionOwn; - } + ShowDirection = g_Config.m_ClVideoShowDirection; #endif - if((ShowDirectionOther && !pPlayerInfo->m_Local) || (ShowDirectionOwn && pPlayerInfo->m_Local) || (ShowDirectionOwn && Client()->DummyConnected() && Client()->State() != IClient::STATE_DEMOPLAYBACK && ClientID == m_pClient->m_aLocalIDs[!g_Config.m_ClDummy])) + if((ShowDirection && ShowDirection != 3 && !pPlayerInfo->m_Local) || (ShowDirection >= 2 && pPlayerInfo->m_Local) || (ShowDirection == 3 && Client()->DummyConnected() && Client()->State() != IClient::STATE_DEMOPLAYBACK && ClientID == m_pClient->m_aLocalIDs[!g_Config.m_ClDummy])) { Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); Graphics()->QuadsSetRotation(0); @@ -286,10 +282,10 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP void CNamePlates::OnRender() { - int ShowDirection = (g_Config.m_ClShowDirectionOther || g_Config.m_ClShowDirectionOwn); + int ShowDirection = g_Config.m_ClShowDirection; #if defined(CONF_VIDEORECORDER) if(IVideo::Current()) - ShowDirection = (g_Config.m_ClVideoShowDirectionOther || g_Config.m_ClVideoShowDirectionOwn); + ShowDirection = g_Config.m_ClVideoShowDirection; #endif if(!g_Config.m_ClNameplates && ShowDirection == 0) return; diff --git a/src/game/variables.h b/src/game/variables.h index 6ebf172602d..ddd53b82dc3 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -213,8 +213,7 @@ MACRO_CONFIG_INT(ClVideoShowhud, cl_video_showhud, 0, 0, 1, CFGFLAG_CLIENT | CFG MACRO_CONFIG_INT(ClVideoShowChat, cl_video_showchat, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show chat when rendering video") MACRO_CONFIG_INT(ClVideoSndEnable, cl_video_sound_enable, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Use sound when rendering video") MACRO_CONFIG_INT(ClVideoShowHookCollOther, cl_video_show_hook_coll_other, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show other players' hook collision lines when rendering video") -MACRO_CONFIG_INT(ClVideoShowDirectionOther, cl_video_show_direction_other, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show other players' key presses when rendering video") -MACRO_CONFIG_INT(ClVideoShowDirectionOwn, cl_video_show_direction_own, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show local player's key presses when rendering video") +MACRO_CONFIG_INT(ClVideoShowDirection, cl_video_show_direction, 0, 0, 3, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show players' key presses when rendering video (1 = other players', 2 = also your own, 3 = only your own)") MACRO_CONFIG_INT(ClVideoX264Crf, cl_video_crf, 18, 0, 51, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Set crf when encode video with libx264 (0 for highest quality, 51 for lowest)") MACRO_CONFIG_INT(ClVideoX264Preset, cl_video_preset, 5, 0, 9, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Set preset when encode video with libx264, default is 5 (medium), 0 is ultrafast, 9 is placebo (the slowest, not recommend)") From 992a26c7ca2440170659be3bd0b7784eb27fc20c Mon Sep 17 00:00:00 2001 From: furo Date: Thu, 21 Sep 2023 19:40:37 +0200 Subject: [PATCH 06/70] Add ability to swap without a name when team only has 2 players. --- src/game/server/ddracechat.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 629adf951f4..a1cdd3fe480 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -708,13 +708,30 @@ void CGameContext::ConSwap(IConsole::IResult *pResult, void *pUserData) } int TargetClientId = -1; - for(int i = 0; i < MAX_CLIENTS; i++) + if(pResult->NumArguments() == 1) { - if(pSelf->m_apPlayers[i] && !str_comp(pName, pSelf->Server()->ClientName(i))) + for(int i = 0; i < MAX_CLIENTS; i++) { - TargetClientId = i; - break; + if(pSelf->m_apPlayers[i] && !str_comp(pName, pSelf->Server()->ClientName(i))) + { + TargetClientId = i; + break; + } + } + } + else + { + int TeamSize = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(pSelf->m_apPlayers[i] && Teams.m_Core.Team(i) == Team && i != pResult->m_ClientID) + { + TargetClientId = i; + TeamSize++; + } } + if(TeamSize > 1) + TargetClientId = -1; } if(TargetClientId < 0) From f23e5ceb6f0ec23a78c950bf776d39eb7048e16a Mon Sep 17 00:00:00 2001 From: furo Date: Thu, 21 Sep 2023 23:15:42 +0200 Subject: [PATCH 07/70] Correct value of TeamSize. --- src/game/server/ddracechat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index a1cdd3fe480..172ed3b059a 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -721,7 +721,7 @@ void CGameContext::ConSwap(IConsole::IResult *pResult, void *pUserData) } else { - int TeamSize = 0; + int TeamSize = 1; for(int i = 0; i < MAX_CLIENTS; i++) { if(pSelf->m_apPlayers[i] && Teams.m_Core.Team(i) == Team && i != pResult->m_ClientID) @@ -730,7 +730,7 @@ void CGameContext::ConSwap(IConsole::IResult *pResult, void *pUserData) TeamSize++; } } - if(TeamSize > 1) + if(TeamSize != 2) TargetClientId = -1; } From 5793891d91e09ac5e8ebff50fafa6275f366b854 Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 22 Sep 2023 12:43:36 +0200 Subject: [PATCH 08/70] Add a message when using sv_chat_initial_delay --- src/game/server/gamecontext.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 4d88ce0a334..1c000b37e94 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1555,8 +1555,11 @@ void CGameContext::OnClientEnter(int ClientID) // initial chat delay if(g_Config.m_SvChatInitialDelay != 0 && m_apPlayers[ClientID]->m_JoinTick > m_NonEmptySince + 10 * Server()->TickSpeed()) { + char aBuf[128]; NETADDR Addr; Server()->GetClientAddr(ClientID, &Addr); + str_format(aBuf, sizeof aBuf, "This server has an initial chat delay, you will need to wait %d seconds before talking.", g_Config.m_SvChatInitialDelay); + SendChatTarget(ClientID, aBuf); Mute(&Addr, g_Config.m_SvChatInitialDelay, Server()->ClientName(ClientID), "Initial chat delay", true); } @@ -4151,20 +4154,27 @@ bool CGameContext::ProcessSpamProtection(int ClientID, bool RespectChatInitialDe NETADDR Addr; Server()->GetClientAddr(ClientID, &Addr); - int Muted = 0; - for(int i = 0; i < m_NumMutes && Muted <= 0; i++) + CMute Muted; + int Expires = 0; + for(int i = 0; i < m_NumMutes && Expires <= 0; i++) { if(!net_addr_comp_noport(&Addr, &m_aMutes[i].m_Addr)) { if(RespectChatInitialDelay || m_aMutes[i].m_InitialChatDelay) - Muted = (m_aMutes[i].m_Expire - Server()->Tick()) / Server()->TickSpeed(); + { + Muted = m_aMutes[i]; + Expires = (m_aMutes[i].m_Expire - Server()->Tick()) / Server()->TickSpeed(); + } } } - if(Muted > 0) + if(Expires > 0) { char aBuf[128]; - str_format(aBuf, sizeof aBuf, "You are not permitted to talk for the next %d seconds.", Muted); + if(Muted.m_InitialChatDelay) + str_format(aBuf, sizeof aBuf, "This server has an initial chat delay, you will be able to talk in %d seconds.", Expires); + else + str_format(aBuf, sizeof aBuf, "You are not permitted to talk for the next %d seconds.", Expires); SendChatTarget(ClientID, aBuf); return true; } From 98bfed7859cdb134e7b5fb1028e2e5fa73822dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 22 Sep 2023 17:06:45 +0200 Subject: [PATCH 09/70] Fix double-click handling with `gfx_asyncrender_old 0` We need to call `SDL_PumpEvents()` to ensure that we have the latest keyboard, mouse and joystick state before we use it. See: - https://wiki.libsdl.org/SDL2/SDL_GetKeyboardState - https://wiki.libsdl.org/SDL3/SDL_GetMouseState#code-examples Closes #7052. --- src/engine/client/input.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index 6a0245dccf8..79c5eedd124 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -557,6 +557,9 @@ int CInput::Update() // keep the counter between 1..0xFFFF, 0 means not pressed m_InputCounter = (m_InputCounter % 0xFFFF) + 1; + // Ensure that we have the latest keyboard, mouse and joystick state + SDL_PumpEvents(); + int NumKeyStates; const Uint8 *pState = SDL_GetKeyboardState(&NumKeyStates); if(NumKeyStates >= KEY_MOUSE_1) From 318fe09e946e712a32b3fc160d2f4777817e5c1b Mon Sep 17 00:00:00 2001 From: Pavukoplov Date: Fri, 22 Sep 2023 18:19:03 +0300 Subject: [PATCH 10/70] Update Belarusian translation --- data/languages/belarusian.txt | 47 ++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/data/languages/belarusian.txt b/data/languages/belarusian.txt index 4f94ab744b5..8bc1ac50755 100644 --- a/data/languages/belarusian.txt +++ b/data/languages/belarusian.txt @@ -4,6 +4,7 @@ #modified by: # Chill & PoKeMoN 2023-03-31 16:00:00 # Chill & PoKeMoN 2023-07-02 00:54:00 +# Chill [TD] & PoKeMoN [TD] 2023-09-22 17:49:00 # ##### /authors ##### @@ -1091,7 +1092,7 @@ Show HUD == Паказваць HUD Chat command -== Камандны чат +== Каманда чата Enable controller == Уключыць кантролер @@ -1628,13 +1629,13 @@ Grabs == Зах. 1 new mention -== 1 згадка +== 1 новая згадка %d new mentions -== %d згадак +== %d новых згадак 9+ new mentions -== 9+ згадак +== 9+ новых згадак A demo with this name already exists == Дэма з такой назвай ужо існуе @@ -1692,60 +1693,60 @@ Graphics card == Відэакарта Quitting. Please wait… -== +== Выхад. Пачакайце, калі ласка… Restarting. Please wait… -== +== Перазапуск. Пачакайце, калі ласка… Multi-View -== +== Мульты-агляд Rename folder -== +== Пераназваць тэчку A folder with this name already exists -== +== Тэчка з такой назвай ужо існуе Unable to rename the folder -== +== Немагчыма пераназваць тэчку (paused) -== +== (паўза) Go back the specified duration -== +== Назад на выбраную працягласць [Demo player duration] %d min. -== +== %d хв. [Demo player duration] %d sec. -== +== % сек. Change the skip duration -== +== Змяніць працягласць пропуску Go forward the specified duration -== +== Уперад на выбраную працягласць Render cut to video -== +== Рэндэрыць адрэзак у відэа All combined -== +== Усе разам Folder Link -== +== Ярлык тэчкі Are you sure that you want to delete the folder '%s'? -== +== Вы ўпэўненыя, што хочаце выдаліць тэчку '%s'? Delete folder -== +== Выдаліць тэчку Unable to delete the folder '%s'. Make sure it's empty first. -== +== Немагчыма выдаліць тэчку '%s'. Спачатку пераканайцеся, што яна пустая. Moved ingame -== +== Перамешчаны ў гульні From 367f06a7f950f432e4165916025e1c329285c3eb Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 22 Sep 2023 18:30:39 +0200 Subject: [PATCH 11/70] Detect if server started from client is no longer running Add function to check if a process is alive. Fix requsted changes --- src/base/system.cpp | 13 +++++++++++++ src/base/system.h | 9 +++++++++ src/game/client/components/menus_start.cpp | 7 +++++++ 3 files changed, 29 insertions(+) diff --git a/src/base/system.cpp b/src/base/system.cpp index 89a09f68cca..5a5401f8adc 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -4161,6 +4161,19 @@ int kill_process(PROCESS process) #endif } +bool is_process_alive(PROCESS process) +{ + if(process == INVALID_PROCESS) + return false; +#if defined(CONF_FAMILY_WINDOWS) + DWORD exit_code; + GetExitCodeProcess(process, &exit_code); + return exit_code == STILL_ACTIVE; +#else + return waitpid(process, nullptr, WNOHANG) == 0; +#endif +} + int open_link(const char *link) { #if defined(CONF_FAMILY_WINDOWS) diff --git a/src/base/system.h b/src/base/system.h index c1f01bfe325..ef9527f8497 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -2648,6 +2648,15 @@ PROCESS shell_execute(const char *file); */ int kill_process(PROCESS process); +/** + * Checks if a process is alive. + * + * @param process Handle/PID of the process. + * + * @return bool Returns true if the process is currently running, false if the process is not running (dead). + */ +bool is_process_alive(PROCESS process); + /* Function: generate_password Generates a null-terminated password of length `2 * diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index 791afbf061c..309b5096e6e 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -135,6 +135,13 @@ void CMenus::RenderStartMenu(CUIRect MainView) Menu.HSplitBottom(5.0f, &Menu, 0); // little space Menu.HSplitBottom(40.0f, &Menu, &Button); static CButtonContainer s_LocalServerButton; + + if(!is_process_alive(m_ServerProcess.m_Process)) + { + KillServer(); + m_ServerProcess.m_Process = INVALID_PROCESS; + } + if(DoButton_Menu(&s_LocalServerButton, m_ServerProcess.m_Process ? Localize("Stop server") : Localize("Run server"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "local_server" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, vec4(0.0f, 0.0f, 0.0f, 0.5f), m_ServerProcess.m_Process ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(0.0f, 0.0f, 0.0f, 0.25f)) || (CheckHotKey(KEY_R) && Input()->KeyPress(KEY_R))) { if(m_ServerProcess.m_Process) From 1d711d6cf0b4eedb272a97a2fcb9f54e7c34c8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 22 Sep 2023 20:55:27 +0200 Subject: [PATCH 12/70] Correctly update auth keys when using `auth_remove` When using `auth_remove`, the key indices for the default helper, mod and admin passwords were not properly adjusted, causing the wrong passwords to be used for the username-less logins. The key indices for connected clients were also not properly adjusted, causing the wrong identity to be shown for currently authenticated clients when using the `status` command. Closes #6427. --- src/engine/server/authmanager.cpp | 8 ++++---- src/engine/server/authmanager.h | 2 +- src/engine/server/server.cpp | 16 ++++++++++------ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/engine/server/authmanager.cpp b/src/engine/server/authmanager.cpp index 62b7be58299..6014af70fe1 100644 --- a/src/engine/server/authmanager.cpp +++ b/src/engine/server/authmanager.cpp @@ -65,21 +65,21 @@ int CAuthManager::AddKey(const char *pIdent, const char *pPw, int AuthLevel) return AddKeyHash(pIdent, HashPassword(pPw, aSalt), aSalt, AuthLevel); } -int CAuthManager::RemoveKey(int Slot) +void CAuthManager::RemoveKey(int Slot) { m_vKeys.erase(m_vKeys.begin() + Slot); + // Update indices of default keys for(int &Default : m_aDefault) { if(Default == Slot) { Default = -1; } - else if(Default == (int)m_vKeys.size()) + else if(Default > Slot) { - Default = Slot; + --Default; } } - return m_vKeys.size(); } int CAuthManager::FindKey(const char *pIdent) const diff --git a/src/engine/server/authmanager.h b/src/engine/server/authmanager.h index feea4ac4f85..9845100e8e2 100644 --- a/src/engine/server/authmanager.h +++ b/src/engine/server/authmanager.h @@ -30,7 +30,7 @@ class CAuthManager void Init(); int AddKeyHash(const char *pIdent, MD5_DIGEST Hash, const unsigned char *pSalt, int AuthLevel); int AddKey(const char *pIdent, const char *pPw, int AuthLevel); - int RemoveKey(int Slot); // Returns the old key slot that is now in the named one. + void RemoveKey(int Slot); int FindKey(const char *pIdent) const; bool CheckKey(int Slot, const char *pPw) const; int DefaultKey(int AuthLevel) const; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index aeff8a1abde..77223869b66 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -3107,16 +3107,20 @@ static int GetAuthLevel(const char *pLevel) void CServer::AuthRemoveKey(int KeySlot) { - int NewKeySlot = KeySlot; - int OldKeySlot = m_AuthManager.RemoveKey(KeySlot); + m_AuthManager.RemoveKey(KeySlot); LogoutKey(KeySlot, "key removal"); // Update indices. - if(OldKeySlot != NewKeySlot) + for(auto &Client : m_aClients) { - for(auto &Client : m_aClients) - if(Client.m_AuthKey == OldKeySlot) - Client.m_AuthKey = NewKeySlot; + if(Client.m_AuthKey == KeySlot) + { + Client.m_AuthKey = -1; + } + else if(Client.m_AuthKey > KeySlot) + { + --Client.m_AuthKey; + } } } From 1a367431852a87b76feef2739f5db36d17f2c0f9 Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 22 Sep 2023 22:18:20 +0200 Subject: [PATCH 13/70] `kill_process` return true if process doesn't exist --- src/base/system.cpp | 9 ++++----- src/game/client/components/menus_start.cpp | 3 --- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index 5a5401f8adc..d6873891b36 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -4149,12 +4149,11 @@ int kill_process(PROCESS process) { #if defined(CONF_FAMILY_WINDOWS) BOOL success = TerminateProcess(process, 0); - if(success) - { - CloseHandle(process); - } - return success; + CloseHandle(process); + return success || GetLastError() == ERROR_INVALID_HANDLE; #elif defined(CONF_FAMILY_UNIX) + if(!is_process_alive(process)) + return true; int status; kill(process, SIGTERM); return waitpid(process, &status, 0) != -1; diff --git a/src/game/client/components/menus_start.cpp b/src/game/client/components/menus_start.cpp index 309b5096e6e..b4fb286503f 100644 --- a/src/game/client/components/menus_start.cpp +++ b/src/game/client/components/menus_start.cpp @@ -137,10 +137,7 @@ void CMenus::RenderStartMenu(CUIRect MainView) static CButtonContainer s_LocalServerButton; if(!is_process_alive(m_ServerProcess.m_Process)) - { KillServer(); - m_ServerProcess.m_Process = INVALID_PROCESS; - } if(DoButton_Menu(&s_LocalServerButton, m_ServerProcess.m_Process ? Localize("Stop server") : Localize("Run server"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "local_server" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, vec4(0.0f, 0.0f, 0.0f, 0.5f), m_ServerProcess.m_Process ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(0.0f, 0.0f, 0.0f, 0.25f)) || (CheckHotKey(KEY_R) && Input()->KeyPress(KEY_R))) { From 3da0b3444496b8cecc8e79af5e6e06e9e71d8951 Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 22 Sep 2023 23:15:53 +0200 Subject: [PATCH 14/70] Don't close handle if TerminateProcess failed --- src/base/system.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index d6873891b36..d8688ac0152 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -4149,8 +4149,13 @@ int kill_process(PROCESS process) { #if defined(CONF_FAMILY_WINDOWS) BOOL success = TerminateProcess(process, 0); - CloseHandle(process); - return success || GetLastError() == ERROR_INVALID_HANDLE; + BOOL is_alive = is_process_alive(process); + if(success || !is_alive) + { + CloseHandle(process); + return true; + } + return false; #elif defined(CONF_FAMILY_UNIX) if(!is_process_alive(process)) return true; From c4d325e09baeff27ab800283dca4fe8cf6aff79a Mon Sep 17 00:00:00 2001 From: VoxelDoesCode Date: Fri, 22 Sep 2023 17:26:09 -0400 Subject: [PATCH 15/70] Show if your friend is AFK through friends list resolve mege conflict AFK -> Afk (for consistency) --- src/engine/client/client.cpp | 1 + src/engine/serverbrowser.h | 1 + src/engine/shared/serverinfo.cpp | 5 +++++ src/engine/shared/serverinfo.h | 1 + src/game/client/components/menus.h | 4 ++++ src/game/client/components/menus_browser.cpp | 9 +++++---- 6 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 81b4cb76128..a0f752aedb6 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1479,6 +1479,7 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, GET_INT(pClient->m_Country); GET_INT(pClient->m_Score); GET_INT(pClient->m_Player); + GET_INT(pClient->m_Afk); if(SavedType == SERVERINFO_EXTENDED) { Up.GetString(); // extra info, reserved diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 13a4723fded..674b092a6c0 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -51,6 +51,7 @@ class CServerInfo int m_Country; int m_Score; bool m_Player; + bool m_Afk; // skin info char m_aSkin[24 + 1]; diff --git a/src/engine/shared/serverinfo.cpp b/src/engine/shared/serverinfo.cpp index 7fcabcea4da..1e7210ab02b 100644 --- a/src/engine/shared/serverinfo.cpp +++ b/src/engine/shared/serverinfo.cpp @@ -112,12 +112,14 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson) const json_value &Country = Client["country"]; const json_value &Score = Client["score"]; const json_value &IsPlayer = Client["is_player"]; + const json_value &IsAfk = Client["afk"]; Error = false; Error = Error || ClientName.type != json_string || str_has_cc(ClientName); Error = Error || Clan.type != json_string || str_has_cc(ClientName); Error = Error || Country.type != json_integer; Error = Error || Score.type != json_integer; Error = Error || IsPlayer.type != json_boolean; + Error = Error || IsAfk.type != json_boolean; if(Error) { return true; @@ -130,6 +132,7 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson) pClient->m_Country = json_int_get(&Country); pClient->m_Score = json_int_get(&Score); pClient->m_IsPlayer = IsPlayer; + pClient->m_IsAfk = IsAfk; // check if a skin is also available bool HasSkin = false; @@ -203,6 +206,7 @@ bool CServerInfo2::operator==(const CServerInfo2 &Other) const Unequal = Unequal || m_aClients[i].m_Country != Other.m_aClients[i].m_Country; Unequal = Unequal || m_aClients[i].m_Score != Other.m_aClients[i].m_Score; Unequal = Unequal || m_aClients[i].m_IsPlayer != Other.m_aClients[i].m_IsPlayer; + Unequal = Unequal || m_aClients[i].m_IsAfk != Other.m_aClients[i].m_IsAfk; if(Unequal) { return false; @@ -232,6 +236,7 @@ CServerInfo2::operator CServerInfo() const Result.m_aClients[i].m_Country = m_aClients[i].m_Country; Result.m_aClients[i].m_Score = m_aClients[i].m_Score; Result.m_aClients[i].m_Player = m_aClients[i].m_IsPlayer; + Result.m_aClients[i].m_Afk = m_aClients[i].m_IsAfk; str_copy(Result.m_aClients[i].m_aSkin, m_aClients[i].m_aSkin); Result.m_aClients[i].m_CustomSkinColors = m_aClients[i].m_CustomSkinColors; diff --git a/src/engine/shared/serverinfo.h b/src/engine/shared/serverinfo.h index 97ff6b9c118..f2809b2cc7b 100644 --- a/src/engine/shared/serverinfo.h +++ b/src/engine/shared/serverinfo.h @@ -18,6 +18,7 @@ class CServerInfo2 int m_Country; int m_Score; bool m_IsPlayer; + bool m_IsAfk; char m_aSkin[24 + 1]; bool m_CustomSkinColors; int m_CustomSkinColorBody; diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index e1d7b55a9c5..4e97a940810 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -346,6 +346,7 @@ class CMenus : public CComponent const CServerInfo *m_pServerInfo; int m_FriendState; bool m_IsPlayer; + bool m_IsAfk; // skin char m_aSkin[24 + 1]; bool m_CustomSkinColors; @@ -357,6 +358,7 @@ class CMenus : public CComponent CFriendItem(const CFriendInfo *pFriendInfo) : m_pServerInfo(nullptr), m_IsPlayer(false), + m_IsAfk(false), m_CustomSkinColors(false), m_CustomSkinColorBody(0), m_CustomSkinColorFeet(0) @@ -370,6 +372,7 @@ class CMenus : public CComponent m_pServerInfo(pServerInfo), m_FriendState(CurrentClient.m_FriendState), m_IsPlayer(CurrentClient.m_Player), + m_IsAfk(CurrentClient.m_Afk), m_CustomSkinColors(CurrentClient.m_CustomSkinColors), m_CustomSkinColorBody(CurrentClient.m_CustomSkinColorBody), m_CustomSkinColorFeet(CurrentClient.m_CustomSkinColorFeet) @@ -384,6 +387,7 @@ class CMenus : public CComponent const CServerInfo *ServerInfo() const { return m_pServerInfo; } int FriendState() const { return m_FriendState; } bool IsPlayer() const { return m_IsPlayer; } + bool IsAfk() const { return m_IsAfk; } const char *Skin() const { return m_aSkin; } bool CustomSkinColors() const { return m_CustomSkinColors; } int CustomSkinColorBody() const { return m_CustomSkinColorBody; } diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index bdf9c787fd1..e212c5e2a80 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1218,7 +1218,8 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) const float FontSize = 10.0f; static bool s_aListExtended[NUM_FRIEND_TYPES] = {true, true, false}; static const ColorRGBA s_aListColors[NUM_FRIEND_TYPES] = {ColorRGBA(0.5f, 1.0f, 0.5f, 1.0f), ColorRGBA(0.4f, 0.4f, 1.0f, 1.0f), ColorRGBA(1.0f, 0.5f, 0.5f, 1.0f)}; - const ColorRGBA OfflineClanColor = ColorRGBA(0.7f, 0.45f, 0.75f, 1.0f); + // Alternates of s_aListColors include: AFK friend color, AFK clanmate color, Offline clan color. + static const ColorRGBA s_aListColorAlternates[NUM_FRIEND_TYPES] = {ColorRGBA(1.0f, 1.0f, 0.5f, 1.0f), ColorRGBA(0.4f, 0.75f, 1.0f, 1.0f), ColorRGBA(0.7f, 0.45f, 0.75f, 1.0f)}; const float SpacingH = 2.0f; CUIRect List, ServerFriends; @@ -1334,8 +1335,8 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) { GameClient()->m_Tooltips.DoToolTip(Friend.ListItemId(), &Rect, Localize("Click to select server. Double click to join your friend.")); } - const bool OfflineClan = Friend.FriendState() == IFriends::FRIEND_CLAN && FriendType == FRIEND_OFF; - Rect.Draw((OfflineClan ? OfflineClanColor : s_aListColors[FriendType]).WithAlpha(Inside ? 0.5f : 0.3f), IGraphics::CORNER_ALL, 5.0f); + const bool AlternateColor = (FriendType != FRIEND_OFF && Friend.IsAfk()) || (Friend.FriendState() == IFriends::FRIEND_CLAN && FriendType == FRIEND_OFF); + Rect.Draw((AlternateColor ? s_aListColorAlternates[FriendType] : s_aListColors[FriendType]).WithAlpha(Inside ? 0.5f : 0.3f), IGraphics::CORNER_ALL, 5.0f); Rect.Margin(2.0f, &Rect); CUIRect RemoveButton, NameLabel, ClanLabel, InfoLabel; @@ -1360,7 +1361,7 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) vec2 OffsetToMid; RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); const vec2 TeeRenderPos = vec2(Skin.x + Skin.w / 2.0f, Skin.y + Skin.h * 0.55f + OffsetToMid.y); - RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos); + RenderTools()->RenderTee(pIdleState, &TeeInfo, Friend.IsAfk() ? EMOTE_BLINK : EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos); } Rect.HSplitTop(11.0f, &NameLabel, &ClanLabel); From 57ad88d6ee97712c9b01b57ca0fa7d4550fa169e Mon Sep 17 00:00:00 2001 From: furo Date: Sat, 23 Sep 2023 14:45:56 +0200 Subject: [PATCH 16/70] Add numpad shortcuts and button for zooming in envelope editor --- src/game/editor/editor.cpp | 47 ++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 6cefe7a4992..8ba245312a9 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -5540,10 +5540,30 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) if(pEnvelope) { ToolBar.VSplitRight(5.0f, &ToolBar, nullptr); + ToolBar.VSplitRight(20.0f, &ToolBar, &Button); + static int s_ZoomOutButton = 0; + if(DoButton_FontIcon(&s_ZoomOutButton, "-", 0, &Button, 0, "[NumPad-] Zoom out horizontally, hold shift to zoom vertically", IGraphics::CORNER_R, 9.0f)) + { + if(Input()->ShiftIsPressed()) + m_ZoomEnvelopeY.ChangeValue(0.1f * m_ZoomEnvelopeY.GetValue()); + else + m_ZoomEnvelopeX.ChangeValue(0.1f * m_ZoomEnvelopeX.GetValue()); + } + ToolBar.VSplitRight(20.0f, &ToolBar, &Button); static int s_ResetZoomButton = 0; - if(DoButton_FontIcon(&s_ResetZoomButton, FONT_ICON_MAGNIFYING_GLASS, 0, &Button, 0, "Reset zoom to default value", IGraphics::CORNER_ALL, 9.0f)) + if(DoButton_FontIcon(&s_ResetZoomButton, FONT_ICON_MAGNIFYING_GLASS, 0, &Button, 0, "[NumPad*] Reset zoom to default value", IGraphics::CORNER_NONE, 9.0f)) ResetZoomEnvelope(pEnvelope, s_ActiveChannels); + + ToolBar.VSplitRight(20.0f, &ToolBar, &Button); + static int s_ZoomInButton = 0; + if(DoButton_FontIcon(&s_ZoomInButton, "+", 0, &Button, 0, "[NumPad+] Zoom in horizontally, hold shift to zoom vertically", IGraphics::CORNER_L, 9.0f)) + { + if(Input()->ShiftIsPressed()) + m_ZoomEnvelopeY.ChangeValue(-0.1f * m_ZoomEnvelopeY.GetValue()); + else + m_ZoomEnvelopeX.ChangeValue(-0.1f * m_ZoomEnvelopeX.GetValue()); + } } // Margin on the right side @@ -5704,8 +5724,14 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) m_OffsetEnvelopeX += UI()->MouseDeltaX() / Graphics()->ScreenWidth() * UI()->Screen()->w / View.w; m_OffsetEnvelopeY -= UI()->MouseDeltaY() / Graphics()->ScreenHeight() * UI()->Screen()->h / View.h; } + if(Input()->KeyPress(KEY_KP_MULTIPLY)) + ResetZoomEnvelope(pEnvelope, s_ActiveChannels); if(Input()->ShiftIsPressed()) { + if(Input()->KeyPress(KEY_KP_MINUS)) + m_ZoomEnvelopeY.ChangeValue(0.1f * m_ZoomEnvelopeY.GetValue()); + if(Input()->KeyPress(KEY_KP_PLUS)) + m_ZoomEnvelopeY.ChangeValue(-0.1f * m_ZoomEnvelopeY.GetValue()); if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) m_ZoomEnvelopeY.ChangeValue(0.1f * m_ZoomEnvelopeY.GetValue()); if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) @@ -5713,6 +5739,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) } else { + if(Input()->KeyPress(KEY_KP_MINUS)) + m_ZoomEnvelopeX.ChangeValue(0.1f * m_ZoomEnvelopeX.GetValue()); + if(Input()->KeyPress(KEY_KP_PLUS)) + m_ZoomEnvelopeX.ChangeValue(-0.1f * m_ZoomEnvelopeX.GetValue()); if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) m_ZoomEnvelopeX.ChangeValue(0.1f * m_ZoomEnvelopeX.GetValue()); if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) @@ -6996,14 +7026,6 @@ void CEditor::Render() if(m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr) { - // handle zoom hotkeys - if(Input()->KeyPress(KEY_KP_MINUS)) - MapView()->Zoom()->ChangeValue(50.0f); - if(Input()->KeyPress(KEY_KP_PLUS)) - MapView()->Zoom()->ChangeValue(-50.0f); - if(Input()->KeyPress(KEY_KP_MULTIPLY)) - MapView()->ResetZoom(); - // handle brush save/load hotkeys for(int i = KEY_1; i <= KEY_0; i++) { @@ -7197,6 +7219,13 @@ void CEditor::Render() if(m_Dialog == DIALOG_NONE && !UI()->IsPopupHovered() && (!m_GuiActive || UI()->MouseInside(&View))) { + // handle zoom hotkeys + if(Input()->KeyPress(KEY_KP_MINUS)) + MapView()->Zoom()->ChangeValue(50.0f); + if(Input()->KeyPress(KEY_KP_PLUS)) + MapView()->Zoom()->ChangeValue(-50.0f); + if(Input()->KeyPress(KEY_KP_MULTIPLY)) + MapView()->ResetZoom(); if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)) MapView()->Zoom()->ChangeValue(20.0f); if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP)) From 1193361f1c52619c213c176715043e4cabdbc702 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Sun, 17 Sep 2023 18:22:03 +0200 Subject: [PATCH 17/70] Version 17.3 --- src/game/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/version.h b/src/game/version.h index db1d9c0a4f7..bb8461f6049 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -3,11 +3,11 @@ #ifndef GAME_VERSION_H #define GAME_VERSION_H #ifndef GAME_RELEASE_VERSION -#define GAME_RELEASE_VERSION "17.2.1" +#define GAME_RELEASE_VERSION "17.3" #endif #define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION #define GAME_NETVERSION "0.6 626fce9a778df4d4" -#define DDNET_VERSION_NUMBER 17021 +#define DDNET_VERSION_NUMBER 17030 extern const char *GIT_SHORTREV_HASH; #define GAME_NAME "DDNet" #endif From 95675a97ad0fee456e8d22878cc64eb3ff1f75e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 23 Sep 2023 16:33:12 +0200 Subject: [PATCH 18/70] Fix mangled highlighted text in server browser Refresh UI label text containers when the glyph count of the read-cursor has changed. Regression from #7203. --- src/game/client/ui.cpp | 6 +++++- src/game/client/ui.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 0481a7506fb..e88106eaae9 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -51,6 +51,7 @@ void CUIElement::SUIElementRect::Reset() m_TextColor = ColorRGBA(-1, -1, -1, -1); m_TextOutlineColor = ColorRGBA(-1, -1, -1, -1); m_QuadColor = ColorRGBA(-1, -1, -1, -1); + m_ReadCursorGlyphCount = -1; } void CUIElement::SUIElementRect::Draw(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding) @@ -688,9 +689,10 @@ void CUI::DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, cons void CUI::DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps, int StrLen, const CTextCursor *pReadCursor) { + const int ReadCursorGlyphCount = pReadCursor == nullptr ? -1 : pReadCursor->m_GlyphCount; bool NeedsRecreate = false; bool ColorChanged = RectEl.m_TextColor != TextRender()->GetTextColor() || RectEl.m_TextOutlineColor != TextRender()->GetTextOutlineColor(); - if((!RectEl.m_UITextContainer.Valid() && pText[0] != '\0' && StrLen != 0) || RectEl.m_Width != pRect->w || RectEl.m_Height != pRect->h || ColorChanged) + if((!RectEl.m_UITextContainer.Valid() && pText[0] != '\0' && StrLen != 0) || RectEl.m_Width != pRect->w || RectEl.m_Height != pRect->h || ColorChanged || RectEl.m_ReadCursorGlyphCount != ReadCursorGlyphCount) { NeedsRecreate = true; } @@ -723,6 +725,8 @@ void CUI::DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRe else RectEl.m_Text.clear(); + RectEl.m_ReadCursorGlyphCount = ReadCursorGlyphCount; + CUIRect TmpRect; TmpRect.x = 0; TmpRect.y = 0; diff --git a/src/game/client/ui.h b/src/game/client/ui.h index ac26a640be1..895961b61b4 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -153,6 +153,7 @@ class CUIElement int m_Corners; std::string m_Text; + int m_ReadCursorGlyphCount; CTextCursor m_Cursor; From 2d4bac8c0c349ee1205ec312426bb81a43d85b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 23 Sep 2023 21:06:39 +0200 Subject: [PATCH 19/70] Ensure strings in demo header are zero-terminated and valid UTF-8 Previously, if the demo header strings did not contain zero-termination, the client would render the strings and any following non-zero memory from the demo header. Now, demos will not be loaded, if any string in the header is not zero-terminated or not valid UTF-8. --- src/engine/demo.h | 12 ++++++++++++ src/engine/shared/demo.cpp | 25 +++++++++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/engine/demo.h b/src/engine/demo.h index 91f4f79989a..bf3b2e050ce 100644 --- a/src/engine/demo.h +++ b/src/engine/demo.h @@ -13,6 +13,8 @@ enum MAX_TIMELINE_MARKERS = 64 }; +static const unsigned char gs_aHeaderMarker[7] = {'T', 'W', 'D', 'E', 'M', 'O', 0}; + constexpr int g_DemoSpeeds = 22; extern const double g_aSpeeds[g_DemoSpeeds]; @@ -34,6 +36,16 @@ struct CDemoHeader char m_aType[8]; unsigned char m_aLength[sizeof(int32_t)]; char m_aTimestamp[20]; + + bool Valid() const + { + // Check marker and ensure that strings are zero-terminated and valid UTF-8. + return mem_comp(m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) == 0 && + mem_has_null(m_aNetversion, sizeof(m_aNetversion)) && str_utf8_check(m_aNetversion) && + mem_has_null(m_aMapName, sizeof(m_aMapName)) && str_utf8_check(m_aMapName) && + mem_has_null(m_aType, sizeof(m_aType)) && str_utf8_check(m_aType) && + mem_has_null(m_aTimestamp, sizeof(m_aTimestamp)) && str_utf8_check(m_aTimestamp); + } }; struct CTimelineMarkers diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index db27e699c5d..1dd5c04a43a 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -23,7 +23,6 @@ const CUuid SHA256_EXTENSION = {{0x6b, 0xe6, 0xda, 0x4a, 0xce, 0xbd, 0x38, 0x0c, 0x9b, 0x5b, 0x12, 0x89, 0xc8, 0x42, 0xd7, 0x80}}; -static const unsigned char gs_aHeaderMarker[7] = {'T', 'W', 'D', 'E', 'M', 'O', 0}; static const unsigned char gs_CurVersion = 6; static const unsigned char gs_OldVersion = 3; static const unsigned char gs_Sha256Version = 6; @@ -749,13 +748,12 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const m_LastSnapshotDataSize = -1; // read the header - io_read(m_File, &m_Info.m_Header, sizeof(m_Info.m_Header)); - if(mem_comp(m_Info.m_Header.m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) != 0) + if(io_read(m_File, &m_Info.m_Header, sizeof(m_Info.m_Header)) != sizeof(m_Info.m_Header) || !m_Info.m_Header.Valid()) { if(m_pConsole) { char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "'%s' is not a demo file", pFilename); + str_format(aBuf, sizeof(aBuf), "'%s' is not a valid demo file", pFilename); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", aBuf); } io_close(m_File); @@ -1112,18 +1110,25 @@ void CDemoPlayer::GetDemoName(char *pBuffer, int BufferSize) const bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo) const { - if(!pDemoHeader || !pTimelineMarkers || !pMapInfo) - return false; - mem_zero(pDemoHeader, sizeof(CDemoHeader)); mem_zero(pTimelineMarkers, sizeof(CTimelineMarkers)); + mem_zero(pMapInfo, sizeof(CMapInfo)); IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType); if(!File) return false; - io_read(File, pDemoHeader, sizeof(CDemoHeader)); - io_read(File, pTimelineMarkers, sizeof(CTimelineMarkers)); + if(io_read(File, pDemoHeader, sizeof(CDemoHeader)) != sizeof(CDemoHeader) || !pDemoHeader->Valid() || pDemoHeader->m_Version < gs_OldVersion) + { + mem_zero(pDemoHeader, sizeof(CDemoHeader)); + io_close(File); + return false; + } + + if(pDemoHeader->m_Version > gs_OldVersion) + { + io_read(File, pTimelineMarkers, sizeof(CTimelineMarkers)); + } str_copy(pMapInfo->m_aName, pDemoHeader->m_aMapName); pMapInfo->m_Crc = bytes_be_to_uint(pDemoHeader->m_aMapCrc); @@ -1150,7 +1155,7 @@ bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, i pMapInfo->m_Size = bytes_be_to_uint(pDemoHeader->m_aMapSize); io_close(File); - return !(mem_comp(pDemoHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || pDemoHeader->m_Version < gs_OldVersion); + return true; } int CDemoPlayer::GetDemoType() const From 436c977f7e54e0418f5da33bbf7e5f5a5e9dbe6d Mon Sep 17 00:00:00 2001 From: furo Date: Sat, 23 Sep 2023 23:51:16 +0200 Subject: [PATCH 20/70] Remove unused config, sv_suicide_penalty --- src/engine/shared/config_variables.h | 1 - src/game/server/ddracecommands.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 5f7c8eedbc5..e70cdac27ce 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -238,7 +238,6 @@ MACRO_CONFIG_INT(SvVoteMajority, sv_vote_majority, 0, 0, 1, CFGFLAG_SERVER, "Whe MACRO_CONFIG_INT(SvVoteMaxTotal, sv_vote_max_total, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "How many people can participate in a vote at max (0 = no limit by default)") MACRO_CONFIG_INT(SvVoteVetoTime, sv_vote_veto_time, 20, 0, 1000, CFGFLAG_SERVER, "Minutes of time on a server until a player can veto map change votes (0 = disabled)") MACRO_CONFIG_INT(SvKillDelay, sv_kill_delay, 1, 0, 9999, CFGFLAG_SERVER, "The minimum time in seconds between kills") -MACRO_CONFIG_INT(SvSuicidePenalty, sv_suicide_penalty, 0, 0, 9999, CFGFLAG_SERVER, "The minimum time in seconds between kill or /kills and respawn") MACRO_CONFIG_INT(SvMapWindow, sv_map_window, 15, 0, 100, CFGFLAG_SERVER, "Map downloading send-ahead window") MACRO_CONFIG_INT(SvFastDownload, sv_fast_download, 1, 0, 1, CFGFLAG_SERVER, "Enables fast download of maps") diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index ea803765034..5b762632bd1 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -401,7 +401,6 @@ void CGameContext::ConKill(IConsole::IResult *pResult, void *pUserData) pPlayer->m_LastKill = pSelf->Server()->Tick(); pPlayer->KillCharacter(WEAPON_SELF); - //pPlayer->m_RespawnTick = pSelf->Server()->Tick() + pSelf->Server()->TickSpeed() * g_Config.m_SvSuicidePenalty; } void CGameContext::ConForcePause(IConsole::IResult *pResult, void *pUserData) From e93325b1413e23b39195b3ace0c1a3ffb27deb86 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 24 Sep 2023 01:19:50 +0200 Subject: [PATCH 21/70] Increase freeview radius to kill tile border --- src/game/client/components/controls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index 3f79d576632..1a60e6cb8d8 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -412,8 +412,8 @@ void CControls::ClampMousePos() { if(m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID < 0) { - m_aMousePos[g_Config.m_ClDummy].x = clamp(m_aMousePos[g_Config.m_ClDummy].x, 0.0f, Collision()->GetWidth() * 32.0f); - m_aMousePos[g_Config.m_ClDummy].y = clamp(m_aMousePos[g_Config.m_ClDummy].y, 0.0f, Collision()->GetHeight() * 32.0f); + m_aMousePos[g_Config.m_ClDummy].x = clamp(m_aMousePos[g_Config.m_ClDummy].x, -201.0f * 32, (Collision()->GetWidth() + 201.0f) * 32.0f); + m_aMousePos[g_Config.m_ClDummy].y = clamp(m_aMousePos[g_Config.m_ClDummy].y, -201.0f * 32, (Collision()->GetHeight() + 201.0f) * 32.0f); } else { From 4476dfacde051550369747c17f054a2a232d0bba Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 24 Sep 2023 15:31:43 +0200 Subject: [PATCH 22/70] Configs used in OnConfigChange should trigger Conchain --- src/engine/server/register.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp index 74aaf7df5b4..958580a2ce1 100644 --- a/src/engine/server/register.cpp +++ b/src/engine/server/register.cpp @@ -503,7 +503,10 @@ CRegister::CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, int str_format(m_aConnlessTokenHex, sizeof(m_aConnlessTokenHex), "%08x", bytes_be_to_uint(aTokenBytes)); m_pConsole->Chain("sv_register", ConchainOnConfigChange, this); + m_pConsole->Chain("sv_register_extra", ConchainOnConfigChange, this); + m_pConsole->Chain("sv_register_url", ConchainOnConfigChange, this); m_pConsole->Chain("sv_sixup", ConchainOnConfigChange, this); + m_pConsole->Chain("sv_ipv4only", ConchainOnConfigChange, this); } void CRegister::Update() From ecaded8cce59e463cdb6c1300d55cdee4a7521bc Mon Sep 17 00:00:00 2001 From: Learath Date: Sun, 24 Sep 2023 18:14:09 +0200 Subject: [PATCH 23/70] Fix dead reckoning --- src/game/gamecore.cpp | 4 +++- src/game/server/entities/character.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index bf7e80c694a..a1de8899736 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -93,7 +93,9 @@ void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore m_Id = -1; // fail safe, if core's tuning didn't get updated at all, just fallback to world tuning. - m_Tuning = m_pWorld->m_aTuning[g_Config.m_ClDummy]; + if(m_pWorld) + m_Tuning = m_pWorld->m_aTuning[g_Config.m_ClDummy]; + Reset(); } diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 4960d3d1630..0fe23769516 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -85,6 +85,9 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) m_SendCore = CCharacterCore(); m_ReckoningCore = CCharacterCore(); + m_ReckoningCore.Init(nullptr, Collision()); + m_ReckoningCore.m_Tuning = *Tuning(); + GameServer()->m_World.InsertEntity(this); m_Alive = true; @@ -785,9 +788,6 @@ void CCharacter::TickDeferred() { // advance the dummy { - CWorldCore TempWorld; - m_ReckoningCore.Init(&TempWorld, Collision(), &Teams()->m_Core, m_pTeleOuts); - m_ReckoningCore.m_Id = m_pPlayer->GetCID(); m_ReckoningCore.Tick(false); m_ReckoningCore.Move(); m_ReckoningCore.Quantize(); @@ -1915,9 +1915,9 @@ void CCharacter::HandleTuneLayer() m_TuneZone = Collision()->IsTune(CurrentIndex); if(m_TuneZone) - m_Core.m_Tuning = TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore + m_Core.m_Tuning = m_ReckoningCore.m_Tuning = TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore else - m_Core.m_Tuning = *Tuning(); + m_Core.m_Tuning = m_ReckoningCore.m_Tuning = *Tuning(); if(m_TuneZone != m_TuneZoneOld) // don't send tunigs all the time { From 003d96e1c8b86484182e7f18fa09f262caddd4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 24 Sep 2023 19:46:49 +0200 Subject: [PATCH 24/70] Add `IConfigManager` getter to `CGameClient` and `CComponent` --- src/game/client/component.cpp | 1 + src/game/client/component.h | 4 ++++ src/game/client/components/binds.cpp | 5 +---- src/game/client/components/menus.cpp | 4 +--- src/game/client/gameclient.cpp | 3 ++- src/game/client/gameclient.h | 2 ++ 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/game/client/component.cpp b/src/game/client/component.cpp index e0d556612e1..754aea48d50 100644 --- a/src/game/client/component.cpp +++ b/src/game/client/component.cpp @@ -11,6 +11,7 @@ class IStorage *CComponent::Storage() const { return m_pClient->Storage(); } class CUI *CComponent::UI() const { return m_pClient->UI(); } class ISound *CComponent::Sound() const { return m_pClient->Sound(); } class CRenderTools *CComponent::RenderTools() const { return m_pClient->RenderTools(); } +class IConfigManager *CComponent::ConfigManager() const { return m_pClient->ConfigManager(); } class CConfig *CComponent::Config() const { return m_pClient->Config(); } class IConsole *CComponent::Console() const { return m_pClient->Console(); } class IDemoPlayer *CComponent::DemoPlayer() const { return m_pClient->DemoPlayer(); } diff --git a/src/game/client/component.h b/src/game/client/component.h index fb0f731b08e..8e754e7f32d 100644 --- a/src/game/client/component.h +++ b/src/game/client/component.h @@ -58,6 +58,10 @@ class CComponent * Get the render tools interface. */ class CRenderTools *RenderTools() const; + /** + * Get the config manager interface. + */ + class IConfigManager *ConfigManager() const; /** * Get the config interface. */ diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index b4d4526258f..594bc7f061c 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -250,10 +250,7 @@ void CBinds::SetDefaults() void CBinds::OnConsoleInit() { - // bindings - IConfigManager *pConfigManager = Kernel()->RequestInterface(); - if(pConfigManager) - pConfigManager->RegisterCallback(ConfigSaveCallback, this); + ConfigManager()->RegisterCallback(ConfigSaveCallback, this); Console()->Register("bind", "s[key] ?r[command]", CFGFLAG_CLIENT, ConBind, this, "Bind key to execute a command or view keybindings"); Console()->Register("binds", "?s[key]", CFGFLAG_CLIENT, ConBinds, this, "Print command executed by this keybinding or all binds"); diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 5d074e5d2ad..b6b7758e34a 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -882,9 +882,7 @@ void CMenus::OnInit() void CMenus::OnConsoleInit() { - auto *pConfigManager = Kernel()->RequestInterface(); - if(pConfigManager != nullptr) - pConfigManager->RegisterCallback(CMenus::ConfigSaveCallback, this); + ConfigManager()->RegisterCallback(CMenus::ConfigSaveCallback, this); Console()->Register("add_favorite_skin", "s[skin_name]", CFGFLAG_CLIENT, Con_AddFavoriteSkin, this, "Add a skin as a favorite"); Console()->Register("remove_favorite_skin", "s[skin_name]", CFGFLAG_CLIENT, Con_RemFavoriteSkin, this, "Remove a skin from the favorites"); } diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index ae72bcdf970..f6c1aba8f53 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -86,7 +86,8 @@ void CGameClient::OnConsoleInit() m_pClient = Kernel()->RequestInterface(); m_pTextRender = Kernel()->RequestInterface(); m_pSound = Kernel()->RequestInterface(); - m_pConfig = Kernel()->RequestInterface()->Values(); + m_pConfigManager = Kernel()->RequestInterface(); + m_pConfig = m_pConfigManager->Values(); m_pInput = Kernel()->RequestInterface(); m_pConsole = Kernel()->RequestInterface(); m_pStorage = Kernel()->RequestInterface(); diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index d758e07a5d9..d3b19931bbb 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -164,6 +164,7 @@ class CGameClient : public IGameClient class ITextRender *m_pTextRender; class IClient *m_pClient; class ISound *m_pSound; + class IConfigManager *m_pConfigManager; class CConfig *m_pConfig; class IConsole *m_pConsole; class IStorage *m_pStorage; @@ -230,6 +231,7 @@ class CGameClient : public IGameClient class ISound *Sound() const { return m_pSound; } class IInput *Input() const { return m_pInput; } class IStorage *Storage() const { return m_pStorage; } + class IConfigManager *ConfigManager() const { return m_pConfigManager; } class CConfig *Config() const { return m_pConfig; } class IConsole *Console() { return m_pConsole; } class ITextRender *TextRender() const { return m_pTextRender; } From 2a17d1b8ac0820e563fd80026dfe1e9f427a6d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 24 Sep 2023 20:01:33 +0200 Subject: [PATCH 25/70] Rename variables containing `ForeGround` and `BackGround` To `Foreground` and `Background` respectively. --- src/game/client/components/mapsounds.cpp | 4 ++-- src/game/client/components/menus_demo.cpp | 8 ++++---- src/game/client/components/menus_settings.cpp | 6 +++--- src/game/client/gameclient.cpp | 6 +++--- src/game/client/gameclient.h | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/game/client/components/mapsounds.cpp b/src/game/client/components/mapsounds.cpp index 704aaa5e603..c333637daeb 100644 --- a/src/game/client/components/mapsounds.cpp +++ b/src/game/client/components/mapsounds.cpp @@ -199,7 +199,7 @@ void CMapSounds::OnRender() if(Voice.m_pSource->m_PosEnv >= 0) { ColorRGBA Channels; - CMapLayers::EnvelopeEval(Voice.m_pSource->m_PosEnvOffset, Voice.m_pSource->m_PosEnv, Channels, &m_pClient->m_MapLayersBackGround); + CMapLayers::EnvelopeEval(Voice.m_pSource->m_PosEnvOffset, Voice.m_pSource->m_PosEnv, Channels, &m_pClient->m_MapLayersBackground); OffsetX = Channels.r; OffsetY = Channels.g; } @@ -218,7 +218,7 @@ void CMapSounds::OnRender() if(Voice.m_pSource->m_SoundEnv >= 0) { ColorRGBA Channels; - CMapLayers::EnvelopeEval(Voice.m_pSource->m_SoundEnvOffset, Voice.m_pSource->m_SoundEnv, Channels, &m_pClient->m_MapLayersBackGround); + CMapLayers::EnvelopeEval(Voice.m_pSource->m_SoundEnvOffset, Voice.m_pSource->m_SoundEnv, Channels, &m_pClient->m_MapLayersBackground); float Volume = clamp(Channels.r, 0.0f, 1.0f); Sound()->SetVoiceVolume(Voice.m_Voice, Volume); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index aa8078b9f85..1cb2a4dd85c 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -89,8 +89,8 @@ void CMenus::HandleDemoSeeking(float PositionToSeek, float TimeToSeek) else DemoPlayer()->SeekPercent(PositionToSeek); m_pClient->m_SuppressEvents = false; - m_pClient->m_MapLayersBackGround.EnvelopeUpdate(); - m_pClient->m_MapLayersForeGround.EnvelopeUpdate(); + m_pClient->m_MapLayersBackground.EnvelopeUpdate(); + m_pClient->m_MapLayersForeground.EnvelopeUpdate(); if(!DemoPlayer()->BaseInfo()->m_Paused && PositionToSeek == 1.0f) DemoPlayer()->Pause(); } @@ -102,8 +102,8 @@ void CMenus::DemoSeekTick(IDemoPlayer::ETickOffset TickOffset) DemoPlayer()->SeekTick(TickOffset); m_pClient->m_SuppressEvents = false; DemoPlayer()->Pause(); - m_pClient->m_MapLayersBackGround.EnvelopeUpdate(); - m_pClient->m_MapLayersForeGround.EnvelopeUpdate(); + m_pClient->m_MapLayersBackground.EnvelopeUpdate(); + m_pClient->m_MapLayersForeground.EnvelopeUpdate(); } void CMenus::RenderDemoPlayer(CUIRect MainView) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 5a47f1ca5f5..1a98e0c99c4 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -3127,8 +3127,8 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView) static CButtonContainer s_BackgroundEntitiesReloadButton; if(DoButton_Menu(&s_BackgroundEntitiesReloadButton, Localize("Reload"), 0, &Button)) { - if(str_comp(g_Config.m_ClBackgroundEntities, m_pClient->m_BackGround.MapName()) != 0) - m_pClient->m_BackGround.LoadBackground(); + if(str_comp(g_Config.m_ClBackgroundEntities, m_pClient->m_Background.MapName()) != 0) + m_pClient->m_Background.LoadBackground(); } Background.HSplitTop(20.0f, &Button, &Background); @@ -3140,7 +3140,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView) g_Config.m_ClBackgroundEntities[0] = '\0'; else str_copy(g_Config.m_ClBackgroundEntities, CURRENT_MAP); - m_pClient->m_BackGround.LoadBackground(); + m_pClient->m_Background.LoadBackground(); } Background.HSplitTop(20.0f, &Button, &Background); diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index ae72bcdf970..e6d11e64740 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -118,13 +118,13 @@ void CGameClient::OnConsoleInit() &m_Particles, // doesn't render anything, just updates all the particles &m_RaceDemo, &m_MapSounds, - &m_BackGround, // render instead of m_MapLayersBackGround when g_Config.m_ClOverlayEntities == 100 - &m_MapLayersBackGround, // first to render + &m_Background, // render instead of m_MapLayersBackground when g_Config.m_ClOverlayEntities == 100 + &m_MapLayersBackground, // first to render &m_Particles.m_RenderTrail, &m_Items, &m_Players, &m_Ghost, - &m_MapLayersForeGround, + &m_MapLayersForeground, &m_Particles.m_RenderExplosions, &m_NamePlates, &m_Particles.m_RenderExtra, diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index d758e07a5d9..b99a76e9ece 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -141,9 +141,9 @@ class CGameClient : public IGameClient CItems m_Items; CMapImages m_MapImages; - CMapLayers m_MapLayersBackGround = CMapLayers{CMapLayers::TYPE_BACKGROUND}; - CMapLayers m_MapLayersForeGround = CMapLayers{CMapLayers::TYPE_FOREGROUND}; - CBackground m_BackGround; + CMapLayers m_MapLayersBackground = CMapLayers{CMapLayers::TYPE_BACKGROUND}; + CMapLayers m_MapLayersForeground = CMapLayers{CMapLayers::TYPE_FOREGROUND}; + CBackground m_Background; CMenuBackground m_MenuBackground; CMapSounds m_MapSounds; From 99cee23de45306ef48622eeaf8d977cb860d4053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 24 Sep 2023 22:16:22 +0200 Subject: [PATCH 26/70] Add assertion when double-freeing texture index Simplify the handling of free texture indices by using `-1` only for indices which are currently in use, whereas the size of the vector is now used to indicate the last free index. Otherwise the assertions incorrectly detect the last texture index always being in use because `-1` was used for both states. --- src/engine/client/graphics_threaded.cpp | 17 +++++++---------- src/engine/client/graphics_threaded.h | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 474c593a8ad..0960182b400 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -264,18 +264,14 @@ void CGraphics_Threaded::LinesDraw(const CLineItem *pArray, int Num) IGraphics::CTextureHandle CGraphics_Threaded::FindFreeTextureIndex() { - int Tex = m_FirstFreeTexture; - if(Tex == -1) + const size_t CurSize = m_vTextureIndices.size(); + if(m_FirstFreeTexture == CurSize) { - const size_t CurSize = m_vTextureIndices.size(); m_vTextureIndices.resize(CurSize * 2); - for(size_t i = 0; i < CurSize - 1; ++i) - { + for(size_t i = 0; i < CurSize; ++i) m_vTextureIndices[CurSize + i] = CurSize + i + 1; - } - m_vTextureIndices.back() = -1; - Tex = CurSize; } + const size_t Tex = m_FirstFreeTexture; m_FirstFreeTexture = m_vTextureIndices[Tex]; m_vTextureIndices[Tex] = -1; return CreateTextureHandle(Tex); @@ -284,6 +280,8 @@ IGraphics::CTextureHandle CGraphics_Threaded::FindFreeTextureIndex() void CGraphics_Threaded::FreeTextureIndex(CTextureHandle *pIndex) { dbg_assert(pIndex->IsValid(), "Cannot free invalid texture index"); + dbg_assert(m_vTextureIndices[pIndex->Id()] == -1, "Cannot free already freed texture index"); + m_vTextureIndices[pIndex->Id()] = m_FirstFreeTexture; m_FirstFreeTexture = pIndex->Id(); pIndex->Invalidate(); @@ -2598,9 +2596,8 @@ int CGraphics_Threaded::Init() // init textures m_FirstFreeTexture = 0; m_vTextureIndices.resize(CCommandBuffer::MAX_TEXTURES); - for(size_t i = 0; i < m_vTextureIndices.size() - 1; ++i) + for(size_t i = 0; i < m_vTextureIndices.size(); ++i) m_vTextureIndices[i] = i + 1; - m_vTextureIndices.back() = -1; m_FirstFreeVertexArrayInfo = -1; m_FirstFreeBufferObjectIndex = -1; diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index 7a39326844f..72877e793b2 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -819,7 +819,7 @@ class CGraphics_Threaded : public IEngineGraphics CTextureHandle m_InvalidTexture; std::vector m_vTextureIndices; - int m_FirstFreeTexture; + size_t m_FirstFreeTexture; int m_TextureMemoryUsage; std::vector m_vSpriteHelper; From 96a68455caf00ac58195c8989e7a75645cbe7d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 24 Sep 2023 22:24:40 +0200 Subject: [PATCH 27/70] Ensure OpenGL texture vectors are large enough In the unlikely case that the wanted texture slot is larger than twice the size of the original texture vector. --- src/engine/client/backend/opengl/backend_opengl.cpp | 2 +- src/engine/client/backend/opengl/backend_opengl3.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/client/backend/opengl/backend_opengl.cpp b/src/engine/client/backend/opengl/backend_opengl.cpp index cb0ef482401..176f74b1f33 100644 --- a/src/engine/client/backend/opengl/backend_opengl.cpp +++ b/src/engine/client/backend/opengl/backend_opengl.cpp @@ -729,7 +729,7 @@ void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int He glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_MaxTexSize); } - if(Slot >= (int)m_vTextures.size()) + while(Slot >= (int)m_vTextures.size()) m_vTextures.resize(m_vTextures.size() * 2); m_vTextures[Slot].m_ResizeWidth = -1.f; diff --git a/src/engine/client/backend/opengl/backend_opengl3.cpp b/src/engine/client/backend/opengl/backend_opengl3.cpp index e251329ed0b..5fdee8c2c65 100644 --- a/src/engine/client/backend/opengl/backend_opengl3.cpp +++ b/src/engine/client/backend/opengl/backend_opengl3.cpp @@ -579,7 +579,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Destroy(const CCommandBuff void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, void *pTexData) { - if(Slot >= (int)m_vTextures.size()) + while(Slot >= (int)m_vTextures.size()) m_vTextures.resize(m_vTextures.size() * 2); // resample if needed From 25101d2cb094bf9c66ca2eea46225f4c19935c14 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 24 Sep 2023 23:21:10 +0200 Subject: [PATCH 28/70] Add missing descriptions of commands. --- src/engine/client/client.cpp | 4 ++-- src/game/client/components/ghost.cpp | 2 +- src/game/ddracecommands.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index a0f752aedb6..c628d292b7a 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4417,8 +4417,8 @@ void CClient::RegisterCommands() m_pConsole->Register("end_favorite_group", "", CFGFLAG_CLIENT, Con_EndFavoriteGroup, this, "Use this after `add_favorite` to group favorites. Start with `begin_favorite_group`"); m_pConsole->Register("add_favorite", "s[host|ip] ?s['allow_ping']", CFGFLAG_CLIENT, Con_AddFavorite, this, "Add a server as a favorite"); m_pConsole->Register("remove_favorite", "r[host|ip]", CFGFLAG_CLIENT, Con_RemoveFavorite, this, "Remove a server from favorites"); - m_pConsole->Register("demo_slice_start", "", CFGFLAG_CLIENT, Con_DemoSliceBegin, this, ""); - m_pConsole->Register("demo_slice_end", "", CFGFLAG_CLIENT, Con_DemoSliceEnd, this, ""); + m_pConsole->Register("demo_slice_start", "", CFGFLAG_CLIENT, Con_DemoSliceBegin, this, "Mark the beginning of a cut"); + m_pConsole->Register("demo_slice_end", "", CFGFLAG_CLIENT, Con_DemoSliceEnd, this, "Mark the end of a cut"); m_pConsole->Register("demo_play", "", CFGFLAG_CLIENT, Con_DemoPlay, this, "Play demo"); m_pConsole->Register("demo_speed", "i[speed]", CFGFLAG_CLIENT, Con_DemoSpeed, this, "Set demo speed"); diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index e1f007c8a46..2bffbaa216d 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -608,7 +608,7 @@ void CGhost::OnConsoleInit() m_pGhostLoader = Kernel()->RequestInterface(); m_pGhostRecorder = Kernel()->RequestInterface(); - Console()->Register("gplay", "", CFGFLAG_CLIENT, ConGPlay, this, ""); + Console()->Register("gplay", "", CFGFLAG_CLIENT, ConGPlay, this, "Start playback of ghosts"); } void CGhost::OnMessage(int MsgType, void *pRawMsg) diff --git a/src/game/ddracecommands.h b/src/game/ddracecommands.h index 7e17a78039c..bb8ecc8e3f4 100644 --- a/src/game/ddracecommands.h +++ b/src/game/ddracecommands.h @@ -52,12 +52,12 @@ CONSOLE_COMMAND("uninvite", "v[id] i[team]", CFGFLAG_SERVER, ConUninvite, this, CONSOLE_COMMAND("vote_mute", "v[id] i[seconds] ?r[reason]", CFGFLAG_SERVER, ConVoteMute, this, "Remove v's right to vote for i seconds") CONSOLE_COMMAND("vote_unmute", "v[id]", CFGFLAG_SERVER, ConVoteUnmute, this, "Give back v's right to vote.") CONSOLE_COMMAND("vote_mutes", "", CFGFLAG_SERVER, ConVoteMutes, this, "List the current active vote mutes.") -CONSOLE_COMMAND("mute", "", CFGFLAG_SERVER, ConMute, this, "") +CONSOLE_COMMAND("mute", "", CFGFLAG_SERVER, ConMute, this, "Use either 'muteid ' or 'muteip '") CONSOLE_COMMAND("muteid", "v[id] i[seconds] ?r[reason]", CFGFLAG_SERVER, ConMuteID, this, "Mute player with id") CONSOLE_COMMAND("muteip", "s[ip] i[seconds] ?r[reason]", CFGFLAG_SERVER, ConMuteIP, this, "Mute player with IP address") CONSOLE_COMMAND("unmute", "i[muteid]", CFGFLAG_SERVER, ConUnmute, this, "Unmute mute with number from \"mutes\"") CONSOLE_COMMAND("unmuteid", "v[id]", CFGFLAG_SERVER, ConUnmuteID, this, "Unmute player with id") -CONSOLE_COMMAND("mutes", "", CFGFLAG_SERVER, ConMutes, this, "") +CONSOLE_COMMAND("mutes", "", CFGFLAG_SERVER, ConMutes, this, "Show all active mutes") CONSOLE_COMMAND("moderate", "", CFGFLAG_SERVER, ConModerate, this, "Enables/disables active moderator mode for the player") CONSOLE_COMMAND("vote_no", "", CFGFLAG_SERVER, ConVoteNo, this, "Same as \"vote no\"") CONSOLE_COMMAND("save_dry", "", CFGFLAG_SERVER, ConDrySave, this, "Dump the current savestring") From 8ebe3c1b3599fcf1da0591987d66ad6c09a74ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 25 Sep 2023 17:19:48 +0200 Subject: [PATCH 29/70] Use `FILE *` on Windows again, only use `HANDLE` for opening This mostly reverts #6937 by making our `IOHANDLE` effectively `FILE *` on all systems again. We still use `CreateFileW` to open a `HANDLE` initially so we can specify the necessary flag so the file can be moved/deleted while open, which we can't do with the `FILE *` based `fopen` functions on Windows. This brings back the automatic I/O buffering on Windows, causing significantly less system calls when saving files. Closes #7226. --- src/base/system.cpp | 76 +++++++-------------------------------------- 1 file changed, 11 insertions(+), 65 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index d8688ac0152..c26df32e3f5 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -92,29 +92,17 @@ IOHANDLE io_stdin() { -#if defined(CONF_FAMILY_WINDOWS) - return GetStdHandle(STD_INPUT_HANDLE); -#else return stdin; -#endif } IOHANDLE io_stdout() { -#if defined(CONF_FAMILY_WINDOWS) - return GetStdHandle(STD_OUTPUT_HANDLE); -#else return stdout; -#endif } IOHANDLE io_stderr() { -#if defined(CONF_FAMILY_WINDOWS) - return GetStdHandle(STD_ERROR_HANDLE); -#else return stderr; -#endif } IOHANDLE io_current_exe() @@ -281,20 +269,24 @@ IOHANDLE io_open_impl(const char *filename, int flags) const std::wstring wide_filename = windows_utf8_to_wide(filename); DWORD desired_access; DWORD creation_disposition; + const char *open_mode; if((flags & IOFLAG_READ) != 0) { desired_access = FILE_READ_DATA; creation_disposition = OPEN_EXISTING; + open_mode = "rb"; } else if(flags == IOFLAG_WRITE) { desired_access = FILE_WRITE_DATA; creation_disposition = OPEN_ALWAYS; + open_mode = "wb"; } else if(flags == IOFLAG_APPEND) { desired_access = FILE_APPEND_DATA; creation_disposition = OPEN_ALWAYS; + open_mode = "ab"; } else { @@ -303,8 +295,12 @@ IOHANDLE io_open_impl(const char *filename, int flags) } HANDLE handle = CreateFileW(wide_filename.c_str(), desired_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr); if(handle == INVALID_HANDLE_VALUE) - return nullptr; // otherwise all existing checks don't work for the invalid handle - return handle; + return nullptr; + const int file_descriptor = _open_osfhandle((intptr_t)handle, 0); + dbg_assert(file_descriptor != -1, "_open_osfhandle failure"); + FILE *file_stream = _fdopen(file_descriptor, open_mode); + dbg_assert(file_stream != nullptr, "_fdopen failure"); + return file_stream; #else const char *open_mode; if((flags & IOFLAG_READ) != 0) @@ -345,13 +341,7 @@ IOHANDLE io_open(const char *filename, int flags) unsigned io_read(IOHANDLE io, void *buffer, unsigned size) { -#if defined(CONF_FAMILY_WINDOWS) - DWORD actual_size; - ReadFile((HANDLE)io, buffer, size, &actual_size, nullptr); - return actual_size; -#else return fread(buffer, 1, size, (FILE *)io); -#endif } void io_read_all(IOHANDLE io, void **result, unsigned *result_len) @@ -406,25 +396,6 @@ unsigned io_skip(IOHANDLE io, int size) int io_seek(IOHANDLE io, int offset, int origin) { -#if defined(CONF_FAMILY_WINDOWS) - DWORD move_method; - switch(origin) - { - case IOSEEK_START: - move_method = FILE_BEGIN; - break; - case IOSEEK_CUR: - move_method = FILE_CURRENT; - break; - case IOSEEK_END: - move_method = FILE_END; - break; - default: - dbg_assert(false, "origin invalid"); - return -1; - } - return SetFilePointer((HANDLE)io, offset, nullptr, move_method) == INVALID_SET_FILE_POINTER ? -1 : 0; -#else int real_origin; switch(origin) { @@ -442,17 +413,11 @@ int io_seek(IOHANDLE io, int offset, int origin) return -1; } return fseek((FILE *)io, offset, real_origin); -#endif } long int io_tell(IOHANDLE io) { -#if defined(CONF_FAMILY_WINDOWS) - const DWORD position = SetFilePointer((HANDLE)io, 0, nullptr, FILE_CURRENT); - return position == INVALID_SET_FILE_POINTER ? -1 : position; -#else return ftell((FILE *)io); -#endif } long int io_length(IOHANDLE io) @@ -466,23 +431,12 @@ long int io_length(IOHANDLE io) int io_error(IOHANDLE io) { -#if defined(CONF_FAMILY_WINDOWS) - // Only works when called directly after the operation that failed - return GetLastError(); -#else return ferror((FILE *)io); -#endif } unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) { -#if defined(CONF_FAMILY_WINDOWS) - DWORD actual_size; - WriteFile((HANDLE)io, buffer, size, &actual_size, nullptr); - return actual_size; -#else return fwrite(buffer, 1, size, (FILE *)io); -#endif } bool io_write_newline(IOHANDLE io) @@ -496,20 +450,12 @@ bool io_write_newline(IOHANDLE io) int io_close(IOHANDLE io) { -#if defined(CONF_FAMILY_WINDOWS) - return CloseHandle((HANDLE)io) == 0; -#else return fclose((FILE *)io) != 0; -#endif } int io_flush(IOHANDLE io) { -#if defined(CONF_FAMILY_WINDOWS) - return FlushFileBuffers((HANDLE)io) == FALSE; -#else return fflush((FILE *)io); -#endif } int io_sync(IOHANDLE io) @@ -519,7 +465,7 @@ int io_sync(IOHANDLE io) return 1; } #if defined(CONF_FAMILY_WINDOWS) - return FlushFileBuffers((HANDLE)io) == 0; + return FlushFileBuffers((HANDLE)_get_osfhandle(_fileno((FILE *)io))) == FALSE; #else return fsync(fileno((FILE *)io)) != 0; #endif From 3023d0b1002a537eb35dd2f2effb229387813113 Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Mon, 25 Sep 2023 14:23:46 -0300 Subject: [PATCH 30/70] Update brazilian_portuguese.txt --- data/languages/brazilian_portuguese.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data/languages/brazilian_portuguese.txt b/data/languages/brazilian_portuguese.txt index fa08983a57e..e4da0b4fd8f 100644 --- a/data/languages/brazilian_portuguese.txt +++ b/data/languages/brazilian_portuguese.txt @@ -28,6 +28,7 @@ # Rafael Fontenelle 2023-03-26 19:11:00 # Rafael Fontenelle 2023-07-07 12:11:00 # Rafael Fontenelle 2023-08-14 14:17:00 +# Rafael Fontenelle 2023-09-25 14:23:00 ##### /authors ##### ##### translated strings ##### @@ -1754,21 +1755,21 @@ Moved ingame == Movido no jogo Go back the specified duration -== +== Voltar na duração especificada [Demo player duration] %d min. -== +== %d min. [Demo player duration] %d sec. -== +== $d seg. Change the skip duration -== +== Alterar a duração de pulo Go forward the specified duration -== +== Avançar na duração especificada Render cut to video -== +== Renderizar corte do vídeo From d165f9e8a9da7a38fa51b92ca99af0e2f92d6c7b Mon Sep 17 00:00:00 2001 From: furo Date: Tue, 26 Sep 2023 02:24:00 +0200 Subject: [PATCH 31/70] Add votes command to rcon --- src/game/server/gamecontext.cpp | 25 +++++++++++++++++++++++++ src/game/server/gamecontext.h | 1 + 2 files changed, 26 insertions(+) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 1c000b37e94..2c2df7d254e 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3388,6 +3388,30 @@ void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) pSelf->ForceVote(pResult->m_ClientID, false); } +void CGameContext::ConVotes(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + + int Page = pResult->NumArguments() > 0 ? pResult->GetInteger(0) : 0; + static const int s_EntriesPerPage = 20; + const int Start = Page * s_EntriesPerPage; + const int End = (Page + 1) * s_EntriesPerPage; + + char aBuf[512]; + int Count = 0; + for(CVoteOptionServer *pOption = pSelf->m_pVoteOptionFirst; pOption; pOption = pOption->m_pNext, Count++) + { + if(Count < Start || Count >= End) + { + continue; + } + str_format(aBuf, sizeof(aBuf), "\"%s\" -> \"%s\"", pOption->m_aDescription, pOption->m_aCommand); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "votes", aBuf); + } + str_format(aBuf, sizeof(aBuf), "%d %s, showing entries %d - %d", Count, Count == 1 ? "vote" : "votes", Start, End - 1); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "votes", aBuf); +} + void CGameContext::ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { pfnCallback(pResult, pCallbackUserData); @@ -3443,6 +3467,7 @@ void CGameContext::OnConsoleInit() Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options"); Console()->Register("add_map_votes", "", CFGFLAG_SERVER, ConAddMapVotes, this, "Automatically adds voting options for all maps"); Console()->Register("vote", "r['yes'|'no']", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no"); + Console()->Register("votes", "?i[page]", CFGFLAG_SERVER, ConVotes, this, "Show all votes (page 0 by default, 20 entries per page)"); Console()->Register("dump_antibot", "", CFGFLAG_SERVER, ConDumpAntibot, this, "Dumps the antibot status"); Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index e8b84df6399..54d81f249ff 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -128,6 +128,7 @@ class CGameContext : public IGameServer static void ConClearVotes(IConsole::IResult *pResult, void *pUserData); static void ConAddMapVotes(IConsole::IResult *pResult, void *pUserData); static void ConVote(IConsole::IResult *pResult, void *pUserData); + static void ConVotes(IConsole::IResult *pResult, void *pUserData); static void ConVoteNo(IConsole::IResult *pResult, void *pUserData); static void ConDrySave(IConsole::IResult *pResult, void *pUserData); static void ConDumpAntibot(IConsole::IResult *pResult, void *pUserData); From eabe59b4131a8ad5f896b9370319dfd9c80211da Mon Sep 17 00:00:00 2001 From: furo Date: Tue, 26 Sep 2023 12:13:49 +0200 Subject: [PATCH 32/70] Change format of `votes` --- src/game/server/gamecontext.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 2c2df7d254e..984d4e2daeb 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3405,7 +3405,15 @@ void CGameContext::ConVotes(IConsole::IResult *pResult, void *pUserData) { continue; } - str_format(aBuf, sizeof(aBuf), "\"%s\" -> \"%s\"", pOption->m_aDescription, pOption->m_aCommand); + + str_copy(aBuf, "add_vote \""); + char *pDst = aBuf + str_length(aBuf); + str_escape(&pDst, pOption->m_aDescription, aBuf + sizeof(aBuf)); + str_append(aBuf, "\" \""); + pDst = aBuf + str_length(aBuf); + str_escape(&pDst, pOption->m_aCommand, aBuf + sizeof(aBuf)); + str_append(aBuf, "\""); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "votes", aBuf); } str_format(aBuf, sizeof(aBuf), "%d %s, showing entries %d - %d", Count, Count == 1 ? "vote" : "votes", Start, End - 1); From 74192b905196de952691574c0b3c50fafb1aeaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Tue, 26 Sep 2023 20:03:45 +0200 Subject: [PATCH 33/70] Add `CSnapshot::EmptySnapshot`, mark pointer arguments as `const` Instead of keeping track of a permanently empty `CSnapshot` object in client and server separately, add `CSnapshot::EmptySnapshot` to access a singleton empty `CSnapshot`. Mark pointer parameters of snapshot functions as `const` when possible. --- src/engine/client/client.cpp | 6 +----- src/engine/server/server.cpp | 5 +---- src/engine/shared/snapshot.cpp | 14 ++++++++------ src/engine/shared/snapshot.h | 23 +++++++++++------------ 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index c628d292b7a..eac356cacec 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1985,8 +1985,6 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy) if((NumParts < CSnapshot::MAX_PARTS && m_aSnapshotParts[Conn] == (((uint64_t)(1) << NumParts) - 1)) || (NumParts == CSnapshot::MAX_PARTS && m_aSnapshotParts[Conn] == std::numeric_limits::max())) { - static CSnapshot Emptysnap; - CSnapshot *pDeltaShot = &Emptysnap; unsigned char aTmpBuffer2[CSnapshot::MAX_SIZE]; unsigned char aTmpBuffer3[CSnapshot::MAX_SIZE]; CSnapshot *pTmpBuffer3 = (CSnapshot *)aTmpBuffer3; // Fix compiler warning for strict-aliasing @@ -1995,9 +1993,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy) m_aSnapshotParts[Conn] = 0; // find snapshot that we should use as delta - Emptysnap.Clear(); - - // find delta + const CSnapshot *pDeltaShot = CSnapshot::EmptySnapshot(); if(DeltaTick >= 0) { int DeltashotSize = m_aSnapshotStorage[Conn].Get(DeltaTick, 0, &pDeltaShot, 0); diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 77223869b66..412758d568d 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -974,11 +974,8 @@ void CServer::DoSnapshot() m_aClients[i].m_Snapshots.Add(m_CurrentGameTick, time_get(), SnapshotSize, pData, 0, nullptr); // find snapshot that we can perform delta against - static CSnapshot s_EmptySnap; - s_EmptySnap.Clear(); - int DeltaTick = -1; - CSnapshot *pDeltashot = &s_EmptySnap; + const CSnapshot *pDeltashot = CSnapshot::EmptySnapshot(); { int DeltashotSize = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &pDeltashot, 0); if(DeltashotSize >= 0) diff --git a/src/engine/shared/snapshot.cpp b/src/engine/shared/snapshot.cpp index 4e4c53e30a0..5449a071692 100644 --- a/src/engine/shared/snapshot.cpp +++ b/src/engine/shared/snapshot.cpp @@ -19,6 +19,8 @@ const CSnapshotItem *CSnapshot::GetItem(int Index) const return (const CSnapshotItem *)(DataStart() + Offsets()[Index]); } +const CSnapshot CSnapshot::ms_EmptySnapshot; + int CSnapshot::GetItemSize(int Index) const { if(Index == m_NumItems - 1) @@ -168,7 +170,7 @@ inline size_t CalcHashID(int Key) return Hash % HASHLIST_SIZE; } -static void GenerateHash(CItemList *pHashlist, CSnapshot *pSnapshot) +static void GenerateHash(CItemList *pHashlist, const CSnapshot *pSnapshot) { for(int i = 0; i < HASHLIST_SIZE; i++) pHashlist[i].m_Num = 0; @@ -215,7 +217,7 @@ int CSnapshotDelta::DiffItem(const int *pPast, const int *pCurrent, int *pOut, i return Needed; } -void CSnapshotDelta::UndiffItem(const int *pPast, int *pDiff, int *pOut, int Size, int *pDataRate) +void CSnapshotDelta::UndiffItem(const int *pPast, const int *pDiff, int *pOut, int Size, int *pDataRate) { while(Size) { @@ -267,7 +269,7 @@ const CSnapshotDelta::CData *CSnapshotDelta::EmptyDelta() const } // TODO: OPT: this should be made much faster -int CSnapshotDelta::CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pDstData) +int CSnapshotDelta::CreateDelta(const CSnapshot *pFrom, CSnapshot *pTo, void *pDstData) { CData *pDelta = (CData *)pDstData; int *pData = (int *)pDelta->m_aData; @@ -357,7 +359,7 @@ static int RangeCheck(void *pEnd, void *pPtr, int Size) return 0; } -int CSnapshotDelta::UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, const void *pSrcData, int DataSize) +int CSnapshotDelta::UnpackDelta(const CSnapshot *pFrom, CSnapshot *pTo, const void *pSrcData, int DataSize) { CData *pDelta = (CData *)pSrcData; int *pData = (int *)pDelta->m_aData; @@ -509,7 +511,7 @@ void CSnapshotStorage::PurgeUntil(int Tick) m_pLast = 0; } -void CSnapshotStorage::Add(int Tick, int64_t Tagtime, int DataSize, void *pData, int AltDataSize, void *pAltData) +void CSnapshotStorage::Add(int Tick, int64_t Tagtime, int DataSize, const void *pData, int AltDataSize, const void *pAltData) { // allocate memory for holder + snapshot_data int TotalSize = sizeof(CHolder) + DataSize; @@ -550,7 +552,7 @@ void CSnapshotStorage::Add(int Tick, int64_t Tagtime, int DataSize, void *pData, m_pLast = pHolder; } -int CSnapshotStorage::Get(int Tick, int64_t *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData) +int CSnapshotStorage::Get(int Tick, int64_t *pTagtime, const CSnapshot **ppData, const CSnapshot **ppAltData) { CHolder *pHolder = m_pFirst; diff --git a/src/engine/shared/snapshot.h b/src/engine/shared/snapshot.h index addb5dbd277..0388e533f9e 100644 --- a/src/engine/shared/snapshot.h +++ b/src/engine/shared/snapshot.h @@ -26,8 +26,8 @@ class CSnapshotItem class CSnapshot { friend class CSnapshotBuilder; - int m_DataSize; - int m_NumItems; + int m_DataSize = 0; + int m_NumItems = 0; int *Offsets() const { return (int *)(this + 1); } char *DataStart() const { return (char *)(Offsets() + m_NumItems); } @@ -35,6 +35,8 @@ class CSnapshot size_t OffsetSize() const { return sizeof(int) * m_NumItems; } size_t TotalSize() const { return sizeof(CSnapshot) + OffsetSize() + m_DataSize; } + static const CSnapshot ms_EmptySnapshot; + public: enum { @@ -46,11 +48,6 @@ class CSnapshot MAX_SIZE = MAX_PARTS * 1024 }; - void Clear() - { - m_DataSize = 0; - m_NumItems = 0; - } int NumItems() const { return m_NumItems; } const CSnapshotItem *GetItem(int Index) const; int GetItemSize(int Index) const; @@ -62,6 +59,8 @@ class CSnapshot unsigned Crc(); void DebugDump(); bool IsValid(size_t ActualSize) const; + + static const CSnapshot *EmptySnapshot() { return &ms_EmptySnapshot; } }; // CSnapshotDelta @@ -88,7 +87,7 @@ class CSnapshotDelta int m_aSnapshotDataUpdates[CSnapshot::MAX_TYPE + 1]; CData m_Empty; - static void UndiffItem(const int *pPast, int *pDiff, int *pOut, int Size, int *pDataRate); + static void UndiffItem(const int *pPast, const int *pDiff, int *pOut, int Size, int *pDataRate); public: static int DiffItem(const int *pPast, const int *pCurrent, int *pOut, int Size); @@ -98,8 +97,8 @@ class CSnapshotDelta int GetDataUpdates(int Index) const { return m_aSnapshotDataUpdates[Index]; } void SetStaticsize(int ItemType, int Size); const CData *EmptyDelta() const; - int CreateDelta(class CSnapshot *pFrom, class CSnapshot *pTo, void *pDstData); - int UnpackDelta(class CSnapshot *pFrom, class CSnapshot *pTo, const void *pSrcData, int DataSize); + int CreateDelta(const class CSnapshot *pFrom, class CSnapshot *pTo, void *pDstData); + int UnpackDelta(const class CSnapshot *pFrom, class CSnapshot *pTo, const void *pSrcData, int DataSize); }; // CSnapshotStorage @@ -131,8 +130,8 @@ class CSnapshotStorage void Init(); void PurgeAll(); void PurgeUntil(int Tick); - void Add(int Tick, int64_t Tagtime, int DataSize, void *pData, int AltDataSize, void *pAltData); - int Get(int Tick, int64_t *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData); + void Add(int Tick, int64_t Tagtime, int DataSize, const void *pData, int AltDataSize, const void *pAltData); + int Get(int Tick, int64_t *pTagtime, const CSnapshot **ppData, const CSnapshot **ppAltData); }; class CSnapshotBuilder From 03f3994870afde4f419a41182e5f309c45f52098 Mon Sep 17 00:00:00 2001 From: +KZ <60852359+M0REKZ@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:08:59 -0300 Subject: [PATCH 34/70] Update Info.plist.in --- other/bundle/client/Info.plist.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/other/bundle/client/Info.plist.in b/other/bundle/client/Info.plist.in index 2a11df822db..525a2aa3197 100644 --- a/other/bundle/client/Info.plist.in +++ b/other/bundle/client/Info.plist.in @@ -56,6 +56,8 @@ NSPrefersDisplaySafeAreaCompatibilityMode + LSApplicationCategoryType + public.app-category.games NSQualityOfService NSQualityOfServiceUserInteractive From 23477584d1b5ed9e1163f997a9945518e4dc2001 Mon Sep 17 00:00:00 2001 From: furo Date: Wed, 27 Sep 2023 11:31:53 +0200 Subject: [PATCH 35/70] Don't show cl_showpred in demos --- src/game/client/components/hud.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 0462ca633d5..e4e34a5ca84 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -524,7 +524,7 @@ void CHud::RenderTextInfo() TextRender()->RenderTextContainer(m_FPSTextContainerIndex, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor()); } } - if(g_Config.m_ClShowpred) + if(g_Config.m_ClShowpred && Client()->State() != IClient::STATE_DEMOPLAYBACK) { char aBuf[64]; str_from_int(Client()->GetPredictionTime(), aBuf); From 4f0552a8df38d5450c77c8ed3f70f23fda000e97 Mon Sep 17 00:00:00 2001 From: furo Date: Wed, 27 Sep 2023 12:12:12 +0200 Subject: [PATCH 36/70] Expire server info when changing m_Afk --- src/game/server/player.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index cccc67ac6fa..5a6b6effd93 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -219,7 +219,7 @@ void CPlayer::Tick() if(Server()->GetNetErrorString(m_ClientID)[0]) { - m_Afk = true; + SetAfk(true); char aBuf[512]; str_format(aBuf, sizeof(aBuf), "'%s' would have timed out, but can use timeout protection now", Server()->ClientName(m_ClientID)); @@ -705,11 +705,14 @@ void CPlayer::UpdatePlaytime() void CPlayer::AfkTimer() { - m_Afk = g_Config.m_SvMaxAfkTime != 0 && m_LastPlaytime < time_get() - time_freq() * g_Config.m_SvMaxAfkTime; + SetAfk(g_Config.m_SvMaxAfkTime != 0 && m_LastPlaytime < time_get() - time_freq() * g_Config.m_SvMaxAfkTime); } void CPlayer::SetAfk(bool Afk) { + if(m_Afk != Afk) + Server()->ExpireServerInfo(); + if(g_Config.m_SvMaxAfkTime == 0) { m_Afk = false; From 744434be833971ead2de5d10e8abcd4056922339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 28 Sep 2023 17:04:00 +0200 Subject: [PATCH 37/70] Add flags for compatibility with Windows 8 - 11 We previously only defined compatibility with Windows Vista and 7. Now we define compatibility with Windows Vista - 11, which is what we currently support. This means that Windows 8 - 11 will no longer try to run the client in a compatibility mode for Windows 7 applications. This should effectively not change anything, as we don't directly use any of the [components that function differently depending on the compatibility information](https://learn.microsoft.com/en-us/windows/compatibility/application-executable-manifest#manifestation). --- other/manifest/client.manifest.in | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/other/manifest/client.manifest.in b/other/manifest/client.manifest.in index dca4cf522ee..802656614e1 100644 --- a/other/manifest/client.manifest.in +++ b/other/manifest/client.manifest.in @@ -11,10 +11,16 @@ - - - - + + + + + + + + + + From 247ce380941275b0e6549709675fe8dca24fa74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 28 Sep 2023 17:07:30 +0200 Subject: [PATCH 38/70] Remove unused variables `m_RenderFrameTimeLow/High` --- src/engine/client/client.cpp | 6 ------ src/engine/client/client.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index eac356cacec..49c52064b02 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -293,8 +293,6 @@ CClient::CClient() : DemoRecorder = CDemoRecorder(&m_SnapshotDelta); m_RenderFrameTime = 0.0001f; - m_RenderFrameTimeLow = 1.0f; - m_RenderFrameTimeHigh = 0.0f; m_RenderFrames = 0; m_LastRenderTime = time_get(); @@ -3238,10 +3236,6 @@ void CClient::Run() // update frametime m_RenderFrameTime = (Now - m_LastRenderTime) / (float)time_freq(); - if(m_RenderFrameTime < m_RenderFrameTimeLow) - m_RenderFrameTimeLow = m_RenderFrameTime; - if(m_RenderFrameTime > m_RenderFrameTimeHigh) - m_RenderFrameTimeHigh = m_RenderFrameTime; m_FpsGraph.Add(1.0f / m_RenderFrameTime, 1, 1, 1); if(m_BenchmarkFile) diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 4f4a78ca16d..f929619fc1e 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -149,8 +149,6 @@ class CClient : public IClient, public CDemoPlayer::IListener IGraphics::CTextureHandle m_DebugFont; int64_t m_LastRenderTime; - float m_RenderFrameTimeLow; - float m_RenderFrameTimeHigh; int m_RenderFrames; int m_SnapCrcErrors; From 06948ddecd6f54e9a4341087a5f1f16b2ef120cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 28 Sep 2023 17:15:45 +0200 Subject: [PATCH 39/70] Remove remains of `dbg_stress` from server Using `dbg_stress 1` on a server made clients always auto-join team 0 and nothing else, which is not useful on its own for stress testing. --- src/engine/shared/config_variables.h | 2 +- src/game/server/gamecontroller.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index e70cdac27ce..0f55481fc9a 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -195,7 +195,7 @@ MACRO_CONFIG_INT(DbgCurl, dbg_curl, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SERVER, "D MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs") MACRO_CONFIG_INT(DbgGfx, dbg_gfx, 0, 0, 4, CFGFLAG_CLIENT, "Show graphic library warnings and errors, if the GPU supports it (0: none, 1: minimal, 2: affects performance, 3: verbose, 4: all)") #ifdef CONF_DEBUG -MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Stress systems (Debug build only)") +MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 1, CFGFLAG_CLIENT, "Stress systems (Debug build only)") MACRO_CONFIG_STR(DbgStressServer, dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress (Debug build only)") #endif diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 21c578e870a..1d656e1c84e 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -679,12 +679,6 @@ void IGameController::Snap(int SnappingClient) int IGameController::GetAutoTeam(int NotThisID) { - // this will force the auto balancer to work overtime as well -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress) - return 0; -#endif - int aNumplayers[2] = {0, 0}; for(int i = 0; i < MAX_CLIENTS; i++) { From c64ec0f677d4c00a97d8ae2ac114ae49ac2188da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 28 Sep 2023 17:29:09 +0200 Subject: [PATCH 40/70] Limit effects of `dbg_stress` to features useful for debugging Using `dbg_stress 1` now only does the following (in debug build): - Randomly send inputs. - Randomly send chat messages. - Randomly connect/disconnect to server configured with `dbg_stress_server` (`localhost` by default). Previously it also did the following, which is not useful for this debugging feature and only complicates the code unnecessarily: - Cause images and sounds not to be loaded. - Render only every tenth frame. - Always use inactive graphics refresh rate. --- src/engine/client/client.cpp | 38 ++++--------------------- src/engine/client/client.h | 1 - src/engine/client/graphics_threaded.cpp | 6 ---- src/engine/client/sound.cpp | 24 ---------------- 4 files changed, 6 insertions(+), 63 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 49c52064b02..3908ba2f066 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -293,7 +293,6 @@ CClient::CClient() : DemoRecorder = CDemoRecorder(&m_SnapshotDelta); m_RenderFrameTime = 0.0001f; - m_RenderFrames = 0; m_LastRenderTime = time_get(); m_GameTickSpeed = SERVER_TICK_SPEED; @@ -3232,8 +3231,6 @@ void CClient::Run() (!AsyncRenderOld || m_pGraphics->IsIdle()) && (!GfxRefreshRate || (time_freq() / (int64_t)g_Config.m_GfxRefreshRate) <= Now - LastRenderTime)) { - m_RenderFrames++; - // update frametime m_RenderFrameTime = (Now - m_LastRenderTime) / (float)time_freq(); m_FpsGraph.Add(1.0f / m_RenderFrameTime, 1, 1, 1); @@ -3261,33 +3258,14 @@ void CClient::Run() LastRenderTime = Now - AdditionalTime; m_LastRenderTime = Now; -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress) - { - if((m_RenderFrames % 10) == 0) - { - if(!m_EditorActive) - Render(); - else - { - m_pEditor->OnRender(); - DebugRender(); - } - m_pGraphics->Swap(); - } - } + if(!m_EditorActive) + Render(); else -#endif { - if(!m_EditorActive) - Render(); - else - { - m_pEditor->OnRender(); - DebugRender(); - } - m_pGraphics->Swap(); + m_pEditor->OnRender(); + DebugRender(); } + m_pGraphics->Swap(); } else if(!IsRenderActive) { @@ -3327,11 +3305,7 @@ void CClient::Run() auto Now = time_get_nanoseconds(); decltype(Now) SleepTimeInNanoSeconds{0}; bool Slept = false; - if( -#ifdef CONF_DEBUG - g_Config.m_DbgStress || -#endif - (g_Config.m_ClRefreshRateInactive && !m_pGraphics->WindowActive())) + if(g_Config.m_ClRefreshRateInactive && !m_pGraphics->WindowActive()) { SleepTimeInNanoSeconds = (std::chrono::nanoseconds(1s) / (int64_t)g_Config.m_ClRefreshRateInactive) - (Now - LastTime); std::this_thread::sleep_for(SleepTimeInNanoSeconds); diff --git a/src/engine/client/client.h b/src/engine/client/client.h index f929619fc1e..67297b06726 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -149,7 +149,6 @@ class CClient : public IClient, public CDemoPlayer::IListener IGraphics::CTextureHandle m_DebugFont; int64_t m_LastRenderTime; - int m_RenderFrames; int m_SnapCrcErrors; bool m_AutoScreenshotRecycle; diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 0960182b400..db5b2e2a9de 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -428,12 +428,6 @@ bool CGraphics_Threaded::IsSpriteTextureFullyTransparent(CImageInfo &FromImageIn IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName) { - // don't waste memory on texture if we are stress testing -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress && m_InvalidTexture.IsValid()) - return m_InvalidTexture; -#endif - if((Flags & IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE) != 0 || (Flags & IGraphics::TEXLOAD_TO_3D_TEXTURE) != 0) { if(Width == 0 || (Width % 16) != 0 || Height == 0 || (Height % 16) != 0) diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp index 22076857337..2cd48e7d66c 100644 --- a/src/engine/client/sound.cpp +++ b/src/engine/client/sound.cpp @@ -498,12 +498,6 @@ bool CSound::DecodeWV(CSample &Sample, const void *pData, unsigned DataSize) int CSound::LoadOpus(const char *pFilename, int StorageType) { - // don't waste memory on sound when we are stress testing -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress) - return -1; -#endif - // no need to load sound when we are running with no sound if(!m_SoundEnabled) return -1; @@ -540,12 +534,6 @@ int CSound::LoadOpus(const char *pFilename, int StorageType) int CSound::LoadWV(const char *pFilename, int StorageType) { - // don't waste memory on sound when we are stress testing -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress) - return -1; -#endif - // no need to load sound when we are running with no sound if(!m_SoundEnabled) return -1; @@ -582,12 +570,6 @@ int CSound::LoadWV(const char *pFilename, int StorageType) int CSound::LoadOpusFromMem(const void *pData, unsigned DataSize, bool FromEditor = false) { - // don't waste memory on sound when we are stress testing -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress) - return -1; -#endif - // no need to load sound when we are running with no sound if(!m_SoundEnabled && !FromEditor) return -1; @@ -608,12 +590,6 @@ int CSound::LoadOpusFromMem(const void *pData, unsigned DataSize, bool FromEdito int CSound::LoadWVFromMem(const void *pData, unsigned DataSize, bool FromEditor = false) { - // don't waste memory on sound when we are stress testing -#ifdef CONF_DEBUG - if(g_Config.m_DbgStress) - return -1; -#endif - // no need to load sound when we are running with no sound if(!m_SoundEnabled && !FromEditor) return -1; From 7b58b77c5e7c2e5183b0638f4096cc590b2a31b8 Mon Sep 17 00:00:00 2001 From: Steinchen99 Date: Thu, 28 Sep 2023 19:02:05 +0200 Subject: [PATCH 41/70] QoL ; Made it so f2 cmd 'tele' resets speed and unfreezes the tee upon teleporting --- src/game/server/ddracecommands.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index 5b762632bd1..19977b557ab 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -386,6 +386,8 @@ void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData) if(pChr && pSelf->GetPlayerChar(TeleTo)) { pSelf->Teleport(pChr, pSelf->m_apPlayers[TeleTo]->m_ViewPos); + pChr->UnFreeze(); + pChr->Core()->m_Vel = vec2(0, 0); } } From fcffac6fa8f52387b8cfc29604ba191f6374c33c Mon Sep 17 00:00:00 2001 From: Learath Date: Sat, 30 Sep 2023 01:27:58 +0200 Subject: [PATCH 42/70] Fix the fix to dead reckoning --- src/game/server/entities/character.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 0fe23769516..987689c3884 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -876,6 +876,7 @@ void CCharacter::TickDeferred() m_ReckoningTick = Server()->Tick(); m_SendCore = m_Core; m_ReckoningCore = m_Core; + m_ReckoningCore.SetCoreWorld(nullptr, Collision(), nullptr); m_Core.m_Reset = false; } } From e78305e1cdaf80cfece28f7716ba9d64d3b2b2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:20:04 +0200 Subject: [PATCH 43/70] Remove unused `m_aFilter(Gametype)String` variables --- src/engine/client/serverbrowser.cpp | 7 +------ src/engine/client/serverbrowser.h | 5 +---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 41fdf2a6560..a35807a0c7d 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -66,16 +66,13 @@ CServerBrowser::CServerBrowser() m_NumRequests = 0; m_NeedResort = false; + m_Sorthash = 0; m_NumSortedServers = 0; m_NumSortedServersCapacity = 0; m_NumServers = 0; m_NumServerCapacity = 0; - m_Sorthash = 0; - m_aFilterString[0] = '\0'; - m_aFilterGametypeString[0] = '\0'; - m_ServerlistType = 0; m_BroadcastTime = 0; secure_random_fill(m_aTokenSeed, sizeof(m_aTokenSeed)); @@ -476,8 +473,6 @@ void CServerBrowser::Sort() else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE) std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, CSortWrap(this, &CServerBrowser::SortCompareGametype)); - str_copy(m_aFilterGametypeString, g_Config.m_BrFilterGametype); - str_copy(m_aFilterString, g_Config.m_BrFilterString); m_Sorthash = SortHash(); } diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index 9e9ef6eae8d..d2288e79542 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -125,6 +125,7 @@ class CServerBrowser : public IServerBrowser int m_NumRequests; bool m_NeedResort; + int m_Sorthash; // used instead of g_Config.br_max_requests to get more servers int m_CurrentMaxRequests; @@ -134,10 +135,6 @@ class CServerBrowser : public IServerBrowser int m_NumServers; int m_NumServerCapacity; - int m_Sorthash; - char m_aFilterString[64]; - char m_aFilterGametypeString[128]; - int m_ServerlistType; int64_t m_BroadcastTime; unsigned char m_aTokenSeed[16]; From 6753e37772f9cbd42fbba5904a427e9d9b82f862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:24:12 +0200 Subject: [PATCH 44/70] Move function definitions to remove and organize includes --- src/engine/client/serverbrowser.cpp | 16 ++++++++++------ src/engine/client/serverbrowser.h | 16 +++------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index a35807a0c7d..7fcc199552a 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -11,27 +11,21 @@ #include #include -#include #include #include #include #include -#include #include #include #include -#include #include #include #include #include -#include #include -#include - #include // PAGE_DDNET class CSortWrap @@ -175,6 +169,16 @@ void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserDa } } +int CServerBrowser::Players(const CServerInfo &Item) const +{ + return g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients; +} + +int CServerBrowser::Max(const CServerInfo &Item) const +{ + return g_Config.m_BrFilterSpectators ? Item.m_MaxPlayers : Item.m_MaxClients; +} + const CServerInfo *CServerBrowser::SortedGet(int Index) const { if(Index < 0 || Index >= m_NumSortedServers) diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index d2288e79542..49f8b97dfc4 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -7,12 +7,11 @@ #include #include -#include -#include #include #include +typedef struct _json_value json_value; class CNetClient; class IConfigManager; class IConsole; @@ -49,17 +48,8 @@ class CServerBrowser : public IServerBrowser void RequestResort() { m_NeedResort = true; } int NumServers() const override { return m_NumServers; } - - int Players(const CServerInfo &Item) const override - { - return g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients; - } - - int Max(const CServerInfo &Item) const override - { - return g_Config.m_BrFilterSpectators ? Item.m_MaxPlayers : Item.m_MaxClients; - } - + int Players(const CServerInfo &Item) const override; + int Max(const CServerInfo &Item) const override; int NumSortedServers() const override { return m_NumSortedServers; } const CServerInfo *SortedGet(int Index) const override; From 721a55c60312a2a913263a947f0257b42f653c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:26:21 +0200 Subject: [PATCH 45/70] Remove game menus includes in engine Use config manager to reset `ui_page` to the default, instead of using `CMenus::PAGE_DDNET` directly (which is the default). Check current type of serverbrowser instead of checking the current `ui_page` against `CMenus::PAGE_DDNET` and `CMenus::PAGE_KOG`. --- src/engine/client/client.cpp | 25 +++++++++---------------- src/engine/client/serverbrowser.cpp | 5 ++--- src/engine/client/serverbrowser.h | 1 + 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 3908ba2f066..a116fa5f894 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3,11 +3,6 @@ #define _WIN32_WINNT 0x0501 -#include -#include -#include -#include - #include #include #include @@ -16,9 +11,6 @@ #include -#include -#include - #include #include #include @@ -35,7 +27,6 @@ #include #include -#include #include #include #include @@ -52,13 +43,14 @@ #include #include +#include #include #include -#include - #include "client.h" +#include "demoedit.h" #include "friends.h" +#include "notifications.h" #include "serverbrowser.h" #if defined(CONF_VIDEORECORDER) @@ -71,7 +63,11 @@ #endif #include +#include +#include +#include #include +#include using namespace std::chrono_literals; @@ -2379,11 +2375,8 @@ void CClient::FinishDDNetInfo() { m_pStorage->RenameFile(m_aDDNetInfoTmp, DDNET_INFO, IStorage::TYPE_SAVE); LoadDDNetInfo(); - - if(g_Config.m_UiPage == CMenus::PAGE_DDNET) - m_ServerBrowser.Refresh(IServerBrowser::TYPE_DDNET); - else if(g_Config.m_UiPage == CMenus::PAGE_KOG) - m_ServerBrowser.Refresh(IServerBrowser::TYPE_KOG); + if(m_ServerBrowser.GetCurrentType() == IServerBrowser::TYPE_DDNET || m_ServerBrowser.GetCurrentType() == IServerBrowser::TYPE_KOG) + m_ServerBrowser.Refresh(m_ServerBrowser.GetCurrentType()); } else { diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 7fcc199552a..7f50493f5cb 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -26,8 +26,6 @@ #include #include -#include // PAGE_DDNET - class CSortWrap { typedef bool (CServerBrowser::*SortFunc)(int, int) const; @@ -91,6 +89,7 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers m_pNetClient = pClient; str_copy(m_aNetVersion, pNetVersion); m_pConsole = Kernel()->RequestInterface(); + m_pConfigManager = Kernel()->RequestInterface(); m_pEngine = Kernel()->RequestInterface(); m_pFavorites = Kernel()->RequestInterface(); m_pFriends = Kernel()->RequestInterface(); @@ -1334,7 +1333,7 @@ const char *CServerBrowser::GetTutorialServer() { // Use DDNet tab as default after joining tutorial, also makes sure Find() actually works // Note that when no server info has been loaded yet, this will not return a result immediately. - g_Config.m_UiPage = CMenus::PAGE_DDNET; + m_pConfigManager->Reset("ui_page"); Refresh(IServerBrowser::TYPE_DDNET); const CCommunity *pCommunity = Community(COMMUNITY_DDNET); diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index 49f8b97dfc4..e339be4bdcc 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -88,6 +88,7 @@ class CServerBrowser : public IServerBrowser private: CNetClient *m_pNetClient = nullptr; + IConfigManager *m_pConfigManager = nullptr; IConsole *m_pConsole = nullptr; IEngine *m_pEngine = nullptr; IFriends *m_pFriends = nullptr; From dcff6d92f0312c886791a7364e98dbbbc2bacaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:35:53 +0200 Subject: [PATCH 46/70] Add name for enum `EClientScoreKind` --- src/engine/serverbrowser.h | 4 ++-- src/engine/shared/serverinfo.cpp | 1 - src/engine/shared/serverinfo.h | 4 +++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 674b092a6c0..e5e05de4084 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -35,7 +35,7 @@ class CServerInfo NUM_LOCS, }; - enum + enum EClientScoreKind { CLIENT_SCORE_KIND_UNSPECIFIED, CLIENT_SCORE_KIND_POINTS, @@ -79,7 +79,7 @@ class CServerInfo int m_MaxPlayers; int m_NumPlayers; int m_Flags; - int m_ClientScoreKind; + EClientScoreKind m_ClientScoreKind; TRISTATE m_Favorite; TRISTATE m_FavoriteAllowPing; bool m_Official; diff --git a/src/engine/shared/serverinfo.cpp b/src/engine/shared/serverinfo.cpp index 1e7210ab02b..e292022e7df 100644 --- a/src/engine/shared/serverinfo.cpp +++ b/src/engine/shared/serverinfo.cpp @@ -3,7 +3,6 @@ #include "json.h" #include #include -#include #include diff --git a/src/engine/shared/serverinfo.h b/src/engine/shared/serverinfo.h index f2809b2cc7b..edb06a467d1 100644 --- a/src/engine/shared/serverinfo.h +++ b/src/engine/shared/serverinfo.h @@ -2,7 +2,9 @@ #define ENGINE_SHARED_SERVERINFO_H #include "protocol.h" + #include +#include typedef struct _json_value json_value; class CServerInfo; @@ -30,7 +32,7 @@ class CServerInfo2 int m_NumClients; // Indirectly serialized. int m_MaxPlayers; int m_NumPlayers; // Not serialized. - int m_ClientScoreKind; + CServerInfo::EClientScoreKind m_ClientScoreKind; bool m_Passworded; char m_aGameType[16]; char m_aName[64]; From 034962ae24c7052fc2eb40dbfef4cbc4d818815a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:39:30 +0200 Subject: [PATCH 47/70] Add enum `ERankState` to replace magic numbers --- src/engine/client/serverbrowser.cpp | 12 ++++++------ src/engine/client/serverbrowser.h | 2 +- src/engine/serverbrowser.h | 9 ++++++++- src/game/client/components/menus_browser.cpp | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 7f50493f5cb..a5697404b9a 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -666,7 +666,7 @@ CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR *pAddrs, int Num pEntry->m_Info.m_NumAddresses = NumAddrs; pEntry->m_Info.m_Latency = 999; - pEntry->m_Info.m_HasRank = -1; + pEntry->m_Info.m_HasRank = CServerInfo::RANK_UNAVAILABLE; ServerBrowserFormatAddresses(pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), pEntry->m_Info.m_aAddresses, pEntry->m_Info.m_NumAddresses); str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName)); @@ -1275,14 +1275,14 @@ void CServerBrowser::LoadDDNetRanks() } } -int CServerBrowser::HasRank(const char *pMap) +CServerInfo::ERankState CServerBrowser::HasRank(const char *pMap) { if(m_ServerlistType != IServerBrowser::TYPE_DDNET || !m_pDDNetInfo) - return -1; + return CServerInfo::RANK_UNAVAILABLE; const json_value &Ranks = (*m_pDDNetInfo)["maps"]; if(Ranks.type != json_array) - return -1; + return CServerInfo::RANK_UNAVAILABLE; for(unsigned i = 0; i < Ranks.u.array.length; ++i) { @@ -1291,10 +1291,10 @@ int CServerBrowser::HasRank(const char *pMap) continue; if(str_comp(pMap, Entry.u.string.ptr) == 0) - return 1; + return CServerInfo::RANK_RANKED; } - return 0; + return CServerInfo::RANK_UNRANKED; } void CServerBrowser::LoadDDNetInfoJson() diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index e339be4bdcc..eee178e946b 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -59,7 +59,7 @@ class CServerBrowser : public IServerBrowser void LoadDDNetServers(); void LoadDDNetInfoJson(); const json_value *LoadDDNetInfo(); - int HasRank(const char *pMap); + CServerInfo::ERankState HasRank(const char *pMap); const std::vector &Communities() const override; const CCommunity *Community(const char *pCommunityId) const override; diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index e5e05de4084..a97db7c100e 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -43,6 +43,13 @@ class CServerInfo CLIENT_SCORE_KIND_TIME_BACKCOMPAT, }; + enum ERankState + { + RANK_UNAVAILABLE, + RANK_RANKED, + RANK_UNRANKED, + }; + class CClient { public: @@ -86,7 +93,7 @@ class CServerInfo int m_Location; bool m_LatencyIsEstimated; int m_Latency; // in ms - int m_HasRank; + ERankState m_HasRank; char m_aGameType[16]; char m_aName[64]; char m_aMap[MAX_MAP_LENGTH]; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index e212c5e2a80..acddecb0a77 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -342,7 +342,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemAct CUIRect Icon; Button.VMargin(4.0f, &Button); Button.VSplitLeft(Button.h, &Icon, &Button); - if(g_Config.m_BrIndicateFinished && pItem->m_HasRank == 1) + if(g_Config.m_BrIndicateFinished && pItem->m_HasRank == CServerInfo::RANK_RANKED) { Icon.Margin(2.0f, &Icon); RenderBrowserIcons(*pUiElement->Rect(UI_ELEM_FINISH_ICON), &Icon, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), FONT_ICON_FLAG_CHECKERED, TEXTALIGN_MC); From 28d4451057391b6b9ec2982ea5a5043ec858420f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:41:01 +0200 Subject: [PATCH 48/70] Add local variable `Info` to reduce duplicate code --- src/engine/client/serverbrowser.cpp | 61 +++++++++++++++-------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index a5697404b9a..bd438b5d90e 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -277,21 +277,22 @@ void CServerBrowser::Filter() // filter the servers for(int i = 0; i < m_NumServers; i++) { + CServerInfo &Info = m_ppServerlist[i]->m_Info; bool Filtered = false; - if(g_Config.m_BrFilterEmpty && m_ppServerlist[i]->m_Info.m_NumFilteredPlayers == 0) + if(g_Config.m_BrFilterEmpty && Info.m_NumFilteredPlayers == 0) Filtered = true; - else if(g_Config.m_BrFilterFull && Players(m_ppServerlist[i]->m_Info) == Max(m_ppServerlist[i]->m_Info)) + else if(g_Config.m_BrFilterFull && Players(Info) == Max(Info)) Filtered = true; - else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags & SERVER_FLAG_PASSWORD) + else if(g_Config.m_BrFilterPw && Info.m_Flags & SERVER_FLAG_PASSWORD) Filtered = true; - else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress)) + else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(Info.m_aAddress, g_Config.m_BrFilterServerAddress)) Filtered = true; - else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(Info.m_aGameType, g_Config.m_BrFilterGametype)) Filtered = true; - else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_utf8_find_nocase(Info.m_aGameType, g_Config.m_BrFilterGametype)) Filtered = true; - else if(g_Config.m_BrFilterUnfinishedMap && m_ppServerlist[i]->m_Info.m_HasRank == 1) + else if(g_Config.m_BrFilterUnfinishedMap && Info.m_HasRank == CServerInfo::RANK_RANKED) Filtered = true; else { @@ -299,9 +300,9 @@ void CServerBrowser::Filter() { Filtered = true; // match against player country - for(int p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++) + for(int p = 0; p < minimum(Info.m_NumClients, (int)MAX_CLIENTS); p++) { - if(m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex) + if(Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex) { Filtered = false; break; @@ -311,7 +312,7 @@ void CServerBrowser::Filter() if(!Filtered && g_Config.m_BrFilterString[0] != '\0') { - m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0; + Info.m_QuickSearchHit = 0; const char *pStr = g_Config.m_BrFilterString; char aFilterStr[sizeof(g_Config.m_BrFilterString)]; @@ -330,36 +331,36 @@ void CServerBrowser::Filter() } // match against server name - if(MatchesFn(m_ppServerlist[i]->m_Info.m_aName, aFilterStr)) + if(MatchesFn(Info.m_aName, aFilterStr)) { - m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME; + Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME; } // match against players - for(int p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++) + for(int p = 0; p < minimum(Info.m_NumClients, (int)MAX_CLIENTS); p++) { - if(MatchesFn(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, aFilterStr) || - MatchesFn(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, aFilterStr)) + if(MatchesFn(Info.m_aClients[p].m_aName, aFilterStr) || + MatchesFn(Info.m_aClients[p].m_aClan, aFilterStr)) { if(g_Config.m_BrFilterConnectingPlayers && - str_comp(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, "(connecting)") == 0 && - m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan[0] == '\0') + str_comp(Info.m_aClients[p].m_aName, "(connecting)") == 0 && + Info.m_aClients[p].m_aClan[0] == '\0') { continue; } - m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER; + Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER; break; } } // match against map - if(MatchesFn(m_ppServerlist[i]->m_Info.m_aMap, aFilterStr)) + if(MatchesFn(Info.m_aMap, aFilterStr)) { - m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME; + Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME; } } - if(!m_ppServerlist[i]->m_Info.m_QuickSearchHit) + if(!Info.m_QuickSearchHit) Filtered = true; } @@ -382,21 +383,21 @@ void CServerBrowser::Filter() } // match against server name - if(MatchesFn(m_ppServerlist[i]->m_Info.m_aName, aExcludeStr)) + if(MatchesFn(Info.m_aName, aExcludeStr)) { Filtered = true; break; } // match against map - if(MatchesFn(m_ppServerlist[i]->m_Info.m_aMap, aExcludeStr)) + if(MatchesFn(Info.m_aMap, aExcludeStr)) { Filtered = true; break; } // match against gametype - if(MatchesFn(m_ppServerlist[i]->m_Info.m_aGameType, aExcludeStr)) + if(MatchesFn(Info.m_aGameType, aExcludeStr)) { Filtered = true; break; @@ -408,15 +409,15 @@ void CServerBrowser::Filter() if(!Filtered) { // check for friend - m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO; - for(int p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++) + Info.m_FriendState = IFriends::FRIEND_NO; + for(int p = 0; p < minimum(Info.m_NumClients, (int)MAX_CLIENTS); p++) { - m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, - m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan); - m_ppServerlist[i]->m_Info.m_FriendState = maximum(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState); + Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(Info.m_aClients[p].m_aName, + Info.m_aClients[p].m_aClan); + Info.m_FriendState = maximum(Info.m_FriendState, Info.m_aClients[p].m_FriendState); } - if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO) + if(!g_Config.m_BrFilterFriends || Info.m_FriendState != IFriends::FRIEND_NO) m_pSortedServerlist[m_NumSortedServers++] = i; } } From 7e8442d394c8bb681d27f9f28ad01df07bc89ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:48:02 +0200 Subject: [PATCH 49/70] Move and rename `UpdateFilteredPlayers` function, mark as `const` --- src/engine/client/serverbrowser.cpp | 30 ++++++++++++++--------------- src/engine/client/serverbrowser.h | 1 + 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index bd438b5d90e..ba066178f70 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -439,25 +439,12 @@ int CServerBrowser::SortHash() const return i; } -void UpdateFilteredPlayers(CServerInfo &Item) -{ - Item.m_NumFilteredPlayers = g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients; - if(g_Config.m_BrFilterConnectingPlayers) - { - for(const auto &Client : Item.m_aClients) - { - if((!g_Config.m_BrFilterSpectators || Client.m_Player) && str_comp(Client.m_aName, "(connecting)") == 0 && Client.m_aClan[0] == '\0') - Item.m_NumFilteredPlayers--; - } - } -} - void CServerBrowser::Sort() { - // fill m_NumFilteredPlayers + // update number of filtered players for(int i = 0; i < m_NumServers; i++) { - UpdateFilteredPlayers(m_ppServerlist[i]->m_Info); + UpdateServerFilteredPlayers(&m_ppServerlist[i]->m_Info); } // create filtered list @@ -1330,6 +1317,19 @@ void CServerBrowser::LoadDDNetInfoJson() } } +void CServerBrowser::UpdateServerFilteredPlayers(CServerInfo *pInfo) const +{ + pInfo->m_NumFilteredPlayers = g_Config.m_BrFilterSpectators ? pInfo->m_NumPlayers : pInfo->m_NumClients; + if(g_Config.m_BrFilterConnectingPlayers) + { + for(const auto &Client : pInfo->m_aClients) + { + if((!g_Config.m_BrFilterSpectators || Client.m_Player) && str_comp(Client.m_aName, "(connecting)") == 0 && Client.m_aClan[0] == '\0') + pInfo->m_NumFilteredPlayers--; + } + } +} + const char *CServerBrowser::GetTutorialServer() { // Use DDNet tab as default after joining tutorial, also makes sure Find() actually works diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index eee178e946b..944ff835ce0 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -59,6 +59,7 @@ class CServerBrowser : public IServerBrowser void LoadDDNetServers(); void LoadDDNetInfoJson(); const json_value *LoadDDNetInfo(); + void UpdateServerFilteredPlayers(CServerInfo *pInfo) const; CServerInfo::ERankState HasRank(const char *pMap); const std::vector &Communities() const override; From cf35594fa4e97f6f1fd9392ad4954719d55a9a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 12:57:10 +0200 Subject: [PATCH 50/70] Extract `UpdateServerFriends` function, cache friends on server Calculate the number of friends on each server when refreshing the friend state of the server info instead of every time the server entry is rendered. --- src/engine/client/serverbrowser.cpp | 22 +++++++++++++------- src/engine/client/serverbrowser.h | 1 + src/engine/serverbrowser.h | 1 + src/game/client/components/menus_browser.cpp | 17 ++------------- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index ba066178f70..8e90946513f 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -408,14 +408,7 @@ void CServerBrowser::Filter() if(!Filtered) { - // check for friend - Info.m_FriendState = IFriends::FRIEND_NO; - for(int p = 0; p < minimum(Info.m_NumClients, (int)MAX_CLIENTS); p++) - { - Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(Info.m_aClients[p].m_aName, - Info.m_aClients[p].m_aClan); - Info.m_FriendState = maximum(Info.m_FriendState, Info.m_aClients[p].m_FriendState); - } + UpdateServerFriends(&Info); if(!g_Config.m_BrFilterFriends || Info.m_FriendState != IFriends::FRIEND_NO) m_pSortedServerlist[m_NumSortedServers++] = i; @@ -1330,6 +1323,19 @@ void CServerBrowser::UpdateServerFilteredPlayers(CServerInfo *pInfo) const } } +void CServerBrowser::UpdateServerFriends(CServerInfo *pInfo) const +{ + pInfo->m_FriendState = IFriends::FRIEND_NO; + pInfo->m_FriendNum = 0; + for(int ClientIndex = 0; ClientIndex < minimum(pInfo->m_NumReceivedClients, (int)MAX_CLIENTS); ClientIndex++) + { + pInfo->m_aClients[ClientIndex].m_FriendState = m_pFriends->GetFriendState(pInfo->m_aClients[ClientIndex].m_aName, pInfo->m_aClients[ClientIndex].m_aClan); + pInfo->m_FriendState = maximum(pInfo->m_FriendState, pInfo->m_aClients[ClientIndex].m_FriendState); + if(pInfo->m_aClients[ClientIndex].m_FriendState != IFriends::FRIEND_NO) + pInfo->m_FriendNum++; + } +} + const char *CServerBrowser::GetTutorialServer() { // Use DDNet tab as default after joining tutorial, also makes sure Find() actually works diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index 944ff835ce0..2708f105ed4 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -60,6 +60,7 @@ class CServerBrowser : public IServerBrowser void LoadDDNetInfoJson(); const json_value *LoadDDNetInfo(); void UpdateServerFilteredPlayers(CServerInfo *pInfo) const; + void UpdateServerFriends(CServerInfo *pInfo) const; CServerInfo::ERankState HasRank(const char *pMap); const std::vector &Communities() const override; diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index a97db7c100e..5db54ec4991 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -80,6 +80,7 @@ class CServerInfo int m_QuickSearchHit; int m_FriendState; + int m_FriendNum; int m_MaxClients; int m_NumClients; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index acddecb0a77..2fc449a9de6 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -248,19 +248,6 @@ void CMenus::RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemAct if(ListItem.m_Selected) m_SelectedIndex = i; - // update friend counter - int FriendsOnServer = 0; - if(pItem->m_FriendState != IFriends::FRIEND_NO) - { - for(int j = 0; j < pItem->m_NumReceivedClients; ++j) - { - if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO) - { - FriendsOnServer++; - } - } - } - if(!ListItem.m_Visible) { // reset active item, if not visible @@ -374,9 +361,9 @@ void CMenus::RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemAct Button.VSplitRight(50.0f, &Icon, &Button); Icon.Margin(2.0f, &Icon); RenderBrowserIcons(*pUiElement->Rect(UI_ELEM_FRIEND_ICON), &Icon, ColorRGBA(0.94f, 0.4f, 0.4f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC); - if(FriendsOnServer > 1) + if(pItem->m_FriendNum > 1) { - str_from_int(FriendsOnServer, aTemp); + str_from_int(pItem->m_FriendNum, aTemp); TextRender()->TextColor(0.94f, 0.8f, 0.8f, 1.0f); UI()->DoLabel(&Icon, aTemp, 9.0f, TEXTALIGN_MC); TextRender()->TextColor(TextRender()->DefaultTextColor()); From ca8d9d4f1881c49902d60de6c7277938320a4781 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Sun, 20 Aug 2023 20:35:56 +0200 Subject: [PATCH 51/70] Add streamer mode to avoid leaks --- src/game/client/components/chat.cpp | 44 ++++++++++++++++++++++++++++- src/game/client/components/chat.h | 2 ++ src/game/client/lineinput.cpp | 20 +++++++------ src/game/client/lineinput.h | 10 +++++-- src/game/variables.h | 1 + 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index ead69b10b6a..742b37e1ce4 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -19,6 +19,8 @@ #include "chat.h" +char CChat::ms_aDisplayText[512] = {'\0'}; + CChat::CChat() { for(auto &Line : m_aLines) @@ -36,6 +38,37 @@ CChat::CChat() m_Mode = MODE_NONE; m_Input.SetClipboardLineCallback([this](const char *pStr) { SayChat(pStr); }); + m_Input.SetCalculateOffsetCallback([this]() { return m_IsInputCensored; }); + m_Input.SetDisplayTextCallback([this](char *pStr, size_t NumChars) { + m_IsInputCensored = false; + if( + g_Config.m_ClStreamerMode && + (str_startswith(pStr, "/login ") || + str_startswith(pStr, "/register ") || + str_startswith(pStr, "/code ") || + str_startswith(pStr, "/timeout ") || + str_startswith(pStr, "/save ") || + str_startswith(pStr, "/load "))) + { + bool Censor = false; + const size_t NumLetters = minimum(NumChars, sizeof(ms_aDisplayText) - 1); + for(size_t i = 0; i < NumLetters; ++i) + { + if(Censor) + ms_aDisplayText[i] = '*'; + else + ms_aDisplayText[i] = pStr[i]; + if(pStr[i] == ' ') + { + Censor = true; + m_IsInputCensored = true; + } + } + ms_aDisplayText[NumLetters] = '\0'; + return ms_aDisplayText; + } + return pStr; + }); } void CChat::RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp) @@ -86,6 +119,7 @@ void CChat::Reset() m_PendingChatCounter = 0; m_LastChatSend = 0; m_CurrentLine = 0; + m_IsInputCensored = false; DisableMode(); for(int64_t &LastSoundPlayed : m_aLastSoundPlayed) @@ -1017,7 +1051,15 @@ void CChat::OnPrepareLines() OriginalWidth = Cursor.m_LongestLineWidth; } - TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &AppendCursor, m_aLines[r].m_aText); + const char *pText = m_aLines[r].m_aText; + if(Config()->m_ClStreamerMode && m_aLines[r].m_ClientID == SERVER_MSG) + { + if(str_startswith(m_aLines[r].m_aText, "Team save in progress. You'll be able to load with '/load") && str_endswith(m_aLines[r].m_aText, "if it fails")) + pText = "Team save in progress. You'll be able to load with '/load ***' if save is successful or with '/load *** *** ***' if it fails"; + else if(str_startswith(m_aLines[r].m_aText, "Team successfully saved by ") && str_endswith(m_aLines[r].m_aText, " to continue")) + pText = "Team successfully saved by ***. Use '/load ***' to continue"; + } + TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &AppendCursor, pText); if(!g_Config.m_ClChatOld && (m_aLines[r].m_aText[0] != '\0' || m_aLines[r].m_aName[0] != '\0')) { diff --git a/src/game/client/components/chat.h b/src/game/client/components/chat.h index b50b84a4e3a..04937726850 100644 --- a/src/game/client/components/chat.h +++ b/src/game/client/components/chat.h @@ -85,6 +85,7 @@ class CChat : public CComponent char m_aCompletionBuffer[256]; int m_PlaceholderOffset; int m_PlaceholderLength; + static char ms_aDisplayText[512]; struct CRateablePlayer { int ClientID; @@ -121,6 +122,7 @@ class CChat : public CComponent int m_PendingChatCounter; int64_t m_LastChatSend; int64_t m_aLastSoundPlayed[CHAT_NUM]; + bool m_IsInputCensored; static void ConSay(IConsole::IResult *pResult, void *pUserData); static void ConSayTeam(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index ac44c8574cd..c01043f4918 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -1,6 +1,7 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include #include "lineinput.h" #include "ui.h" @@ -111,6 +112,9 @@ void CLineInput::UpdateStrData() const char *CLineInput::GetDisplayedString() { + if(m_pfnDisplayTextCallback) + return m_pfnDisplayTextCallback(m_pStr, GetNumChars()); + if(!IsHidden()) return m_pStr; @@ -167,18 +171,18 @@ void CLineInput::SetSelection(size_t Start, size_t End) m_WasChanged = true; } -size_t CLineInput::OffsetFromActualToDisplay(size_t ActualOffset) const +size_t CLineInput::OffsetFromActualToDisplay(size_t ActualOffset) { - if(!IsHidden()) - return ActualOffset; - return str_utf8_offset_bytes_to_chars(m_pStr, ActualOffset); + if(IsHidden() || (m_pfnCalculateOffsetCallback && m_pfnCalculateOffsetCallback())) + return str_utf8_offset_bytes_to_chars(m_pStr, ActualOffset); + return ActualOffset; } -size_t CLineInput::OffsetFromDisplayToActual(size_t DisplayOffset) const +size_t CLineInput::OffsetFromDisplayToActual(size_t DisplayOffset) { - if(!IsHidden()) - return DisplayOffset; - return str_utf8_offset_chars_to_bytes(m_pStr, DisplayOffset); + if(IsHidden() || (m_pfnCalculateOffsetCallback && m_pfnCalculateOffsetCallback())) + return str_utf8_offset_bytes_to_chars(m_pStr, DisplayOffset); + return DisplayOffset; } bool CLineInput::ProcessInput(const IInput::CEvent &Event) diff --git a/src/game/client/lineinput.h b/src/game/client/lineinput.h index e3e23e4406d..970bcdcafbd 100644 --- a/src/game/client/lineinput.h +++ b/src/game/client/lineinput.h @@ -33,6 +33,8 @@ class CLineInput }; typedef std::function FClipboardLineCallback; + typedef std::function FDisplayTextCallback; + typedef std::function FCalculateOffsetCallback; private: static IClient *ms_pClient; @@ -72,6 +74,8 @@ class CLineInput bool m_Hidden; const char *m_pEmptyText; FClipboardLineCallback m_pfnClipboardLineCallback; + FDisplayTextCallback m_pfnDisplayTextCallback; + FCalculateOffsetCallback m_pfnCalculateOffsetCallback; bool m_WasChanged; bool m_WasRendered; @@ -147,8 +151,8 @@ class CLineInput SetSelection(0, GetLength()); } - size_t OffsetFromActualToDisplay(size_t ActualOffset) const; - size_t OffsetFromDisplayToActual(size_t DisplayOffset) const; + size_t OffsetFromActualToDisplay(size_t ActualOffset); + size_t OffsetFromDisplayToActual(size_t DisplayOffset); // used either for vertical or horizontal scrolling float GetScrollOffset() const { return m_ScrollOffset; } @@ -165,6 +169,8 @@ class CLineInput void SetEmptyText(const char *pText) { m_pEmptyText = pText; } void SetClipboardLineCallback(FClipboardLineCallback pfnClipboardLineCallback) { m_pfnClipboardLineCallback = pfnClipboardLineCallback; } + void SetDisplayTextCallback(FDisplayTextCallback pfnDisplayTextCallback) { m_pfnDisplayTextCallback = pfnDisplayTextCallback; } + void SetCalculateOffsetCallback(FCalculateOffsetCallback pfnCalculateOffsetCallback) { m_pfnCalculateOffsetCallback = pfnCalculateOffsetCallback; } bool ProcessInput(const IInput::CEvent &Event); bool WasChanged() diff --git a/src/game/variables.h b/src/game/variables.h index f65cea0abeb..708cd2d45de 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -36,6 +36,7 @@ MACRO_CONFIG_INT(ClNameplatesFriendMark, cl_nameplates_friendmark, 0, 0, 1, CFGF MACRO_CONFIG_INT(ClNameplatesStrong, cl_nameplates_strong, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show strong/weak in name plates (0 - off, 1 - icons, 2 - icons + numbers)") MACRO_CONFIG_INT(ClTextEntities, cl_text_entities, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Render textual entity data") MACRO_CONFIG_INT(ClTextEntitiesSize, cl_text_entities_size, 100, 1, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Size of textual entity data from 1 to 100%") +MACRO_CONFIG_INT(ClStreamerMode, cl_streamer_mode, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Censor sensitive information such as /save password") MACRO_CONFIG_COL(ClAuthedPlayerColor, cl_authed_player_color, 5898211, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Color of name of authenticated player in scoreboard") MACRO_CONFIG_COL(ClSameClanColor, cl_same_clan_color, 5898211, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Clan color of players with the same clan as you in scoreboard.") From 0025b6c1f309c25825c1feaa9adb6186c30b8a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 15:48:07 +0200 Subject: [PATCH 52/70] Fix large FPS drop rendering server details of favorite server The server list was being resorted every frame when the server details of a favorite server were being rendered, which was causing the FPS to drop by a factor of 5. --- src/game/client/components/menus_browser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 2fc449a9de6..00e0df15d7e 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1030,8 +1030,8 @@ void CMenus::RenderServerbrowserInfo(CUIRect View) if(DoButton_CheckBox_Tristate(&s_LeakIpButton, Localize("Leak IP"), pSelectedServer->m_FavoriteAllowPing, &ButtonLeakIp)) { Favorites()->AllowPing(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses, pSelectedServer->m_FavoriteAllowPing == TRISTATE::NONE); + Client()->ServerBrowserUpdate(); } - Client()->ServerBrowserUpdate(); } } From f07e7fef76345f8948c810a024a9ca9c00961e97 Mon Sep 17 00:00:00 2001 From: furo Date: Sat, 30 Sep 2023 17:28:47 +0200 Subject: [PATCH 53/70] Add export button to sounds --- src/game/editor/editor.cpp | 35 ++++++++++++++++++++++++++++++++--- src/game/editor/editor.h | 2 ++ src/game/editor/popups.cpp | 21 +++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index b74f53e1437..0d19c5c1257 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -815,7 +815,7 @@ bool CEditor::CallbackSaveImage(const char *pFileName, int StorageType, void *pU OutputFormat = IMAGE_FORMAT_R; break; default: - pEditor->ShowFileDialogError("Image has invalid format."); + dbg_assert(false, "Image has invalid format."); return false; }; @@ -841,6 +841,33 @@ bool CEditor::CallbackSaveImage(const char *pFileName, int StorageType, void *pU } } +bool CEditor::CallbackSaveSound(const char *pFileName, int StorageType, void *pUser) +{ + dbg_assert(StorageType == IStorage::TYPE_SAVE, "Saving only allowed for IStorage::TYPE_SAVE"); + + CEditor *pEditor = static_cast(pUser); + char aBuf[IO_MAX_PATH_LENGTH]; + + // add file extension + if(!str_endswith(pFileName, ".opus")) + { + str_format(aBuf, sizeof(aBuf), "%s.opus", pFileName); + pFileName = aBuf; + } + std::shared_ptr pSound = pEditor->m_Map.m_vpSounds[pEditor->m_SelectedSound]; + IOHANDLE File = pEditor->Storage()->OpenFile(pFileName, IOFLAG_WRITE, StorageType); + + if(File) + { + io_write(File, pSound->m_pData, pSound->m_DataSize); + io_close(File); + pEditor->m_Dialog = DIALOG_NONE; + return true; + } + pEditor->ShowFileDialogError("Failed to open file '%s'.", pFileName); + return false; +} + void CEditor::DoToolbarLayers(CUIRect ToolBar) { const bool ModPressed = Input()->ModifierIsPressed(); @@ -4287,7 +4314,7 @@ void CEditor::RenderSounds(CUIRect ToolBox) if(Result == 2) { static SPopupMenuId s_PopupSoundId; - UI()->DoPopupMenu(&s_PopupSoundId, UI()->MouseX(), UI()->MouseY(), 120, 56, this, PopupSound); + UI()->DoPopupMenu(&s_PopupSoundId, UI()->MouseX(), UI()->MouseY(), 120, 73, this, PopupSound); } } } @@ -4799,8 +4826,10 @@ void CEditor::RenderFileDialog() m_PopupEventType = POPEVENT_SAVE; else if(m_pfnFileDialogFunc == &CallbackSaveCopyMap) m_PopupEventType = POPEVENT_SAVE_COPY; - else + else if(m_pfnFileDialogFunc == &CallbackSaveImage) m_PopupEventType = POPEVENT_SAVE_IMG; + else + m_PopupEventType = POPEVENT_SAVE_SOUND; m_PopupEventActivated = true; } else if(m_pfnFileDialogFunc) diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 9f08c4b7853..5364f63b804 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -995,6 +995,7 @@ class CEditor : public IEditor POPEVENT_SAVE, POPEVENT_SAVE_COPY, POPEVENT_SAVE_IMG, + POPEVENT_SAVE_SOUND, POPEVENT_LARGELAYER, POPEVENT_PREVENTUNUSEDTILES, POPEVENT_IMAGEDIV16, @@ -1288,6 +1289,7 @@ class CEditor : public IEditor static bool CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser); static bool CallbackAddTileart(const char *pFilepath, int StorageType, void *pUser); static bool CallbackSaveImage(const char *pFileName, int StorageType, void *pUser); + static bool CallbackSaveSound(const char *pFileName, int StorageType, void *pUser); void PopupSelectImageInvoke(int Current, float x, float y); int PopupSelectImageResult(); diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index a3665636c27..91baeab4d9d 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1546,6 +1546,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View, if(pEditor->DoButton_MenuItem(&s_ExportButton, "Export", 0, &Slot, 0, "Export the image")) { pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_IMG, "Save image", "Save", "mapres", false, CallbackSaveImage, pEditor); + pEditor->m_FileDialogFileNameInput.Set(pImg->m_aName); return CUI::POPUP_CLOSE_CURRENT; } } @@ -1560,6 +1561,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSound(void *pContext, CUIRect View, static int s_ReaddButton = 0; static int s_ReplaceButton = 0; static int s_RemoveButton = 0; + static int s_ExportButton = 0; CUIRect Slot; View.HSplitTop(12.0f, &Slot, &View); @@ -1615,6 +1617,15 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSound(void *pContext, CUIRect View, return CUI::POPUP_CLOSE_CURRENT; } + View.HSplitTop(5.0f, nullptr, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_ExportButton, "Export", 0, &Slot, 0, "Export sound")) + { + pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_SOUND, "Save sound", "Save", "mapres", false, CallbackSaveSound, pEditor); + pEditor->m_FileDialogFileNameInput.Set(pSound->m_aName); + return CUI::POPUP_CLOSE_CURRENT; + } + return CUI::POPUP_KEEP_OPEN; } @@ -1767,6 +1778,11 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, pTitle = "Save image"; pMessage = "The file already exists.\n\nDo you want to overwrite the image?"; } + else if(pEditor->m_PopupEventType == POPEVENT_SAVE_SOUND) + { + pTitle = "Save sound"; + pMessage = "The file already exists.\n\nDo you want to overwrite the sound?"; + } else if(pEditor->m_PopupEventType == POPEVENT_LARGELAYER) { pTitle = "Large layer"; @@ -1891,6 +1907,11 @@ CUI::EPopupMenuFunctionResult CEditor::PopupEvent(void *pContext, CUIRect View, CallbackSaveImage(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); return CUI::POPUP_CLOSE_CURRENT; } + else if(pEditor->m_PopupEventType == POPEVENT_SAVE_SOUND) + { + CallbackSaveSound(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); + return CUI::POPUP_CLOSE_CURRENT; + } else if(pEditor->m_PopupEventType == POPEVENT_PLACE_BORDER_TILES) { pEditor->PlaceBorderTiles(); From 3b2389daa4a9525c168862f396d06c3d60679895 Mon Sep 17 00:00:00 2001 From: furo Date: Sat, 30 Sep 2023 19:56:01 +0200 Subject: [PATCH 54/70] Make IsAfk attribute optional --- src/engine/shared/serverinfo.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/shared/serverinfo.cpp b/src/engine/shared/serverinfo.cpp index 1e7210ab02b..9cccc5cf073 100644 --- a/src/engine/shared/serverinfo.cpp +++ b/src/engine/shared/serverinfo.cpp @@ -119,7 +119,6 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson) Error = Error || Country.type != json_integer; Error = Error || Score.type != json_integer; Error = Error || IsPlayer.type != json_boolean; - Error = Error || IsAfk.type != json_boolean; if(Error) { return true; @@ -132,7 +131,10 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson) pClient->m_Country = json_int_get(&Country); pClient->m_Score = json_int_get(&Score); pClient->m_IsPlayer = IsPlayer; - pClient->m_IsAfk = IsAfk; + + pClient->m_IsAfk = false; + if(IsAfk.type == json_boolean) + pClient->m_IsAfk = IsAfk; // check if a skin is also available bool HasSkin = false; From a3064ae1058c8aa6b7a64f2b7dac14d16f419c5f Mon Sep 17 00:00:00 2001 From: furo Date: Sat, 30 Sep 2023 20:51:20 +0200 Subject: [PATCH 55/70] Don't run `GET_INT` on afk attribute in `ProcessServerInfo` --- src/engine/client/client.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 3908ba2f066..8989c5071bd 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1476,7 +1476,6 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, GET_INT(pClient->m_Country); GET_INT(pClient->m_Score); GET_INT(pClient->m_Player); - GET_INT(pClient->m_Afk); if(SavedType == SERVERINFO_EXTENDED) { Up.GetString(); // extra info, reserved From a7748ba87e88c10411c2f91dff9d77c004948c48 Mon Sep 17 00:00:00 2001 From: marmare314 <49279081+Marmare314@users.noreply.github.com> Date: Sat, 30 Sep 2023 23:43:21 +0200 Subject: [PATCH 56/70] revert quad selection correctly fixes #7248 --- src/game/editor/editor.cpp | 50 +++++++++++++++++++++++++++----------- src/game/editor/editor.h | 7 +++--- src/game/editor/popups.cpp | 10 ++++---- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 0d19c5c1257..50be9e718e9 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -542,14 +542,30 @@ void CEditor::DeselectQuadPoints() m_SelectedQuadPoints = 0; } -void CEditor::SelectQuadPoint(int Index) +void CEditor::SelectQuadPoint(int QuadIndex, int Index) { + SelectQuad(QuadIndex); m_SelectedQuadPoints = 1 << Index; } -void CEditor::ToggleSelectQuadPoint(int Index) +void CEditor::ToggleSelectQuadPoint(int QuadIndex, int Index) { - m_SelectedQuadPoints ^= 1 << Index; + if(IsQuadPointSelected(QuadIndex, Index)) + { + m_SelectedQuadPoints ^= 1 << Index; + } + else + { + if(!IsQuadSelected(QuadIndex)) + { + ToggleSelectQuad(QuadIndex); + } + + if(!(m_SelectedQuadPoints & 1 << Index)) + { + m_SelectedQuadPoints ^= 1 << Index; + } + } } void CEditor::DeleteSelectedQuads() @@ -575,11 +591,16 @@ bool CEditor::IsQuadSelected(int Index) const return FindSelectedQuadIndex(Index) >= 0; } -bool CEditor::IsQuadPointSelected(int Index) const +bool CEditor::IsQuadCornerSelected(int Index) const { return m_SelectedQuadPoints & (1 << Index); } +bool CEditor::IsQuadPointSelected(int QuadIndex, int Index) const +{ + return IsQuadSelected(QuadIndex) && IsQuadCornerSelected(Index); +} + int CEditor::FindSelectedQuadIndex(int Index) const { for(size_t i = 0; i < m_vSelectedQuads.size(); ++i) @@ -1643,7 +1664,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) float py = fx2f(pQuad->m_aPoints[V].y); // draw selection background - if(IsQuadPointSelected(V)) + if(IsQuadPointSelected(QuadIndex, V)) { Graphics()->SetColor(0, 0, 0, 1); IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale); @@ -1676,8 +1697,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) if(x * x + y * y > 20.0f) { - if(!IsQuadPointSelected(V)) - SelectQuadPoint(V); + if(!IsQuadPointSelected(QuadIndex, V)) + SelectQuadPoint(QuadIndex, V); if(Input()->ShiftIsPressed()) { @@ -1701,7 +1722,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) for(int m = 0; m < 4; m++) { - if(IsQuadPointSelected(m)) + if(IsQuadPointSelected(QuadIndex, m)) { pQuad->m_aPoints[m].x += OffsetX; pQuad->m_aPoints[m].y += OffsetY; @@ -1712,7 +1733,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { for(int m = 0; m < 4; m++) { - if(IsQuadPointSelected(m)) + if(IsQuadPointSelected(QuadIndex, m)) { // 0,2;1,3 - line x // 0,1;2,3 - line y @@ -1752,9 +1773,9 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) if(s_Operation == OP_SELECT) { if(Input()->ShiftIsPressed()) - ToggleSelectQuadPoint(V); + ToggleSelectQuadPoint(QuadIndex, V); else - SelectQuadPoint(V); + SelectQuadPoint(QuadIndex, V); } UI()->DisableMouseLock(); @@ -1786,8 +1807,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) UI()->SetActiveItem(pID); - if(!IsQuadPointSelected(V)) - SelectQuadPoint(V); + if(!IsQuadPointSelected(QuadIndex, V)) + SelectQuadPoint(QuadIndex, V); } } else @@ -2530,6 +2551,7 @@ void CEditor::DoMapEditor(CUIRect View) std::shared_ptr pQuadLayer = std::static_pointer_cast(GetSelectedLayerType(0, LAYERTYPE_QUADS)); if(Input()->ShiftIsPressed() && pQuadLayer) { + DeselectQuads(); for(size_t i = 0; i < pQuadLayer->m_vQuads.size(); i++) { const CQuad &Quad = pQuadLayer->m_vQuads[i]; @@ -2537,7 +2559,7 @@ void CEditor::DoMapEditor(CUIRect View) float py = fx2f(Quad.m_aPoints[4].y); if(r.Inside(px, py) && !IsQuadSelected(i)) - SelectQuad(i); + ToggleSelectQuad(i); } } else diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 5364f63b804..287a4db77c5 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -950,11 +950,12 @@ class CEditor : public IEditor void ToggleSelectQuad(int Index); void DeselectQuads(); void DeselectQuadPoints(); - void SelectQuadPoint(int Index); - void ToggleSelectQuadPoint(int Index); + void SelectQuadPoint(int QuadIndex, int Index); + void ToggleSelectQuadPoint(int QuadIndex, int Index); void DeleteSelectedQuads(); bool IsQuadSelected(int Index) const; - bool IsQuadPointSelected(int Index) const; + bool IsQuadCornerSelected(int Index) const; + bool IsQuadPointSelected(int QuadIndex, int Index) const; int FindSelectedQuadIndex(int Index) const; int FindEnvPointIndex(int Index, int Channel) const; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 91baeab4d9d..c9b1a71d150 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1257,20 +1257,20 @@ CUI::EPopupMenuFunctionResult CEditor::PopupPoint(void *pContext, CUIRect View, if(Prop == PROP_POS_X) { for(int v = 0; v < 4; v++) - if(pEditor->IsQuadPointSelected(v)) + if(pEditor->IsQuadCornerSelected(v)) pQuad->m_aPoints[v].x = i2fx(fx2i(pQuad->m_aPoints[v].x) + NewVal - X); } else if(Prop == PROP_POS_Y) { for(int v = 0; v < 4; v++) - if(pEditor->IsQuadPointSelected(v)) + if(pEditor->IsQuadCornerSelected(v)) pQuad->m_aPoints[v].y = i2fx(fx2i(pQuad->m_aPoints[v].y) + NewVal - Y); } else if(Prop == PROP_COLOR) { for(int v = 0; v < 4; v++) { - if(pEditor->IsQuadPointSelected(v)) + if(pEditor->IsQuadCornerSelected(v)) { pQuad->m_aColors[v].r = (NewVal >> 24) & 0xff; pQuad->m_aColors[v].g = (NewVal >> 16) & 0xff; @@ -1282,13 +1282,13 @@ CUI::EPopupMenuFunctionResult CEditor::PopupPoint(void *pContext, CUIRect View, else if(Prop == PROP_TEX_U) { for(int v = 0; v < 4; v++) - if(pEditor->IsQuadPointSelected(v)) + if(pEditor->IsQuadCornerSelected(v)) pQuad->m_aTexcoords[v].x = f2fx(fx2f(pQuad->m_aTexcoords[v].x) + (NewVal - TextureU) / 1024.0f); } else if(Prop == PROP_TEX_V) { for(int v = 0; v < 4; v++) - if(pEditor->IsQuadPointSelected(v)) + if(pEditor->IsQuadCornerSelected(v)) pQuad->m_aTexcoords[v].y = f2fx(fx2f(pQuad->m_aTexcoords[v].y) + (NewVal - TextureV) / 1024.0f); } } From f55086f2bf35a2e5495998ac69f1f2f8aa1a9976 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 1 Oct 2023 01:14:35 +0200 Subject: [PATCH 57/70] Use `m_aInputData` for dummy in `cl_show_direction` --- src/game/client/components/nameplates.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 0b5cd8a34b9..b612006c7e4 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -68,7 +68,12 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP DirRight = m_pClient->m_Controls.m_aInputData[g_Config.m_ClDummy].m_Direction == 1; Jump = m_pClient->m_Controls.m_aInputData[g_Config.m_ClDummy].m_Jump == 1; } - + if(Client()->DummyConnected() && Client()->State() != IClient::STATE_DEMOPLAYBACK && pPlayerInfo->m_ClientID == m_pClient->m_aLocalIDs[!g_Config.m_ClDummy]) + { + DirLeft = m_pClient->m_Controls.m_aInputData[!g_Config.m_ClDummy].m_Direction == -1; + DirRight = m_pClient->m_Controls.m_aInputData[!g_Config.m_ClDummy].m_Direction == 1; + Jump = m_pClient->m_Controls.m_aInputData[!g_Config.m_ClDummy].m_Jump == 1; + } if(DirLeft) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); From 55120499464fc705bfd5e24e3e365af914a21938 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 1 Oct 2023 11:42:58 +0200 Subject: [PATCH 58/70] Remove unused commands from autoexec cfg --- data/autoexec_server.cfg | 6 ------ 1 file changed, 6 deletions(-) diff --git a/data/autoexec_server.cfg b/data/autoexec_server.cfg index bc80ef06835..e9d5b5aa87a 100644 --- a/data/autoexec_server.cfg +++ b/data/autoexec_server.cfg @@ -55,9 +55,6 @@ logfile "autoexec_server.log" # Log level (-3 = None, -2 = Error, -1 = Warn, 0 = Info, 1 = Debug, 2 = Trace) loglevel 0 -# Folder where map records will be saved -sv_score_folder "records" - # Max players on server sv_max_clients 64 @@ -75,9 +72,6 @@ sv_rescue 1 # Number of seconds between two rescues sv_rescue_delay 5 -# Enable ranks after rcon cheats have been used -sv_rank_cheats 1 - # SERVER CUSTOMIZATION From 0661eafb492fc46cf56965a25192d9b26d3f94e1 Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 1 Oct 2023 17:32:51 +0200 Subject: [PATCH 59/70] Send the player's latency if a menu is open --- src/game/server/player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 5a6b6effd93..dbad705d2b3 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -286,6 +286,9 @@ void CPlayer::Tick() void CPlayer::PostTick() { // update latency value + if(m_PlayerFlags & PLAYERFLAG_IN_MENU) + m_aCurLatency[m_ClientID] = GameServer()->m_apPlayers[m_ClientID]->m_Latency.m_Min; + if(m_PlayerFlags & PLAYERFLAG_SCOREBOARD) { for(int i = 0; i < MAX_CLIENTS; ++i) From 6dd43fb08636e4caa0e9e5c9f04a8471d7bd54bb Mon Sep 17 00:00:00 2001 From: furo Date: Sun, 1 Oct 2023 18:34:07 +0200 Subject: [PATCH 60/70] Don't reset `LastTelePos` on death --- src/game/server/ddracechat.cpp | 6 +++--- src/game/server/entities/character.h | 2 -- src/game/server/player.h | 2 ++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 935ade688c2..cfbcfae654e 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1540,7 +1540,7 @@ void CGameContext::ConTele(IConsole::IResult *pResult, void *pUserData) return; Pos = pChrTo->m_Pos; } - pChr->LastTelePos = Pos; + pPlayer->LastTelePos = Pos; pSelf->Teleport(pChr, Pos); pChr->UnFreeze(); pChr->Core()->m_Vel = vec2(0, 0); @@ -1565,12 +1565,12 @@ void CGameContext::ConLastTele(IConsole::IResult *pResult, void *pUserData) pSelf->SendChatTarget(pPlayer->GetCID(), "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled."); return; } - if(!pChr->LastTelePos.x) + if(!pPlayer->LastTelePos.x) { pSelf->SendChatTarget(pPlayer->GetCID(), "You haven't previously teleported. Use /tp before using this command."); return; } - pSelf->Teleport(pChr, pChr->LastTelePos); + pSelf->Teleport(pChr, pPlayer->LastTelePos); pChr->UnFreeze(); pChr->Core()->m_Vel = vec2(0, 0); } diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 8870ef6364a..89dc031c4a9 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -217,8 +217,6 @@ class CCharacter : public CEntity int m_SpawnTick; int m_WeaponChangeTick; - vec2 LastTelePos; - // Setters/Getters because i don't want to modify vanilla vars access modifiers int GetLastWeapon() { return m_LastWeapon; } void SetLastWeapon(int LastWeap) { m_LastWeapon = LastWeap; } diff --git a/src/game/server/player.h b/src/game/server/player.h index 799a0440744..651cc5e62c5 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -220,6 +220,8 @@ class CPlayer bool m_VotedForPractice; int m_SwapTargetsClientID; //Client ID of the swap target for the given player bool m_BirthdayAnnounced; + + vec2 LastTelePos; }; #endif From fe27752d40eeea3e9ae78460850f4a167f760086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 30 Sep 2023 16:58:04 +0200 Subject: [PATCH 61/70] Refactor menu image scan Remove unnecessary check for duplicate menu images. Listing directories with the storage now prevents duplicates, so this additional check is not needed anymore. Ensure menu images are in RGBA format. Ensure menu image names are not truncated. Minor code format improvements. --- src/game/client/components/menus.cpp | 78 +++++++++++++--------------- src/game/client/components/menus.h | 2 - 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index b7ec3f7cce0..e100f171728 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -2191,58 +2191,52 @@ int CMenus::DoButton_CheckBox_Tristate(const void *pID, const char *pText, TRIST int CMenus::MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser) { - CMenus *pSelf = (CMenus *)pUser; - if(IsDir || !str_endswith(pName, ".png")) + const char *pExtension = ".png"; + CMenuImage MenuImage; + CMenus *pSelf = static_cast(pUser); + if(IsDir || !str_endswith(pName, pExtension) || str_length(pName) - str_length(pExtension) >= (int)sizeof(MenuImage.m_aName)) return 0; - char aBuf[IO_MAX_PATH_LENGTH]; - bool ImgExists = false; - for(const auto &Img : pSelf->m_vMenuImages) + char aPath[IO_MAX_PATH_LENGTH]; + str_format(aPath, sizeof(aPath), "menuimages/%s", pName); + + CImageInfo Info; + if(!pSelf->Graphics()->LoadPNG(&Info, aPath, DirType)) { - str_format(aBuf, std::size(aBuf), "%s.png", Img.m_aName); - if(str_comp(aBuf, pName) == 0) - { - ImgExists = true; - break; - } + char aError[IO_MAX_PATH_LENGTH + 64]; + str_format(aError, sizeof(aError), "Failed to load menu image from '%s'", aPath); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "menus", aError); + return 0; } - - if(!ImgExists) + if(Info.m_Format != CImageInfo::FORMAT_RGBA) { - str_format(aBuf, sizeof(aBuf), "menuimages/%s", pName); - CImageInfo Info; - if(!pSelf->Graphics()->LoadPNG(&Info, aBuf, DirType)) - { - str_format(aBuf, sizeof(aBuf), "failed to load menu image from %s", pName); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); - return 0; - } - - CMenuImage MenuImage; - MenuImage.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); + pSelf->Graphics()->FreePNG(&Info); + char aError[IO_MAX_PATH_LENGTH + 64]; + str_format(aError, sizeof(aError), "Failed to load menu image from '%s': must be an RGBA image", aPath); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "menus", aError); + return 0; + } - unsigned char *pData = (unsigned char *)Info.m_pData; + MenuImage.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); - // create colorless version - const size_t Step = Info.PixelSize(); + // create gray scale version + unsigned char *pData = static_cast(Info.m_pData); + const size_t Step = Info.PixelSize(); + for(int i = 0; i < Info.m_Width * Info.m_Height; i++) + { + int v = (pData[i * Step] + pData[i * Step + 1] + pData[i * Step + 2]) / 3; + pData[i * Step] = v; + pData[i * Step + 1] = v; + pData[i * Step + 2] = v; + } + MenuImage.m_GreyTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); + pSelf->Graphics()->FreePNG(&Info); - // make the texture gray scale - for(int i = 0; i < Info.m_Width * Info.m_Height; i++) - { - int v = (pData[i * Step] + pData[i * Step + 1] + pData[i * Step + 2]) / 3; - pData[i * Step] = v; - pData[i * Step + 1] = v; - pData[i * Step + 2] = v; - } + str_truncate(MenuImage.m_aName, sizeof(MenuImage.m_aName), pName, str_length(pName) - str_length(pExtension)); + pSelf->m_vMenuImages.push_back(MenuImage); - MenuImage.m_GreyTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); - pSelf->Graphics()->FreePNG(&Info); + pSelf->RenderLoading(Localize("Loading DDNet Client"), Localize("Loading menu images"), 1); - // set menu image data - str_truncate(MenuImage.m_aName, sizeof(MenuImage.m_aName), pName, str_length(pName) - 4); - pSelf->m_vMenuImages.push_back(MenuImage); - pSelf->RenderLoading(Localize("Loading DDNet Client"), Localize("Loading menu images"), 1); - } return 0; } diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 2f1db122926..c0646d0bff4 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -169,9 +169,7 @@ class CMenus : public CComponent IGraphics::CTextureHandle m_GreyTexture; }; std::vector m_vMenuImages; - static int MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser); - const CMenuImage *FindMenuImage(const char *pName); // loading From b1d90aaaf2e9e5656c3361290af814b08d7f71f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 1 Oct 2023 20:17:55 +0200 Subject: [PATCH 62/70] Store number of filtered players when filtering Only update total number of filtered players when the filter is updated instead of calculating it every frame. --- src/engine/client/serverbrowser.cpp | 6 ++++++ src/engine/client/serverbrowser.h | 2 ++ src/engine/serverbrowser.h | 1 + src/game/client/components/menus_browser.cpp | 10 +++------- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 8e90946513f..42eecb7d042 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -62,6 +62,7 @@ CServerBrowser::CServerBrowser() m_NumSortedServers = 0; m_NumSortedServersCapacity = 0; + m_NumSortedPlayers = 0; m_NumServers = 0; m_NumServerCapacity = 0; @@ -265,6 +266,7 @@ bool CServerBrowser::SortCompareNumPlayersAndPing(int Index1, int Index2) const void CServerBrowser::Filter() { m_NumSortedServers = 0; + m_NumSortedPlayers = 0; // allocate the sorted list if(m_NumSortedServersCapacity < m_NumServers) @@ -411,7 +413,10 @@ void CServerBrowser::Filter() UpdateServerFriends(&Info); if(!g_Config.m_BrFilterFriends || Info.m_FriendState != IFriends::FRIEND_NO) + { + m_NumSortedPlayers += Info.m_NumFilteredPlayers; m_pSortedServerlist[m_NumSortedServers++] = i; + } } } } @@ -1051,6 +1056,7 @@ void CServerBrowser::CleanUp() m_ServerlistHeap.Reset(); m_NumServers = 0; m_NumSortedServers = 0; + m_NumSortedPlayers = 0; m_ByAddr.clear(); m_pFirstReqServer = nullptr; m_pLastReqServer = nullptr; diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index 2708f105ed4..63cf2db2954 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -51,6 +51,7 @@ class CServerBrowser : public IServerBrowser int Players(const CServerInfo &Item) const override; int Max(const CServerInfo &Item) const override; int NumSortedServers() const override { return m_NumSortedServers; } + int NumSortedPlayers() const override { return m_NumSortedPlayers; } const CServerInfo *SortedGet(int Index) const override; const char *GetTutorialServer() override; @@ -125,6 +126,7 @@ class CServerBrowser : public IServerBrowser int m_NumSortedServers; int m_NumSortedServersCapacity; + int m_NumSortedPlayers; int m_NumServers; int m_NumServerCapacity; diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 5db54ec4991..6acce820998 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -235,6 +235,7 @@ class IServerBrowser : public IInterface virtual int Max(const CServerInfo &Item) const = 0; virtual int NumSortedServers() const = 0; + virtual int NumSortedPlayers() const = 0; virtual const CServerInfo *SortedGet(int Index) const = 0; virtual const std::vector &Communities() const = 0; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 00e0df15d7e..b80aaf22508 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -517,14 +517,10 @@ void CMenus::RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItem str_format(aBuf, sizeof(aBuf), Localize("%d of %d server"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers()); UI()->DoLabel(&ServersOnline, aBuf, 12.0f, TEXTALIGN_MR); - int NumPlayers = 0; - for(int i = 0; i < ServerBrowser()->NumSortedServers(); i++) - NumPlayers += ServerBrowser()->SortedGet(i)->m_NumFilteredPlayers; - - if(NumPlayers != 1) - str_format(aBuf, sizeof(aBuf), Localize("%d players"), NumPlayers); + if(ServerBrowser()->NumSortedPlayers() != 1) + str_format(aBuf, sizeof(aBuf), Localize("%d players"), ServerBrowser()->NumSortedPlayers()); else - str_format(aBuf, sizeof(aBuf), Localize("%d player"), NumPlayers); + str_format(aBuf, sizeof(aBuf), Localize("%d player"), ServerBrowser()->NumSortedPlayers()); UI()->DoLabel(&PlayersOnline, aBuf, 12.0f, TEXTALIGN_MR); } From fa07f6498225d0d32cdf87b9b91733ed33ce699c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 1 Oct 2023 20:09:56 +0200 Subject: [PATCH 63/70] Improve performance of menus checker background, refactoring Avoid duplicate `SetColor` calls in loop. Avoid `QuadsDrawTL` calls by batching quads together before drawing. Use `SetColor` instead of `SetColorVertex`. --- src/game/client/components/menus.cpp | 50 +++++++++++++++------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index b7ec3f7cce0..7793ba37e79 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -2121,45 +2121,49 @@ void CMenus::RenderBackground() { Graphics()->BlendNormal(); - float sw = 300 * Graphics()->ScreenAspect(); - float sh = 300; - Graphics()->MapScreen(0, 0, sw, sh); + const float ScreenHeight = 300.0f; + const float ScreenWidth = ScreenHeight * Graphics()->ScreenAspect(); + Graphics()->MapScreen(0.0f, 0.0f, ScreenWidth, ScreenHeight); // render background color Graphics()->TextureClear(); Graphics()->QuadsBegin(); - ColorRGBA Bottom(ms_GuiColor.r, ms_GuiColor.g, ms_GuiColor.b, 1.0f); - ColorRGBA Top(ms_GuiColor.r, ms_GuiColor.g, ms_GuiColor.b, 1.0f); - IGraphics::CColorVertex Array[4] = { - IGraphics::CColorVertex(0, Top.r, Top.g, Top.b, Top.a), - IGraphics::CColorVertex(1, Top.r, Top.g, Top.b, Top.a), - IGraphics::CColorVertex(2, Bottom.r, Bottom.g, Bottom.b, Bottom.a), - IGraphics::CColorVertex(3, Bottom.r, Bottom.g, Bottom.b, Bottom.a)}; - Graphics()->SetColorVertex(Array, 4); - IGraphics::CQuadItem QuadItem(0, 0, sw, sh); - Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->SetColor(ms_GuiColor.WithAlpha(1.0f)); + const IGraphics::CQuadItem BackgroundQuadItem = IGraphics::CQuadItem(0, 0, ScreenWidth, ScreenHeight); + Graphics()->QuadsDrawTL(&BackgroundQuadItem, 1); Graphics()->QuadsEnd(); // render the tiles Graphics()->TextureClear(); Graphics()->QuadsBegin(); - float Size = 15.0f; - float OffsetTime = std::fmod(LocalTime() * 0.15f, 2.0f); - for(int y = -2; y < (int)(sw / Size); y++) - for(int x = -2; x < (int)(sh / Size); x++) + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.045f); + const float Size = 15.0f; + const float OffsetTime = std::fmod(LocalTime() * 0.15f, 2.0f); + IGraphics::CQuadItem aCheckerItems[64]; + size_t NumCheckerItems = 0; + for(int y = -2; y < (int)(ScreenWidth / Size); y++) + { + for(int x = -2; x < (int)(ScreenHeight / Size); x++) { - Graphics()->SetColor(0, 0, 0, 0.045f); - QuadItem = IGraphics::CQuadItem((x - OffsetTime) * Size * 2 + (y & 1) * Size, (y + OffsetTime) * Size, Size, Size); - Graphics()->QuadsDrawTL(&QuadItem, 1); + aCheckerItems[NumCheckerItems] = IGraphics::CQuadItem((x - OffsetTime) * Size * 2 + (y & 1) * Size, (y + OffsetTime) * Size, Size, Size); + NumCheckerItems++; + if(NumCheckerItems == std::size(aCheckerItems)) + { + Graphics()->QuadsDrawTL(aCheckerItems, NumCheckerItems); + NumCheckerItems = 0; + } } + } + if(NumCheckerItems != 0) + Graphics()->QuadsDrawTL(aCheckerItems, NumCheckerItems); Graphics()->QuadsEnd(); // render border fade Graphics()->TextureSet(m_TextureBlob); Graphics()->QuadsBegin(); - Graphics()->SetColor(1, 1, 1, 1); - QuadItem = IGraphics::CQuadItem(-100, -100, sw + 200, sh + 200); - Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + const IGraphics::CQuadItem BlobQuadItem = IGraphics::CQuadItem(-100, -100, ScreenWidth + 200, ScreenHeight + 200); + Graphics()->QuadsDrawTL(&BlobQuadItem, 1); Graphics()->QuadsEnd(); // restore screen From 7e045d4f32f74f704e669f276a09baac87f52c0e Mon Sep 17 00:00:00 2001 From: furo Date: Mon, 2 Oct 2023 11:58:50 +0200 Subject: [PATCH 64/70] Teleport the player to their cursor if no argument is specified. Check for `NumArguments()` --- src/game/server/ddracechat.cpp | 7 +++++-- src/game/server/ddracecommands.cpp | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 935ade688c2..557731c0387 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1518,8 +1518,11 @@ void CGameContext::ConTele(IConsole::IResult *pResult, void *pUserData) } vec2 Pos = pPlayer->m_ViewPos; - - if(pResult->NumArguments() > 0) + if(pResult->NumArguments() == 0 && !pPlayer->IsPaused()) + { + Pos = Pos + vec2(pChr->Core()->m_Input.m_TargetX, pChr->Core()->m_Input.m_TargetY); + } + else if(pResult->NumArguments() > 0) { int ClientID; for(ClientID = 0; ClientID < MAX_CLIENTS; ClientID++) diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index 19977b557ab..8481a975d9e 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -383,9 +383,15 @@ void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData) } CCharacter *pChr = pSelf->GetPlayerChar(Tele); - if(pChr && pSelf->GetPlayerChar(TeleTo)) + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + + if(pChr && pPlayer && pSelf->GetPlayerChar(TeleTo)) { - pSelf->Teleport(pChr, pSelf->m_apPlayers[TeleTo]->m_ViewPos); + vec2 Pos = pSelf->m_apPlayers[TeleTo]->m_ViewPos; + if(!pPlayer->IsPaused() && !pResult->NumArguments()) + Pos = Pos + vec2(pChr->Core()->m_Input.m_TargetX, pChr->Core()->m_Input.m_TargetY); + + pSelf->Teleport(pChr, Pos); pChr->UnFreeze(); pChr->Core()->m_Vel = vec2(0, 0); } From 9029fb9593aad002b633b054d46efcca72d218f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 24 Sep 2023 20:50:34 +0200 Subject: [PATCH 65/70] Remove `CTeeRenderInfo` member variables of `CPlayers` The variables can be local variables in `CPlayers::OnRender`. --- src/game/client/components/players.cpp | 49 ++++++++++++++------------ src/game/client/components/players.h | 2 -- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 95e137b78b1..6bb5a646367 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -749,18 +749,20 @@ inline bool CPlayers::IsPlayerInfoAvailable(int ClientID) const void CPlayers::OnRender() { + CTeeRenderInfo aRenderInfo[MAX_CLIENTS]; + // update RenderInfo for ninja bool IsTeamplay = false; if(m_pClient->m_Snap.m_pGameInfoObj) IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS) != 0; for(int i = 0; i < MAX_CLIENTS; ++i) { - m_aRenderInfo[i] = m_pClient->m_aClients[i].m_RenderInfo; - m_aRenderInfo[i].m_TeeRenderFlags = 0; + aRenderInfo[i] = m_pClient->m_aClients[i].m_RenderInfo; + aRenderInfo[i].m_TeeRenderFlags = 0; if(m_pClient->m_aClients[i].m_FreezeEnd != 0) - m_aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN | TEE_NO_WEAPON; + aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN | TEE_NO_WEAPON; if(m_pClient->m_aClients[i].m_LiveFrozen) - m_aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN; + aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN; const CGameClient::CSnapState::CCharacterInfo &CharacterInfo = m_pClient->m_Snap.m_aCharacters[i]; const bool Frozen = CharacterInfo.m_HasExtendedData && CharacterInfo.m_ExtendedData.m_FreezeEnd != 0; @@ -770,27 +772,28 @@ void CPlayers::OnRender() const auto *pSkin = m_pClient->m_Skins.FindOrNullptr("x_ninja"); if(pSkin != nullptr) { - m_aRenderInfo[i].m_OriginalRenderSkin = pSkin->m_OriginalSkin; - m_aRenderInfo[i].m_ColorableRenderSkin = pSkin->m_ColorableSkin; - m_aRenderInfo[i].m_BloodColor = pSkin->m_BloodColor; - m_aRenderInfo[i].m_SkinMetrics = pSkin->m_Metrics; - m_aRenderInfo[i].m_CustomColoredSkin = IsTeamplay; + aRenderInfo[i].m_OriginalRenderSkin = pSkin->m_OriginalSkin; + aRenderInfo[i].m_ColorableRenderSkin = pSkin->m_ColorableSkin; + aRenderInfo[i].m_BloodColor = pSkin->m_BloodColor; + aRenderInfo[i].m_SkinMetrics = pSkin->m_Metrics; + aRenderInfo[i].m_CustomColoredSkin = IsTeamplay; if(!IsTeamplay) { - m_aRenderInfo[i].m_ColorBody = ColorRGBA(1, 1, 1); - m_aRenderInfo[i].m_ColorFeet = ColorRGBA(1, 1, 1); + aRenderInfo[i].m_ColorBody = ColorRGBA(1, 1, 1); + aRenderInfo[i].m_ColorFeet = ColorRGBA(1, 1, 1); } } } } const CSkin *pSkin = m_pClient->m_Skins.Find("x_spec"); - m_RenderInfoSpec.m_OriginalRenderSkin = pSkin->m_OriginalSkin; - m_RenderInfoSpec.m_ColorableRenderSkin = pSkin->m_ColorableSkin; - m_RenderInfoSpec.m_BloodColor = pSkin->m_BloodColor; - m_RenderInfoSpec.m_SkinMetrics = pSkin->m_Metrics; - m_RenderInfoSpec.m_CustomColoredSkin = false; - m_RenderInfoSpec.m_Size = 64.0f; - int LocalClientID = m_pClient->m_Snap.m_LocalClientID; + CTeeRenderInfo RenderInfoSpec; + RenderInfoSpec.m_OriginalRenderSkin = pSkin->m_OriginalSkin; + RenderInfoSpec.m_ColorableRenderSkin = pSkin->m_ColorableSkin; + RenderInfoSpec.m_BloodColor = pSkin->m_BloodColor; + RenderInfoSpec.m_SkinMetrics = pSkin->m_Metrics; + RenderInfoSpec.m_CustomColoredSkin = false; + RenderInfoSpec.m_Size = 64.0f; + const int LocalClientID = m_pClient->m_Snap.m_LocalClientID; // get screen edges to avoid rendering offscreen float ScreenX0, ScreenY0, ScreenX1, ScreenY1; @@ -812,12 +815,12 @@ void CPlayers::OnRender() { continue; } - RenderHook(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &m_aRenderInfo[ClientID], ClientID); + RenderHook(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &aRenderInfo[ClientID], ClientID); } if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID)) { const CGameClient::CClientData *pLocalClientData = &m_pClient->m_aClients[LocalClientID]; - RenderHook(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &m_aRenderInfo[LocalClientID], LocalClientID); + RenderHook(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &aRenderInfo[LocalClientID], LocalClientID); } // render spectating players @@ -827,7 +830,7 @@ void CPlayers::OnRender() { continue; } - RenderTools()->RenderTee(CAnimState::GetIdle(), &m_RenderInfoSpec, EMOTE_BLINK, vec2(1, 0), m_aClient.m_SpecChar); + RenderTools()->RenderTee(CAnimState::GetIdle(), &RenderInfoSpec, EMOTE_BLINK, vec2(1, 0), m_aClient.m_SpecChar); } // render everyone else's tee, then our own @@ -846,13 +849,13 @@ void CPlayers::OnRender() { continue; } - RenderPlayer(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &m_aRenderInfo[ClientID], ClientID); + RenderPlayer(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &aRenderInfo[ClientID], ClientID); } if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID)) { const CGameClient::CClientData *pLocalClientData = &m_pClient->m_aClients[LocalClientID]; RenderHookCollLine(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, LocalClientID); - RenderPlayer(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &m_aRenderInfo[LocalClientID], LocalClientID); + RenderPlayer(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &aRenderInfo[LocalClientID], LocalClientID); } } diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h index 3617b47f4cb..00c66d6ae3d 100644 --- a/src/game/client/components/players.h +++ b/src/game/client/components/players.h @@ -11,8 +11,6 @@ class CPlayers : public CComponent { friend class CGhost; - CTeeRenderInfo m_RenderInfoSpec; - CTeeRenderInfo m_aRenderInfo[MAX_CLIENTS]; void RenderHand(const CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset, float Alpha = 1.0f); void RenderPlayer( const CNetObj_Character *pPrevChar, From 1dedde542afdda7e878659dbae8c9b81a271ffc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 24 Sep 2023 21:05:49 +0200 Subject: [PATCH 66/70] Refactor ghost skin updating, ensure current ghost is updated Rename function for consistency. --- src/game/client/components/ghost.cpp | 46 ++++++++++++---------------- src/game/client/components/ghost.h | 2 +- src/game/client/gameclient.cpp | 2 +- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index 2bffbaa216d..e21a99f9e2a 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -667,40 +667,32 @@ int CGhost::GetLastRaceTick() return m_LastRaceTick; } -void CGhost::RefindSkin() +void CGhost::RefindSkins() { - char aSkinName[64]; - for(auto &Ghost : m_aActiveGhosts) - { - if(!Ghost.Empty()) - { - IntsToStr(&Ghost.m_Skin.m_Skin0, 6, aSkinName); - CTeeRenderInfo *pRenderInfo = &Ghost.m_RenderInfo; - if(aSkinName[0] != '\0') - { - const CSkin *pSkin = m_pClient->m_Skins.Find(aSkinName); - pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin; - pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin; - pRenderInfo->m_SkinMetrics = pSkin->m_Metrics; - } - else - { - pRenderInfo->m_OriginalRenderSkin.Reset(); - pRenderInfo->m_ColorableRenderSkin.Reset(); - } - } - } - if(!m_CurGhost.Empty()) - { - IntsToStr(&m_CurGhost.m_Skin.m_Skin0, 6, aSkinName); + const auto &&RefindSkin = [&](auto &Ghost) { + if(Ghost.Empty()) + return; + char aSkinName[64]; + IntsToStr(&Ghost.m_Skin.m_Skin0, 6, aSkinName); + CTeeRenderInfo *pRenderInfo = &Ghost.m_RenderInfo; if(aSkinName[0] != '\0') { - CTeeRenderInfo *pRenderInfo = &m_CurGhost.m_RenderInfo; - const CSkin *pSkin = m_pClient->m_Skins.Find(aSkinName); pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin; pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin; + pRenderInfo->m_BloodColor = pSkin->m_BloodColor; pRenderInfo->m_SkinMetrics = pSkin->m_Metrics; } + else + { + pRenderInfo->m_OriginalRenderSkin.Reset(); + pRenderInfo->m_ColorableRenderSkin.Reset(); + } + }; + + for(auto &Ghost : m_aActiveGhosts) + { + RefindSkin(Ghost); } + RefindSkin(m_CurGhost); } diff --git a/src/game/client/components/ghost.h b/src/game/client/components/ghost.h index e9233eee659..3da2ade8ec7 100644 --- a/src/game/client/components/ghost.h +++ b/src/game/client/components/ghost.h @@ -176,7 +176,7 @@ class CGhost : public CComponent int GetLastRaceTick(); - void RefindSkin(); + void RefindSkins(); }; #endif diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index f3c573f843f..384e36e5958 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -3358,7 +3358,7 @@ void CGameClient::RefindSkins() Client.m_SkinInfo.m_ColorableRenderSkin.Reset(); } } - m_Ghost.RefindSkin(); + m_Ghost.RefindSkins(); m_Chat.RefindSkins(); m_KillMessages.RefindSkins(); } From e41b3a9b57445104511d757c697b3f6600ca8471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 2 Oct 2023 15:17:48 +0200 Subject: [PATCH 67/70] Use `bool` instead of `int` for `m_GotAirJump` --- src/game/client/components/players.cpp | 2 +- src/game/client/render.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 6bb5a646367..0e0a6d06eab 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -404,7 +404,7 @@ void CPlayers::RenderPlayer( m_pClient->m_Flow.Add(Position, Vel * 100.0f, 10.0f); - RenderInfo.m_GotAirJump = Player.m_Jumped & 2 ? 0 : 1; + RenderInfo.m_GotAirJump = Player.m_Jumped & 2 ? false : true; RenderInfo.m_FeetFlipped = false; diff --git a/src/game/client/render.h b/src/game/client/render.h index 96af2ac54c8..f4b406d9924 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -34,7 +34,7 @@ class CTeeRenderInfo m_ColorBody = ColorRGBA(1, 1, 1); m_ColorFeet = ColorRGBA(1, 1, 1); m_Size = 1.0f; - m_GotAirJump = 1; + m_GotAirJump = true; m_TeeRenderFlags = 0; }; @@ -49,7 +49,7 @@ class CTeeRenderInfo ColorRGBA m_ColorBody; ColorRGBA m_ColorFeet; float m_Size; - int m_GotAirJump; + bool m_GotAirJump; int m_TeeRenderFlags; bool m_FeetFlipped; }; From 6094061a81aeb7a7e8a310dd8409ca3f64f2e220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 2 Oct 2023 15:19:46 +0200 Subject: [PATCH 68/70] Add `CTeeRenderInfo::Reset`, ensure everything is initialized --- src/game/client/render.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/game/client/render.h b/src/game/client/render.h index f4b406d9924..bd30d55bd83 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -31,12 +31,23 @@ class CTeeRenderInfo public: CTeeRenderInfo() { - m_ColorBody = ColorRGBA(1, 1, 1); - m_ColorFeet = ColorRGBA(1, 1, 1); + Reset(); + } + + void Reset() + { + m_OriginalRenderSkin.Reset(); + m_ColorableRenderSkin.Reset(); + m_SkinMetrics.Reset(); + m_CustomColoredSkin = false; + m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); m_Size = 1.0f; m_GotAirJump = true; m_TeeRenderFlags = 0; - }; + m_FeetFlipped = false; + } CSkin::SSkinTextures m_OriginalRenderSkin; CSkin::SSkinTextures m_ColorableRenderSkin; From 96ef1fbc88d21966c581176afab71ed5dca69004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 2 Oct 2023 15:25:20 +0200 Subject: [PATCH 69/70] Fix client render info not being updated when skin not found The skin info was always updated, but the render info was only being updated for skins that can be found. Now the render info is also always updated. --- src/game/client/gameclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 384e36e5958..78181ea0f12 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -3350,13 +3350,13 @@ void CGameClient::RefindSkins() const CSkin *pSkin = m_Skins.Find(Client.m_aSkinName); Client.m_SkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; Client.m_SkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; - Client.UpdateRenderInfo(IsTeamPlay()); } else { Client.m_SkinInfo.m_OriginalRenderSkin.Reset(); Client.m_SkinInfo.m_ColorableRenderSkin.Reset(); } + Client.UpdateRenderInfo(IsTeamPlay()); } m_Ghost.RefindSkins(); m_Chat.RefindSkins(); From 589b047ee4b1e384034d049f95591257b4a89c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 2 Oct 2023 15:32:10 +0200 Subject: [PATCH 70/70] Track team killmessage victims separately, ensure initialization Store client IDs of all victims for team killmessages instead of storing only the first victim ID, so the render info for all victims can be properly restored when skins are updated. Ensure all killmessages are properly initialized. --- src/game/client/components/killmessages.cpp | 148 ++++++++++---------- src/game/client/components/killmessages.h | 6 +- 2 files changed, 78 insertions(+), 76 deletions(-) diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 7c4a69af31b..ee38a2cc461 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -73,7 +73,7 @@ void CKillMessages::CreateKillmessageNamesIfNotCreated(CKillMsg &Kill) Cursor.m_LineWidth = -1; unsigned Color = g_Config.m_ClKillMessageNormalColor; - if(Kill.m_VictimID == m_pClient->m_Snap.m_LocalClientID) + if(Kill.m_aVictimIds[0] == m_pClient->m_Snap.m_LocalClientID) { Color = g_Config.m_ClKillMessageHighlightColor; } @@ -111,10 +111,7 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) { CNetMsg_Sv_KillMsgTeam *pMsg = (CNetMsg_Sv_KillMsgTeam *)pRawMsg; - // unpack messages CKillMsg Kill{}; - Kill.m_aVictimName[0] = '\0'; - Kill.m_aKillerName[0] = '\0'; std::vector> vStrongWeakSorted; for(int i = 0; i < MAX_CLIENTS; i++) @@ -132,19 +129,30 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) if(Kill.m_TeamSize > MAX_KILLMSG_TEAM_MEMBERS) Kill.m_TeamSize = MAX_KILLMSG_TEAM_MEMBERS; - Kill.m_VictimID = vStrongWeakSorted.empty() ? -1 : vStrongWeakSorted[0].first; - if(Kill.m_VictimID >= 0 && Kill.m_VictimID < MAX_CLIENTS) + Kill.m_VictimDDTeam = pMsg->m_Team; + for(int i = 0; i < Kill.m_TeamSize; i++) { - Kill.m_VictimTeam = m_pClient->m_aClients[Kill.m_VictimID].m_Team; - Kill.m_VictimDDTeam = pMsg->m_Team; - Kill.m_VictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_VictimID].m_RenderInfo; - - for(int i = 1; i < Kill.m_TeamSize; i++) - Kill.m_VictimRenderInfo[i] = m_pClient->m_aClients[vStrongWeakSorted[i].first].m_RenderInfo; + if(m_pClient->m_aClients[vStrongWeakSorted[i].first].m_Active) + { + Kill.m_aVictimIds[i] = vStrongWeakSorted[i].first; + Kill.m_aVictimRenderInfo[i] = m_pClient->m_aClients[vStrongWeakSorted[i].first].m_RenderInfo; + } + else + { + Kill.m_aVictimIds[i] = -1; + Kill.m_aVictimRenderInfo[i].Reset(); + } + } + for(int i = Kill.m_TeamSize; i < MAX_KILLMSG_TEAM_MEMBERS; i++) + { + Kill.m_aVictimIds[i] = -1; + Kill.m_aVictimRenderInfo[i].Reset(); } str_format(Kill.m_aVictimName, sizeof(Kill.m_aVictimName), Localize("Team %d"), pMsg->m_Team); - Kill.m_KillerID = Kill.m_VictimID; + Kill.m_KillerID = -1; + Kill.m_aKillerName[0] = '\0'; + Kill.m_KillerRenderInfo.Reset(); Kill.m_Weapon = -1; Kill.m_ModeSpecial = 0; @@ -161,13 +169,11 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) CreateKillmessageNamesIfNotCreated(Kill); - int VictimSkinsValid = 0; + bool KillMsgValid = true; for(int i = 0; i < Kill.m_TeamSize; i++) { - if((Kill.m_VictimRenderInfo[i].m_CustomColoredSkin && Kill.m_VictimRenderInfo[i].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_VictimRenderInfo[i].m_CustomColoredSkin && Kill.m_VictimRenderInfo[i].m_OriginalRenderSkin.m_Body.IsValid())) - VictimSkinsValid++; + KillMsgValid = KillMsgValid && Kill.m_aVictimIds[i] >= 0 && ((Kill.m_aVictimRenderInfo[i].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[i].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_aVictimRenderInfo[i].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[i].m_OriginalRenderSkin.m_Body.IsValid())); } - bool KillMsgValid = VictimSkinsValid == Kill.m_TeamSize; if(KillMsgValid) { @@ -192,29 +198,38 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) { CNetMsg_Sv_KillMsg *pMsg = (CNetMsg_Sv_KillMsg *)pRawMsg; - // unpack messages - CKillMsg Kill; - Kill.m_aVictimName[0] = '\0'; - Kill.m_aKillerName[0] = '\0'; + CKillMsg Kill{}; Kill.m_TeamSize = 1; - - Kill.m_VictimID = pMsg->m_Victim; - if(Kill.m_VictimID >= 0 && Kill.m_VictimID < MAX_CLIENTS) + Kill.m_aVictimIds[0] = pMsg->m_Victim; + if(Kill.m_aVictimIds[0] >= 0 && Kill.m_aVictimIds[0] < MAX_CLIENTS) + { + Kill.m_VictimDDTeam = m_pClient->m_Teams.Team(Kill.m_aVictimIds[0]); + str_copy(Kill.m_aVictimName, m_pClient->m_aClients[Kill.m_aVictimIds[0]].m_aName); + Kill.m_aVictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_aVictimIds[0]].m_RenderInfo; + } + else { - Kill.m_VictimTeam = m_pClient->m_aClients[Kill.m_VictimID].m_Team; - Kill.m_VictimDDTeam = m_pClient->m_Teams.Team(Kill.m_VictimID); - str_copy(Kill.m_aVictimName, m_pClient->m_aClients[Kill.m_VictimID].m_aName); - Kill.m_VictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_VictimID].m_RenderInfo; + Kill.m_VictimDDTeam = 0; + Kill.m_aVictimName[0] = '\0'; + } + for(int i = Kill.m_TeamSize; i < MAX_KILLMSG_TEAM_MEMBERS; i++) + { + Kill.m_aVictimIds[i] = -1; + Kill.m_aVictimRenderInfo[i].Reset(); } Kill.m_KillerID = pMsg->m_Killer; if(Kill.m_KillerID >= 0 && Kill.m_KillerID < MAX_CLIENTS) { - Kill.m_KillerTeam = m_pClient->m_aClients[Kill.m_KillerID].m_Team; str_copy(Kill.m_aKillerName, m_pClient->m_aClients[Kill.m_KillerID].m_aName); Kill.m_KillerRenderInfo = m_pClient->m_aClients[Kill.m_KillerID].m_RenderInfo; } + else + { + Kill.m_aKillerName[0] = '\0'; + Kill.m_KillerRenderInfo.Reset(); + } Kill.m_Weapon = pMsg->m_Weapon; Kill.m_ModeSpecial = pMsg->m_ModeSpecial; @@ -233,9 +248,8 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) CreateKillmessageNamesIfNotCreated(Kill); - bool KillMsgValid = (Kill.m_VictimRenderInfo[0].m_CustomColoredSkin && Kill.m_VictimRenderInfo[0].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_VictimRenderInfo[0].m_CustomColoredSkin && Kill.m_VictimRenderInfo[0].m_OriginalRenderSkin.m_Body.IsValid()); - // if killer != victim, killer must be valid too - KillMsgValid &= Kill.m_KillerID == Kill.m_VictimID || ((Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_OriginalRenderSkin.m_Body.IsValid())); + bool KillMsgValid = (Kill.m_aVictimRenderInfo[0].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[0].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_aVictimRenderInfo[0].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[0].m_OriginalRenderSkin.m_Body.IsValid()); + KillMsgValid &= Kill.m_KillerID == -1 || ((Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_OriginalRenderSkin.m_Body.IsValid())); if(KillMsgValid) { // add the message @@ -281,7 +295,7 @@ void CKillMessages::OnRender() // render victim name x -= m_aKillmsgs[r].m_VictimTextWidth; ColorRGBA TextColor; - if(m_aKillmsgs[r].m_VictimID >= 0 && g_Config.m_ClChatTeamColors && m_aKillmsgs[r].m_VictimDDTeam) + if(g_Config.m_ClChatTeamColors && m_aKillmsgs[r].m_VictimDDTeam) TextColor = m_pClient->GetDDTeamColor(m_aKillmsgs[r].m_VictimDDTeam, 0.75f); else TextColor = TextRender()->DefaultTextColor(); @@ -299,7 +313,7 @@ void CKillMessages::OnRender() if(m_aKillmsgs[r].m_ModeSpecial & 1) { int QuadOffset = 0; - if(m_aKillmsgs[r].m_VictimID == m_aKillmsgs[r].m_FlagCarrierBlue) + if(m_aKillmsgs[r].m_aVictimIds[0] == m_aKillmsgs[r].m_FlagCarrierBlue) ++QuadOffset; if(QuadOffset == 0) @@ -311,21 +325,18 @@ void CKillMessages::OnRender() } } - if(m_aKillmsgs[r].m_VictimID >= 0) + for(int j = (m_aKillmsgs[r].m_TeamSize - 1); j >= 0; j--) { - for(int j = (m_aKillmsgs[r].m_TeamSize - 1); j >= 0; j--) - { - CTeeRenderInfo TeeInfo = m_aKillmsgs[r].m_VictimRenderInfo[j]; - - const CAnimState *pIdleState = CAnimState::GetIdle(); - vec2 OffsetToMid; - RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); - vec2 TeeRenderPos(x, y + 46.0f / 2.0f + OffsetToMid.y); + if(m_aKillmsgs[r].m_aVictimIds[j] < 0) + continue; - RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_PAIN, vec2(-1, 0), TeeRenderPos); + const CAnimState *pIdleState = CAnimState::GetIdle(); + vec2 OffsetToMid; + RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &m_aKillmsgs[r].m_aVictimRenderInfo[j], OffsetToMid); + const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y); + RenderTools()->RenderTee(pIdleState, &m_aKillmsgs[r].m_aVictimRenderInfo[j], EMOTE_PAIN, vec2(-1, 0), TeeRenderPos); - x -= 44.0f; - } + x -= 44.0f; } // render weapon @@ -337,7 +348,7 @@ void CKillMessages::OnRender() } x -= 52.0f; - if(m_aKillmsgs[r].m_VictimID != m_aKillmsgs[r].m_KillerID) + if(m_aKillmsgs[r].m_aVictimIds[0] != m_aKillmsgs[r].m_KillerID) { if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS) { @@ -361,14 +372,11 @@ void CKillMessages::OnRender() if(m_aKillmsgs[r].m_KillerID >= 0) { - CTeeRenderInfo TeeInfo = m_aKillmsgs[r].m_KillerRenderInfo; - const CAnimState *pIdleState = CAnimState::GetIdle(); vec2 OffsetToMid; - RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); - vec2 TeeRenderPos(x, y + 46.0f / 2.0f + OffsetToMid.y); - - RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_ANGRY, vec2(1, 0), TeeRenderPos); + RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &m_aKillmsgs[r].m_KillerRenderInfo, OffsetToMid); + const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y); + RenderTools()->RenderTee(pIdleState, &m_aKillmsgs[r].m_KillerRenderInfo, EMOTE_ANGRY, vec2(1, 0), TeeRenderPos); } x -= 32.0f; @@ -386,33 +394,29 @@ void CKillMessages::OnRender() void CKillMessages::RefindSkins() { - for(int i = 0; i < MAX_KILLMSGS; i++) + for(auto &KillMsg : m_aKillmsgs) { - int r = i % MAX_KILLMSGS; - if(Client()->GameTick(g_Config.m_ClDummy) > m_aKillmsgs[r].m_Tick + 50 * 10) - continue; - - if(m_aKillmsgs[r].m_KillerID >= 0) + KillMsg.m_KillerRenderInfo.Reset(); + if(KillMsg.m_KillerID >= 0) { - m_aKillmsgs[r].m_KillerRenderInfo = {}; - const CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_KillerID]; - if(Client.m_aSkinName[0] != '\0') - m_aKillmsgs[r].m_KillerRenderInfo = Client.m_RenderInfo; + const CGameClient::CClientData &Client = GameClient()->m_aClients[KillMsg.m_KillerID]; + if(Client.m_Active && Client.m_aSkinName[0] != '\0') + KillMsg.m_KillerRenderInfo = Client.m_RenderInfo; else - m_aKillmsgs[r].m_KillerID = -1; + KillMsg.m_KillerID = -1; } - if(m_aKillmsgs[r].m_VictimID >= 0) + for(int i = 0; i < MAX_KILLMSG_TEAM_MEMBERS; i++) { - for(auto &VictimRenderInfo : m_aKillmsgs[r].m_VictimRenderInfo) + KillMsg.m_aVictimRenderInfo[i].Reset(); + if(KillMsg.m_aVictimIds[i] >= 0) { - VictimRenderInfo = {}; + const CGameClient::CClientData &Client = GameClient()->m_aClients[KillMsg.m_aVictimIds[i]]; + if(Client.m_Active && Client.m_aSkinName[0] != '\0') + KillMsg.m_aVictimRenderInfo[i] = Client.m_RenderInfo; + else + KillMsg.m_aVictimIds[i] = -1; } - const CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_VictimID]; - if(Client.m_aSkinName[0] != '\0') - m_aKillmsgs[r].m_VictimRenderInfo[0] = Client.m_RenderInfo; - else - m_aKillmsgs[r].m_VictimID = -1; } } } diff --git a/src/game/client/components/killmessages.h b/src/game/client/components/killmessages.h index 05a4bface49..44be229d2b9 100644 --- a/src/game/client/components/killmessages.h +++ b/src/game/client/components/killmessages.h @@ -20,15 +20,13 @@ class CKillMessages : public CComponent { int m_Weapon; - int m_VictimID; - int m_VictimTeam; + int m_aVictimIds[MAX_KILLMSG_TEAM_MEMBERS]; int m_VictimDDTeam; char m_aVictimName[64]; STextContainerIndex m_VictimTextContainerIndex; float m_VictimTextWidth; - CTeeRenderInfo m_VictimRenderInfo[MAX_KILLMSG_TEAM_MEMBERS]; + CTeeRenderInfo m_aVictimRenderInfo[MAX_KILLMSG_TEAM_MEMBERS]; int m_KillerID; - int m_KillerTeam; char m_aKillerName[64]; STextContainerIndex m_KillerTextContainerIndex; float m_KillerTextWidth;