From b78f97c6b11e04117490baacef78100112bdd773 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Tue, 23 Apr 2024 22:23:04 +0200 Subject: [PATCH] Remove `union` in `struct Settings` (type punning is UB in C++). --- src/include/settings.h | 42 ++++++++++++++++++++++++------------- src/network/net_message.cpp | 7 ++++--- src/tolua/game.pkg | 1 - 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/include/settings.h b/src/include/settings.h index df6ff62eda..b665db4c22 100644 --- a/src/include/settings.h +++ b/src/include/settings.h @@ -241,18 +241,14 @@ struct Settings { FieldOfViewTypes FoV; /// Which field of view is used - important to be shared for unit sight MapRevealModes RevealMap; /// Reveal map kind RevealTypes DefeatReveal; - union { - struct { - unsigned NoFogOfWar:1; /// if dynamic fog of war is disabled - unsigned Inside:1; /// if game uses interior tileset or is generally "inside" for the purpose of obstacles - unsigned AiExplores:1; /// If true, AI sends explorers to search for resources (almost useless thing) - unsigned SimplifiedAutoTargeting:1; /// Use alternate target choosing algorithm for auto attack mode (idle, attack-move, patrol, etc.) - unsigned AiChecksDependencies:1; /// If false, the AI can do upgrades even if the dependencies are not met. This can be desirable to simplify AI scripting. - unsigned AllyDepositsAllowed:1; /// If false, the AI does not consider allied player's townhalls as deposits, so it will prefer harvesting gold closer to their own base - unsigned UserGameSettings:26; /// A bitfield for use by games and their settings - }; - uint32_t _Bitfield; - }; + + unsigned NoFogOfWar:1; /// if dynamic fog of war is disabled + unsigned Inside:1; /// if game uses interior tileset or is generally "inside" for the purpose of obstacles + unsigned AiExplores:1; /// If true, AI sends explorers to search for resources (almost useless thing) + unsigned SimplifiedAutoTargeting:1; /// Use alternate target choosing algorithm for auto attack mode (idle, attack-move, patrol, etc.) + unsigned AiChecksDependencies:1; /// If false, the AI can do upgrades even if the dependencies are not met. This can be desirable to simplify AI scripting. + unsigned AllyDepositsAllowed:1; /// If false, the AI does not consider allied player's townhalls as deposits, so it will prefer harvesting gold closer to their own base + unsigned UserGameSettings:26; /// A bitfield for use by games and their settings bool GetUserGameSetting(int i) { return std::bitset<26>(UserGameSettings).test(i); @@ -264,6 +260,22 @@ struct Settings { UserGameSettings = bs.to_ulong(); } + std::uint32_t getBitfield() const + { + return NoFogOfWar | (Inside << 1) | (AiExplores << 2) | (SimplifiedAutoTargeting << 3) + | (AiChecksDependencies << 4) | (AllyDepositsAllowed << 5) | (UserGameSettings << 6); + } + void setBitfield(std::uint32_t bitfield) + { + NoFogOfWar = bitfield & 1; + Inside = (bitfield >> 1) & 0x1; + AiExplores = (bitfield >> 2) & 0x1; + SimplifiedAutoTargeting = (bitfield >> 3) & 0x1; + AiChecksDependencies = (bitfield >> 4) & 0x1; + AllyDepositsAllowed = (bitfield >> 5) & 0x1; + UserGameSettings = bitfield >> 6; + } + bool operator==(const Settings &other) const { for (int i = 0; i < PlayerMax; i++) { if (Presets[i] == other.Presets[i]) { @@ -281,7 +293,7 @@ struct Settings { FoV == other.FoV && RevealMap == other.RevealMap && DefeatReveal == other.DefeatReveal && - _Bitfield == other._Bitfield; + getBitfield() == other.getBitfield(); } void Save(const std::function & f, bool withPlayers = true) { @@ -301,7 +313,7 @@ struct Settings { f(std::string("FoV = ") + std::to_string(static_cast(FoV))); f(std::string("RevealMap = ") + std::to_string(static_cast(RevealMap))); f(std::string("DefeatReveal = ") + std::to_string(static_cast(DefeatReveal))); - f(std::string("Flags = ") + std::to_string(_Bitfield)); + f(std::string("Flags = ") + std::to_string(getBitfield())); } bool SetField(std::string field, int value) { @@ -324,7 +336,7 @@ struct Settings { } else if (field == "DefeatReveal") { DefeatReveal = static_cast(value); } else if (field == "Flags") { - _Bitfield = value; + setBitfield(value); } else { return false; } diff --git a/src/network/net_message.cpp b/src/network/net_message.cpp index e7f1dcecd7..a9a46d4f28 100644 --- a/src/network/net_message.cpp +++ b/src/network/net_message.cpp @@ -261,7 +261,7 @@ size_t CServerSetup::Serialize(unsigned char *buf) const p += serialize8(p, static_cast(this->ServerGameSettings.Resources)); p += serialize8(p, static_cast(this->ServerGameSettings.RevealMap)); // The bitfield contains Inside and NoFogOfWar, as well as game-defined settings - p += serialize32(p, this->ServerGameSettings._Bitfield); + p += serialize32(p, this->ServerGameSettings.getBitfield()); for (const auto &preset : this->ServerGameSettings.Presets) { p += serialize8(p, static_cast(preset.Race)); @@ -293,8 +293,9 @@ size_t CServerSetup::Deserialize(const unsigned char *p) p += deserialize8(p, reinterpret_cast(&this->ServerGameSettings.Resources)); p += deserialize8(p, reinterpret_cast(&this->ServerGameSettings.RevealMap)); // The bitfield contains Inside and NoFogOfWar, as well as game-defined settings - p += deserialize32(p, &this->ServerGameSettings._Bitfield); - + std::uint32_t bitfield = 0; + p += deserialize32(p, &bitfield); + this->ServerGameSettings.setBitfield(bitfield); for (auto &preset : this->ServerGameSettings.Presets) { p += deserialize8(p, reinterpret_cast(&preset.Race)); p += deserialize8(p, reinterpret_cast(&preset.PlayerColor)); diff --git a/src/tolua/game.pkg b/src/tolua/game.pkg index 3ace69bf5d..b249ef4e0e 100644 --- a/src/tolua/game.pkg +++ b/src/tolua/game.pkg @@ -81,7 +81,6 @@ class Settings { bool Inside; bool AiExplores; bool SimplifiedAutoTargeting; - int _Bitfield @ Flags; bool GetUserGameSetting(int i); void SetUserGameSetting(int i, bool v);