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

Rejection screen for async join allow, docs #3820

Closed
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
14 changes: 11 additions & 3 deletions doc/CmdInterface.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ All messages are sent in plain-text UTF-8 and end with 0x0a (`\n`).
- `%s` base64-encoded name of the player
- `%s` base64-encoded chat message content

* `WZEVENT: join approval needed: <joinid> <ip> <hash> <b64pubkey> <b64name> [spec|play]`\
* `WZEVENT: join approval needed: <joinid> <ip> <hash> <b64pubkey> <b64name> <spec|play>`\
Passes a join request that requires approval (if `--async-join-approve` is enabled)\
**Important: If the cmdinterface client does not respond to this request with a `join <approve/reject>` command in a timely manner, the joiner will be kicked (or drop).**\
Fields:
Expand Down Expand Up @@ -83,14 +83,19 @@ If state of interface buffer is unknown and/or corrupted, interface can send a f
* `admin remove <pkey|hash>`\
Removes admin from room admins list by public key or hash

* `join <approve|reject> <joinid>`\
* `join <approve|reject> <joinid> <code> <msg [^\n]`\
Approve or reject an attempt to join the game.\
Pass in the `<joinid>` received in a `WZEVENT: join approval needed` event.
Code is LOBBY_ERROR_TYPES enum, use 7 to avoid double-screening client with "you are kicked".

* `ban ip <ip>`\
Find and kick (with adding to ip banlist) player with specified ip
(result of `WZEVENT: bancheck:` from outside)

* `kick identity <pkey|hash> <msg [^\n]>`\
Find and kick (*without* adding to ip banlist) player with specified ip
(result of `WZEVENT: bancheck:` from outside)

* `permissions set connect:allow <pkey|hash>`\
Allows player with the specified identity to connect (bypassing any ip bans)

Expand All @@ -104,9 +109,12 @@ If state of interface buffer is unknown and/or corrupted, interface can send a f
Allows or mutes chat.
- Parameter 1: If "allow" is specified, allows all chat (both free chat and quick chat). If "quickchat" is specified, mutes / disallows free chat (but still allows quick chat).
- Parameter 2: If "all" is specified instead of an identity, applies to all. If "newjoin" is specified instead of an identity, applies to future joins.

* `chat bcast <message [^\n]>`\
Send system level message to the room from stdin.

* `chat direct <pkey|hash> <message [^\n]>`\
Send system level message to the player with specified pkey/hash from stdin.

* `shutdown now`\
Trigger graceful shutdown of the game regardless of state.
2 changes: 1 addition & 1 deletion doc/hosting/AutohostConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The `challenge` object defines the game parameters for a multiplayer game.
* `scavengers` sets which scavengers should be in game : `0` for none, `1` for basic scavengers, `2` for ultimate scavengers.
* `alliances` sets the alliance mode. `0` for free for all, `1` for allow alliances, `2` for fixed teams, `3` for fixed teams without research sharing.
* `powerLevel` sets the power generation rate. `0` for low, `1` for medium, `2` for high.
* `bases` sets the starting base. `0` for no base, `1` for small base, `2` for advanced base.
* `bases` sets the starting base. `1` for no base, `2` for small base, `3` for advanced base.
* `name` your game name, as it will be shown in the lobby.
* `techLevel` sets the starting technology level. `1` for level 1 (wheel), `2` for level 2 (water mill), `3` for level 3 (chip), `4` for level 4 (computer).
* `spectatorHost` when `true` or `1`, the host will spectate the game. When `false` or `0`, the host will play the game.
Expand Down
23 changes: 21 additions & 2 deletions lib/netplay/netplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ struct TmpSocketInfo
// async join approval
std::string uniqueJoinID;
optional<LOBBY_ERROR_TYPES> asyncJoinApprovalResult = nullopt;
std::string asyncJoinApprovalRejectReason = "";

void reset()
{
Expand Down Expand Up @@ -524,7 +525,7 @@ void NETsetAsyncJoinApprovalRequired(bool enabled)
}

