initial commit of plugin just filling the tables with data for the playtime display
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
#pragma semicolon 1
|
||||
#define PLUGIN_AUTHOR "jenz"
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
#include <sourcemod>
|
||||
#include <geoip>
|
||||
#include <mapchooser_extended>
|
||||
#include <cstrike>
|
||||
|
||||
Database g_hDatabase;
|
||||
bool g_bInsertedPlayerSession[MAXPLAYERS + 1];
|
||||
bool g_bCorrectServer = false;
|
||||
|
||||
int g_iClientHours[MAXPLAYERS + 1];
|
||||
int g_iClientMinutes[MAXPLAYERS + 1];
|
||||
|
||||
ArrayList g_hPendingVoteChoices;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "UNLOZE_playtime_display",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "collects all info for playtime display",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "www.unloze.com"
|
||||
};
|
||||
|
||||
enum struct VoteChoiceRecord
|
||||
{
|
||||
char SteamId[64];
|
||||
char VoteChoice[64];
|
||||
int VoteWeight;
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
}
|
||||
g_hPendingVoteChoices = new ArrayList(sizeof(VoteChoiceRecord));
|
||||
g_bCorrectServer = GetConVarInt(FindConVar("hostport")) == 27015 ? true : false;
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsValidClient(i) && !IsFakeClient(i))
|
||||
{
|
||||
g_bInsertedPlayerSession[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
|
||||
{
|
||||
if(!db || strlen(error))
|
||||
{
|
||||
LogError("Database error: %s", error);
|
||||
return;
|
||||
}
|
||||
g_hDatabase = db;
|
||||
OnMapStart();
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
if (!g_bCorrectServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_hDatabase)
|
||||
{
|
||||
Database.Connect(SQL_OnDatabaseConnect, "unloze_playtimestats");
|
||||
return;
|
||||
}
|
||||
|
||||
char sMapname[64];
|
||||
GetCurrentMap(sMapname, sizeof(sMapname));
|
||||
insert_map(sMapname);
|
||||
}
|
||||
|
||||
public void insert_map(char sMapname[64])
|
||||
{
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `playtime_display_map_history` (map_name) VALUES('%s')", sMapname);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
if (!g_bCorrectServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "UPDATE `playtime_display_map_history` set map_end_dt = NOW() order by map_id desc limit 1");
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] sSteamID32)
|
||||
{
|
||||
g_bInsertedPlayerSession[client] = false; //the query will take a little to finish for playtime so should be save to set here
|
||||
}
|
||||
|
||||
//forward is called from OnClientAuthorized but also from timer every 1 minute.
|
||||
public void GetPlayerHoursServer(int client, int hours, int minutes)
|
||||
{
|
||||
if (!g_bCorrectServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (g_bInsertedPlayerSession[client])
|
||||
{
|
||||
//update the last session entry every time the forward is called, in case of server crashes this still means we have it up to date.
|
||||
update_play_session(client, hours, minutes);
|
||||
return;
|
||||
}
|
||||
|
||||
g_iClientHours[client] = hours;
|
||||
g_iClientMinutes[client] = minutes;
|
||||
|
||||
//chaining the calls
|
||||
upsert_player(client);
|
||||
}
|
||||
|
||||
public void update_play_session(int client, int client_hours, int client_minutes)
|
||||
{
|
||||
char sSID[64];
|
||||
GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
|
||||
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "UPDATE `playtime_display_sessions` SET session_end_playtime_minutes='%i', session_end_dt=NOW() WHERE steamid = '%s' order by session_id desc limit 1", (client_hours * 60) + client_minutes, sSID);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void upsert_player(int client)
|
||||
{
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
GetClientName(client, sName, sizeof(sName));
|
||||
int size2 = 2 * strlen(sName) + 1;
|
||||
char[] sEscapedName = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sName, sEscapedName, size2 + 1);
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char countryCode[3];
|
||||
GeoipCode2(sIP, countryCode);
|
||||
|
||||
char sSID[64];
|
||||
GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `playtime_display_players` (`steamid`, `current_name`, `current_country`) VALUES('%s', '%s', '%s') ON DUPLICATE KEY UPDATE `current_name` = '%s', `current_country` = '%s', `last_seen` = CURRENT_TIMESTAMP", sSID, sEscapedName, countryCode, sEscapedName, countryCode);
|
||||
g_hDatabase.Query(SQL_FinishedQueryUpsertPlayerCallback, sQuery, GetClientSerial(client), DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQueryUpsertPlayerCallback(Database db, DBResultSet results, const char[] error, int Serial)
|
||||
{
|
||||
if (!db)
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(Serial)) == 0)
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
if (!IsValidClient(client))
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
upsert_name_history(client);
|
||||
}
|
||||
|
||||
public void upsert_name_history(int client)
|
||||
{
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
GetClientName(client, sName, sizeof(sName));
|
||||
int size2 = 2 * strlen(sName) + 1;
|
||||
char[] sEscapedName = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sName, sEscapedName, size2 + 1);
|
||||
|
||||
char sSID[64];
|
||||
GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `playtime_display_name_history` (`steamid`, `player_name`) VALUES('%s', '%s') ON DUPLICATE KEY UPDATE `player_name` = '%s', `last_seen` = CURRENT_TIMESTAMP", sSID, sEscapedName, sEscapedName);
|
||||
g_hDatabase.Query(SQL_FinishedQueryUpsertNameHistoryCallback, sQuery, GetClientSerial(client), DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQueryUpsertNameHistoryCallback(Database db, DBResultSet results, const char[] error, int Serial)
|
||||
{
|
||||
if (!db)
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
int client;
|
||||
if ((client = GetClientFromSerial(Serial)) == 0)
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
if (!IsValidClient(client))
|
||||
{
|
||||
delete results;
|
||||
return;
|
||||
}
|
||||
insert_play_session(client, g_iClientHours[client], g_iClientMinutes[client]);
|
||||
}
|
||||
|
||||
public void insert_play_session(int client, int hours, int minutes)
|
||||
{
|
||||
g_bInsertedPlayerSession[client] = true;
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
GetClientName(client, sName, sizeof(sName));
|
||||
int size2 = 2 * strlen(sName) + 1;
|
||||
char[] sEscapedName = new char[size2 + 1];
|
||||
g_hDatabase.Escape(sName, sEscapedName, size2 + 1);
|
||||
|
||||
char sIP[32];
|
||||
GetClientIP(client, sIP, sizeof(sIP));
|
||||
|
||||
char countryCode[3];
|
||||
GeoipCode2(sIP, countryCode);
|
||||
|
||||
char sSID[64];
|
||||
GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
|
||||
char sQuery[512];
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `playtime_display_sessions` (`steamid`, `country_code`, `player_name`, `session_start_playtime_minutes`) VALUES('%s', '%s','%s','%i')", sSID, countryCode, sEscapedName, (hours * 60) + minutes);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQuery(Database db, DBResultSet results, const char[] error, any data)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error 3: %s", error);
|
||||
}
|
||||
delete results;
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
g_bInsertedPlayerSession[client] = false;
|
||||
g_iClientHours[client] = 0;
|
||||
g_iClientMinutes[client] = 0;
|
||||
}
|
||||
|
||||
public void OnMapVoteEnd(const char[] map)
|
||||
{
|
||||
if (!g_bCorrectServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char sMapname[64];
|
||||
GetCurrentMap(sMapname, sizeof(sMapname));
|
||||
|
||||
char sQuery[512]; //picking max() is usually considered haram
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `playtime_display_map_vote_events` (`map_id`, `current_map`, `result`) VALUES((select max(map_id) from `playtime_display_map_history`), '%s','%s')", sMapname, map);
|
||||
g_hDatabase.Query(SQL_FinishedQueryMapVoteEndCallback, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
|
||||
public void SQL_FinishedQueryMapVoteEndCallback(Database db, DBResultSet results, const char[] error, any data)
|
||||
{
|
||||
if (!db || strlen(error))
|
||||
{
|
||||
LogError("Query error 3: %s", error);
|
||||
}
|
||||
delete results;
|
||||
|
||||
for (int j = 0; j < GetArraySize(g_hPendingVoteChoices); j++)
|
||||
{
|
||||
VoteChoiceRecord entry;
|
||||
GetArrayArray(g_hPendingVoteChoices, j, entry);
|
||||
|
||||
char sQuery[512]; //again, selecting with max() is generally not very good but should be reliable given the context here.
|
||||
Format(sQuery, sizeof(sQuery), "INSERT INTO `playtime_display_map_vote_choices` (`vote_id`, `steamid`, `vote_choice`, `vote_weight`) VALUES ((select max(vote_id) from `playtime_display_map_vote_events`), '%s', '%s','%i')", entry.SteamId, entry.VoteChoice, entry.VoteWeight);
|
||||
g_hDatabase.Query(SQL_FinishedQuery, sQuery, _, DBPrio_Normal);
|
||||
}
|
||||
g_hPendingVoteChoices.Clear();
|
||||
}
|
||||
|
||||
public void OnMapVoteClientResult(char[] steamID, char[] vote_choice, int voteweight)
|
||||
{
|
||||
VoteChoiceRecord entry;
|
||||
strcopy(entry.SteamId, sizeof(entry.SteamId), steamID);
|
||||
strcopy(entry.VoteChoice, sizeof(entry.VoteChoice), vote_choice);
|
||||
entry.VoteWeight = voteweight;
|
||||
g_hPendingVoteChoices.PushArray(entry);
|
||||
}
|
||||
|
||||
stock bool IsValidClient(int client)
|
||||
{
|
||||
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
CREATE TABLE `playtime_display_map_history` (
|
||||
`map_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`map_name` varchar(64) NOT NULL,
|
||||
`map_start_dt` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`map_end_dt` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`map_id`),
|
||||
KEY `idx_map_name` (`map_name`,`map_start_dt`),
|
||||
KEY `idx_start` (`map_start_dt`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `playtime_display_map_vote_events` (
|
||||
`vote_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`map_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`vote_time` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`current_map` varchar(64) DEFAULT NULL,
|
||||
`result` varchar(64) DEFAULT NULL,
|
||||
PRIMARY KEY (`vote_id`),
|
||||
KEY `idx_map_id` (`map_id`),
|
||||
CONSTRAINT `fk_voteevent_map` FOREIGN KEY (`map_id`) REFERENCES `playtime_display_map_history` (`map_id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `playtime_display_players` (
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`current_name` varchar(128) NOT NULL,
|
||||
`current_country` char(2) DEFAULT NULL,
|
||||
`first_seen` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`last_seen` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`steamid`),
|
||||
KEY `idx_country` (`current_country`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
|
||||
|
||||
CREATE TABLE `playtime_display_name_history` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`player_name` varchar(128) NOT NULL,
|
||||
`first_seen` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`last_seen` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_steamid_name` (`steamid`,`player_name`),
|
||||
KEY `idx_player_name` (`player_name`),
|
||||
CONSTRAINT `fk_namehist_steamid` FOREIGN KEY (`steamid`) REFERENCES `playtime_display_players` (`steamid`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=197 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE `playtime_display_map_vote_choices` (
|
||||
`vote_id` int(10) unsigned NOT NULL,
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`vote_choice` varchar(64) NOT NULL,
|
||||
`vote_weight` int(11) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`vote_id`,`steamid`),
|
||||
KEY `idx_vote_choice` (`vote_choice`),
|
||||
KEY `fk_votechoice_steamid` (`steamid`),
|
||||
CONSTRAINT `fk_votechoice_event` FOREIGN KEY (`vote_id`) REFERENCES `playtime_display_map_vote_events` (`vote_id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_votechoice_steamid` FOREIGN KEY (`steamid`) REFERENCES `playtime_display_players` (`steamid`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- unloze_playtimestats.playtime_display_sessions definition
|
||||
|
||||
CREATE TABLE `playtime_display_sessions` (
|
||||
`session_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`country_code` char(2) DEFAULT NULL,
|
||||
`player_name` varchar(128) NOT NULL,
|
||||
`session_start_playtime_minutes` bigint(20) NOT NULL,
|
||||
`session_end_playtime_minutes` bigint(20) DEFAULT NULL,
|
||||
`session_start_dt` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`session_end_dt` datetime DEFAULT NULL,
|
||||
`session_active_minutes` bigint(20) GENERATED ALWAYS AS (`session_end_playtime_minutes` - `session_start_playtime_minutes`) STORED,
|
||||
`session_start_dow` tinyint(4) GENERATED ALWAYS AS (dayofweek(`session_start_dt`)) STORED,
|
||||
`session_start_tod` time GENERATED ALWAYS AS (cast(`session_start_dt` as time)) STORED,
|
||||
PRIMARY KEY (`session_id`),
|
||||
KEY `idx_steamid_start` (`steamid`,`session_start_dt`),
|
||||
KEY `idx_interval` (`session_start_dt`,`session_end_dt`),
|
||||
KEY `idx_dow_tod` (`session_start_dow`,`session_start_tod`),
|
||||
KEY `idx_country` (`country_code`),
|
||||
CONSTRAINT `fk_session_steamid` FOREIGN KEY (`steamid`) REFERENCES `playtime_display_players` (`steamid`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=215 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
Reference in New Issue
Block a user