diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6b494a7548..ccbbac656e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -52,7 +52,7 @@ jobs: macOS: name: macOS - runs-on: macOS-latest + runs-on: macOS-13 steps: - uses: actions/checkout@v4 diff --git a/Client/core/CSettings.cpp b/Client/core/CSettings.cpp index 085241c468..847735a891 100644 --- a/Client/core/CSettings.cpp +++ b/Client/core/CSettings.cpp @@ -363,6 +363,18 @@ void CSettings::CreateGUI() m_pEditNick->SetMaxLength(MAX_PLAYER_NICK_LENGTH); m_pEditNick->SetTextAcceptedHandler(GUI_CALLBACK(&CSettings::OnOKButtonClick, this)); + m_pButtonGenerateNick = reinterpret_cast(pManager->CreateButton(pTabMultiplayer)); + m_pButtonGenerateNick->SetPosition(CVector2D(vecSize.fX + vecTemp.fX + 50.0f + 178.0f + 5.0f, vecTemp.fY - 1.0f), false); + m_pButtonGenerateNick->SetSize(CVector2D(26.0f, 26.0f), false); + m_pButtonGenerateNick->SetClickHandler(GUI_CALLBACK(&CSettings::OnNickButtonClick, this)); + m_pButtonGenerateNick->SetZOrderingEnabled(false); + + m_pButtonGenerateNickIcon = reinterpret_cast(pManager->CreateStaticImage(m_pButtonGenerateNick)); + m_pButtonGenerateNickIcon->SetSize(CVector2D(1, 1), true); + m_pButtonGenerateNickIcon->LoadFromFile("cgui\\images\\serverbrowser\\refresh.png"); + m_pButtonGenerateNickIcon->SetProperty("MousePassThroughEnabled", "True"); + m_pButtonGenerateNickIcon->SetProperty("DistributeCapturedInputs", "True"); + m_pSavePasswords = reinterpret_cast(pManager->CreateCheckBox(pTabMultiplayer, _("Save server passwords"), true)); m_pSavePasswords->SetPosition(CVector2D(vecTemp.fX, vecTemp.fY + 50.0f)); m_pSavePasswords->GetPosition(vecTemp, false); @@ -2940,6 +2952,12 @@ bool CSettings::OnOKButtonClick(CGUIElement* pElement) return true; } +bool CSettings::OnNickButtonClick(CGUIElement* pElement) +{ + m_pEditNick->SetText(CNickGen::GetRandomNickname()); + return true; +} + bool CSettings::OnCancelButtonClick(CGUIElement* pElement) { CMainMenu* pMainMenu = CLocalGUI::GetSingleton().GetMainMenu(); diff --git a/Client/core/CSettings.h b/Client/core/CSettings.h index 8ee44437f5..bfeec87c44 100644 --- a/Client/core/CSettings.h +++ b/Client/core/CSettings.h @@ -132,6 +132,8 @@ class CSettings CGUIButton* m_pButtonOK; CGUIButton* m_pButtonCancel; CGUILabel* m_pLabelNick; + CGUIButton* m_pButtonGenerateNick; + CGUIStaticImage* m_pButtonGenerateNickIcon; CGUIEdit* m_pEditNick; CGUICheckBox* m_pSavePasswords; CGUICheckBox* m_pAutoRefreshBrowser; @@ -347,6 +349,7 @@ class CSettings bool OnVideoDefaultClick(CGUIElement* pElement); bool OnBindsListClick(CGUIElement* pElement); bool OnOKButtonClick(CGUIElement* pElement); + bool OnNickButtonClick(CGUIElement* pElement); bool OnCancelButtonClick(CGUIElement* pElement); bool OnFieldOfViewChanged(CGUIElement* pElement); bool OnDrawDistanceChanged(CGUIElement* pElement); diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 236fad9fd7..b1127b16d3 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -729,6 +729,17 @@ void CGameSA::SetFireballDestructEnabled(bool isEnabled) m_isFireballDestructEnabled = isEnabled; } +void CGameSA::SetRoadSignsTextEnabled(bool isEnabled) +{ + if (isEnabled == m_isRoadSignsTextEnabled) + return; + + // Skip JMP to CCustomRoadsignMgr::RenderRoadsignAtomic + MemPut(0x5342ED, isEnabled ? 0xEB : 0x74); + + m_isRoadSignsTextEnabled = isEnabled; +} + bool CGameSA::PerformChecks() { std::map::iterator it; diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 5a2be9f3ae..7ad273350f 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -219,6 +219,9 @@ class CGameSA : public CGame bool IsFireballDestructEnabled() const noexcept override { return m_isFireballDestructEnabled; } void SetFireballDestructEnabled(bool isEnabled) override; + bool IsRoadSignsTextEnabled() const noexcept override { return m_isRoadSignsTextEnabled; } + void SetRoadSignsTextEnabled(bool isEnabled) override; + unsigned long GetMinuteDuration(); void SetMinuteDuration(unsigned long ulTime); @@ -336,6 +339,7 @@ class CGameSA : public CGame bool m_areWaterCreaturesEnabled{true}; bool m_isBurnFlippedCarsEnabled{true}; bool m_isFireballDestructEnabled{true}; + bool m_isRoadSignsTextEnabled{true}; static unsigned int& ClumpOffset; diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 63bd6174be..142827db6a 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -6091,6 +6091,9 @@ bool CClientGame::SetWorldSpecialProperty(WorldSpecialProperty property, bool is case WorldSpecialProperty::FIREBALLDESTRUCT: g_pGame->SetFireballDestructEnabled(isEnabled); return true; + case WorldSpecialProperty::ROADSIGNSTEXT: + g_pGame->SetRoadSignsTextEnabled(isEnabled); + return true; } return false; } @@ -6122,6 +6125,8 @@ bool CClientGame::IsWorldSpecialProperty(WorldSpecialProperty property) return g_pGame->IsBurnFlippedCarsEnabled(); case WorldSpecialProperty::FIREBALLDESTRUCT: return g_pGame->IsFireballDestructEnabled(); + case WorldSpecialProperty::ROADSIGNSTEXT: + return g_pGame->IsRoadSignsTextEnabled(); } return false; } diff --git a/Client/mods/deathmatch/logic/CClientIMG.cpp b/Client/mods/deathmatch/logic/CClientIMG.cpp index 0778385184..61ff89fa4c 100644 --- a/Client/mods/deathmatch/logic/CClientIMG.cpp +++ b/Client/mods/deathmatch/logic/CClientIMG.cpp @@ -28,6 +28,11 @@ CClientIMG::CClientIMG(class CClientManager* pManager, ElementID ID) CClientIMG::~CClientIMG() { m_pImgManager->RemoveFromList(this); + Unlink(); +} + +void CClientIMG::Unlink() +{ if (IsStreamed()) StreamDisable(); diff --git a/Client/mods/deathmatch/logic/CClientIMG.h b/Client/mods/deathmatch/logic/CClientIMG.h index 5cd50c93dd..2074e64fbc 100644 --- a/Client/mods/deathmatch/logic/CClientIMG.h +++ b/Client/mods/deathmatch/logic/CClientIMG.h @@ -50,7 +50,7 @@ class CClientIMG : public CClientEntity CClientIMG(class CClientManager* pManager, ElementID ID); ~CClientIMG(); - void Unlink(){}; + void Unlink(); void GetPosition(CVector& vecPosition) const {}; void SetPosition(const CVector& vecPosition){}; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 51cad6aed4..a0df3d5849 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -2387,6 +2387,7 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream) g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::WATERCREATURES, wsProps.data.watercreatures); g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::BURNFLIPPEDCARS, wsProps.data.burnflippedcars); g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::FIREBALLDESTRUCT, wsProps.data2.fireballdestruct); + g_pClientGame->SetWorldSpecialProperty(WorldSpecialProperty::ROADSIGNSTEXT, wsProps.data3.roadsignstext); float fJetpackMaxHeight = 100; if (!bitStream.Read(fJetpackMaxHeight)) diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 6df946ad3c..a2290af6cf 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -217,6 +217,9 @@ class __declspec(novtable) CGame virtual bool IsFireballDestructEnabled() const noexcept = 0; virtual void SetFireballDestructEnabled(bool isEnabled) = 0; + virtual bool IsRoadSignsTextEnabled() const noexcept = 0; + virtual void SetRoadSignsTextEnabled(bool isEnabled) = 0; + virtual CWeapon* CreateWeapon() = 0; virtual CWeaponStat* CreateWeaponStat(eWeaponType weaponType, eWeaponSkill weaponSkill) = 0; diff --git a/Server/mods/deathmatch/logic/CConsoleCommands.cpp b/Server/mods/deathmatch/logic/CConsoleCommands.cpp index 119f09af1d..7d749e5c55 100644 --- a/Server/mods/deathmatch/logic/CConsoleCommands.cpp +++ b/Server/mods/deathmatch/logic/CConsoleCommands.cpp @@ -596,8 +596,10 @@ bool CConsoleCommands::Msg(CConsole* pConsole, const char* szInArguments, CClien // Send the message and player pointer to the script CLuaArguments Arguments; - Arguments.PushString(szArguments); + Arguments.PushString(szArguments); // We don't want to remove this for backwards compatibility reasons Arguments.PushElement(pPlayer); + Arguments.PushString(szMessage); // Fix #2135 + bool bContinue = pSender->CallEvent("onPlayerPrivateMessage", Arguments); if (bContinue) { diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index c2c8485562..f5beb2a36e 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -246,6 +246,7 @@ CGame::CGame() : m_FloodProtect(4, 30000, 30000) // Max of 4 connecti m_WorldSpecialProps[WorldSpecialProperty::WATERCREATURES] = true; m_WorldSpecialProps[WorldSpecialProperty::BURNFLIPPEDCARS] = true; m_WorldSpecialProps[WorldSpecialProperty::FIREBALLDESTRUCT] = true; + m_WorldSpecialProps[WorldSpecialProperty::ROADSIGNSTEXT] = true; m_JetpackWeapons[WEAPONTYPE_MICRO_UZI] = true; m_JetpackWeapons[WEAPONTYPE_TEC9] = true; diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp index 418af9aee3..f524dc4abc 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.cpp +++ b/Server/mods/deathmatch/logic/CMainConfig.cpp @@ -1455,6 +1455,7 @@ const std::vector& CMainConfig::GetIntSettingList() {true, true, 10, 50, 1000, "update_cycle_messages_limit", &m_iUpdateCycleMessagesLimit, &CMainConfig::ApplyNetOptions}, {true, true, 50, 100, 400, "ped_syncer_distance", &g_TickRateSettings.iPedSyncerDistance, &CMainConfig::OnTickRateChange}, {true, true, 50, 130, 400, "unoccupied_vehicle_syncer_distance", &g_TickRateSettings.iUnoccupiedVehicleSyncerDistance, &CMainConfig::OnTickRateChange}, + {true, true, 0, 30, 130, "vehicle_contact_sync_radius", &g_TickRateSettings.iVehicleContactSyncRadius, &CMainConfig::OnTickRateChange}, {false, false, 0, 1, 2, "compact_internal_databases", &m_iCompactInternalDatabases, NULL}, {true, true, 0, 1, 2, "minclientversion_auto_update", &m_iMinClientVersionAutoUpdate, NULL}, {true, true, 0, 0, 100, "server_logic_fps_limit", &m_iServerLogicFpsLimit, NULL}, diff --git a/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp b/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp index 1bbcb69569..4159cd11b0 100644 --- a/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp +++ b/Server/mods/deathmatch/logic/CUnoccupiedVehicleSync.cpp @@ -487,7 +487,9 @@ void CUnoccupiedVehicleSync::Packet_UnoccupiedVehiclePushSync(CUnoccupiedVehicle CVehicle* pVehicle = static_cast(pVehicleElement); // Is the player syncing this vehicle and there is no driver? Also only process // this packet if the time context matches. - if (pVehicle->GetSyncer() != pPlayer && pVehicle->GetTimeSinceLastPush() >= MIN_PUSH_ANTISPAM_RATE) + if (pVehicle->GetSyncer() != pPlayer && pVehicle->GetTimeSinceLastPush() >= MIN_PUSH_ANTISPAM_RATE && + IsPointNearPoint3D(pVehicle->GetPosition(), pPlayer->GetPosition(), g_TickRateSettings.iVehicleContactSyncRadius) + && pVehicle->GetDimension() == pPlayer->GetDimension()) { // Is there no player driver? CPed* pOccupant = pVehicle->GetOccupant(0); diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.cpp index 948ed90a0d..e07f5c7655 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.cpp @@ -24,6 +24,7 @@ void CLuaObjectDefs::LoadFunctions() {"getObjectRotation", GetObjectRotation}, {"getObjectScale", GetObjectScale}, {"isObjectBreakable", ArgumentParser}, + {"isObjectMoving", ArgumentParser}, // Object set funcs {"setObjectRotation", SetObjectRotation}, @@ -50,9 +51,11 @@ void CLuaObjectDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "setScale", "setObjectScale"); lua_classfunction(luaVM, "isBreakable", "isObjectBreakable"); lua_classfunction(luaVM, "setBreakable", "setObjectBreakable"); + lua_classfunction(luaVM, "isMoving", "isObjectMoving"); lua_classvariable(luaVM, "scale", "setObjectScale", "getObjectScale"); lua_classvariable(luaVM, "breakable", "setObjectBreakable", "isObjectBreakable"); + lua_classvariable(luaVM, "moving", nullptr, "isObjectMoving"); lua_registerclass(luaVM, "Object", "Element"); } @@ -222,6 +225,11 @@ int CLuaObjectDefs::SetObjectScale(lua_State* luaVM) return 1; } +bool CLuaObjectDefs::IsObjectMoving(CObject* const pObject) +{ + return pObject->IsMoving(); +} + int CLuaObjectDefs::MoveObject(lua_State* luaVM) { // bool moveObject ( object theObject, int time, diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.h b/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.h index ac4be795d0..bef8a4af45 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.h +++ b/Server/mods/deathmatch/logic/luadefs/CLuaObjectDefs.h @@ -26,6 +26,7 @@ class CLuaObjectDefs : public CLuaDefs LUA_DECLARE(GetObjectRotation); LUA_DECLARE(GetObjectScale); static bool IsObjectBreakable(CObject* const pObject); + static bool IsObjectMoving(CObject* const pObject); // Object set functions LUA_DECLARE(SetObjectName); diff --git a/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp b/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp index cace38952a..c4b0a58f9a 100644 --- a/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp @@ -189,6 +189,7 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const wsProps.data.watercreatures = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::WATERCREATURES); wsProps.data.burnflippedcars = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::BURNFLIPPEDCARS); wsProps.data2.fireballdestruct = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::FIREBALLDESTRUCT); + wsProps.data3.roadsignstext = g_pGame->IsWorldSpecialPropertyEnabled(WorldSpecialProperty::ROADSIGNSTEXT); BitStream.Write(&wsProps); } diff --git a/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp b/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp index 54c63e49ff..a68f7b31a1 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CPlayerPuresyncPacket.cpp @@ -14,6 +14,7 @@ #include "CElementIDs.h" #include "CWeaponNames.h" #include "Utils.h" +#include "CTickRateSettings.h" #include extern CGame* g_pGame; @@ -70,6 +71,21 @@ bool CPlayerPuresyncPacket::Read(NetBitStreamInterface& BitStream) return false; pContactElement = CElementIDs::GetElement(Temp); } + + // Player position + SPositionSync position(false); + if (!BitStream.Read(&position)) + return false; + + if (pContactElement != nullptr && + (!IsPointNearPoint3D(pSourcePlayer->GetPosition(), pContactElement->GetPosition(), g_TickRateSettings.iVehicleContactSyncRadius) || + pSourcePlayer->GetDimension() != pContactElement->GetDimension())) + { + pContactElement = nullptr; + // Use current player position. They are not reporting their absolute position so we have to disregard it. + position.data.vecPosition = pSourcePlayer->GetPosition(); + } + CElement* pPreviousContactElement = pSourcePlayer->GetContactElement(); pSourcePlayer->SetContactElement(pContactElement); @@ -89,11 +105,6 @@ bool CPlayerPuresyncPacket::Read(NetBitStreamInterface& BitStream) pSourcePlayer->CallEvent("onPlayerContact", Arguments); } - // Player position - SPositionSync position(false); - if (!BitStream.Read(&position)) - return false; - if (pContactElement) { pSourcePlayer->SetContactPosition(position.data.vecPosition); @@ -102,6 +113,7 @@ bool CPlayerPuresyncPacket::Read(NetBitStreamInterface& BitStream) CVector vecTempPos = pContactElement->GetPosition(); position.data.vecPosition += vecTempPos; } + pSourcePlayer->SetPosition(position.data.vecPosition); // Player rotation diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf index 36532e0a1b..19fe4a05d8 100644 --- a/Server/mods/deathmatch/mtaserver.conf +++ b/Server/mods/deathmatch/mtaserver.conf @@ -135,6 +135,10 @@ Values: 0 - disabled , 1 - enabled ; default value: 1. --> 1 + + 30 + 1 + + 30 +