diff --git a/lib/netplay/netplay.cpp b/lib/netplay/netplay.cpp index cd80b7ff7bf..fa741845cdb 100644 --- a/lib/netplay/netplay.cpp +++ b/lib/netplay/netplay.cpp @@ -33,6 +33,7 @@ #include "src/console.h" #include "src/component.h" // FIXME: we need to handle this better #include "src/modding.h" // FIXME: we need to handle this better +#include "src/multilobbycommands.h" #include // for stats #include @@ -4293,9 +4294,12 @@ static void NETallowJoining() NEThostPromoteTempSocketToPermanentPlayerConnection(i, index); + bool isAdminJoining = identityMatchesAdmin(joinRequestInfo.identity); + NETbeginEncode(NETnetQueue(index), NET_ACCEPTED); NETuint8_t(&index); NETuint32_t(&NetPlay.hostPlayer); + NETbool(&isAdminJoining); NETend(); // First send info about players to newcomer. diff --git a/lib/netplay/netplay.h b/lib/netplay/netplay.h index e16e5d21b34..b3c80406c49 100644 --- a/lib/netplay/netplay.h +++ b/lib/netplay/netplay.h @@ -316,6 +316,7 @@ struct NETPLAY uint32_t hostPlayer; ///< Index of host in player array bool bComms; ///< Actually do the comms? bool isHost; ///< True if we are hosting the game + bool isAdmin; ///< True if we are promoted to admin by the host bool isPortMappingEnabled; // if we want the automatic Port mapping setup routines to run bool isHostAlive; /// if the host is still alive char gamePassword[password_string_size]; // diff --git a/src/multiint.cpp b/src/multiint.cpp index 86258b9b6ea..ffec876897f 100644 --- a/src/multiint.cpp +++ b/src/multiint.cpp @@ -1982,7 +1982,7 @@ static std::set validPlayerIdxTargetsForPlayerPositionMove(uint32_t pl for (uint32_t i = 0; i < game.maxPlayers; i++) { if (player != i - && (NetPlay.isHost || !isHumanPlayer(i)) // host can move a player to any slot, player can only move to empty slots + && (NetPlay.isHost || (!isHumanPlayer(i) || NetPlay.isAdmin)) // host/admin can move a player to any slot, player can only move to empty slots && !isSpectatorOnlySlot(i)) // target cannot be a spectator only slot (for player position changes) { validTargetPlayerIdx.insert(i); @@ -2835,7 +2835,7 @@ bool recvPositionRequest(NETQUEUE queue) return false; } - if (whosResponsible(player) != queue.index) + if (whosResponsible(player) != queue.index && !senderHasLobbyCommandAdminPrivs(queue.index)) { HandleBadParam("NET_POSITIONREQUEST given incorrect params.", player, queue.index); return false; @@ -3996,7 +3996,7 @@ class WzPlayerRow : public WIDGET widget->colorButton->addOnClickHandler([playerIdx, titleUI](W_BUTTON& button){ auto strongTitleUI = titleUI.lock(); ASSERT_OR_RETURN(, strongTitleUI != nullptr, "Title UI is gone?"); - if (playerIdx == selectedPlayer || NetPlay.isHost) + if (playerIdx == selectedPlayer || (NetPlay.isHost || NetPlay.isAdmin)) { if (!NetPlay.players[playerIdx].isSpectator) // not a spectator { @@ -4031,11 +4031,13 @@ class WzPlayerRow : public WIDGET widget->playerInfo->addOnClickHandler([playerIdx, titleUI](W_BUTTON& button){ auto strongTitleUI = titleUI.lock(); ASSERT_OR_RETURN(, strongTitleUI != nullptr, "Title UI is gone?"); - if (playerIdx == selectedPlayer || NetPlay.isHost) + if (playerIdx == selectedPlayer || (NetPlay.isHost || NetPlay.isAdmin)) { uint32_t player = playerIdx; // host can move any player, clients can request to move themselves if there are available slots - if (((player == selectedPlayer && validPlayerIdxTargetsForPlayerPositionMove(player).size() > 0) || (NetPlay.players[player].allocated && NetPlay.isHost)) + // admins can move people as if they are host + if (((player == selectedPlayer && validPlayerIdxTargetsForPlayerPositionMove(player).size() > 0) || + (NetPlay.players[player].allocated && (NetPlay.isHost || NetPlay.isAdmin))) && !locked.position && player < MAX_PLAYERS && !isSpectatorOnlySlot(player)) diff --git a/src/multilobbycommands.cpp b/src/multilobbycommands.cpp index 57f735007a1..f0106d38505 100644 --- a/src/multilobbycommands.cpp +++ b/src/multilobbycommands.cpp @@ -57,6 +57,13 @@ bool removeLobbyAdminPublicKey(const std::string& publicKeyB64Str) return lobbyAdminPublicKeys.erase(publicKeyB64Str) > 0; } +// checks for specific identity being an admin +bool identityMatchesAdmin(const EcKey& identity) { + std::string senderIdentityHash = identity.publicHashString(); + std::string senderPublicKeyB64 = base64Encode(identity.toBytes(EcKey::Public)); + return lobbyAdminPublicKeys.count(senderPublicKeyB64) != 0 || lobbyAdminPublicHashStrings.count(senderIdentityHash) != 0; +} + // NOTE: **IMPORTANT** this should *NOT* be used for determining whether a sender has permission to execute admin commands // (Use senderHasLobbyCommandAdminPrivs instead) static bool senderApparentlyMatchesAdmin(uint32_t playerIdx) @@ -86,7 +93,7 @@ static bool senderApparentlyMatchesAdmin(uint32_t playerIdx) } // **THIS** is the function that should be used to determine whether a sender currently has permission to execute admin commands -static bool senderHasLobbyCommandAdminPrivs(uint32_t playerIdx) +bool senderHasLobbyCommandAdminPrivs(uint32_t playerIdx) { if (playerIdx >= MAX_CONNECTED_PLAYERS) { diff --git a/src/multilobbycommands.h b/src/multilobbycommands.h index 112bab5d0bf..c554d3ec74c 100644 --- a/src/multilobbycommands.h +++ b/src/multilobbycommands.h @@ -52,6 +52,9 @@ void cmdInterfaceLogChatMsg(const NetworkTextMessage& message, const char* log_p bool processChatLobbySlashCommands(const NetworkTextMessage& message, HostLobbyOperationsInterface& cmdInterface); +bool identityMatchesAdmin(const EcKey& identity); +bool senderHasLobbyCommandAdminPrivs(uint32_t playerIdx); + bool addLobbyAdminIdentityHash(const std::string& playerIdentityHash); bool removeLobbyAdminIdentityHash(const std::string& playerIdentityHash); diff --git a/src/multiplay.cpp b/src/multiplay.cpp index 932937c6bb9..67ca91675a1 100644 --- a/src/multiplay.cpp +++ b/src/multiplay.cpp @@ -1031,6 +1031,7 @@ HandleMessageAction getMessageHandlingAction(NETQUEUE& queue, uint8_t type) } bool senderIsSpectator = NetPlay.players[queue.index].isSpectator; + bool senderIsAdmin = senderHasLobbyCommandAdminPrivs(queue.index); if (type > NET_MIN_TYPE && type < NET_MAX_TYPE) { @@ -1053,7 +1054,7 @@ HandleMessageAction getMessageHandlingAction(NETQUEUE& queue, uint8_t type) case NET_BEACONMSG: case NET_TEAMREQUEST: // spectators should not be allowed to request a team / non-spectator slot status case NET_POSITIONREQUEST: - if (senderIsSpectator) + if (senderIsSpectator && !senderIsAdmin) { return HandleMessageAction::Disallow_And_Kick_Sender; } diff --git a/src/screens/joiningscreen.cpp b/src/screens/joiningscreen.cpp index 796285f4bda..a2355d35ab1 100644 --- a/src/screens/joiningscreen.cpp +++ b/src/screens/joiningscreen.cpp @@ -1388,6 +1388,7 @@ void WzJoiningGameScreen_HandlerRoot::processJoining() // Retrieve the player ID the game host arranged for us NETuint8_t(&index); NETuint32_t(&hostPlayer); // and the host player idx + NETbool(&NetPlay.isAdmin); NETend(); NETpop(tmpJoiningQUEUE);