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

Various netplay, voting enhancements #3430

Merged
merged 4 commits into from
Oct 27, 2023
Merged
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
7 changes: 3 additions & 4 deletions lib/netplay/netplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,7 @@ void NET_InitPlayers(bool initTeams, bool initSpectator)

static void NETSendNPlayerInfoTo(uint32_t *index, uint32_t indexLen, unsigned to)
{
if (NetPlay.bComms && ingame.localJoiningInProgress)
{
ASSERT_HOST_ONLY(return);
}
ASSERT_HOST_ONLY(return);
NETbeginEncode(NETnetQueue(to), NET_PLAYER_INFO);
NETuint32_t(&indexLen);
for (unsigned n = 0; n < indexLen; ++n)
Expand Down Expand Up @@ -751,12 +748,14 @@ static void NETSendAllPlayerInfoTo(unsigned to)

void NETBroadcastTwoPlayerInfo(uint32_t index1, uint32_t index2)
{
ASSERT_HOST_ONLY(return);
uint32_t indices[2] = {index1, index2};
NETSendNPlayerInfoTo(indices, 2, NET_ALL_PLAYERS);
}

void NETBroadcastPlayerInfo(uint32_t index)
{
ASSERT_HOST_ONLY(return);
NETSendPlayerInfoTo(index, NET_ALL_PLAYERS);
}

Expand Down
8 changes: 6 additions & 2 deletions src/hci/quickchat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@
lastWidgetWidth = width();

int textX0 = xPos + QuickChatButtonHorizontalPadding;
int textY0 = yPos + (h - wzMessageText.lineSize()) / 2 - float(wzMessageText.aboveBase());

Check warning on line 275 in src/hci/quickchat.cpp

View workflow job for this annotation

GitHub Actions / Arch :LATEST [GCC]

conversion from ‘float’ to ‘int’ may change value [-Wfloat-conversion]

Check warning on line 275 in src/hci/quickchat.cpp

View workflow job for this annotation

GitHub Actions / Fedora :LATEST [GCC -m32]

conversion from 'float' to 'int' may change value [-Wfloat-conversion]

Check warning on line 275 in src/hci/quickchat.cpp

View workflow job for this annotation

GitHub Actions / Arch :LATEST [Clang]

implicit conversion turns floating-point number into integer: 'float' to 'int' [-Wfloat-conversion]

Check warning on line 275 in src/hci/quickchat.cpp

View workflow job for this annotation

GitHub Actions / Fedora :LATEST [GCC]

conversion from 'float' to 'int' may change value [-Wfloat-conversion]

Check warning on line 275 in src/hci/quickchat.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu 20.04 [Clang]

implicit conversion turns floating-point number into integer: 'float' to 'int' [-Wfloat-conversion]

Check warning on line 275 in src/hci/quickchat.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu 22.04 [Clang]

implicit conversion turns floating-point number into integer: 'float' to 'int' [-Wfloat-conversion]

int maxTextDisplayableWidth = w - (QuickChatButtonHorizontalPadding * 2);
isTruncated = maxTextDisplayableWidth < wzMessageText.width();
Expand Down Expand Up @@ -2618,6 +2618,8 @@
// WZ-generated internal messages - not for users to deliberately send
case WzQuickChatMessage::INTERNAL_MSG_DELIVERY_FAILURE_TRY_AGAIN:
return _("Message delivery failure - try again");
case WzQuickChatMessage::INTERNAL_LOBBY_NOTICE_MAP_DOWNLOADED:
return _("Map Downloaded");

// not a valid message
case WzQuickChatMessage::MESSAGE_COUNT:
Expand Down Expand Up @@ -2817,6 +2819,7 @@
bool isInGame = (GetGameMode() == GS_NORMAL);
auto senderTeam = checkedGetPlayerTeam(fromPlayer);
bool senderIsSpectator = NetPlay.players[fromPlayer].isSpectator;
bool senderCanUseSecuredMessages = realSelectedPlayer == NetPlay.hostPlayer || realSelectedPlayer < MAX_PLAYERS; // non-host spectator slots don't currently support / send secured messages

auto isPotentiallyValidTarget = [&](uint32_t playerIdx) -> bool {
if (playerIdx == fromPlayer)
Expand Down Expand Up @@ -2919,7 +2922,7 @@
}

NETQUEUE queue = NETnetQueue((recipient < MAX_PLAYERS) ? whosResponsible(recipient) : recipient);
bool sendSecured = isInGame && (queue.index == NetPlay.hostPlayer || queue.index < MAX_PLAYERS);
bool sendSecured = isInGame && (queue.index == NetPlay.hostPlayer || queue.index < MAX_PLAYERS) && senderCanUseSecuredMessages;
if (sendSecured)
{
if (!NETbeginEncodeSecured(queue, NET_QUICK_CHAT_MSG))
Expand Down Expand Up @@ -2970,7 +2973,8 @@
bool recvQuickChat(NETQUEUE queue)
{
bool isInGame = (GetGameMode() == GS_NORMAL);
bool expectingSecuredMessage = isInGame && (realSelectedPlayer == NetPlay.hostPlayer || realSelectedPlayer < MAX_PLAYERS); // spectators do not expect secured messages
bool senderCanUseSecuredMessages = queue.index == NetPlay.hostPlayer || queue.index < MAX_PLAYERS;
bool expectingSecuredMessage = isInGame && (realSelectedPlayer == NetPlay.hostPlayer || realSelectedPlayer < MAX_PLAYERS) && senderCanUseSecuredMessages; // spectator slots do not expect secured messages

uint32_t sender = MAX_CONNECTED_PLAYERS;
uint32_t recipient = MAX_CONNECTED_PLAYERS;
Expand Down
3 changes: 2 additions & 1 deletion src/hci/quickchat.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
\
/* FROM THIS POINT ON - ONLY INTERNAL MESSAGES! */ \
/* WZ-generated internal messages - not for users to deliberately send */ \
MSG(INTERNAL_MSG_DELIVERY_FAILURE_TRY_AGAIN) // This should always be the first internal message!
MSG(INTERNAL_MSG_DELIVERY_FAILURE_TRY_AGAIN) /* This should always be the first internal message! */ \
MSG(INTERNAL_LOBBY_NOTICE_MAP_DOWNLOADED)

#define GENERATE_ENUM(ENUM) ENUM,

Expand Down
137 changes: 20 additions & 117 deletions src/multiint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,15 @@
#include "stdinreader.h"
#include "urlhelpers.h"
#include "hci/quickchat.h"
#include "hci/teamstrategy.h"
#include "multivote.h"

#include "activity.h"
#include <algorithm>
#include "3rdparty/gsl_finally.h"

#define MAP_PREVIEW_DISPLAY_TIME 2500 // number of milliseconds to show map in preview
#define LOBBY_DISABLED_TAG "lobbyDisabled"
#define VOTE_TAG "voting"
#define KICK_REASON_TAG "kickReason"
#define SLOTTYPE_TAG_PREFIX "slotType"
#define SLOTTYPE_REQUEST_TAG SLOTTYPE_TAG_PREFIX "::request"
Expand Down Expand Up @@ -179,7 +180,6 @@ char sPlayer[128] = {'\0'}; // player name (to be used)
bool multiintDisableLobbyRefresh = false; // if we allow lobby to be refreshed or not.

static UDWORD hideTime = 0;
static uint8_t playerVotes[MAX_PLAYERS];
LOBBY_ERROR_TYPES LobbyError = ERROR_NOERROR;
static bool bInActualHostedLobby = false;
static bool bRequestedSelfMoveToPlayers = false;
Expand Down Expand Up @@ -1279,114 +1279,14 @@ WzString formatGameName(WzString name)
return withoutTechlevel + " (T" + WzString::number(game.techLevel) + " " + WzString::number(game.maxPlayers) + "P)";
}

void resetVoteData()
{
for (unsigned int i = 0; i < MAX_PLAYERS; ++i)
{
playerVotes[i] = 0;
}
}

static void sendVoteData(uint8_t currentVote)
{
NETbeginEncode(NETbroadcastQueue(), NET_VOTE);
NETuint32_t(&selectedPlayer);
NETuint8_t(&currentVote);
NETend();
}

static uint8_t getVoteTotal()
{
ASSERT_HOST_ONLY(return true);

uint8_t total = 0;

for (unsigned i = 0; i < MAX_PLAYERS; ++i)
{
if (isHumanPlayer(i))
{
if (selectedPlayer == i)
{
// always count the host as a "yes" vote.
playerVotes[i] = 1;
}
total += playerVotes[i];
}
else
{
playerVotes[i] = 0;
}
}

return total;
}

static bool recvVote(NETQUEUE queue)
{
ASSERT_HOST_ONLY(return true);

uint8_t newVote;
uint32_t player;

NETbeginDecode(queue, NET_VOTE);
NETuint32_t(&player); // TODO: check that NETQUEUE belongs to that player :wink:
NETuint8_t(&newVote);
NETend();

if (player >= MAX_PLAYERS)
{
debug(LOG_ERROR, "Invalid NET_VOTE from player %d: player id = %d", queue.index, static_cast<int>(player));
return false;
}

playerVotes[player] = (newVote == 1) ? 1 : 0;

debug(LOG_NET, "total votes: %d/%d", static_cast<int>(getVoteTotal()), static_cast<int>(NET_numHumanPlayers()));

// there is no "votes" that disallows map change so assume they are all allowing
if(newVote == 1) {
char msg[128] = {0};
ssprintf(msg, _("%s (%d) allowed map change. Total: %d/%d"), NetPlay.players[player].name, player, static_cast<int>(getVoteTotal()), static_cast<int>(NET_numHumanPlayers()));
sendRoomSystemMessage(msg);
}

return true;
}

// Show a vote popup to allow changing maps or using the randomization feature.
static void setupVoteChoice()
{
//This shouldn't happen...
if (NetPlay.isHost)
{
ASSERT(false, "Host tried to send vote data to themself");
return;
}

if (!hasNotificationsWithTag(VOTE_TAG))
{
WZ_Notification notification;
notification.duration = 0;
notification.contentTitle = _("Vote");
notification.contentText = _("Allow host to change map or randomize?");
notification.action = WZ_Notification_Action("Allow", [](const WZ_Notification&) {
uint8_t vote = 1;
sendVoteData(vote);
});
notification.tag = VOTE_TAG;

addNotification(notification, WZ_Notification_Trigger(GAME_TICKS_PER_SEC * 1));
}
}

static bool canChangeMapOrRandomize()
{
ASSERT_HOST_ONLY(return true);

uint8_t numHumans = NET_numHumanPlayers();
bool allowed = (static_cast<float>(getVoteTotal()) / static_cast<float>(numHumans)) > 0.5f;
bool allowed = (static_cast<float>(getLobbyChangeVoteTotal()) / static_cast<float>(numHumans)) > 0.5f;

resetVoteData(); //So the host can only do one change every vote session
resetLobbyChangeVoteData(); //So the host can only do one change every vote session

if (numHumans == 1)
{
Expand All @@ -1395,10 +1295,7 @@ static bool canChangeMapOrRandomize()

if (!allowed)
{
//setup a vote popup for the clients
NETbeginEncode(NETbroadcastQueue(), NET_VOTE_REQUEST);
NETend();

startLobbyChangeVote();
displayRoomSystemMessage(_("Not enough votes to randomize or change the map."));
}

Expand Down Expand Up @@ -1759,6 +1656,8 @@ static std::shared_ptr<WzMultiButton> addMultiButWithClickHandler(const std::sha

void WzMultiplayerOptionsTitleUI::openDifficultyChooser(uint32_t player)
{
ASSERT_HOST_ONLY(return);

std::shared_ptr<IntFormAnimated> aiForm = initRightSideChooser(_("DIFFICULTY"));
if (!aiForm)
{
Expand Down Expand Up @@ -1825,6 +1724,8 @@ void WzMultiplayerOptionsTitleUI::openDifficultyChooser(uint32_t player)

void WzMultiplayerOptionsTitleUI::openAiChooser(uint32_t player)
{
ASSERT_HOST_ONLY(return);

std::shared_ptr<IntFormAnimated> aiForm = initRightSideChooser(_("CHOOSE AI"));
if (!aiForm)
{
Expand Down Expand Up @@ -3335,7 +3236,7 @@ static SwapPlayerIndexesResult recvSwapPlayerIndexes(NETQUEUE queue, const std::
NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, playerIndex); // needed??
if (playerIndex < MAX_PLAYERS)
{
playerVotes[playerIndex] = 0;
resetLobbyChangePlayerVote(playerIndex);
}
}
swapPlayerMultiStatsLocal(playerIndexA, playerIndexB);
Expand Down Expand Up @@ -5425,7 +5326,7 @@ static void stopJoining(std::shared_ptr<WzTitleUI> parent)
bInActualHostedLobby = false;

reloadMPConfig(); // reload own settings
cancelOrDismissNotificationsWithTag(VOTE_TAG);
cancelOrDismissVoteNotifications();
cancelOrDismissNotificationIfTag([](const std::string& tag) {
return (tag.rfind(SLOTTYPE_TAG_PREFIX, 0) == 0);
});
Expand Down Expand Up @@ -6355,7 +6256,7 @@ void WzMultiplayerOptionsTitleUI::processMultiopWidgets(UDWORD id)
sstrcpy(game.name, widgGetString(psWScreen, MULTIOP_GNAME));
sstrcpy(sPlayer, widgGetString(psWScreen, MULTIOP_PNAME));

resetVoteData();
resetLobbyChangeVoteData();
resetDataHash();

startHost();
Expand All @@ -6377,7 +6278,7 @@ void WzMultiplayerOptionsTitleUI::processMultiopWidgets(UDWORD id)
if (NetPlay.bComms && ingame.side == InGameSide::MULTIPLAYER_CLIENT && !NetPlay.isHost)
{
// remove a potential "allow" vote if we gracefully leave
sendVoteData(0);
sendLobbyChangeVoteData(0);
}
NETGameLocked(false); // reset status on a cancel
stopJoining(parent);
Expand Down Expand Up @@ -6910,7 +6811,7 @@ void WzMultiplayerOptionsTitleUI::frontendMultiMessages(bool running)
NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, player_id);
if (player_id < MAX_PLAYERS)
{
playerVotes[player_id] = 0;
resetLobbyChangePlayerVote(player_id);
}
ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
if (player_id == NetPlay.hostPlayer || player_id == selectedPlayer) // if host quits or we quit, abort out
Expand All @@ -6935,7 +6836,7 @@ void WzMultiplayerOptionsTitleUI::frontendMultiMessages(bool running)
break;
}
case NET_FIREUP: // campaign game started.. can fire the whole shebang up...
cancelOrDismissNotificationsWithTag(VOTE_TAG); // don't need vote notifications anymore
cancelOrDismissVoteNotifications(); // don't need vote notifications anymore
cancelOrDismissNotificationsWithTag(LOBBY_DISABLED_TAG);
cancelOrDismissNotificationIfTag([](const std::string& tag) {
return (tag.rfind(SLOTTYPE_TAG_PREFIX, 0) == 0);
Expand Down Expand Up @@ -6992,7 +6893,7 @@ void WzMultiplayerOptionsTitleUI::frontendMultiMessages(bool running)

if (player_id < MAX_PLAYERS)
{
playerVotes[player_id] = 0;
resetLobbyChangePlayerVote(player_id);
}

if (player_id == NetPlay.hostPlayer)
Expand Down Expand Up @@ -7079,7 +6980,7 @@ void WzMultiplayerOptionsTitleUI::frontendMultiMessages(bool running)
case NET_VOTE_REQUEST:
if (!NetPlay.isHost && !NetPlay.players[selectedPlayer].isSpectator)
{
setupVoteChoice();
recvVoteRequest(queue);
}
break;

Expand Down Expand Up @@ -7362,7 +7263,7 @@ TITLECODE WzMultiplayerOptionsTitleUI::run()
}
if (!NetPlay.isHostAlive && ingame.side == InGameSide::MULTIPLAYER_CLIENT)
{
cancelOrDismissNotificationsWithTag(VOTE_TAG);
cancelOrDismissVoteNotifications();
cancelOrDismissNotificationsWithTag(LOBBY_DISABLED_TAG);
cancelOrDismissNotificationIfTag([](const std::string& tag) {
return (tag.rfind(SLOTTYPE_TAG_PREFIX, 0) == 0);
Expand Down Expand Up @@ -8323,13 +8224,15 @@ static bool multiplayIsStartingGame()

void sendRoomSystemMessage(char const *text)
{
ASSERT_HOST_ONLY(return);
NetworkTextMessage message(SYSTEM_MESSAGE, text);
displayRoomSystemMessage(text);
message.enqueue(NETbroadcastQueue());
}

void sendRoomNotifyMessage(char const *text)
{
ASSERT_HOST_ONLY(return);
NetworkTextMessage message(NOTIFY_MESSAGE, text);
displayRoomSystemMessage(text);
message.enqueue(NETbroadcastQueue());
Expand Down
1 change: 0 additions & 1 deletion src/multiint.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ void loadMapPreview(bool hideInterface);

bool changeReadyStatus(UBYTE player, bool bReady);
WzString formatGameName(WzString name);
void resetVoteData();
void sendRoomSystemMessage(char const *text);
void sendRoomNotifyMessage(char const *text);
void sendRoomSystemMessageToSingleReceiver(char const *text, uint32_t receiver);
Expand Down
2 changes: 2 additions & 0 deletions src/multijoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#include "multiint.h"
#include "multistat.h"
#include "multigifts.h"
#include "multivote.h"
#include "qtscript.h"
#include "clparse.h"
#include "multilobbycommands.h"
Expand Down Expand Up @@ -417,6 +418,7 @@ void recvPlayerLeft(NETQUEUE queue)
NetPlay.players[playerIndex].allocated = false;

NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, playerIndex);
cancelOrDismissKickVote(playerIndex);

debug(LOG_INFO, "** player %u has dropped, in-game! (gameTime: %" PRIu32 ")", playerIndex, gameTime);
ActivityManager::instance().updateMultiplayGameData(game, ingame, NETGameIsLocked());
Expand Down
9 changes: 2 additions & 7 deletions src/multimenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#include "loop.h"
#include "frontend.h"
#include "hci/teamstrategy.h"
#include "multivote.h"

// ////////////////////////////////////////////////////////////////////////////
// defines
Expand Down Expand Up @@ -877,17 +878,11 @@ class MultiMenuGrid: public GridLayout

if (mouseDown(MOUSE_RMB) && NetPlay.isHost) // both buttons....
{
char buf[250];

// Allow the host to kick the AI only in a MP game, or if they activated cheats in a skirmish game
if ((NetPlay.bComms || Cheated) && (NetPlay.players[i].allocated || (NetPlay.players[i].allocated == false && NetPlay.players[i].ai != AI_OPEN)))
{
inputLoseFocus();
ssprintf(buf, _("The host has kicked %s from the game!"), getPlayerName((unsigned int) i));
sendInGameSystemMessage(buf);
ssprintf(buf, _("kicked %s : %s from the game, and added them to the banned list!"), getPlayerName((unsigned int) i), NetPlay.players[i].IPtextAddress);
NETlogEntry(buf, SYNC_FLAG, (unsigned int) i);
kickPlayer((unsigned int) i, _("The host has kicked you from the game."), ERROR_KICKED, false);
startKickVote(static_cast<uint32_t>(i));
return;
}
}
Expand Down
Loading
Loading