Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
12 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include <stdio.h>
#include "PlayerPanel.h"
#include "PlayerContextMenu.h"
#include "PlayerListCompare.h"
#include "DialogAddBan.h"
#include <vgui/ISystem.h>
#include <vgui/ISurface.h>
#include <vgui/ILocalize.h>
#include <vgui/IVGui.h>
#include <vgui/KeyCode.h>
#include <KeyValues.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/ListPanel.h>
#include <vgui_controls/QueryBox.h>
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CPlayerPanel::CPlayerPanel(vgui::Panel *parent, const char *name) : vgui::PropertyPage(parent, name)
{
m_pPlayerListPanel = new vgui::ListPanel(this, "Players list");
m_pPlayerListPanel->AddColumnHeader(0, "name", "#Player_Panel_Name", 200, ListPanel::COLUMN_RESIZEWITHWINDOW );
m_pPlayerListPanel->AddColumnHeader(1, "authid", "#Player_Panel_ID", 100);
m_pPlayerListPanel->AddColumnHeader(2, "ping", "#Player_Panel_Ping", 50);
m_pPlayerListPanel->AddColumnHeader(3, "loss", "#Player_Panel_Loss", 50);
m_pPlayerListPanel->AddColumnHeader(4, "frags", "#Player_Panel_Frags", 50);
m_pPlayerListPanel->AddColumnHeader(5, "time", "#Player_Panel_Time", 75);
/*
// TODO: update me!!
m_pPlayerListPanel->SetSortFunc(0, PlayerNameCompare);
m_pPlayerListPanel->SetSortFunc(1, PlayerAuthCompare);
m_pPlayerListPanel->SetSortFunc(2, PlayerPingCompare);
m_pPlayerListPanel->SetSortFunc(3, PlayerLossCompare);
m_pPlayerListPanel->SetSortFunc(4, PlayerFragsCompare);
*/
m_pPlayerListPanel->SetSortFunc(5, PlayerTimeCompare);
m_pPlayerListPanel->SetEmptyListText("#Player_Panel_No_Players");
// Sort by ping time by default
m_pPlayerListPanel->SetSortColumn(0);
m_pKickButton = new Button(this, "Kick", "#Player_Panel_Kick");
m_pBanButton = new Button(this, "Ban", "#Player_Panel_Ban");
m_pPlayerContextMenu = new CPlayerContextMenu(this);
m_pPlayerContextMenu->SetVisible(false);
LoadControlSettings("Admin/PlayerPanel.res", "PLATFORM");
m_pKickButton->SetCommand(new KeyValues("KickPlayer"));
m_pBanButton->SetCommand(new KeyValues("BanPlayer"));
OnItemSelected(); // disable the buttons
m_flUpdateTime = 0.0f;
RemoteServer().AddServerMessageHandler(this, "UpdatePlayers");
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CPlayerPanel::~CPlayerPanel()
{
RemoteServer().RemoveServerDataResponseTarget(this);
}
//-----------------------------------------------------------------------------
// Purpose: Rebuilds player info
//-----------------------------------------------------------------------------
void CPlayerPanel::OnResetData()
{
RemoteServer().RequestValue(this, "playerlist");
// update once every minute
m_flUpdateTime = (float)system()->GetFrameTime() + (60 * 1.0f);
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if we should update player info
//-----------------------------------------------------------------------------
void CPlayerPanel::OnThink()
{
if (m_flUpdateTime < vgui::system()->GetFrameTime())
{
OnResetData();
}
}
//-----------------------------------------------------------------------------
// Purpose: Refreshes page if user hits F5
//-----------------------------------------------------------------------------
void CPlayerPanel::OnKeyCodeTyped(vgui::KeyCode code)
{
if (code == KEY_F5)
{
OnResetData();
}
else
{
BaseClass::OnKeyCodeTyped(code);
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns data on the player who is currently selected in the list
//-----------------------------------------------------------------------------
KeyValues *CPlayerPanel::GetSelected()
{
return m_pPlayerListPanel->GetItem(m_pPlayerListPanel->GetSelectedItem(0));
}
static const char *FormatSeconds( int seconds )
{
static char string[64];
int hours = 0;
int minutes = seconds / 60;
if ( minutes > 0 )
{
seconds -= (minutes * 60);
hours = minutes / 60;
if ( hours > 0 )
{
minutes -= (hours * 60);
}
}
if ( hours > 0 )
{
Q_snprintf( string, sizeof(string), "%2i:%02i:%02i", hours, minutes, seconds );
}
else
{
Q_snprintf( string, sizeof(string), "%02i:%02i", minutes, seconds );
}
return string;
}
//-----------------------------------------------------------------------------
// Purpose: called when the server has returned a requested value
//-----------------------------------------------------------------------------
void CPlayerPanel::OnServerDataResponse(const char *value, const char *response)
{
if (!stricmp(value, "UpdatePlayers"))
{
// server has indicated a change, force an update
m_flUpdateTime = 0.0f;
}
else if (!stricmp(value, "playerlist"))
{
// new list of players
m_pPlayerListPanel->DeleteAllItems();
// parse response
const char *parse = response;
while (parse && *parse)
{
// first should be the size of the player name
char name[64];
name[0] = '\0';
char authID[64];
authID[0] = '\0';
char netAdr[32];
netAdr[0] = '\0';
int ping = 0, packetLoss = 0, frags = 0, connectTime = 0;
ivgui()->DPrintf2("orig: %s\n", parse);
// parse out the name, which is quoted
Assert(*parse == '\"');
if (*parse != '\"')
break;
++parse; // move past start quote
int pos = 0;
while (*parse && *parse != '\"')
{
name[pos++] = *parse;
parse++;
}
name[pos] = 0;
parse++; // move past end quote
if (6 != sscanf(parse, " %s %s %d %d %d %d\n", authID, netAdr, &ping, &packetLoss, &frags, &connectTime))
break;
const char *timeStr = FormatSeconds(connectTime);
ivgui()->DPrintf2("pars: \"%s\" %s %s %d %d %d %s\n", name, authID, netAdr, ping, packetLoss, frags, timeStr);
// add to list
KeyValues *player = new KeyValues("Player");
player->SetString("name", name);
player->SetString("authID", authID);
player->SetString("netAdr", netAdr);
player->SetInt("ping", ping);
player->SetInt("loss", packetLoss);
player->SetInt("frags", frags);
player->SetString("time", timeStr);
m_pPlayerListPanel->AddItem(player, 0, false, false);
// move to next line
parse = strchr(parse, '\n');
if (parse)
{
parse++;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Buttom command handlers
//-----------------------------------------------------------------------------
void CPlayerPanel::OnCommand(const char *command)
{
BaseClass::OnCommand(command);
}
//-----------------------------------------------------------------------------
// Purpose: Handles the UI for kicking a player from the server
//-----------------------------------------------------------------------------
void CPlayerPanel::OnKickButtonPressed()
{
if (m_pPlayerListPanel->GetSelectedItemsCount() < 1)
return;
// open a message box to ask the user if they want to follow through on this
QueryBox *box;
if (m_pPlayerListPanel->GetSelectedItemsCount() > 1)
{
box = new QueryBox("#Kick_Multiple_Players_Title", "#Kick_Multiple_Players_Question");
}
else
{
// show the player name in the message box if only one player is being booted
KeyValues *kv = m_pPlayerListPanel->GetItem(m_pPlayerListPanel->GetSelectedItem(0));
Assert(kv != NULL);
if (!kv)
return;
wchar_t playerName[64];
g_pVGuiLocalize->ConvertANSIToUnicode( kv->GetString("name"), playerName, sizeof(playerName) );
wchar_t msg[512];
g_pVGuiLocalize->ConstructString( msg, sizeof(msg), g_pVGuiLocalize->Find("Kick_Single_Player_Question"), 1, playerName );
box = new QueryBox(g_pVGuiLocalize->Find("#Kick_Single_Player_Title"), msg);
}
box->AddActionSignalTarget(this);
box->SetOKCommand(new KeyValues("KickSelectedPlayers"));
box->ShowWindow();
}
//-----------------------------------------------------------------------------
// Purpose: Prompts a user to be banned
//-----------------------------------------------------------------------------
void CPlayerPanel::OnBanButtonPressed()
{
if (m_pPlayerListPanel->GetSelectedItemsCount() != 1)
return;
// open a message box to ask the user if they want to follow through on this
KeyValues *kv = m_pPlayerListPanel->GetItem(m_pPlayerListPanel->GetSelectedItem(0));
Assert(kv != NULL);
if (!kv)
return;
const char *player = kv->GetString("name");
const char *authid = kv->GetString("authid");
const char *netAdr = kv->GetString("netAdr");
char buf[64];
if ( !strcmp( authid, "UNKNOWN" ) )
{
int s1, s2, s3, s4;
if (4 == sscanf(netAdr, "%d.%d.%d.%d", &s1, &s2, &s3, &s4))
{
Q_snprintf( buf, sizeof(buf), "%d.%d.%d.%d", s1, s2, s3, s4 );
authid = buf;
}
}
CDialogAddBan *box = new CDialogAddBan(this);
box->AddActionSignalTarget(this);
box->Activate("addban", player, authid);
}
//-----------------------------------------------------------------------------
// Purpose: Kicks all the players currently selected
//-----------------------------------------------------------------------------
void CPlayerPanel::KickSelectedPlayers()
{
for (int i = 0; i < m_pPlayerListPanel->GetSelectedItemsCount(); i++)
{
// get the player info
int row = m_pPlayerListPanel->GetSelectedItem(i);
KeyValues *pl = m_pPlayerListPanel->GetItem(row);
if (!pl)
continue;
// kick 'em
char cmd[512];
_snprintf(cmd, sizeof(cmd), "kick \"%s\"", pl->GetString("name"));
RemoteServer().SendCommand(cmd);
}
m_pPlayerListPanel->ClearSelectedItems();
m_pKickButton->SetEnabled(false);
m_pBanButton->SetEnabled(false);
OnResetData();
}
//-----------------------------------------------------------------------------
// Purpose: Adds a new ban
//-----------------------------------------------------------------------------
void CPlayerPanel::AddBanByID(const char *id, const char *newtime)
{
Assert(id && *id);
if (!id || !*id)
return;
// if the newtime string is not valid, then set it to 0 (permanent ban)
if (!newtime || atof(newtime) < 0.001)
{
newtime = "0";
}
const char *banCmd = "banid";
const char *saveCmd = "writeip";
int s1, s2, s3, s4;
if (4 == sscanf(id, "%d.%d.%d.%d", &s1, &s2, &s3, &s4))
{
banCmd = "addip";
saveCmd = "writeid";
}
// send down the ban command
char cmd[512];
_snprintf(cmd, sizeof(cmd) -1, "%s %s %s\n", banCmd, newtime, id);
RemoteServer().SendCommand(cmd);
// force the file to update
RemoteServer().SendCommand(saveCmd);
// refresh
OnResetData();
}
//-----------------------------------------------------------------------------
// Purpose: opens context menu (user right clicked on a server)
//-----------------------------------------------------------------------------
void CPlayerPanel::OnOpenContextMenu(int itemID)
{
/* CODE DISABLED UNTIL VERIFIED AS WORKING
// show the player menus only if the cursor is over it
if (IsCursorOver() && m_pPlayerListPanel->GetNumSelectedRows())
{
// get the server
unsigned int playerID = m_pPlayerListPanel->GetDataItem(m_pPlayerListPanel->GetSelectedRow(0))->userData;
// activate context menu
m_pPlayerContextMenu->ShowMenu(this, playerID);
}
*/
}
//-----------------------------------------------------------------------------
// Purpose: called when an item on the list panel is selected.
//-----------------------------------------------------------------------------
void CPlayerPanel::OnItemSelected()
{
bool state = true;
if ( m_pPlayerListPanel->GetSelectedItemsCount() == 0 )
{
state = false;
}
m_pKickButton->SetEnabled(state);
m_pBanButton->SetEnabled(state);
if ( m_pPlayerListPanel->GetSelectedItemsCount() > 1 )
{
m_pBanButton->SetEnabled(false);
}
}