Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial support for admins being allowed to perform host-only actions #4076

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/netplay/netplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <time.h> // for stats
#include <physfs.h>
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions lib/netplay/netplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]; //
Expand Down
94 changes: 78 additions & 16 deletions src/multiint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1448,8 +1448,8 @@ static void addGameOptions()

static bool safeToUseColour(unsigned player, unsigned otherPlayer)
{
// Player wants to take the colour from otherPlayer. May not take from a human otherPlayer, unless we're the host.
return player == otherPlayer || NetPlay.isHost || !isHumanPlayer(otherPlayer);
// Player wants to take the colour from otherPlayer. May not take from a human otherPlayer, unless we're the host/admin.
return player == otherPlayer || (NetPlay.isHost || NetPlay.isAdmin) || !isHumanPlayer(otherPlayer);
}

static int getPlayerTeam(int i)
Expand Down Expand Up @@ -1982,7 +1982,7 @@ static std::set<uint32_t> 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);
Expand Down Expand Up @@ -2114,9 +2114,9 @@ void WzMultiplayerOptionsTitleUI::openTeamChooser(uint32_t player)

UDWORD i;
int disallow = allPlayersOnSameTeam(player);
if (bIsTrueMultiplayerGame && NetPlay.isHost)
if (bIsTrueMultiplayerGame && (NetPlay.isHost || NetPlay.isAdmin))
{
// allow configuration of all teams in true multiplayer mode (by host), even if they would block the game starting
// allow configuration of all teams in true multiplayer mode (by host/admin), even if they would block the game starting
// (i.e. even if all players would be configured to be on the same team)
disallow = -1;
}
Expand Down Expand Up @@ -2513,17 +2513,32 @@ bool recvTeamRequest(NETQUEUE queue)
return false;
}

if (whosResponsible(player) != queue.index)
if (whosResponsible(player) != queue.index && !senderHasLobbyCommandAdminPrivs(queue.index))
{
HandleBadParam("NET_TEAMREQUEST given incorrect params.", player, queue.index);
return false;
}

if (locked.teams)
if (locked.teams && !senderHasLobbyCommandAdminPrivs(queue.index))
{
return false;
}

if (senderHasLobbyCommandAdminPrivs(queue.index) && (queue.index != player || locked.teams))
{
sendRoomSystemMessage(astringf("Admin %s changed team of player [%d] %s to %d",
NetPlay.players[queue.index].name,
NetPlay.players[player].position,
NetPlay.players[player].name,
team).c_str());
debug(LOG_INFO, "Admin %s (%s) changed team of player [%d] %s to %d",
NetPlay.players[queue.index].name,
getMultiStats(queue.index).identity.publicKeyHexString().c_str(),
NetPlay.players[player].position,
NetPlay.players[player].name,
team);
}

if (!alliancesSetTeamsBeforeGame(game.alliance))
{
return false;
Expand Down Expand Up @@ -2767,7 +2782,7 @@ bool recvFactionRequest(NETQUEUE queue)
return false;
}

if (whosResponsible(player) != queue.index)
if (whosResponsible(player) != queue.index && !senderHasLobbyCommandAdminPrivs(queue.index))
{
HandleBadParam("NET_FACTIONREQUEST given incorrect params.", player, queue.index);
return false;
Expand All @@ -2780,6 +2795,21 @@ bool recvFactionRequest(NETQUEUE queue)
return false;
}

if (senderHasLobbyCommandAdminPrivs(queue.index) && queue.index != player)
{
sendRoomSystemMessage(astringf("Admin %s changed faction of player [%d] %s to %d",
NetPlay.players[queue.index].name,
NetPlay.players[player].position,
NetPlay.players[player].name,
faction).c_str());
debug(LOG_INFO, "Admin %s (%s) changed faction of player [%d] %s to %d",
NetPlay.players[queue.index].name,
getMultiStats(queue.index).identity.publicKeyHexString().c_str(),
NetPlay.players[player].position,
NetPlay.players[player].name,
faction);
}

resetReadyStatus(false, true);

NetPlay.players[player].faction = newFactionId.value();
Expand All @@ -2805,15 +2835,30 @@ bool recvColourRequest(NETQUEUE queue)
return false;
}

if (whosResponsible(player) != queue.index)
if (whosResponsible(player) != queue.index && !senderHasLobbyCommandAdminPrivs(queue.index))
{
HandleBadParam("NET_COLOURREQUEST given incorrect params.", player, queue.index);
return false;
}

if (senderHasLobbyCommandAdminPrivs(queue.index) && (queue.index != player))
{
sendRoomSystemMessage(astringf("Admin %s changed color of player [%d] %s to %d",
NetPlay.players[queue.index].name,
NetPlay.players[player].position,
NetPlay.players[player].name,
col).c_str());
debug(LOG_INFO, "Admin %s (%s) changed color of player [%d] %s to %d",
NetPlay.players[queue.index].name,
getMultiStats(queue.index).identity.publicKeyHexString().c_str(),
NetPlay.players[player].position,
NetPlay.players[player].name,
col);
}

resetReadyStatus(false, true);

return changeColour(player, col, false);
return changeColour(player, col, senderHasLobbyCommandAdminPrivs(queue.index));
}

bool recvPositionRequest(NETQUEUE queue)
Expand All @@ -2835,17 +2880,32 @@ 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;
}

if (locked.position)
if (locked.position && !senderHasLobbyCommandAdminPrivs(queue.index))
{
return false;
}

if (senderHasLobbyCommandAdminPrivs(queue.index) && (queue.index != player || (locked.position)))
{
sendRoomSystemMessage(astringf("Admin %s changed position of player [%d] %s to %d",
NetPlay.players[queue.index].name,
NetPlay.players[player].position,
NetPlay.players[player].name,
position).c_str());
debug(LOG_INFO, "Admin %s (%s) changed position of player [%d] %s to %d",
NetPlay.players[queue.index].name,
getMultiStats(queue.index).identity.publicKeyHexString().c_str(),
NetPlay.players[player].position,
NetPlay.players[player].name,
position);
}

resetReadyStatus(false);

return changePosition(player, position);
Expand Down Expand Up @@ -3269,7 +3329,7 @@ static SwapPlayerIndexesResult recvSwapPlayerIndexes(NETQUEUE queue, const std::

static bool canChooseTeamFor(int i)
{
return (i == selectedPlayer || NetPlay.isHost);
return (i == selectedPlayer || (NetPlay.isHost || NetPlay.isAdmin));
}

// ////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -3996,7 +4056,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
{
Expand Down Expand Up @@ -4031,11 +4091,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))
Expand Down
9 changes: 8 additions & 1 deletion src/multilobbycommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down
3 changes: 3 additions & 0 deletions src/multilobbycommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/multiplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions src/screens/joiningscreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading