diff --git a/datasrc/network.py b/datasrc/network.py index e43b74f86..69e9665c6 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -334,6 +334,13 @@ Messages = [ NetBool("m_Silent"), ]), + NetMessage("Sv_SkinChange", [ + NetIntRange("m_ClientID", 0, 'MAX_CLIENTS-1'), + NetArray(NetStringStrict("m_apSkinPartNames"), 6), + NetArray(NetBool("m_aUseCustomColors"), 6), + NetArray(NetIntAny("m_aSkinPartColors"), 6), + ]), + NetMessage("Sv_GameInfo", [ NetFlag("m_GameFlags", GameFlags), @@ -390,6 +397,12 @@ Messages = [ NetArray(NetIntAny("m_aSkinPartColors"), 6), ]), + NetMessage("Cl_SkinChange", [ + NetArray(NetStringStrict("m_apSkinPartNames"), 6), + NetArray(NetBool("m_aUseCustomColors"), 6), + NetArray(NetIntAny("m_aSkinPartColors"), 6), + ]), + NetMessage("Cl_Kill", []), NetMessage("Cl_ReadyChange", []), diff --git a/src/engine/server.h b/src/engine/server.h index 2bb5a0b99..b65bb0786 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -32,6 +32,7 @@ public: virtual bool ClientIngame(int ClientID) const = 0; virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) const = 0; virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) const = 0; + virtual int GetClientVersion(int ClientID) const = 0; virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 613dc6b99..6c1848521 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -414,6 +414,12 @@ void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size) const net_addr_str(m_NetServer.ClientAddr(ClientID), pAddrStr, Size, false); } +int CServer::GetClientVersion(int ClientID) const +{ + if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME) + return m_aClients[ClientID].m_Version; + return 0; +} const char *CServer::ClientName(int ClientID) const { diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 9ff0d2c6c..2c0e0e4ce 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -218,6 +218,7 @@ public: bool IsBanned(int ClientID); int GetClientInfo(int ClientID, CClientInfo *pInfo) const; void GetClientAddr(int ClientID, char *pAddrStr, int Size) const; + int GetClientVersion(int ClientID) const; const char *ClientName(int ClientID) const; const char *ClientClan(int ClientID) const; int ClientCountry(int ClientID) const; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index c901da4e4..dc84864a5 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -52,7 +52,6 @@ CMenus::CMenus() m_NeedRestartGraphics = false; m_NeedRestartSound = false; m_NeedRestartPlayer = false; - m_NeedRestartTee = false; m_TeePartSelected = SKINPART_BODY; m_aSaveSkinName[0] = 0; m_RefreshSkinSelector = true; @@ -61,6 +60,7 @@ CMenus::CMenus() m_SeekBarActivatedTime = 0; m_SeekBarActive = true; m_UseMouseButtons = true; + m_SkinModified = false; SetMenuPage(PAGE_START); m_MenuPageOld = PAGE_START; @@ -2429,11 +2429,20 @@ void CMenus::SetActive(bool Active) if(Client()->State() == IClient::STATE_ONLINE) { m_pClient->OnRelease(); + if(Client()->State() == IClient::STATE_ONLINE && m_SkinModified) + { + m_SkinModified = false; + m_pClient->SendSkinChange(); + } } } - else if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + else { - m_pClient->OnRelease(); + m_SkinModified = false; + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + m_pClient->OnRelease(); + } } } diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index dca968e1b..2d28bfe65 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -297,6 +297,7 @@ private: vec2 m_PrevMousePos; bool m_PopupActive; int m_ActiveListBox; + bool m_SkinModified; // images struct CMenuImage @@ -371,7 +372,6 @@ private: // for settings bool m_NeedRestartPlayer; - bool m_NeedRestartTee; bool m_NeedRestartGraphics; bool m_NeedRestartSound; int m_TeePartSelected; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 8be0f7992..a368b2819 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -170,7 +170,10 @@ void CMenus::RenderHSLPicker(CUIRect MainView) MainView.HSplitTop(ButtonHeight, &Button, &MainView); static int s_CustomColors = 0; if(DoButton_CheckBox(&s_CustomColors, Localize("Custom colors"), *CSkins::ms_apUCCVariables[m_TeePartSelected], &Button)) + { *CSkins::ms_apUCCVariables[m_TeePartSelected] ^= 1; + m_SkinModified = true; + } if(!(*CSkins::ms_apUCCVariables[m_TeePartSelected])) return; @@ -404,6 +407,7 @@ void CMenus::RenderHSLPicker(CUIRect MainView) } if(UseAlpha) g_Config.m_PlayerColorMarking = (Alp << 24) + NewVal; + m_SkinModified = true; } } @@ -477,6 +481,7 @@ void CMenus::RenderSkinSelection(CUIRect MainView) *CSkins::ms_apUCCVariables[p] = m_pSelectedSkin->m_aUseCustomColors[p]; *CSkins::ms_apColorVariables[p] = m_pSelectedSkin->m_aPartColors[p]; } + m_SkinModified = true; } } OldSelected = NewSelected; @@ -560,6 +565,7 @@ void CMenus::RenderSkinPartSelection(CUIRect MainView) const CSkins::CSkinPart *s = s_paList[m_TeePartSelected][NewSelected]; mem_copy(CSkins::ms_apSkinVariables[m_TeePartSelected], s->m_aName, 24); g_Config.m_PlayerSkin[0] = 0; + m_SkinModified = true; } } OldSelected = NewSelected; @@ -1409,7 +1415,6 @@ void CMenus::RenderSettingsTee(CUIRect MainView) else s_CustomSkinMenu = true; } - m_NeedRestartTee = str_comp(g_Config.m_PlayerSkin, s_aPlayerSkin); } //typedef void (*pfnAssignFuncCallback)(CConfiguration *pConfig, int Value); @@ -1990,7 +1995,7 @@ void CMenus::RenderSettings(CUIRect MainView) MainView.HSplitBottom(32.0f, 0, &MainView); // reset warning - bool NeedReconnect = (m_NeedRestartPlayer || m_NeedRestartTee) && this->Client()->State() == IClient::STATE_ONLINE; + bool NeedReconnect = (m_NeedRestartPlayer || (m_SkinModified && m_pClient->m_LastSkinChangeTime + 6.0f > Client()->LocalTime())) && this->Client()->State() == IClient::STATE_ONLINE; if(m_NeedRestartGraphics || m_NeedRestartSound || NeedReconnect) { // background @@ -2004,8 +2009,17 @@ void CMenus::RenderSettings(CUIRect MainView) RestartWarning.y += 2.0f; if(m_NeedRestartGraphics || m_NeedRestartSound) UI()->DoLabel(&RestartWarning, Localize("You must restart the game for all settings to take effect."), RestartWarning.h*ms_FontmodHeight*0.75f, CUI::ALIGN_CENTER); - else if(NeedReconnect) - UI()->DoLabel(&RestartWarning, Localize("You must reconnect to change identity."), RestartWarning.h*ms_FontmodHeight*0.75f, CUI::ALIGN_CENTER); + else if(Client()->State() == IClient::STATE_ONLINE) + { + if(m_NeedRestartPlayer) + UI()->DoLabel(&RestartWarning, Localize("You must reconnect to change identity."), RestartWarning.h*ms_FontmodHeight*0.75f, CUI::ALIGN_CENTER); + else if(m_SkinModified) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), Localize("You have to wait %1.f seconds to change identity."), m_pClient->m_LastSkinChangeTime+6.5f - Client()->LocalTime()); + UI()->DoLabel(&RestartWarning, aBuf, RestartWarning.h*ms_FontmodHeight*0.75f, CUI::ALIGN_CENTER); + } + } TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 4aa4b2e38..e2fe4db68 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -246,6 +246,25 @@ void CGameClient::OnConsoleInit() Console()->Chain("add_friend", ConchainFriendUpdate, this); Console()->Chain("remove_friend", ConchainFriendUpdate, this); Console()->Chain("cl_show_xmas_hats", ConchainXmasHatUpdate, this); + Console()->Chain("player_color_body", ConchainSkinChange, this); + Console()->Chain("player_color_marking", ConchainSkinChange, this); + Console()->Chain("player_color_decoration", ConchainSkinChange, this); + Console()->Chain("player_color_hands", ConchainSkinChange, this); + Console()->Chain("player_color_feet", ConchainSkinChange, this); + Console()->Chain("player_color_eyes", ConchainSkinChange, this); + Console()->Chain("player_use_custom_color_body", ConchainSkinChange, this); + Console()->Chain("player_use_custom_color_marking", ConchainSkinChange, this); + Console()->Chain("player_use_custom_color_decoration", ConchainSkinChange, this); + Console()->Chain("player_use_custom_color_hands", ConchainSkinChange, this); + Console()->Chain("player_use_custom_color_feet", ConchainSkinChange, this); + Console()->Chain("player_use_custom_color_eyes", ConchainSkinChange, this); + Console()->Chain("player_skin", ConchainSkinChange, this); + Console()->Chain("player_skin_body", ConchainSkinChange, this); + Console()->Chain("player_skin_marking", ConchainSkinChange, this); + Console()->Chain("player_skin_decoration", ConchainSkinChange, this); + Console()->Chain("player_skin_hands", ConchainSkinChange, this); + Console()->Chain("player_skin_feet", ConchainSkinChange, this); + Console()->Chain("player_skin_eyes", ConchainSkinChange, this); for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->m_pClient = this; @@ -384,6 +403,7 @@ void CGameClient::OnReset() m_LocalClientID = -1; m_TeamCooldownTick = 0; m_TeamChangeTime = 0.0f; + m_LastSkinChangeTime = Client()->LocalTime(); mem_zero(&m_GameInfo, sizeof(m_GameInfo)); m_DemoSpecMode = SPEC_FREEVIEW; m_DemoSpecID = -1; @@ -716,6 +736,26 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) m_aClients[pMsg->m_ClientID].Reset(this, pMsg->m_ClientID); } + else if(MsgId == NETMSGTYPE_SV_SKINCHANGE && Client()->State() != IClient::STATE_DEMOPLAYBACK) + { + Client()->RecordGameMessage(false); + CNetMsg_Sv_SkinChange *pMsg = (CNetMsg_Sv_SkinChange *)pRawMsg; + + if(!m_aClients[pMsg->m_ClientID].m_Active) + { + if(g_Config.m_Debug) + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", "invalid skin info"); + return; + } + + for(int i = 0; i < 6; i++) + { + str_copy(m_aClients[pMsg->m_ClientID].m_aaSkinPartNames[i], pMsg->m_apSkinPartNames[i], 24); + m_aClients[pMsg->m_ClientID].m_aUseCustomColors[i] = pMsg->m_aUseCustomColors[i]; + m_aClients[pMsg->m_ClientID].m_aSkinPartColors[i] = pMsg->m_aSkinPartColors[i]; + } + m_aClients[pMsg->m_ClientID].UpdateRenderInfo(this, pMsg->m_ClientID, true); + } else if(MsgId == NETMSGTYPE_SV_GAMEINFO && Client()->State() != IClient::STATE_DEMOPLAYBACK) { Client()->RecordGameMessage(false); @@ -1493,6 +1533,19 @@ void CGameClient::SendReadyChange() Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } +void CGameClient::SendSkinChange() +{ + CNetMsg_Cl_SkinChange Msg; + for(int p = 0; p < NUM_SKINPARTS; p++) + { + Msg.m_apSkinPartNames[p] = CSkins::ms_apSkinVariables[p]; + Msg.m_aUseCustomColors[p] = *CSkins::ms_apUCCVariables[p]; + Msg.m_aSkinPartColors[p] = *CSkins::ms_apColorVariables[p]; + } + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD|MSGFLAG_FLUSH); + m_LastSkinChangeTime = Client()->LocalTime(); +} + void CGameClient::ConTeam(IConsole::IResult *pResult, void *pUserData) { CGameClient *pClient = static_cast(pUserData); @@ -1523,6 +1576,13 @@ void CGameClient::ConReadyChange(IConsole::IResult *pResult, void *pUserData) if(pClient->Client()->State() == IClient::STATE_ONLINE) pClient->SendReadyChange(); } + +void CGameClient::ConchainSkinChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + CGameClient *pClient = static_cast(pUserData); + if(pClient->Client()->State() == IClient::STATE_ONLINE && pResult->NumArguments()) + pClient->SendSkinChange(); } void CGameClient::ConchainFriendUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index a7e98f61b..e68425ffe 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -59,6 +59,7 @@ class CGameClient : public IGameClient static void ConTeam(IConsole::IResult *pResult, void *pUserData); static void ConKill(IConsole::IResult *pResult, void *pUserData); static void ConReadyChange(IConsole::IResult *pResult, void *pUserData); + static void ConchainSkinChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainFriendUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainXmasHatUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); @@ -197,6 +198,7 @@ public: bool m_MuteServerBroadcast; float m_TeamChangeTime; bool m_IsXmasDay; + float m_LastSkinChangeTime; struct CGameInfo { @@ -265,6 +267,7 @@ public: void SendStartInfo(); void SendKill(); void SendReadyChange(); + void SendSkinChange(); // pointers to all systems class CGameConsole *m_pGameConsole; diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 05a49cef5..3b0aa5a5a 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -279,6 +279,18 @@ void CGameContext::SendSettings(int ClientID) Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); } +void CGameContext::SendSkinChange(int ClientID) +{ + CNetMsg_Sv_SkinChange Msg; + Msg.m_ClientID = ClientID; + for(int p = 0; p < 6; p++) + { + Msg.m_apSkinPartNames[p] = m_apPlayers[ClientID]->m_TeeInfos.m_aaSkinPartNames[p]; + Msg.m_aUseCustomColors[p] = m_apPlayers[ClientID]->m_TeeInfos.m_aUseCustomColors[p]; + Msg.m_aSkinPartColors[p] = m_apPlayers[ClientID]->m_TeeInfos.m_aSkinPartColors[p]; + } + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD, ClientID); +} void CGameContext::SendGameMsg(int GameMsgID, int ClientID) { @@ -980,6 +992,30 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) pPlayer->m_LastReadyChange = Server()->Tick(); m_pController->OnPlayerReadyChange(pPlayer); } + else if(MsgID == NETMSGTYPE_CL_SKINCHANGE) + { + if(pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo+Server()->TickSpeed()*5 > Server()->Tick()) + return; + + pPlayer->m_LastChangeInfo = Server()->Tick(); + CNetMsg_Cl_SkinChange *pMsg = (CNetMsg_Cl_SkinChange *)pRawMsg; + + for(int p = 0; p < 6; p++) + { + str_copy(pPlayer->m_TeeInfos.m_aaSkinPartNames[p], pMsg->m_apSkinPartNames[p], 24); + pPlayer->m_TeeInfos.m_aUseCustomColors[p] = pMsg->m_aUseCustomColors[p]; + pPlayer->m_TeeInfos.m_aSkinPartColors[p] = pMsg->m_aSkinPartColors[p]; + } + + // update all clients + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(!m_apPlayers[i] || (!Server()->ClientIngame(i) && !m_apPlayers[i]->IsDummy()) || Server()->GetClientVersion(i) < MIN_SKINCHANGE_CLIENTVERSION) + continue; + + SendSkinChange(i); + } + } } else { diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index e22ddedda..3911de0bb 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -119,6 +119,8 @@ public: VOTE_TIME=25, VOTE_CANCEL_TIME = 10, + + MIN_SKINCHANGE_CLIENTVERSION = 0x0703, }; class CHeap *m_pVoteOptionHeap; CVoteOptionServer *m_pVoteOptionFirst; @@ -139,6 +141,7 @@ public: void SendWeaponPickup(int ClientID, int Weapon); void SendMotd(int ClientID); void SendSettings(int ClientID); + void SendSkinChange(int ClientID); void SendGameMsg(int GameMsgID, int ClientID); void SendGameMsg(int GameMsgID, int ParaI1, int ClientID);