diff --git a/Code/client/Games/Skyrim/PlayerCharacter.cpp b/Code/client/Games/Skyrim/PlayerCharacter.cpp index 70c9a5dc8..d815ddba2 100644 --- a/Code/client/Games/Skyrim/PlayerCharacter.cpp +++ b/Code/client/Games/Skyrim/PlayerCharacter.cpp @@ -29,9 +29,20 @@ static TCalculateExperience* RealCalculateExperience = nullptr; void PlayerCharacter::SetDifficulty(const int32_t aDifficulty) noexcept { + if (aDifficulty > 5) + return; + + // TODO(cosideci): cache pre-connect difficulty + + // Can't find "iDifficulty:GamePlay" + /* auto* pSettings = INISettingCollection::Get(); Setting* pSetting = pSettings->GetSetting("iDifficulty:GamePlay"); pSetting->data = aDifficulty; + */ + + POINTER_SKYRIMSE(int32_t, s_difficulty, 381472); + *s_difficulty = aDifficulty; difficulty = aDifficulty; } diff --git a/Code/client/Services/Generic/PlayerService.cpp b/Code/client/Services/Generic/PlayerService.cpp index 9e8aa4966..8fc5b4498 100644 --- a/Code/client/Services/Generic/PlayerService.cpp +++ b/Code/client/Services/Generic/PlayerService.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -20,6 +22,7 @@ PlayerService::PlayerService(World& aWorld, entt::dispatcher& aDispatcher, Trans : m_world(aWorld), m_dispatcher(aDispatcher), m_transport(aTransport) { m_updateConnection = m_dispatcher.sink().connect<&PlayerService::OnUpdate>(this); + m_settingsConnection = m_dispatcher.sink().connect<&PlayerService::OnServerSettingsReceived>(this); m_notifyRespawnConnection = m_dispatcher.sink().connect<&PlayerService::OnNotifyPlayerRespawn>(this); m_gridCellChangeConnection = m_dispatcher.sink().connect<&PlayerService::OnGridCellChangeEvent>(this); m_cellChangeConnection = m_dispatcher.sink().connect<&PlayerService::OnCellChangeEvent>(this); @@ -30,6 +33,12 @@ void PlayerService::OnUpdate(const UpdateEvent& acEvent) noexcept RunRespawnUpdates(acEvent.Delta); } +void PlayerService::OnServerSettingsReceived(const ServerSettings& acSettings) const noexcept +{ + if (acSettings.Difficulty <= 5) + PlayerCharacter::Get()->SetDifficulty(acSettings.Difficulty); +} + void PlayerService::OnNotifyPlayerRespawn(const NotifyPlayerRespawn& acMessage) const noexcept { PlayerCharacter::Get()->PayGold(acMessage.GoldLost); diff --git a/Code/client/Services/Generic/TransportService.cpp b/Code/client/Services/Generic/TransportService.cpp index add6bca5c..24b703844 100644 --- a/Code/client/Services/Generic/TransportService.cpp +++ b/Code/client/Services/Generic/TransportService.cpp @@ -154,6 +154,7 @@ void TransportService::HandleAuthenticationResponse(const AuthenticationResponse { m_connected = true; m_dispatcher.trigger(acMessage.UserMods); + m_dispatcher.trigger(acMessage.Settings); m_dispatcher.trigger(ConnectedEvent()); break; } diff --git a/Code/client/Services/PlayerService.h b/Code/client/Services/PlayerService.h index edd20074b..2278a6b8c 100644 --- a/Code/client/Services/PlayerService.h +++ b/Code/client/Services/PlayerService.h @@ -4,6 +4,7 @@ struct World; struct TransportService; struct UpdateEvent; +struct ServerSettings; struct GridCellChangeEvent; struct CellChangeEvent; @@ -22,6 +23,7 @@ struct PlayerService protected: void OnUpdate(const UpdateEvent& acEvent) noexcept; + void OnServerSettingsReceived(const ServerSettings& acSettings) const noexcept; void OnNotifyPlayerRespawn(const NotifyPlayerRespawn& acMessage) const noexcept; void OnGridCellChangeEvent(const GridCellChangeEvent& acEvent) const noexcept; void OnCellChangeEvent(const CellChangeEvent& acEvent) const noexcept; @@ -40,6 +42,7 @@ struct PlayerService double m_respawnTimer = 0.0; entt::scoped_connection m_updateConnection; + entt::scoped_connection m_settingsConnection; entt::scoped_connection m_notifyRespawnConnection; entt::scoped_connection m_gridCellChangeConnection; entt::scoped_connection m_cellChangeConnection; diff --git a/Code/encoding/Messages/AuthenticationResponse.cpp b/Code/encoding/Messages/AuthenticationResponse.cpp index d08c12bac..bfbaf52f5 100644 --- a/Code/encoding/Messages/AuthenticationResponse.cpp +++ b/Code/encoding/Messages/AuthenticationResponse.cpp @@ -5,6 +5,7 @@ void AuthenticationResponse::SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter Serialization::WriteVarInt(aWriter, static_cast(Type)); Serialization::WriteString(aWriter, Version); UserMods.Serialize(aWriter); + Settings.Serialize(aWriter); } void AuthenticationResponse::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept @@ -12,4 +13,5 @@ void AuthenticationResponse::DeserializeRaw(TiltedPhoques::Buffer::Reader& aRead Type = static_cast(Serialization::ReadVarInt(aReader) & 0xFFFFFFFF); Version = Serialization::ReadString(aReader); UserMods.Deserialize(aReader); + Settings.Deserialize(aReader); } diff --git a/Code/encoding/Messages/AuthenticationResponse.h b/Code/encoding/Messages/AuthenticationResponse.h index 71b372254..7c4b3bca0 100644 --- a/Code/encoding/Messages/AuthenticationResponse.h +++ b/Code/encoding/Messages/AuthenticationResponse.h @@ -2,6 +2,7 @@ #include "Message.h" #include +#include struct AuthenticationResponse final : ServerMessage { @@ -23,10 +24,14 @@ struct AuthenticationResponse final : ServerMessage bool operator==(const AuthenticationResponse& achRhs) const noexcept { - return GetOpcode() == achRhs.GetOpcode() && Type == achRhs.Type && UserMods == achRhs.UserMods; + return GetOpcode() == achRhs.GetOpcode() && + Type == achRhs.Type && + UserMods == achRhs.UserMods && + Settings == achRhs.Settings; } ResponseType Type; String Version; Mods UserMods{}; + ServerSettings Settings{}; }; diff --git a/Code/encoding/Structs/ServerSettings.cpp b/Code/encoding/Structs/ServerSettings.cpp new file mode 100644 index 000000000..cabff2085 --- /dev/null +++ b/Code/encoding/Structs/ServerSettings.cpp @@ -0,0 +1,25 @@ +#include "ServerSettings.h" +#include + +using TiltedPhoques::Serialization; + +bool ServerSettings::operator==(const ServerSettings& acRhs) const noexcept +{ + return Difficulty == acRhs.Difficulty; +} + +bool ServerSettings::operator!=(const ServerSettings& acRhs) const noexcept +{ + return !this->operator==(acRhs); +} + +void ServerSettings::Serialize(TiltedPhoques::Buffer::Writer& aWriter) const noexcept +{ + Serialization::WriteVarInt(aWriter, Difficulty); +} + +void ServerSettings::Deserialize(TiltedPhoques::Buffer::Reader& aReader) noexcept +{ + Difficulty = Serialization::ReadVarInt(aReader) & 0xFFFFFFFF; +} + diff --git a/Code/encoding/Structs/ServerSettings.h b/Code/encoding/Structs/ServerSettings.h new file mode 100644 index 000000000..13f42a511 --- /dev/null +++ b/Code/encoding/Structs/ServerSettings.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +using TiltedPhoques::Buffer; + +struct ServerSettings +{ + bool operator==(const ServerSettings& acRhs) const noexcept; + bool operator!=(const ServerSettings& acRhs) const noexcept; + + void Serialize(TiltedPhoques::Buffer::Writer& aWriter) const noexcept; + void Deserialize(TiltedPhoques::Buffer::Reader& aReader) noexcept; + + uint32_t Difficulty; +}; diff --git a/Code/server/GameServer.cpp b/Code/server/GameServer.cpp index 0790ecc7f..2660639d1 100644 --- a/Code/server/GameServer.cpp +++ b/Code/server/GameServer.cpp @@ -40,6 +40,8 @@ Console::StringSetting sAdminPassword{"GameServer:sAdminPassword", "Admin authen Console::StringSetting sToken{"GameServer:sToken", "Admin token", ""}; Console::Setting bEnableMoPo{"ModPolicy:bEnabled", "Bypass the mod policy restrictions.", true, Console::SettingsFlags::kHidden | Console::SettingsFlags::kLocked}; +// TODO: if difficulty is higher than 5, close server with error +Console::Setting uDifficulty{"Gameplay:uDifficulty", "In game difficulty (0 to 5)", 4u}; // -- Commands -- Console::Command TogglePremium("TogglePremium", "Toggle the premium mode", [](Console::ArgStack& aStack) { bPremiumTickrate = aStack.Pop(); }); @@ -554,6 +556,8 @@ void GameServer::HandleAuthenticationRequest(const ConnectionId_t aConnectionId, spdlog::info("New player {:x} connected with {} mods\n\t: {}", aConnectionId, acRequest->UserMods.ModList.size(), modList.c_str()); + serverResponse.Settings.Difficulty = uDifficulty.value_as(); + serverResponse.Type = AuthenticationResponse::ResponseType::kAccepted; Send(aConnectionId, serverResponse);