hostile-takeover/server/endpoint.cpp
2016-08-31 23:54:48 -04:00

1724 lines
56 KiB
C++

#include "server/endpoint.h"
#include "server/game.h"
#include "server/ids.h"
#include "server/tokenauth.h"
#include "server/room.h"
#include "mpshared/xpump.h"
#include "mpshared/messages.h"
#include "base/sigslot.h"
#include "base/log.h"
#include "base/tick.h"
namespace wi {
#define MISSED_ECHO_COUNT 3
#define CHAT_FRAGMENT_LENGTH 250
const int kcMinutesBanTimeoutDefault = 5;
const int kcMinutesBanTimeoutMaximum = 60 * 24 * 2;
Endpoint::Endpoint(Server& server, base::Socket *socket, dword id,
bool serverfull) : server_(server), id_(id),
serverfull_(serverfull), clientid_(0),
protocolid_(0), state_(ES_HANDSHAKE), game_(NULL), echo_(true),
roomid_(0), name_(NULL), anonymous_(true), missed_(0), okecho_(false),
admin_(false), muted_(false), sigvisible_(false), seechat_(false),
roomlimiter_(2, 120), teamchat_(false) {
did_[0] = 0;
platform_[0] = 0;
xpump_.Attach(socket, this, server.log());
}
Endpoint::~Endpoint() {
LOG() << base::Log::Format("0x%p", this);
SignalOnDelete(this);
delete name_;
}
void Endpoint::SetState(State state)
{
LOG() << base::Log::Format("0x%p ", this)
<< "From: " << EsLabels.Find(state_)
<< " To: " << EsLabels.Find(state);
state_ = state;
}
bool Endpoint::CheckState(State state0, State state1)
{
#ifdef DEV_BUILD
Assert(state0 == state_ || state1 == state_);
#endif
if (state0 != state_ && state1 != state_) {
LOG() << base::Log::Format("0x%p ", this)
<< "Warning! Current: " << EsLabels.Find(state_);
if (state1 == (State)-1) {
LOG() << base::Log::Format("0x%p ", this)
<< " Expected: " << EsLabels.Find(state0);
} else {
LOG() << base::Log::Format("0x%p ", this)
<< " Expected: " << EsLabels.Find(state0)
<< " or " << EsLabels.Find(state1);
}
return false;
}
return true;
}
void Endpoint::OnHandshake(dword clientid, dword protocolid) {
if (!CheckState(ES_HANDSHAKE)) {
xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultFail, 0));
return;
}
// Check protocol; let client know result
clientid_ = clientid;
protocolid_ = protocolid;
// The server and client need to support the same protocol.
if (protocolid != kdwProtocolCurrent) {
xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultFail, 0));
RLOG() << base::Log::Format("0x%p ", this)
<< "wrong protocolid: " << protocolid
<< " clientid: " << clientid;
return;
}
if (clientid != kdwClientID) {
xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultFail, 0));
RLOG() << base::Log::Format("0x%p ", this)
<< "wrong clientid: " << clientid;
return;
}
if (serverfull_) {
xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultServerFull,
0));
RLOG() << base::Log::Format("0x%p ", this) << "server is full.";
return;
}
xpump_.Send(XMsgHandshakeResult::ToBuffer(knHandshakeResultSuccess, id_));
SetState(ES_HANDSHAKESUCCESS);
// Echos past this point will be acknowledged
okecho_ = true;
}
void Endpoint::OnLogin(const char *username, const char *token,
const char *did, const char *platform) {
LOG() << "username: " << username << " token: " << token << " did: " << did
<< " platform: " << platform;
if (!CheckState(ES_HANDSHAKESUCCESS)) {
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail));
return;
}
// Check to see this user is blocked. Might have did_.
if (FindTracker(server_.tracker())) {
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail));
return;
}
// Try anonymous login if that's what the user wants
if (TokenAuth::IsAnonymous(username, token)) {
if (!server_.AnonsAllowed() && !admin_) {
LOG() << "FAILED LOGIN: Server doesn't allow anon logins: " << username << ", " << token << ", " << did;
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultNoAnons));
return;
}
RememberName(name_);
delete name_;
name_ = AllocString(base::Format::ToString("anon%d", id_));
UpdateDid(did);
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultAnonymousSuccess));
anonymous_ = true;
strncpyz(platform_, platform, sizeof(platform_));
SetState(ES_READY);
return;
}
// Not anonymous; try real login
dword result = TokenAuth::Authenticate(username, token);
LOG() << AuthResults.Find(result) << ": " << username << ", " << token << ", " << did;
if (result == knAuthResultFail) {
RLOG() << "FAILED LOGIN: " << AuthResults.Find(result) << ": " << username << ", " << token << ", " << did;
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail));
return;
}
if (result == knAuthResultStaleToken) {
// Token hash is valid but timed out.
UpdateDid(did);
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultStaleToken));
return;
}
// Update the did first, before seeing if the user is blocked
UpdateDid(did);
if (FindTracker(server_.tracker())) {
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail));
return;
}
// Endpoint has been authed, so take in the new name
RememberName(name_);
delete name_;
name_ = AllocString(base::Format::ToString(username, id_));
// Moderators are allowed to login with multiple devices
if (!server_.AccountSharing() && !IsModerator()) {
if (server_.SharedAccountExists(this, username)) {
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultAccountInUse));
return;
}
}
// Success. Transition to ES_READY
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultSuccess));
anonymous_ = false;
strncpyz(platform_, platform, sizeof(platform_));
SetState(ES_READY);
// Moderators see unfiltered chat by default
seechat_ = IsModerator();
}
void Endpoint::RememberName(const char *name) {
if (name == NULL) {
return;
}
old_names_.insert(old_names_.begin(), name);
while (old_names_.size() >= 10) {
old_names_.pop_back();
}
}
void Endpoint::OnSignOut() {
LOG();
// Only know how to do this from ES_READY, currently
if (!CheckState(ES_READY)) {
xpump_.Send(XMsgSignOutResult::ToBuffer(knSignOutResultFail));
return;
}
// Go back to before login state
xpump_.Send(XMsgSignOutResult::ToBuffer(knSignOutResultSuccess));
SetState(ES_HANDSHAKESUCCESS);
anonymous_ = true;
}
void Endpoint::OnLobbyJoin() {
if (!CheckState(ES_READY)) {
xpump_.Send(XMsgLobbyJoinResult::ToBuffer(
knLobbyJoinResultNotLoggedIn));
return;
}
// See if this endpoint will be allowed to enter the lobby
if (!server_.lobby().CanEnter(this)) {
xpump_.Send(XMsgLobbyJoinResult::ToBuffer(knLobbyJoinResultFail));
}
// Message that entering the lobby was a success, before actually
// entering the lobby, since that sends a bunch of messages.
xpump_.Send(XMsgLobbyJoinResult::ToBuffer(knLobbyJoinResultSuccess));
server_.lobby().Enter(this);
SetState(ES_LOBBY);
}
void Endpoint::OnLobbyCreateRoom(const char *name, const char *password) {
if (!CheckState(ES_LOBBY)) {
xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer(
knLobbyCreateRoomResultFail, 0));
return;
}
// Allow room creation at a pre-determined rate only
if (!IsModerator() && roomlimiter_.IsEmpty()) {
xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer(
knLobbyCreateRoomResultFail, 0));
return;
}
dword result = knLobbyCreateRoomResultFail;
Room *room = server_.lobby().NewRoom(this, name, password,
kroomidInvalid, false, &result);
if (room == NULL) {
xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer(result, 0));
return;
}
char ip[32];
xpump_.socket()->GetRemoteAddress().IPAsString(ip, sizeof(ip));
RLOG() << "NewRoom:" << name << " password:" << password
<< " username:" << name_ << " id:" << id_ << " ip:" << ip;
xpump_.Send(XMsgLobbyCreateRoomResult::ToBuffer(result, room->id()));
}
void Endpoint::OnLobbyCanJoinRoom(dword roomid, const char *password) {
if (!CheckState(ES_LOBBY)) {
xpump_.Send(XMsgLobbyCanJoinRoomResult::ToBuffer(
knRoomJoinResultFail));
return;
}
Room *room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
xpump_.Send(XMsgLobbyCanJoinRoomResult::ToBuffer(
knRoomJoinResultNotFound));
return;
}
dword result = room->CanAddPlayer(this, password);
xpump_.Send(XMsgLobbyCanJoinRoomResult::ToBuffer(result));
}
void Endpoint::OnLobbyLeave() {
if (!CheckState(ES_LOBBY)) {
xpump_.Send(XMsgLobbyLeaveResult::ToBuffer(knLobbyLeaveResultFail));
return;
}
server_.lobby().Leave(this);
xpump_.Send(XMsgLobbyLeaveResult::ToBuffer(knLobbyLeaveResultSuccess));
SetState(ES_READY);
}
void Endpoint::OnRoomJoin(dword roomid, const char *password) {
if (!CheckState(ES_READY)) {
xpump_.Send(XMsgRoomJoinResult::ToBuffer(knRoomJoinResultFail));
return;
}
Room *room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
xpump_.Send(XMsgRoomJoinResult::ToBuffer(knRoomJoinResultNotFound));
return;
}
// See if this endpoint will be allowed to join the room
dword result = room->CanAddPlayer(this, password);
if (result != knRoomJoinResultSuccess) {
xpump_.Send(XMsgRoomJoinResult::ToBuffer(result));
return;
}
// Message that joining the room was a success, before actually
// joining the room, since that sends a bunch of messages.
xpump_.Send(XMsgRoomJoinResult::ToBuffer(knRoomJoinResultSuccess));
// Remember the room and go into ROOM state. The room won't go away as
// long as this player is in it, or in a game that is in it.
roomid_ = room->id();
SetState(ES_ROOM);
// Add the player to the room. This will subscribe the endpoint to
// room changes.
room->AddPlayer(this);
// Now tell the client that room status is complete, so it can track
// changes in room state.
xpump_.Send(XMsgRoomStatusComplete::ToBuffer());
#if 0
// If this room is being logged, tell the user
if (room->password()[0] == 0) {
const char *pszMsg = "Chat is subject to being logged.";
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", pszMsg));
server_.logger().LogSystemMsg(this, pszMsg);
}
#endif
// Broadcast server announcements to the player if they entered a server room.
if (room->creator_id() == 0) {
if (!server().GetAnnouncements().empty()) {
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("",
server().GetAnnouncements().c_str()));
}
}
}
void Endpoint::OnRoomSendChat(const char *chat) {
if (!CheckState(ES_ROOM)) {
return;
}
Room *room = server_.lobby().FindRoom(roomid_);
if (room == NULL) {
return;
}
std::string response;
if (ProcessCommand(chat, &response)) {
if (response.size() != 0) {
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", response.c_str()));
server_.logger().LogSystemMsg(this, response.c_str());
}
return;
}
int minutes_remaining;
const char *pszMsg;
switch (server_.chatlimiter().Submit(this, chat, &minutes_remaining)) {
case knChatLimitResultNewlyLimited:
room->SendChat(NULL, base::Format::ToString("%s has been muted for %d minute(s) due to chat spamming.", name_, minutes_remaining));
break;
case knChatLimitResultLimited:
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("",
base::Format::ToString("Chat blocked. %d minute(s) remaining.",
minutes_remaining)));
break;
case knChatLimitResultNotLimited:
{
if (!room->moderated()) {
// Unmoderated rooms aren't filtered
room->SendChat(this, chat, NULL);
} else {
// Moderated rooms are filtered
const char *filtered;
if (FilterChat(chat, &filtered)) {
room->SendChat(this, filtered, chat);
} else {
room->SendChat(this, filtered, NULL);
}
}
}
break;
}
}
bool Endpoint::FilterChat(const char *chat, const char **result) {
// Users swear vertically, and horizontally. Handle both cases.
// Maintain a chat fragment stream to handle multi-line chat swearing.
// Add the newline, so that filtering can treat multiline appropriately.
std::string fragment = chat_fragment_ + '\n' + chat;
int cch_back = -1;
const char *filtered_frag = server_.badwords().Filter(fragment.c_str(),
&cch_back);
const char *filtered_chat = filtered_frag + chat_fragment_.size() + 1;
int cch_filtered = strlen(filtered_frag);
if (cch_back <= 0) {
chat_fragment_ = "";
} else {
// Take the previous char too, for proper standalone checking
cch_back++;
if (cch_back > cch_filtered) {
cch_back = cch_filtered;
}
if (cch_back > CHAT_FRAGMENT_LENGTH) {
cch_back = CHAT_FRAGMENT_LENGTH;
}
chat_fragment_ = std::string(fragment.c_str() +
cch_filtered - cch_back);
}
*result = filtered_chat;
return strcmp(chat, filtered_chat) != 0;
}
void Endpoint::OnRoomCreateGame(const GameParams& params) {
Room *room = server_.lobby().FindRoom(roomid_);
if (!CheckState(ES_ROOM) || room == NULL) {
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0,
knRoomCreateGameResultFail, NULL));
return;
}
// See if game params are valid
if (!ValidateGameParams(params)) {
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0,
knRoomCreateGameResultFail, NULL));
RLOG() << base::Log::Format("0x%p ", this)
<< "game params invalid";
#ifdef RELEASE_LOGGING
LogGameParams(params);
#endif
return;
}
// Find this level in the cache. Does it exist?
PackId packidUpgrade;
switch (server_.cache().FindInfo(params.packid, params.szLvlFilename,
&info_, &packidUpgrade)) {
case 0:
// Don't know about this pack at all
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0,
knRoomCreateGameResultUnknownMissionPack, NULL));
RLOG() << base::Log::Format("0x%p ", this)
<< "packid not found";
#ifdef RELEASE_LOGGING
LogGameParams(params);
#endif
return;
case 1:
// Know about this pack, and have this version
break;
case 2:
// Know about this pack, but client has wrong version
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0,
knRoomCreateGameResultUpgradeMissionPack, &packidUpgrade));
LOG() << base::Log::Format("0x%p ", this)
<< "client needs packid upgrade";
#ifdef LOGGING
LogGameParams(params);
#endif
return;
}
params_ = params;
// Make sure the room can accept a new game
if (!room->CanAddGame(this)) {
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0,
knRoomCreateGameResultRoomFull, NULL));
RLOG() << base::Log::Format("0x%p ", this)
<< "room full: " << room->name();
return;
}
// Create a game. It'll be joined later. It'll time out and remove
// itself if there are no joiners after awhile.
Game *game = server_.NewGame(this, params_, info_, roomid_);
if (game == NULL) {
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(0,
knRoomCreateGameResultFail, NULL));
return;
}
// Add this game to the room
room->AddGame(this, game);
// Tell the client about the result.
xpump_.Send(XMsgRoomCreateGameResult::ToBuffer(game->id(),
knRoomCreateGameResultSuccess, NULL));
}
void Endpoint::OnRoomCanJoinGame(dword gameid) {
Room *room = server_.lobby().FindRoom(roomid_);
if (!CheckState(ES_ROOM) || room == NULL) {
xpump_.Send(XMsgRoomCanJoinGameResult::ToBuffer(
knGameJoinResultFail));
return;
}
Game *game = room->FindGame(gameid);
if (game == NULL) {
xpump_.Send(XMsgRoomCanJoinGameResult::ToBuffer(
knGameJoinResultGameNotFound));
return;
}
dword result = game->CanAddPlayer(this);
xpump_.Send(XMsgRoomCanJoinGameResult::ToBuffer(result));
}
void Endpoint::OnRoomLeave(dword hint) {
Room *room = server_.lobby().FindRoom(roomid_);
if (!CheckState(ES_ROOM) || room == NULL) {
xpump_.Send(XMsgRoomLeaveResult::ToBuffer(knRoomLeaveResultFail));
return;
}
room->SignalOnDelete.disconnect(this);
room->RemovePlayer(this, hint);
roomid_ = 0;
xpump_.Send(XMsgRoomLeaveResult::ToBuffer(knRoomLeaveResultSuccess));
SetState(ES_READY);
}
void Endpoint::OnGameJoin(dword gameid, dword roomid) {
// Games can be joined after leaving a room, which
// means ES_READY state.
if (!CheckState(ES_READY)) {
xpump_.Send(XMsgGameJoinResult::ToBuffer(knGameJoinResultFail));
return;
}
Room *room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
xpump_.Send(XMsgGameJoinResult::ToBuffer(
knGameJoinResultRoomNotFound));
return;
}
Game *game = room->FindGame(gameid);
if (game == NULL) {
xpump_.Send(XMsgGameJoinResult::ToBuffer(
knGameJoinResultRoomNotFound));
return;
}
dword result = game->CanAddPlayer(this);
if (result != knGameJoinResultSuccess) {
xpump_.Send(XMsgGameJoinResult::ToBuffer(result));
return;
}
xpump_.Send(XMsgGameJoinResult::ToBuffer(knGameJoinResultSuccess));
// Start logging with the game id
xpump_.SetLogId(game->id());
// This doesn't mean this player has "joined" the game. That
// is a separate message / state.
game_ = game;
game_->SignalOnDelete.connect(this, &Endpoint::OnGameDelete);
game->AddPlayer(this);
// Team chat should be turned off by default
teamchat_ = false;
SetState(ES_GAME);
}
std::string Endpoint::GetChatName() {
if (anonymous_) {
return name_;
}
if (!sigvisible_) {
return name_;
}
// Use a character that is illegal in usernames, so people know these tags
// are for real. The \xa0 and " characters are illegal in usernames, but
// double quotes can be simulated with two single quotes, so use \xa0.
if (IsAdmin()) {
return std::string("admin\xa0 ") + name_;
}
if (IsModerator()) {
return std::string("mod\xa0 ") + name_;
}
return name_;
}
bool Endpoint::IsModerator() {
if (IsAdmin()) {
return true;
}
return server_.IsModerator(name_);
}
bool Endpoint::IsAdmin() {
// Admin is sticky across usernames so stealth is possible
if (!admin_) {
admin_ = server_.IsAdmin(name_);
}
return admin_;
}
bool Endpoint::GetArgument(const char *chat, int arg_index, std::string *arg,
const char **rest) {
const char *arg_end = chat;
const char *arg_start = NULL;
for (int index = -1; index != arg_index; index++) {
// Scan past whitespace
const char *pch;
for (pch = arg_end; *pch != 0; pch++) {
if (!isspace(*pch)) {
break;
}
}
arg_start = pch;
// Scan past non-whitespace
for (pch = arg_start; *pch != 0; pch++) {
if (isspace(*pch)) {
break;
}
}
arg_end = pch;
}
if (arg_end <= arg_start) {
return false;
}
*arg = std::string(arg_start, arg_end - arg_start);
if (rest != NULL) {
*rest = arg_end;
while (isspace(**rest)) {
(*rest)++;
}
}
return true;
}
ModeratorCommand Endpoint::GetModeratorCommand(const char *chat) {
std::string arg;
if (!GetArgument(chat, 0, &arg)) {
return kModeratorCommandNone;
}
if (arg.size() == 0 || arg[0] != '/') {
return kModeratorCommandNone;
}
if (strcmp(arg.c_str(), "/mute") == 0) {
return kModeratorCommandMute;
}
if (strcmp(arg.c_str(), "/unmute") == 0) {
return kModeratorCommandUnmute;
}
if (strcmp(arg.c_str(), "/ids") == 0) {
return kModeratorCommandIds;
}
if (strcmp(arg.c_str(), "/ban") == 0) {
return kModeratorCommandBan;
}
if (strcmp(arg.c_str(), "/kick") == 0) {
return kModeratorCommandKick;
}
if (strcmp(arg.c_str(), "/rooms") == 0) {
return kModeratorCommandRooms;
}
if (strcmp(arg.c_str(), "/games") == 0) {
return kModeratorCommandGames;
}
if (strcmp(arg.c_str(), "/clear") == 0) {
return kModeratorCommandClear;
}
if (strcmp(arg.c_str(), "/sig") == 0) {
return kModeratorCommandSig;
}
if (strcmp(arg.c_str(), "/filter") == 0) {
return kModeratorCommandFilter;
}
if (strcmp(arg.c_str(), "/names") == 0) {
return kModeratorCommandNames;
}
if (strcmp(arg.c_str(), "/rules") == 0) {
return kModeratorCommandRules;
}
if (strcmp(arg.c_str(), "/help") == 0) {
return kModeratorCommandHelp;
}
if (strcmp(arg.c_str(), "/see") == 0) {
return kModeratorCommandSee;
}
if (strcmp(arg.c_str(), "/kill") == 0) {
return kModeratorCommandKill;
}
if (strcmp(arg.c_str(), "/perm") == 0) {
return kModeratorCommandPermanent;
}
if (strcmp(arg.c_str(), "/reg") == 0) {
return kModeratorCommandRegisteredOnly;
}
if (strcmp(arg.c_str(), "/w") == 0) {
return kModeratorCommandWhisper;
}
if (strcmp(arg.c_str(), "/m") == 0) {
return kModeratorCommandMods;
}
if (strcmp(arg.c_str(), "/title") == 0) {
return kModeratorCommandTitle;
}
if (strcmp(arg.c_str(), "/anon") == 0) {
return kModeratorCommandAnonBlock;
}
if (strcmp(arg.c_str(), "/swap") == 0) {
return kModeratorCommandSwap;
}
if (strcmp(arg.c_str(), "/flag") == 0) {
return kModeratorCommandFlag;
}
if (strcmp(arg.c_str(), "/ann") == 0) {
return kModeratorCommandAnnouncements;
}
if (strcmp(arg.c_str(), "/t") == 0) {
return kModeratorCommandTeam;
}
return kModeratorCommandUnknown;
}
bool Endpoint::ProcessCommand(const char *chat, std::string *response) {
int id = GetModeratorCommand(chat);
if (id == kModeratorCommandNone) {
if (muted_) {
*response = "You previously muted yourself. To unmute and be able to chat again, type /unmute.";
return true;
}
return false;
}
// A mod command has been entered
server_.logger().LogModCommand(this, chat);
// Default response
*response = "Unknown command. /help for help.";
// Command available to both mods / admins, and regular players
switch (id) {
case kModeratorCommandAnonBlock:
if (state_ == ES_GAME && game_ != NULL &&
game_->ToggleAnonBlock(this)) {
if (game_->anonblock()) {
*response = "You have blocked anons.";
} else {
*response = "You have unblocked anons.";
}
} else {
*response = "This command is not allowed.";
}
return true;
case kModeratorCommandSwap:
// This will get processed by the game.
return false;
case kModeratorCommandTeam:
{
// Player not in game
if (state_ != ES_GAME || game_ == NULL) {
*response = "Command not available.";
return true;
}
if (!game_->CanSendTeamChat(this, true)) {
teamchat_ = false;
return false;
}
std::string dummy;
std::string msg;
const char *rest;
GetArgument(chat, 0, &dummy, &rest);
msg = rest;
if (msg.empty()) {
// Toggle team chat
teamchat_ = !teamchat_;
if (teamchat_) {
this->xpump().Send(XMsgGameReceiveChat::ToBuffer("",
"Team chat has been toggled ON."));
} else {
this->xpump().Send(XMsgGameReceiveChat::ToBuffer("",
"Team chat has been toggled OFF."));
}
} else {
if (!teamchat_) {
// Turn on team chat so OnGameSendChat() will process
// it as a team chat then turn it back off.
teamchat_ = true;
this->OnGameSendChat(msg.c_str());
teamchat_ = false;
} else {
this->OnGameSendChat(msg.c_str());
}
}
*response = "";
return true;
}
case kModeratorCommandFlag:
// Write an entry into the log
{
std::string msg;
GetArgument(chat, 1, &msg);
server().logger().LogMark(this, msg.c_str());
*response = "You wrote a mark in the log.";
}
return true;
}
if (!IsModerator()) {
switch (id) {
case kModeratorCommandMute:
*response = "You have muted yourself. You will not receive chat, or be able to send chat. /unmute to unmute yourself.";
muted_ = true;
break;
case kModeratorCommandUnmute:
*response = "You have unmuted yourself.";
muted_ = false;
break;
case kModeratorCommandHelp:
if (state_ == ES_GAME && game_ != NULL) {
bool anon = game_->IsAnonBlockAllowed(this);
bool swap = game_->IsSwapAllowed(this);
if (anon) {
if (swap) {
*response = "/mute, /unmute, /anon, /swap, /kick, /flag [msg], /t [msg], /help.";
} else {
*response = "/mute, /unmute, /anon, /kick, /flag [msg], /t [msg], /help.";
}
} else if (swap) {
*response = "/mute, /unmute, /swap, /kick, /flag [msg], /t [msg], /help.";
} else {
*response = "/mute, /unmute, /kick, /flag [msg], /t [msg], /help.";
}
} else {
*response = "/mute, /unmute, /kick, /flag [msg], /t [msg], /help.";
}
break;
case kModeratorCommandKick:
*response = "Ouch that hurts.";
break;
}
return true;
}
switch (id) {
case kModeratorCommandNames:
case kModeratorCommandBan:
case kModeratorCommandKill:
case kModeratorCommandFilter:
case kModeratorCommandClear:
case kModeratorCommandPermanent:
case kModeratorCommandRegisteredOnly:
case kModeratorCommandAnnouncements:
if (!IsAdmin()) {
*response = "You need to be an admin to use this command.";
return true;
}
}
RLOG() << "mod: " << name_ << " command: " << chat;
if (state_ == ES_GAME && game_ != NULL) {
RLOG() << "mod: " << name_ << " in game "
<< " server id: " << server_.id()
<< " server_start: " << server_.start_time()
<< " game id: " << game_->id();
}
// Several of the commands take an endpoint id
Endpoint *endpoint = NULL;
std::string arg;
if (GetArgument(chat, 1, &arg)) {
dword chatter_id = 0;
base::Format::ToDword(arg.c_str(), 10, &chatter_id);
endpoint = server_.GetEndpointFromChatterId(chatter_id);
}
char ip[32];
memset(ip, 0, sizeof(ip));
if (endpoint != NULL) {
endpoint->xpump().socket()->GetRemoteAddress().IPAsString(ip,
sizeof(ip));
}
switch (id) {
case kModeratorCommandMute:
if (endpoint != NULL) {
// Check the room the moderator is in. This still allows for
// remote action.
if (!CanModerate(roomid())) {
*response = "Can't moderate in this room.";
break;
}
if (endpoint->IsModerator()) {
*response = "You cannot mute another moderator.";
break;
}
std::string minutes_str;
int minutes = kcMinutesTimeoutDefault;
if (GetArgument(chat, 2, &minutes_str)) {
base::Format::ToInteger(minutes_str.c_str(), 10, &minutes);
if (minutes < 0) {
minutes = kcMinutesTimeoutDefault;
}
if (minutes > kcMinutesTimeoutMaximum) {
minutes = kcMinutesTimeoutMaximum;
}
}
RLOG() << "mod: " << name_ << " muted: " << endpoint->name()
<< " minutes: " << minutes << " ip address: " << ip;
server_.chatlimiter().Mute(endpoint, minutes);
*response = base::Format::ToString("%s has been muted for %d minute(s). Action logged.", endpoint->name(), minutes);
} else {
*response = "Could not find player using that id.";
}
break;
case kModeratorCommandUnmute:
if (endpoint != NULL) {
// Check the room the moderator is in. This still allows for
// remote action.
if (!CanModerate(roomid())) {
*response = "Can't moderate in this room.";
break;
}
RLOG() << "mod: " << name_ << " unmuted: " << endpoint->name()
<< " ip address: " << ip;
server_.chatlimiter().Mute(endpoint, 0);
*response = base::Format::ToString("%s has been unmuted. Action logged.", endpoint->name());
} else {
*response = "Could not find player using that id.";
}
break;
case kModeratorCommandBan:
if (endpoint != NULL) {
// Check the room the moderator is in. This still allows for
// remote action.
if (!CanModerate(roomid())) {
*response = "Can't moderate in this room.";
break;
}
std::string minutes_str;
int minutes = kcMinutesBanTimeoutDefault;
if (endpoint->GetArgument(chat, 2, &minutes_str)) {
base::Format::ToInteger(minutes_str.c_str(), 10, &minutes);
if (minutes < 0) {
minutes = kcMinutesBanTimeoutDefault;
}
if (minutes > kcMinutesBanTimeoutMaximum) {
minutes = kcMinutesBanTimeoutMaximum;
}
}
RLOG() << "mod: " << name_ << " banned: "
<< endpoint->name() << " minutes: " << minutes
<< " ip address: " << ip;
long64 tExpires = base::GetMillisecondCount() +
minutes * 60 * 1000;
endpoint->AddTracker(server_.tracker(), tExpires);
*response = base::Format::ToString("%s has been banned from this server for %d minute(s). Action logged.", endpoint->name(), minutes);
endpoint->Dispose();
} else {
*response = "Could not find player using that id.";
}
break;
case kModeratorCommandRooms:
{
std::vector<dword> roomids = server_.lobby().GetRoomIds();
std::vector<dword>::iterator it = roomids.begin();
for (; it != roomids.end(); it++) {
Room *room = server_.lobby().FindRoom(*it);
if (room != NULL) {
std::ostringstream s;
s << "room id:" << (*it) << " name:" << room->name()
<< " creator:" << room->creator()
<< " id:" << room->creator_id();
if (IsAdmin()) {
s << " password:" << room->password()
<< " ip:" << room->creator_ip();
}
if (state_ == ES_GAME) {
xpump_.Send(XMsgGameReceiveChat::ToBuffer("", s.str().c_str()));
}
if (state_ == ES_ROOM) {
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", s.str().c_str()));
}
server_.logger().LogSystemMsg(this, s.str().c_str());
RLOG() << s.str();
}
}
*response = "";
}
break;
case kModeratorCommandKill:
// Kill a room
{
Room *room = NULL;
std::string roomid_str;
if (!GetArgument(chat, 1, &roomid_str)) {
*response = "Room id not found.";
return true;
}
dword roomid = 0;
base::Format::ToDword(roomid_str.c_str(), 10, &roomid);
room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
*response = "Room not found.";
return true;
}
if (room->Kill()) {
*response = "Room killed. Will be removed within 30 seconds.";
} else {
*response = "Room not killed.";
}
}
break;
case kModeratorCommandGames:
{
Room *room = NULL;
std::string roomid_str;
if (!GetArgument(chat, 1, &roomid_str)) {
*response = "Room id not found.";
return true;
}
dword roomid = 0;
base::Format::ToDword(roomid_str.c_str(), 10, &roomid);
room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
*response = "Room not found.";
return true;
}
std::vector<dword> gameids = room->GetGameIds();
std::vector<dword>::iterator it = gameids.begin();
for (; it != gameids.end(); it++) {
Game *game = room->FindGame(*it);
if (game != NULL) {
std::ostringstream s;
s << "game id:" << (*it) << " creator:" << game->creator()
<< " title:" << game->info().title();
if (state_ == ES_GAME) {
xpump_.Send(XMsgGameReceiveChat::ToBuffer("",
s.str().c_str()));
}
if (state_ == ES_ROOM) {
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("",
s.str().c_str()));
}
server_.logger().LogSystemMsg(this, s.str().c_str());
RLOG() << s.str();
}
}
*response = "";
}
break;
case kModeratorCommandPermanent:
{
Room *room = NULL;
std::string roomid_str;
if (!GetArgument(chat, 1, &roomid_str)) {
*response = "Room id not found.";
return true;
}
dword roomid = 0;
base::Format::ToDword(roomid_str.c_str(), 10, &roomid);
room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
*response = "Room not found.";
return true;
}
bool result;
if (!room->TogglePermanent(&result)) {
*response = "Could not toggle permanent status.";
return true;
}
if (result) {
*response = "Room is now marked permanent.";
} else {
*response = "Room is now marked non-permanent.";
}
}
break;
case kModeratorCommandRegisteredOnly:
{
Room *room = NULL;
std::string roomid_str;
if (!GetArgument(chat, 1, &roomid_str)) {
*response = "Room id not found.";
return true;
}
if (roomid_str == "server" && IsAdmin()) {
bool anonsAllowed = server().AnonsAllowed();
if (anonsAllowed) {
server().SetAnonsAllowed(false);
*response = "The server is now closed to anons.";
} else {
server().SetAnonsAllowed(true);
*response = "The server is now open to everyone.";
}
return true;
}
dword roomid = 0;
base::Format::ToDword(roomid_str.c_str(), 10, &roomid);
room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
*response = "Room not found.";
return true;
}
bool result;
if (!room->ToggleRegistered(&result)) {
*response = "Could not toggle registered status.";
return true;
}
if (result) {
*response = "Room now reg only.";
} else {
*response = "Room now open to all.";
}
}
break;
case kModeratorCommandIds:
{
Room *room = server_.lobby().FindRoom(roomid_);
Game *game = game_;
bool lobby = false;
bool all = false;
dword roomid = roomid_;
std::string roomid_str;
if (GetArgument(chat, 1, &roomid_str)) {
if (strcmp(roomid_str.c_str(), "lobby") == 0) {
lobby = true;
} else if (strcmp(roomid_str.c_str(), "all") == 0) {
all = true;
} else {
base::Format::ToDword(roomid_str.c_str(), 10, &roomid);
room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
*response = "Room not found.";
return true;
}
game = NULL;
std::string gameid_str;
if (GetArgument(chat, 2, &gameid_str)) {
dword gameid = 0;
base::Format::ToDword(gameid_str.c_str(), 10, &gameid);
game = room->FindGame(gameid);
if (game == NULL) {
*response = "Game not found.";
return true;
}
}
}
}
std::vector<std::string> responses;
if (lobby) {
responses = server_.lobby().GetIdsString(this);
if (responses.empty()) {
*response = "There are no players in the lobby.";
return true;
}
} else if (all) {
// Vector for saving temp info
std::vector<std::string> r;
// Add lobby players to responses
r = server_.lobby().GetIdsString(this);
responses.insert(responses.end(), r.begin(), r.end());
// Iterate over all the rooms
std::vector<dword> roomIds = server_.lobby().GetRoomIds();
std::vector<dword>::iterator itRooms = roomIds.begin();
for (; itRooms != roomIds.end(); itRooms++) {
// Add the room's players to responses
room = server_.lobby().FindRoom((*itRooms));
r = room->GetIdsString(this);
responses.insert(responses.end(), r.begin(), r.end());
// Iteratre over all the room's games
std::vector<dword> gameIds = room->GetGameIds();
std::vector<dword>::iterator itGame = gameIds.begin();
for (; itGame != gameIds.end(); itGame++) {
// Add the game's players to responses
game = room->FindGame((*itGame));
r = game->GetIdsString(this);
responses.insert(responses.end(), r.begin(), r.end());
}
}
// This shouldn't happen
if (responses.empty()) {
*response = "There are no players on the server.";
return true;
}
} else {
if (game != NULL) {
responses = game->GetIdsString(this);
if (responses.empty()) {
*response = "There are no players in that game.";
return true;
}
} else if (room != NULL) {
responses = room->GetIdsString(this);
if (responses.empty()) {
*response = "There are no players in that room.";
return true;
}
}
}
std::vector<std::string>::iterator it = responses.begin();
for (; it != responses.end(); it++) {
RLOG() << "mod " << name_ << ": " << *it;
if (state_ == ES_GAME) {
xpump_.Send(XMsgGameReceiveChat::ToBuffer("",
(*it).c_str()));
}
if (state_ == ES_ROOM) {
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("",
(*it).c_str()));
}
server_.logger().LogSystemMsg(this, (*it).c_str());
}
}
*response = "";
break;
case kModeratorCommandClear:
{
server_.lobby().room_tracker().Clear();
server_.chatlimiter().tracker().Clear();
server_.tracker().Clear();
std::vector<dword> roomids = server_.lobby().GetRoomIds();
std::vector<dword>::iterator it = roomids.begin();
for (; it != roomids.end(); it++) {
Room *room = server_.lobby().FindRoom(*it);
if (room != NULL) {
room->tracker().Clear();
}
}
*response = "Cleared.";
}
break;
case kModeratorCommandSig:
sigvisible_ ^= true;
*response = GetChatName();
break;
case kModeratorCommandSee:
seechat_ ^= true;
if (seechat_) {
*response = "You will see unfiltered chat.";
} else {
*response = "You will no longer see unfiltered chat.";
}
break;
case kModeratorCommandFilter:
server_.badwords().toggle();
if (server_.badwords().on()) {
*response = "Word filter is now on.";
} else {
*response = "Word filter is now off.";
}
break;
case kModeratorCommandNames:
if (endpoint != NULL) {
if (endpoint->old_names().size() == 0) {
*response = base::Format::ToString("No other names for %s.",
endpoint->name());
return true;
}
std::vector<std::string>::const_iterator it =
endpoint->old_names().begin();
for (; it != endpoint->old_names().end(); it++) {
const char *s = base::Format::ToString("%s = %s",
endpoint->name(), (*it).c_str());
if (state_ == ES_GAME) {
xpump_.Send(XMsgGameReceiveChat::ToBuffer("", s));
}
if (state_ == ES_ROOM) {
xpump_.Send(XMsgRoomReceiveChat::ToBuffer("", s));
}
server_.logger().LogSystemMsg(this, s);
RLOG() << s;
}
*response = "";
} else {
*response = "Could not find player using that id.";
}
break;
case kModeratorCommandMods:
// Broadcast chat to all mods in every room / game
{
std::string dummy;
const char *rest;
if (GetArgument(chat, 0, &dummy, &rest) && *rest != 0) {
const char *s = base::Format::ToString("%s to mods",
name_);
server_.lobby().SendAdminChat(s, rest, true);
}
}
*response = "";
break;
case kModeratorCommandWhisper:
// Send to specific user id
if (endpoint == NULL) {
*response = "No id or invalid id.";
return true;
} else {
std::string dummy;
const char *rest;
if (GetArgument(chat, 1, &dummy, &rest) && *rest != 0) {
const char *s = base::Format::ToString("%s to %s",
name_, endpoint->name());
if (endpoint->state_ == ES_GAME) {
endpoint->xpump().Send(
XMsgGameReceiveChat::ToBuffer(s, rest));
}
if (endpoint->state_ == ES_ROOM) {
endpoint->xpump().Send(
XMsgRoomReceiveChat::ToBuffer(s, rest));
}
if (endpoint != this) {
if (this->state_ == ES_GAME) {
this->xpump().Send(
XMsgGameReceiveChat::ToBuffer(s, rest));
}
if (this->state_ == ES_ROOM) {
this->xpump().Send(
XMsgRoomReceiveChat::ToBuffer(s, rest));
}
}
server_.logger().LogSystemMsg(this, base::Format::ToString(
"%s: %s", s, rest));
}
*response = "";
return true;
}
break;
case kModeratorCommandTitle:
{
const char *name;
std::string roomid_str;
if (!GetArgument(chat, 1, &roomid_str, &name)) {
*response = "No room id specified.";
return true;
}
dword roomid = 0;
base::Format::ToDword(roomid_str.c_str(), 10, &roomid);
Room *room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
*response = "Room not found.";
return true;
}
if (!room->SetName(server_.badwords().Filter(name))) {
*response = "Failed setting room title.";
return true;
}
*response = "Success";
}
break;
case kModeratorCommandAnnouncements:
{
std::string dummy;
const char *rest;
std::string announcements;
if (GetArgument(chat, 0, &dummy, &rest) && *rest != 0) {
announcements.append(rest);
server().SetAnnouncements(announcements);
const char *s = base::Format::ToString("Announcements have been set to: %s", rest);
*response = s;
} else {
announcements.clear();
server().SetAnnouncements(announcements);
*response = "Announcements have been cleared.";
}
return true;
}
break;
case kModeratorCommandHelp:
if (IsAdmin()) {
if (state_ == ES_GAME) {
*response = "/ids [all] [lobby] [roomid] [gameid], /mute <id> [minutes], /unmute <id>, /ban <id> [minutes], /rooms, /kill <roomid>, /games <roomid>, /names <id>, /w <id>, /m, /title <roomid>, /clear, /filter, /sig, /see, /perm <roomid>, /reg [server] [roomid], /swap, /anon, /rules, /flag [msg], /ann [msg], /t [msg], /help.";
} else {
*response = "/ids [all] [lobby] [roomid] [gameid], /mute <id> [minutes], /unmute <id>, /kick <id> [minutes], /ban <id> [minutes], /rooms, /kill <roomid>, /games <roomid>, /names <id>, /w <id>, /m, /title <roomid>, /clear, /filter, /sig, /see, /perm <roomid>, /reg [server] [roomid], /rules, /flag [msg], /ann [msg], /help.";
}
} else {
if (state_ == ES_GAME) {
*response = "/ids [all] [lobby] [roomid] [gameid], /mute <id> [minutes], /unmute <id>, /rooms, /games <roomid>, /w <id>, /m, /title <roomid>, /sig, /see, /swap, /anon, /rules, /flag [msg], /t [msg], /help.";
} else {
*response = "/ids [all] [lobby] [roomid] [gameid], /mute <id> [minutes], /unmute <id>, /kick <id> [minutes], /rooms, /games <roomid>, /w <id>, /m, /title <roomid>, /sig, /see, /rules, /flag [msg], /help.";
}
}
break;
case kModeratorCommandKick:
case kModeratorCommandRules:
// These command gets processed by the room, or game, since the
// enumeration is different, so pass it through.
return false;
default:
break;
}
return true;
}
void Endpoint::OnGameSendChat(const char *chat) {
if (!CheckState(ES_GAME)) {
return;
}
if (game_ == NULL) {
return;
}
std::string response;
if (ProcessCommand(chat, &response)) {
if (response.size() != 0) {
xpump_.Send(XMsgGameReceiveChat::ToBuffer("", response.c_str()));
server_.logger().LogSystemMsg(this, response.c_str());
}
return;
}
int minutes_remaining;
switch (server_.chatlimiter().Submit(this, chat, &minutes_remaining)) {
case knChatLimitResultNewlyLimited:
game_->SendChat(NULL, base::Format::ToString("%s has been muted for %d minute(s) due to chat spamming.", name_, minutes_remaining));
break;
case knChatLimitResultLimited:
xpump_.Send(XMsgGameReceiveChat::ToBuffer("",
base::Format::ToString("Chat blocked. %d minute(s) remaining.",
minutes_remaining)));
break;
case knChatLimitResultNotLimited:
{
Room *room = server_.lobby().FindRoom(roomid_);
if (room != NULL && !room->moderated()) {
// Unmoderated rooms aren't filtered
if (teamchat_) {
game_->SendTeamChat(this, chat, NULL);
} else {
game_->SendChat(this, chat, NULL);
}
} else {
// Moderated rooms are filtered
const char *filtered;
if (FilterChat(chat, &filtered)) {
if (teamchat_) {
game_->SendTeamChat(this, filtered, chat);
} else {
game_->SendChat(this, filtered, chat);
}
} else {
if (teamchat_) {
game_->SendTeamChat(this, filtered, NULL);
} else {
game_->SendChat(this, filtered, NULL);
}
}
}
}
break;
}
}
void Endpoint::OnGameLeave() {
// Check game_ for NULL first, before state_ is checked,
// since the server can force game_ to NULL legally.
if (game_ == NULL) {
xpump_.Send(XMsgGameLeaveResult::ToBuffer(
knGameLeaveResultNotFound));
LOG() << base::Log::Format("0x%p ", this)
<< "No game to disconnect from. "
<< "Can happen when the server disconnects first";
return;
}
if (!CheckState(ES_GAME)) {
xpump_.Send(XMsgGameLeaveResult::ToBuffer(
knGameLeaveResultFail));
LOG() << base::Log::Format("0x%p ", this)
<< "Not in ES_GAME state.";
return;
}
game_->SignalOnDelete.disconnect(this);
game_->RemovePlayer(this, knDisconnectReasonLeftGame);
game_ = NULL;
teamchat_ = false;
// Stop logging with the game id
xpump_.SetLogId(0);
xpump_.Send(XMsgGameLeaveResult::ToBuffer(knGameLeaveResultSuccess));
// Go back to ES_READY. From here the client will most likely rejoin
// the last room it was in.
SetState(ES_READY);
}
void Endpoint::OnGameNetMessage(NetMessage **ppnm) {
// Check game_ for NULL first, before state_ is checked,
// since the server can force game_ to NULL legally.
if (game_ == NULL) {
LOG() << base::Log::Format("0x%p ", this)
<< "No game for NetMessage. "
<< "Can happen when the server disconnects first";
return;
}
if (!CheckState(ES_GAME)) {
LOG() << base::Log::Format("0x%p ", this) << "Not in ES_GAME!";
return;
}
game_->OnNetMessage(this, *ppnm);
}
void Endpoint::OnGameDelete(Game *game) {
LOG() << "game: " << game->info().title() << " created by: " <<
game->creator();
DropGame(game);
}
void Endpoint::DropGame(Game *game, int reason) {
LOG() << base::Log::Format("0x%p ", this)
<< "Dropping game, reason: " << reason;
Assert(game == game_);
if (game == NULL || game != game_) {
return;
}
game_->RemovePlayer(this, reason);
game_->SignalOnDelete.disconnect(this);
xpump_.Send(XMsgGameKilled::ToBuffer(game_->id()));
game_ = NULL;
SetState(ES_READY);
}
void Endpoint::OnError(int error) {
LOG() << base::Log::Format("0x%p ", this) << error;
Dispose();
}
void Endpoint::OnClose(int error) {
LOG() << base::Log::Format("0x%p ", this) << error;
Dispose();
}
void Endpoint::OnCloseOk() {
LOG() << base::Log::Format("0x%p ", this);
Dispose();
}
void Endpoint::OnUpdateAllies(dword side, dword sidmAllies) {
if (state_ == ES_GAME && game_ != NULL) {
game_->SetAllies((SideMask)side, (SideMask)sidmAllies);
}
}
void Endpoint::OnHeartbeat() {
// If a game is playing, there is a game timer to monitor clients
if (game_ != NULL && game_->playing()) {
return;
}
if (!echo_) {
LOG() << base::Log::Format("0x%p ", this)
<< "client ping timeout";
#ifndef DEV_BUILD
// When a user brings up audio controls (double click home), or
// gets a phone call, the iPhone OS freezes the underlying
// application, so it won't be returning echoes. Make it miss a
// few before killing it, since these cases happen.
missed_++;
if (missed_ == MISSED_ECHO_COUNT) {
Dispose();
return;
}
#endif
}
xpump_.Send(XMsgEcho::ToBuffer());
echo_ = false;
}
void Endpoint::OnEcho() {
// Don't acknowledge echos until it is ok
if (!okecho_) {
return;
}
// Echo received from client
echo_ = true;
missed_ = 0;
}
void Endpoint::UpdateDid(const char *did) {
// The most accurate one is the first one seen
if (did_[0] == 0) {
strncpyz(did_, did, sizeof(did_));
}
}
void Endpoint::AddTracker(Tracker& tracker, long64 tExpires) {
const base::SocketAddress remote = xpump().socket()->GetRemoteAddress();
char szT[64];
remote.IPToString(remote.ip(), szT, sizeof(szT));
tracker.Add(szT, tExpires);
if (did_[0] != 0) {
tracker.Add(did_, tExpires);
}
}
void Endpoint::RemoveTracker(Tracker& tracker) {
const base::SocketAddress remote = xpump().socket()->GetRemoteAddress();
char szT[64];
remote.IPToString(remote.ip(), szT, sizeof(szT));
tracker.Remove(szT);
if (did_[0] != 0) {
tracker.Remove(did_);
}
}
bool Endpoint::FindTracker(Tracker& tracker, long64 *tExpires) {
const base::SocketAddress remote = xpump().socket()->GetRemoteAddress();
char szT[64];
remote.IPToString(remote.ip(), szT, sizeof(szT));
if (tracker.Find(szT, tExpires)) {
return true;
}
if (did_[0] != 0) {
if (tracker.Find(did_, tExpires)) {
return true;
}
}
return false;
}
bool Endpoint::CanModerate(dword roomid) {
if (IsAdmin()) {
return true;
}
Room *room = server_.lobby().FindRoom(roomid);
if (room == NULL) {
return false;
}
return room->moderated();
}
std::string Endpoint::GetRoomName()
{
Room *room = server_.lobby().FindRoom(roomid_);
if (room == NULL) {
return "";
}
return room->name();
}
std::string Endpoint::GetGameName()
{
if (game_ == NULL) {
return "";
}
return game_->info().title();
}
dword Endpoint::gameid() {
return game_ == NULL ? 0 : game_->id();
}
void Endpoint::OnDisconnectSharedAccounts() {
server_.DisconnectSharedAccounts(this, name_);
}
} // namespace wi