new String:g_current_map[64];
new Handle:db = INVALID_HANDLE;
new Handle:g_cvars[CVAR_NUM_CVARS];
new bool:g_SQLite = false;
new Handle:g_admin_menu = INVALID_HANDLE;
new String:g_table_name[32];
new g_lastRateTime[MAXPLAYERS+1];
new bool:g_dismiss = false;
enum MapRatingOrigin
{
MRO_PlayerInitiated,
MRO_ViewRatingsByRating,
MRO_ViewRatingsByMap
};
new MapRatingOrigin:g_maprating_origins[MAXPLAYERS+1];
public Plugin:myinfo =
{
name = "Map Rate",
author = "Ryan \"FLOOR_MASTER\" Mannion & Chefe",
description = "Allow players to rate the current map and view the map's average rating.",
version = MR_VERSION,
url = "http://forums.alliedmods.net/showthread.php?p=1530651"
}
public OnPluginStart()
{
LoadTranslations("maprate.phrases");
g_cvars[CVAR_DB_CONFIG] = CreateConVar("sm_maprate_db_config", "default", "Database configuration to use for Map Rate plugin", FCVAR_PLUGIN);
g_cvars[CVAR_VERSION] = CreateConVar("sm_maprate_version", MR_VERSION, "Map Rate Version", FCVAR_PLUGIN|FCVAR_REPLICATED|FCVAR_NOTIFY);
g_cvars[CVAR_AUTORATE_TIME] = CreateConVar("sm_maprate_autorate_time", "0", "If non-zero, automatically asks dead players to rate map after they have played the map for this number of seconds", FCVAR_PLUGIN);
g_cvars[CVAR_ALLOW_REVOTE] = CreateConVar("sm_maprate_allow_revote", "1", "If non-zero, allow a user to override his/her previous vote on a map", FCVAR_PLUGIN);
g_cvars[CVAR_TABLE] = CreateConVar("sm_maprate_table", "map_ratings", "The name of the database table to use", FCVAR_PLUGIN);
g_cvars[CVAR_AUTORATE_DELAY] = CreateConVar("sm_maprate_autorate_delay", "5", "After a player dies, wait this number of seconds before asking to rate if maprate_autorate_tie is non-zero", FCVAR_PLUGIN);
g_cvars[CVAR_DISMISS] = CreateConVar("sm_maprate_dismiss", "0", "If non-zero, the first voting option will be \"Dismiss\"", FCVAR_PLUGIN);
g_cvars[CVAR_RESULTS] = CreateConVar("sm_maprate_autoresults", "1", "If non-zero, the results graph will automatically be displayed when a player rates a map", FCVAR_PLUGIN);
new Handle:top_menu;
if (LibraryExists("adminmenu") && ((top_menu = GetAdminTopMenu()) != INVALID_HANDLE))
{
OnAdminMenuReady(top_menu);
}
}
public OnConfigsExecuted()
{
GetConVarString(g_cvars[CVAR_TABLE], g_table_name, sizeof(g_table_name));
g_dismiss = GetConVarBool(g_cvars[CVAR_DISMISS]);
PrintToServer("[MAPRATE] Using table name \"%s\"", g_table_name);
if (!ConnectDB())
{
LogError("FATAL: An error occurred while connecting to the database.");
SetFailState("An error occurred while connecting to the database.");
}
}
public OnLibraryRemoved(const String:name[])
{
if (StrEqual(name, "adminmenu"))
{
g_admin_menu = INVALID_HANDLE;
}
}
public OnAdminMenuReady(Handle:topmenu)
{
if (topmenu == g_admin_menu)
{
return;
}
g_admin_menu = topmenu;
new TopMenuObject:server_commands = FindTopMenuCategory(g_admin_menu, ADMINMENU_SERVERCOMMANDS);
if (server_commands == INVALID_TOPMENUOBJECT)
{
return;
}
public OnMapEnd()
{
if (db != INVALID_HANDLE)
{
CloseHandle(db);
db = INVALID_HANDLE;
}
}
public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
new autorateTime = GetConVarInt(g_cvars[CVAR_AUTORATE_TIME]);
if (IsClientInGame(client) && !IsFakeClient(client) && autorateTime && g_lastRateTime[client] + autorateTime < GetTime())
{
new Float:time = GetConVarFloat(g_cvars[CVAR_AUTORATE_DELAY]);
if (time >= 0.0)
{
decl String:steamid[24];
GetClientAuthString(client, steamid, sizeof(steamid));
new Handle:dp = CreateDataPack();
WritePackCell(dp, client);
WritePackString(dp, steamid);
CreateTimer(time, Timer_AutoRateClient, dp);
}
}
return Plugin_Continue;
}
public Action:Timer_AutoRateClient(Handle:timer, any:dp)
{
decl String:steamid_orig[24];
decl String:steamid[24];
ResetPack(dp);
new client = ReadPackCell(dp);
ReadPackString(dp, steamid_orig, sizeof(steamid_orig));
CloseHandle(dp);
g_lastRateTime[client] = GetTime();
if (IsClientConnected(client))
{
GetClientAuthString(client, steamid, sizeof(steamid));
if (!strcmp(steamid, steamid_orig))
{
InitiateRate(client, g_current_map, false);
}
}
}
/* Verify that the configuration is defined in databases.cfg */
if (!SQL_CheckConfig(db_config))
{
LogError("Database configuration \"%s\" does not exist", db_config);
return false;
}
/* Establish a connection */
new String:error[256];
db = SQL_Connect(db_config, true, error, sizeof(error));
if (db == INVALID_HANDLE)
{
LogError("Error establishing database connection: %s", error);
return false;
}
decl String:query[256];
Format(query, sizeof(query), "SELECT map, AVG(rating) AS rating, COUNT(*) AS ratings FROM %s GROUP BY map ORDER BY rating DESC", g_table_name);
SQL_TQuery(db, T_CreateMenuRatingsResults, query, dp);
}
stock ViewRatingsByMap(client)
{
new Handle:dp = CreateDataPack();
WritePackCell(dp, client);
decl String:text[64];
Format(text, sizeof(text), "%T", "Ordered by Map Name Title", client);
WritePackString(dp, text);
g_maprating_origins[client] = MRO_ViewRatingsByMap;
decl String:query[256];
Format(query, sizeof(query), "SELECT map, AVG(rating) AS rating, COUNT(*) AS ratings FROM %s GROUP BY map ORDER BY map", g_table_name);
SQL_TQuery(db, T_CreateMenuRatingsResults, query, dp);
}
public T_CreateMenuRatingsResults(Handle:owner, Handle:hndl, const String:error[], any:data)
{
if (hndl == INVALID_HANDLE)
{
LogError("Query failed! %s", error);
PrintToChat(data, "A database error occurred. Please try again later.");
return;
}
public Menu_ViewMapRatings(Handle:menu, MenuAction:action, param1, param2)
{
new client = param1;
switch (action)
{
case MenuAction_Select:
{
decl String:map[MAXLEN_MAP];
if (GetMenuItem(menu, param2, map, sizeof(map)))
{
GetMapRating(client, map);
}
}
case MenuAction_Cancel:
{
switch (param2)
{
case MenuCancel_ExitBack:
{
CreateMenuRatings(client);
}
}
}
case MenuAction_End: {
CloseHandle(menu);
}
}
}
public T_CreateMenuRating(Handle:owner, Handle:hndl, const String:error[], any:data)
{
ResetPack(data);
new client = ReadPackCell(data);
if (hndl == INVALID_HANDLE)
{
LogError("Query failed! %s", error);
CloseHandle(data);
PrintToChat(client, "A database error occurred. Please try again later.");
return;
}
decl String:map[MAXLEN_MAP];
ReadPackString(data, map, sizeof(map));
new my_rating = ReadPackCell(data);
CloseHandle(data);
/* This is kind of ugly */
new rating = 0;
new arr_ratings[5] = {0, 0, 0, 0, 0};
new ratings = 0;
new total_ratings = 0;
new total_rating = 0;
decl String:menu_item[64];
decl String:query[256];
Format(query, sizeof(query), "SELECT rating, COUNT(*) FROM %s WHERE map = '%s' GROUP BY rating ORDER BY rating DESC", g_table_name, map);
SQL_TQuery(db, T_CreateMenuRating, query, dp);
}
public T_CreateMenuRate(Handle:owner, Handle:hndl, const String:error[], any:data)
{
ResetPack(data);
new client = ReadPackCell(data);
if (hndl == INVALID_HANDLE)
{
LogError("Query failed! %s", error);
if (IsClientConnected(client))
{
PrintToChat(client, "%t", "Database Error");
}
CloseHandle(data);
return;
}
decl String:map[MAXLEN_MAP];
ReadPackString(data, map, sizeof(map));
new bool:voluntary = bool:ReadPackCell(data);
new initiator = ReadPackCell(data);
new rating = 0;
CloseHandle(data);
new allow_revote = GetConVarInt(g_cvars[CVAR_ALLOW_REVOTE]);
/* The player has rated this map before */
if (SQL_GetRowCount(hndl) == 1)
{
SQL_FetchRow(hndl);
rating = SQL_FetchInt(hndl, 0);
/* If the user didn't initiate the maprate, just ignore the request */
if (!voluntary)
{
return;
}
/* Deny rerating if the applicable cvar is set */
else if (!allow_revote)
{
PrintToChat(client, "\03%t", "Already Rated", rating);
return;
}
}
CloseHandle(hndl);
decl String:title[256];
/* If an initiator was set, then this map rating request was initiated by
* an admin. We'll specify who in the map rate panel title. */
if (initiator)
{
decl String:initiator_name[32];
GetClientName(initiator, initiator_name, sizeof(initiator_name));
Format(title, sizeof(title), "%T", "Everyone Rate Title",
client, initiator_name, g_current_map);
}
else
{
Format(title, sizeof(title), "%T", "Rate Map Title", client, map);
}
/* If the player already rated this map, show the previous rating. */
if (rating)
{
Format(title, sizeof(title), "%s\n%T", title, "Previous Rating", client, rating);
}
/* Build the menu panel */
new Handle:menu = CreateMenu(Menu_Rate);
SetMenuTitle(menu, title);
new String:g_current_map[64];
new Handle:db = INVALID_HANDLE;
new Handle:g_cvars[CVAR_NUM_CVARS];
new bool:g_SQLite = false;
new Handle:g_admin_menu = INVALID_HANDLE;
new String:g_table_name[32];
new g_lastRateTime[MAXPLAYERS+1];
new bool:g_dismiss = false;
enum MapRatingOrigin
{
MRO_PlayerInitiated,
MRO_ViewRatingsByRating,
MRO_ViewRatingsByMap
};
new MapRatingOrigin:g_maprating_origins[MAXPLAYERS+1];
public Plugin:myinfo =
{
name = "Map Rate",
author = "Ryan \"FLOOR_MASTER\" Mannion & Chefe",
description = "Allow players to rate the current map and view the map's average rating.",
version = MR_VERSION,
url = "http://forums.alliedmods.net/showthread.php?p=1530651"
}
public OnPluginStart()
{
LoadTranslations("maprate.phrases");
g_cvars[CVAR_DB_CONFIG] = CreateConVar("sm_maprate_db_config", "default", "Database configuration to use for Map Rate plugin", FCVAR_PLUGIN);
g_cvars[CVAR_VERSION] = CreateConVar("sm_maprate_version", MR_VERSION, "Map Rate Version", FCVAR_PLUGIN|FCVAR_REPLICATED|FCVAR_NOTIFY);
g_cvars[CVAR_AUTORATE_TIME] = CreateConVar("sm_maprate_autorate_time", "0", "If non-zero, automatically asks players to rate map after they have played the map for this number of seconds", FCVAR_PLUGIN);
g_cvars[CVAR_ALLOW_REVOTE] = CreateConVar("sm_maprate_allow_revote", "1", "If non-zero, allow a user to override his/her previous vote on a map", FCVAR_PLUGIN);
g_cvars[CVAR_TABLE] = CreateConVar("sm_maprate_table", "map_ratings", "The name of the database table to use", FCVAR_PLUGIN);
g_cvars[CVAR_AUTORATE_DELAY] = CreateConVar("sm_maprate_autorate_delay", "5", "After round start, wait this number of seconds before asking to rate if maprate_autorate_time is non-zero", FCVAR_PLUGIN);
g_cvars[CVAR_DISMISS] = CreateConVar("sm_maprate_dismiss", "0", "If non-zero, the first voting option will be \"Dismiss\"", FCVAR_PLUGIN);
g_cvars[CVAR_RESULTS] = CreateConVar("sm_maprate_autoresults", "1", "If non-zero, the results graph will automatically be displayed when a player rates a map", FCVAR_PLUGIN);
new Handle:top_menu;
if (LibraryExists("adminmenu") && ((top_menu = GetAdminTopMenu()) != INVALID_HANDLE))
{
OnAdminMenuReady(top_menu);
}
}
public OnConfigsExecuted()
{
GetConVarString(g_cvars[CVAR_TABLE], g_table_name, sizeof(g_table_name));
g_dismiss = GetConVarBool(g_cvars[CVAR_DISMISS]);
PrintToServer("[MAPRATE] Using table name \"%s\"", g_table_name);
if (!ConnectDB())
{
LogError("FATAL: An error occurred while connecting to the database.");
SetFailState("An error occurred while connecting to the database.");
}
}
public OnLibraryRemoved(const String:name[])
{
if (StrEqual(name, "adminmenu"))
{
g_admin_menu = INVALID_HANDLE;
}
}
public OnAdminMenuReady(Handle:topmenu)
{
if (topmenu == g_admin_menu)
{
return;
}
g_admin_menu = topmenu;
new TopMenuObject:server_commands = FindTopMenuCategory(g_admin_menu, ADMINMENU_SERVERCOMMANDS);
if (server_commands == INVALID_TOPMENUOBJECT)
{
return;
}
public OnMapEnd()
{
if (db != INVALID_HANDLE)
{
CloseHandle(db);
db = INVALID_HANDLE;
}
}
public Action:Event_PlayerTeam(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
new autorateTime = GetConVarInt(g_cvars[CVAR_AUTORATE_TIME]);
if (IsClientInGame(client) && !IsFakeClient(client) && autorateTime && g_lastRateTime[client] + autorateTime < GetTime())
{
new Float:time = GetConVarFloat(g_cvars[CVAR_AUTORATE_DELAY]);
if (time >= 0.0)
{
decl String:steamid[24];
GetClientAuthString(client, steamid, sizeof(steamid));
new Handle:dp = CreateDataPack();
WritePackCell(dp, client);
WritePackString(dp, steamid);
CreateTimer(time, Timer_AutoRateClient, dp);
}
}
return Plugin_Continue;
}
public Action:Timer_AutoRateClient(Handle:timer, any:dp)
{
decl String:steamid_orig[24];
decl String:steamid[24];
ResetPack(dp);
new client = ReadPackCell(dp);
ReadPackString(dp, steamid_orig, sizeof(steamid_orig));
CloseHandle(dp);
g_lastRateTime[client] = GetTime();
if (IsClientConnected(client))
{
GetClientAuthString(client, steamid, sizeof(steamid));
if (!strcmp(steamid, steamid_orig))
{
InitiateRate(client, g_current_map, false);
}
}
}
/* Verify that the configuration is defined in databases.cfg */
if (!SQL_CheckConfig(db_config))
{
LogError("Database configuration \"%s\" does not exist", db_config);
return false;
}
/* Establish a connection */
new String:error[256];
db = SQL_Connect(db_config, true, error, sizeof(error));
if (db == INVALID_HANDLE)
{
LogError("Error establishing database connection: %s", error);
return false;
}
decl String:query[256];
Format(query, sizeof(query), "SELECT map, AVG(rating) AS rating, COUNT(*) AS ratings FROM %s GROUP BY map ORDER BY rating DESC", g_table_name);
SQL_TQuery(db, T_CreateMenuRatingsResults, query, dp);
}
stock ViewRatingsByMap(client)
{
new Handle:dp = CreateDataPack();
WritePackCell(dp, client);
decl String:text[64];
Format(text, sizeof(text), "%T", "Ordered by Map Name Title", client);
WritePackString(dp, text);
g_maprating_origins[client] = MRO_ViewRatingsByMap;
decl String:query[256];
Format(query, sizeof(query), "SELECT map, AVG(rating) AS rating, COUNT(*) AS ratings FROM %s GROUP BY map ORDER BY map", g_table_name);
SQL_TQuery(db, T_CreateMenuRatingsResults, query, dp);
}
public T_CreateMenuRatingsResults(Handle:owner, Handle:hndl, const String:error[], any:data)
{
if (hndl == INVALID_HANDLE)
{
LogError("Query failed! %s", error);
PrintToChat(data, "A database error occurred. Please try again later.");
return;
}
public Menu_ViewMapRatings(Handle:menu, MenuAction:action, param1, param2)
{
new client = param1;
switch (action)
{
case MenuAction_Select:
{
decl String:map[MAXLEN_MAP];
if (GetMenuItem(menu, param2, map, sizeof(map)))
{
GetMapRating(client, map);
}
}
case MenuAction_Cancel:
{
switch (param2)
{
case MenuCancel_ExitBack:
{
CreateMenuRatings(client);
}
}
}
case MenuAction_End: {
CloseHandle(menu);
}
}
}
public T_CreateMenuRating(Handle:owner, Handle:hndl, const String:error[], any:data)
{
ResetPack(data);
new client = ReadPackCell(data);
if (hndl == INVALID_HANDLE)
{
LogError("Query failed! %s", error);
CloseHandle(data);
PrintToChat(client, "A database error occurred. Please try again later.");
return;
}
decl String:map[MAXLEN_MAP];
ReadPackString(data, map, sizeof(map));
new my_rating = ReadPackCell(data);
CloseHandle(data);
/* This is kind of ugly */
new rating = 0;
new arr_ratings[5] = {0, 0, 0, 0, 0};
new ratings = 0;
new total_ratings = 0;
new total_rating = 0;
decl String:menu_item[64];
decl String:query[256];
Format(query, sizeof(query), "SELECT rating, COUNT(*) FROM %s WHERE map = '%s' GROUP BY rating ORDER BY rating DESC", g_table_name, map);
SQL_TQuery(db, T_CreateMenuRating, query, dp);
}
public T_CreateMenuRate(Handle:owner, Handle:hndl, const String:error[], any:data)
{
ResetPack(data);
new client = ReadPackCell(data);
if (hndl == INVALID_HANDLE)
{
LogError("Query failed! %s", error);
if (IsClientConnected(client))
{
PrintToChat(client, "%t", "Database Error");
}
CloseHandle(data);
return;
}
decl String:map[MAXLEN_MAP];
ReadPackString(data, map, sizeof(map));
new bool:voluntary = bool:ReadPackCell(data);
new initiator = ReadPackCell(data);
new rating = 0;
CloseHandle(data);
new allow_revote = GetConVarInt(g_cvars[CVAR_ALLOW_REVOTE]);
/* The player has rated this map before */
if (SQL_GetRowCount(hndl) == 1)
{
SQL_FetchRow(hndl);
rating = SQL_FetchInt(hndl, 0);
/* If the user didn't initiate the maprate, just ignore the request */
if (!voluntary)
{
return;
}
/* Deny rerating if the applicable cvar is set */
else if (!allow_revote)
{
PrintToChat(client, "\03%t", "Already Rated", rating);
return;
}
}
CloseHandle(hndl);
decl String:title[256];
/* If an initiator was set, then this map rating request was initiated by
* an admin. We'll specify who in the map rate panel title. */
if (initiator)
{
decl String:initiator_name[32];
GetClientName(initiator, initiator_name, sizeof(initiator_name));
Format(title, sizeof(title), "%T", "Everyone Rate Title",
client, initiator_name, g_current_map);
}
else
{
Format(title, sizeof(title), "%T", "Rate Map Title", client, map);
}
/* If the player already rated this map, show the previous rating. */
if (rating)
{
Format(title, sizeof(title), "%s\n%T", title, "Previous Rating", client, rating);
}
/* Build the menu panel */
new Handle:menu = CreateMenu(Menu_Rate);
SetMenuTitle(menu, title);
public OnClientPostAdminCheck(client)
{
g_lastRateTime[client] = GetTime();
}
Basically, the only important lines changed are line 69 and 159. I want players to get the map rate popped to them after playing the map for X amount of seconds, regardless of death (useful for surf & bhop servers where it is one long round, the plugin currently works around player deaths). Instead of hooking player_death I hooked round_start (probably not the ideal way), assuming the vote would be popped to the player X amount of seconds after the round start rather than player death, but the vote never pops. I figure a better and more accurate way of achieving what I want would be to hook a team change, but I'm not sure if that's even an event after a little bit of searching. It works well if I manually type !maprate. No error logs, nothing. Any tips? Still learning the ropes with sourcepawn.
tl;dr Want map rate to pop after X amount of seconds of player being in game, instead of depending on player death.
Last edited by sneaK; 09-23-2016 at 18:40.
Reason: code > php and tldr
I didn't see the 'Generic Source Events' page in the wiki, no wonder I couldn't find a team change event under CS:GO (game it's for).
Ideally, I should be able to replace round_start with player_team and that theoretically should be able to solve my issue, correct? I can test in several hours and report back
So I updated my code above to change round_start to player_team. The vote is now popping, but only if a player switches teams a second time.
I say a second time, because technically after the client enters the game, they change teams from <Unassigned> to either Terrorist, Counter-Terrorist, or Spectator, as demonstrated below:
Code:
17:42:04 "blackhawk74<2><STEAM_1:0:xx><>" entered the game
17:42:04 "blackhawk74<2><STEAM_1:0:xx>" switched from team <Unassigned> to <TERRORIST>
Now, I'm not sure if this is still requiring a player death, because when I switch teams "a second time", this is the order of events:
Code:
17:43:38 "blackhawk74<2><STEAM_1:0:xx>" switched from team <TERRORIST> to <CT>
17:43:38 "blackhawk74<2><STEAM_1:0:xx><CT>" [1920 10424 -6160] committed suicide with "world"
Be careful with blocking join team. On connect the command fires and if blocked it screws with a bunch of crap including the triggering of the client cant select a team bug as of a few updates ago.
Be careful with blocking join team. On connect the command fires and if blocked it screws with a bunch of crap including the triggering of the client cant select a team bug as of a few updates ago.
Yeah, that's why I'm avoiding using blacklagoon's suggested code at the moment, I don't want it to have anything to do with managing team switches.
To be honest, I'm a little lost at the moment. Without going too in-depth, I'm still stuck at requiring a death or second team switch for the vote to actually pop for the client. Any thoughts?