// NOTE: *MUST* be called from the main thread!
bool NETsetAsyncJoinApprovalResult(const std::string& uniqueJoinID, bool approve, LOBBY_ERROR_TYPES rejectedReason)
bool NETsetAsyncJoinApprovalResult(const std::string& uniqueJoinID, bool approve, LOBBY_ERROR_TYPES rejectedReason, std::string rejectedMessage)
{
if (!approve && rejectedReason == ERROR_NOERROR)
{
Expand All @@ -538,6 +539,7 @@ bool NETsetAsyncJoinApprovalResult(const std::string& uniqueJoinID, bool approve
{
// found a match
tmp_info.asyncJoinApprovalResult = (approve) ? ERROR_NOERROR : rejectedReason;
tmp_info.asyncJoinApprovalRejectReason = rejectedMessage;
return true;
}
}
Expand Down Expand Up @@ -4108,6 +4110,7 @@ static void NETallowJoining()
uint8_t rejected = ERROR_FULL;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
connectFailed = true;
Expand Down Expand Up @@ -4168,6 +4171,7 @@ static void NETallowJoining()
rejected = ERROR_WRONGDATA;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
NETpop(NETnetTmpQueue(i));
Expand Down Expand Up @@ -4206,6 +4210,7 @@ static void NETallowJoining()
rejected = ERROR_WRONGDATA;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
NETpop(NETnetTmpQueue(i));
Expand Down Expand Up @@ -4267,6 +4272,7 @@ static void NETallowJoining()
NETlogEntry(buf, SYNC_FLAG, i);
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
NETpop(NETnetTmpQueue(i));
Expand Down Expand Up @@ -4306,6 +4312,7 @@ static void NETallowJoining()
rejected = ERROR_WRONGDATA;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
NETpop(NETnetTmpQueue(i));
Expand Down Expand Up @@ -4345,11 +4352,16 @@ static void NETallowJoining()
// async join rejected
uint8_t rejected = static_cast<uint8_t>(tmp_connectState[i].asyncJoinApprovalResult.value());
char buf[256] = {'\0'};
ssprintf(buf, "**Rejecting player(%s), due to async approval rejection, reason (%u).", tmp_connectState[i].ip.c_str(), static_cast<unsigned int>(rejected));
ssprintf(buf, "**Rejecting player(%s), due to async approval rejection, reason [%u] (%u).", tmp_connectState[i].ip.c_str(), strlen(tmp_connectState[i].asyncJoinApprovalRejectReason.c_str()), static_cast<unsigned int>(rejected));
debug(LOG_INFO, "%s", buf);
NETlogEntry(buf, SYNC_FLAG, i);
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
uint16_t maxrejectlen = 4000;
if (maxrejectlen > strlen(tmp_connectState[i].asyncJoinApprovalRejectReason.c_str())) {
maxrejectlen = tmp_connectState[i].asyncJoinApprovalRejectReason.size();
}
NETstring(tmp_connectState[i].asyncJoinApprovalRejectReason.c_str(), maxrejectlen);
NETend();
NETflush();
auto tmpQueue = NETnetTmpQueue(i);
Expand All @@ -4374,6 +4386,7 @@ static void NETallowJoining()
uint8_t rejected = ERROR_HOSTDROPPED;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
auto tmpQueue = NETnetTmpQueue(i);
Expand Down Expand Up @@ -4409,6 +4422,7 @@ static void NETallowJoining()
rejected = ERROR_FULL;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();

Expand Down Expand Up @@ -4510,6 +4524,7 @@ static void NETallowJoining()
rejected = ERROR_WRONGDATA;
NETbeginEncode(NETnetTmpQueue(i), NET_REJECTED);
NETuint8_t(&rejected);
NETstring("", 0);
NETend();
NETflush();
auto tmpQueue = NETnetTmpQueue(i);
Expand Down Expand Up @@ -5058,14 +5073,18 @@ bool NETjoinGame(const char *host, uint32_t port, const char *playername, const
else if (type == NET_REJECTED)
{
uint8_t rejection = 0;
char* rejectionReason = (char*)malloc(4096);

NETbeginDecode(queue, NET_REJECTED);
NETuint8_t(&rejection);
NETstring(rejectionReason, 4095);
NETend();
NETpop(queue);

debug(LOG_NET, "NET_REJECTED received. Error code: %u", (unsigned int) rejection);

setNETJoinRejectReason(std::string(rejectionReason));
free(rejectionReason);
setLobbyError((LOBBY_ERROR_TYPES)rejection);
NETclose();
return false;
Expand Down
2 changes: 1 addition & 1 deletion lib/netplay/netplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ bool NETcheckPlayerConnectionStatus(CONNECTION_STATUS status, unsigned player);

void NETsetAsyncJoinApprovalRequired(bool enabled);
// NOTE: *MUST* be called from the main thread!
bool NETsetAsyncJoinApprovalResult(const std::string& uniqueJoinID, bool approve, LOBBY_ERROR_TYPES rejectedReason = ERROR_NOERROR);
bool NETsetAsyncJoinApprovalResult(const std::string& uniqueJoinID, bool approve, LOBBY_ERROR_TYPES rejectedReason, std::string rejectedMessage);

const char *messageTypeToString(unsigned messageType);

Expand Down
16 changes: 16 additions & 0 deletions src/multiint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,14 @@ void setLobbyError(LOBBY_ERROR_TYPES error_type)
}
}

std::string NETJoinRejectReason = "";
std::string getNETJoinRejectReason() {
return NETJoinRejectReason;
}
void setNETJoinRejectReason(std::string reason) {
NETJoinRejectReason = reason;
}

// NOTE: Must call NETinit(true); before this will actually work
std::vector<JoinConnectionDescription> findLobbyGame(const std::string& lobbyAddress, unsigned int lobbyPort, uint32_t lobbyGameId)
{
Expand Down Expand Up @@ -1118,6 +1126,14 @@ static JoinGameResult joinGameInternal(std::vector<JoinConnectionDescription> co
// Failed to connect to all IPs / options in list
// Change to an error display.
changeTitleUI(std::make_shared<WzMsgBoxTitleUI>(WzString(_("Unable to join:")), WzString(_("Error while joining.")), wzTitleUICurrent));
std::string reason = getNETJoinRejectReason();
if (reason.empty()) {
changeTitleUI(std::make_shared<WzMsgBoxTitleUI>(WzString(_("Unable to join:")), WzString(_("Error while joining.")), wzTitleUICurrent));
}
else
{
changeTitleUI(std::make_shared<WzMsgBoxTitleUI>(WzString(_("Join attempt rejected:")), WzString::fromUtf8(reason), wzTitleUICurrent));
}
ActivityManager::instance().joinGameFailed(connection_list);
return JoinGameResult::FAILED;
}
Expand Down
3 changes: 3 additions & 0 deletions src/multiint.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ int matchAIbyName(const char* name); ///< only run this -after- readAIs() is cal
LOBBY_ERROR_TYPES getLobbyError();
void setLobbyError(LOBBY_ERROR_TYPES error_type);

std::string getNETJoinRejectReason();
void setNETJoinRejectReason(std::string reason);

/**
* Updates structure limit flags. Flags indicate which structures are disabled.
*/
Expand Down
11 changes: 7 additions & 4 deletions src/stdinreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1006,9 +1006,10 @@ int cmdInputThreadFunc(void *)
{
char action[1024] = {0};
char uniqueJoinID[1024] = {0};
char rejectionMessage[4093] = {0};
unsigned int rejectionReason = static_cast<unsigned int>(ERROR_NOERROR);
int r = sscanf(line, "join %1023s %1023s %u", action, uniqueJoinID, &rejectionReason);
if (r != 2 && r != 3)
int r = sscanf(line, "join %1023s %1023s %u %4090[^\n]s", action, uniqueJoinID, &rejectionReason, rejectionMessage);
if (r != 4)
{
wz_command_interface_output_onmainthread("WZCMD error: Failed to get join action or uniqueJoinID!\n");
}
Expand All @@ -1027,8 +1028,10 @@ int cmdInputThreadFunc(void *)
{
bool approveValue = approve.value();
std::string uniqueJoinIDCopy(uniqueJoinID);
wzAsyncExecOnMainThread([uniqueJoinIDCopy, approveValue, rejectionReason] {
if (!NETsetAsyncJoinApprovalResult(uniqueJoinIDCopy, approveValue, static_cast<LOBBY_ERROR_TYPES>(rejectionReason)))
std::string rejectionMessageCopy(rejectionMessage);
convertEscapedNewlines(rejectionMessageCopy);
wzAsyncExecOnMainThread([uniqueJoinIDCopy, approveValue, rejectionReason, rejectionMessageCopy] {
if (!NETsetAsyncJoinApprovalResult(uniqueJoinIDCopy, approveValue, static_cast<LOBBY_ERROR_TYPES>(rejectionReason), rejectionMessageCopy))
{
wz_command_interface_output("WZCMD info: Could not find currently-waiting join with specified uniqueJoinID\n");
}
Expand Down
Loading