Fix have_not_voted call not sending arrays correctly

/**
 * vim: set ts=4 :
 * =============================================================================
 * map_votes
 * Interact with the Map Votes web api - https://github.com/CrimsonTautology/map_votes
 *
 * Copyright 2013 The Crimson Tautology
 * =============================================================================
 *
 */

#pragma semicolon 1

#include <sourcemod>
#include <steamtools>
#include <base64>
#include <smjansson>
#include <morecolors>

#define PLUGIN_VERSION "0.1"

public Plugin:myinfo = {
    name = "MapVotes",
    author = "CrimsonTautology",
    description = "Interact with the Map Votes web api",
    version = PLUGIN_VERSION,
    url = "https://github.com/CrimsonTautology/sm_map_votes"
};

#define CAST_VOTE_ROUTE "/v1/api/cast_vote"
#define WRITE_MESSAGE_ROUTE "/v1/api/write_message"
#define FAVORITE_ROUTE "/v1/api/favorite"
#define UNFAVORITE_ROUTE "/v1/api/unfavorite"
#define GET_FAVORITES_ROUTE "/v1/api/get_favorites"
#define HAVE_NOT_VOTED_ROUTE "/v1/api/have_not_voted"
#define SERVER_QUERY_ROUTE "/v1/api/server_query"
#define UPDATE_MAP_PLAY_TIME_ROUTE "/v1/api/update_map_play_time"
#define MAPS_ROUTE "/maps"

#define MAX_STEAMID_LENGTH 21
#define MAX_COMMUNITYID_LENGTH 18

new Handle:g_Cvar_MapVotesUrl = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesApiKey = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesVotingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesCommentingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesNominationsName = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesRequestCooldownTime = INVALID_HANDLE;

new bool:g_IsInCooldown[MAXPLAYERS+1];

new g_MapStartTimestamp = 0;

new g_MapFileSerial = -1;
new Handle:g_MapList = INVALID_HANDLE;
new Handle:g_MapTrie = INVALID_HANDLE;

new Handle:g_nominations = INVALID_HANDLE;
new Function:g_Handler_MapSelectMenu = INVALID_FUNCTION;

//Sourcemod older version support
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) {
    MarkNativeAsOptional("GetUserMessageType");
    return APLRes_Success;
}

public OnPluginStart()
{

    LoadTranslations("map_votes.phrases");
    g_Cvar_MapVotesUrl = CreateConVar("sm_map_votes_url", "", "URL to your Map Votes web page.  Remeber the \"http://\" part and to quote the entire url!");
    g_Cvar_MapVotesApiKey = CreateConVar("sm_map_votes_api_key", "", "The API key you generated to interact with the Map Votes web page");
    g_Cvar_MapVotesVotingEnabled = CreateConVar("sm_map_votes_voting_enabled", "1", "Whether players are allowed to vote on the current map");
    g_Cvar_MapVotesCommentingEnabled = CreateConVar("sm_map_votes_commenting_enabled", "1", "Whether players are allowed to comment on the current map");
    g_Cvar_MapVotesNominationsName = CreateConVar("sm_map_votes_nominations_plugin", "nominations.smx", "The nominations plugin used by the server");
    g_Cvar_MapVotesRequestCooldownTime = CreateConVar("sm_map_votes_request_cooldown_time", "2.0", "How long in seconds before a client can send another http request");

    RegConsoleCmd("sm_votemenu", Command_VoteMenu, "Bring up a menu to vote on the current map");
    RegConsoleCmd("sm_voteup", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_like", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_votedown", Command_VoteDown, "Vote that you hate the current map");
    RegConsoleCmd("sm_hate", Command_VoteDown, "Vote that you hate the current map");

    RegConsoleCmd("sm_fav", Command_Favorite, "Add this map to your favorites");
    RegConsoleCmd("sm_unfav", Command_Unfavorite, "Remove this map to your favorites");
    RegConsoleCmd("sm_nomfav", Command_GetFavorites, "Nominate from a list of your favorites");

    RegConsoleCmd("sm_mapcomment", Command_MapComment, "Comment on the current map");
    RegConsoleCmd("sm_mc", Command_MapComment, "Comment on the current map");

    RegConsoleCmd("sm_viewmap", Command_ViewMap, "View the Map Votes web page for this map");

    RegAdminCmd("sm_have_not_voted", Command_HaveNotVoted, ADMFLAG_VOTE, "Popup a vote panel to every player on the server that has not yet voted on this map");

    RegConsoleCmd("sm_test", Command_Test, "TODO - TEST");//TODO

    new array_size = ByteCountToCells(PLATFORM_MAX_PATH);
    g_MapList = CreateArray(array_size);
    g_MapTrie = CreateTrie();
    BuildMapListAndTrie();
}

public OnMapStart()
{

    g_MapStartTimestamp = GetTime();
}

public OnMapEnd()
{
    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));
    UpdateMapPlayTime(g_MapStartTimestamp);
}

public OnConfigsExecuted()
{
    BuildMapListAndTrie();
}

public OnAllPluginsLoaded() {
    new String:noms[PLATFORM_MAX_PATH];
    GetConVarString(g_Cvar_MapVotesNominationsName, noms, sizeof(noms));
    g_nominations = FindPluginByFile(noms);

    //Check if nominations.smx is both available and currently running
    if(g_nominations == INVALID_HANDLE || GetPluginStatus(g_nominations) != Plugin_Running){
        SetFailState("[MapVotes] Error, nominations is currently not running");
    }
    else{
        //We should be clear to link the MapSelectMenu function
        g_Handler_MapSelectMenu = GetFunctionByName(g_nominations, "Handler_MapSelectMenu");
    }
}

public Action:Command_VoteMenu(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        CallVoteOnClient(client);
    }

    return Plugin_Handled;
}

public Action:Command_VoteUp(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        CastVote(client, 1);
    }

    return Plugin_Handled;
}

public Action:Command_VoteDown(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        CastVote(client, -1);
    }

    return Plugin_Handled;
}

public Action:Command_Favorite(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, true);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, FavoriteSearchHandler);
        }
    }

    return Plugin_Handled;
}
public Action:Command_Unfavorite(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, false);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, UnfavoriteSearchHandler);
        }

    }

    return Plugin_Handled;
}
public Action:Command_GetFavorites(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        GetFavorites(client);
    }

    return Plugin_Handled;
}

public Action:Command_ViewMap(client, args)
{
    if(client && IsClientAuthorized(client)){
        ViewMap(client);
    }

    return Plugin_Handled;
}

public Action:Command_MapComment(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if (!GetConVarBool(g_Cvar_MapVotesCommentingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if (args < 1)
    {
        CReplyToCommand(client, "%t", "map_comment_usage");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        decl String:comment[256];
        GetCmdArgString(comment, sizeof(comment));
        WriteMessage(client, comment);
    }

    return Plugin_Handled;
}

public Action:Command_HaveNotVoted(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    HaveNotVoted(client);

    return Plugin_Handled;
}

BuildMapListAndTrie()
{
    //Build the map list
    if (ReadMapList(g_MapList,
                g_MapFileSerial,
                "nominations",
                MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER)
            == INVALID_HANDLE)
    {
        if (g_MapFileSerial == -1)
        {
            SetFailState("Unable to create a valid map list.");
        }else{
            //Build the map trie; note we don't care about the value, just if the map exists in the trie
            ClearTrie(g_MapTrie);
            new String:map[PLATFORM_MAX_PATH];

            for (new i = 0; i < GetArraySize(g_MapList); i++)
            {
                GetArrayString(g_MapList, i, map, sizeof(map));
                SetTrieValue(g_MapTrie, map, 1);
            }
        }
    }
}

public SetAccessCode(&HTTPRequestHandle:request)
{
    //All MapVotes api calls require this code
    decl String:api_key[128];
    GetConVarString(g_Cvar_MapVotesApiKey, api_key, sizeof(api_key));
    Steam_SetHTTPRequestGetOrPostParameter(request, "access_token", api_key);
}

public HTTPRequestHandle:CreateMapVotesRequest(const String:route[])
{
    decl String:base_url[256], String:url[512];
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));
    TrimString(base_url);
    new trim_length = strlen(base_url) - 1;

    if(trim_length < 0)
    {
        //MapVotes Url not set
        return INVALID_HTTP_HANDLE;
    }

    //check for forward slash after base_url;
    if(base_url[trim_length] == '/')
    {
        strcopy(base_url, trim_length + 1, base_url);
    }

    Format(url, sizeof(url),
            "%s%s", base_url, route);

    new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
    SetAccessCode(request);

    return request;
}

public Steam_SetHTTPRequestGetOrPostParameterInt(&HTTPRequestHandle:request, const String:param[], value)
{
    new String:tmp[64];
    IntToString(value, tmp, sizeof(tmp));
    Steam_SetHTTPRequestGetOrPostParameter(request, param, tmp);
}

public StartCooldown(client)
{
    //Ignore the server console
    if (client == 0)
        return;

    g_IsInCooldown[client] = true;
    CreateTimer(GetConVarFloat(g_Cvar_MapVotesRequestCooldownTime), RemoveCooldown, client);
}

public bool:IsClientInCooldown(client)
{
    if(client == 0)
        return false;
    else
        return g_IsInCooldown[client];
}

public Action:RemoveCooldown(Handle:timer, any:client)
{
    g_IsInCooldown[client] = false;
}

public WriteMessage(client, String:message[256])
{
    //Encode the message to be url safe
    decl String:base64[256], String:base64_url[256];
    EncodeBase64(base64, sizeof(base64), message);
    Base64MimeToUrl(base64_url, sizeof(base64_url), base64);

    decl String:uid[MAX_COMMUNITYID_LENGTH];
    Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));

    new HTTPRequestHandle:request = CreateMapVotesRequest(WRITE_MESSAGE_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(client, "%t", "url_invalid");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    Steam_SetHTTPRequestGetOrPostParameter(request, "comment", base64_url);
    Steam_SetHTTPRequestGetOrPostParameter(request, "base64", "1");
    Steam_SendHTTPRequest(request, ReceiveWriteMessage, GetClientUserId(client));
    StartCooldown(client);
}

public ReceiveWriteMessage(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);

    if(client && successful)
    {
        CPrintToChat(client, "%t", "comment_added");
    }

    Steam_ReleaseHTTPRequest(request);
}

public CastVote(client, value)
{

    if(!(value == -1 || value == 0 || value == 1)){
        LogError("[MapVotes] invalid vote value %d (client: %d)", value, client);
    }else{
        decl String:map[PLATFORM_MAX_PATH];
        decl String:uid[MAX_COMMUNITYID_LENGTH];
        Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

        GetCurrentMap(map, sizeof(map));

        new HTTPRequestHandle:request = CreateMapVotesRequest(CAST_VOTE_ROUTE);

        if(request == INVALID_HTTP_HANDLE)
        {
            CReplyToCommand(client, "%t", "url_invalid");
            return;
        }

        Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
        Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "value", value);
        Steam_SendHTTPRequest(request, ReceiveCastVote, GetClientUserId(client));
        StartCooldown(client);
    }

}

public ReceiveCastVote(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);

    if(client && successful)
    {
        decl String:data[4096];
        Steam_GetHTTPResponseBodyData(request, data, sizeof(data));

        new Handle:json = json_load(data);
        new value = json_object_get_int(json, "value");
        CloseHandle(json);

        new String:name[64];
        GetClientName(client, name, sizeof(name));

        if (value == 1)
        {
            CPrintToChatAll("%t", "announce_up_vote", name);
        }else if(value == -1)
        {
            CPrintToChatAll("%t", "announce_down_vote", name);
        }else
        {
            CPrintToChat(client, "%t", "vote_cast");
        }
    }

    Steam_ReleaseHTTPRequest(request);
}

public Favorite(String:map[PLATFORM_MAX_PATH], client, bool:favorite)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

    new HTTPRequestHandle:request;
    if(favorite)
    {
        request = CreateMapVotesRequest(FAVORITE_ROUTE);
    }else{
        request = CreateMapVotesRequest(UNFAVORITE_ROUTE);
    }

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(client, "%t", "url_invalid");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    Steam_SendHTTPRequest(request, ReceiveFavorite, GetClientUserId(client));
    StartCooldown(client);
}

public ReceiveFavorite(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);

    if(client && successful)
    {
        decl String:data[4096];
        Steam_GetHTTPResponseBodyData(request, data, sizeof(data));

        new Handle:json = json_load(data);
        new bool:favorite = json_object_get_bool(json, "favorite");
        new String:map[PLATFORM_MAX_PATH];
        json_object_get_string(json, "map", map, sizeof(map));
        CloseHandle(json);

        new String:name[64];
        GetClientName(client, name, sizeof(name));

        if (favorite)
        {
            CPrintToChatAll("%t", "announce_favorite", name, map);
        }else
        {
            CPrintToChat(client, "%t", "updated_favorites");
        }
    }

    Steam_ReleaseHTTPRequest(request);
}

public MapSearch(client, String:search_key[PLATFORM_MAX_PATH], Handle:map_list, MenuHandler:handler)
{
    new String:map[PLATFORM_MAX_PATH];
    new Handle:menu = CreateMenu(handler, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);
    new bool:found = false;

    for(new i=0; i<GetArraySize(map_list); i++)
    {
        GetArrayString(map_list, i, map, sizeof(map));

        //If this map matches the search key, add it to the menu
        if(StrContains(map, search_key, false) >= 0){
            AddMenuItem(menu, map, map);
            found = true;
        }
    }

    if(found)
    {
        SetMenuTitle(menu, "Found Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }

}

public FavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, true);
    }
}

public UnfavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, false);
    }
}

public GetFavorites(client)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

    new HTTPRequestHandle:request = CreateMapVotesRequest(GET_FAVORITES_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(client, "%t", "url_invalid");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameterInt(request, "player", GetClientUserId(client));
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    Steam_SendHTTPRequest(request, ReceiveGetFavorites, GetClientUserId(client));
    StartCooldown(client);
}

public ReceiveGetFavorites(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);
    if(!client)
    {
        //User logged off
        Steam_ReleaseHTTPRequest(request);
        return;
    }
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at RecivedGetFavorites (HTTP Code %d; success %d)", code, successful);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:maps = json_object_get(json, "maps");
    new String:map_buffer[PLATFORM_MAX_PATH];
    new junk;
    new bool:found;

    new Handle:menu = CreateMenu(NominateMapHandler, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);

    for(new i = 0; i < json_array_size(maps); i++)
    {
        json_array_get_string(maps, i, map_buffer, sizeof(map_buffer));
        TrimString(map_buffer);
        found = GetTrieValue(g_MapTrie, map_buffer, junk);
        if(found)
        {
            AddMenuItem(menu, map_buffer, map_buffer);
        }
    }

    CloseHandle(json);
    CloseHandle(maps);

    //If no maps were found don't even bother displaying a menu
    if(GetMenuItemCount(menu) > 0){
        SetMenuTitle(menu, "Favorited Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }else{
        CReplyToCommand(client, "%t", "no_favorited_maps");
    }
}

public NominateMapHandler(Handle:menu, MenuAction:action, param1, param2)
{

    decl result;

    // Start function call
    Call_StartFunction(g_nominations, g_Handler_MapSelectMenu);

    // Push parameters one at a time
    Call_PushCell(menu);
    Call_PushCell(action);
    Call_PushCell(param1);
    Call_PushCell(param2);

    // Finish the call, get the result
    Call_Finish(result);

    return result;
}

public CallVoteOnClient(client)
{
    new Handle:menu = CreateMenu(VoteMenuHandler);
    SetMenuTitle(menu, "Do you like this map?");
    AddMenuItem(menu, "1","Like it.");
    AddMenuItem(menu, "-1","Hate it.");
    AddMenuItem(menu, "0","I have no strong feelings one way or the other.");
    DisplayMenu(menu, client, 20);
}

public VoteMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new String:info[32];
        GetMenuItem(menu, param2, info, sizeof(info));
        new value = StringToInt(info);

        CastVote(param1, value);
    }
}

public HaveNotVoted(caller)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    decl String:map[PLATFORM_MAX_PATH];
    decl String:tmp[64];
    new player;
    new HTTPRequestHandle:request = CreateMapVotesRequest(HAVE_NOT_VOTED_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(caller, "%t", "url_invalid");
        return;
    }

    GetCurrentMap(map, sizeof(map));
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);

    new i = 0;
    for (new client=1; client <= MaxClients; client++)
    {
        if (!IsClientInGame(client) || IsFakeClient(client))
        {
            continue;
        }
        Steam_GetCSteamIDForClient(client, uid, sizeof(uid));
        player = GetClientUserId(client);

        Format(tmp, sizeof(tmp), "uids[%d]", i);
        Steam_SetHTTPRequestGetOrPostParameter(request, "uids[]", uid);
        Format(tmp, sizeof(tmp), "players[%d]", i);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "players[]", player);
        i++;
    }

    new userid=GetClientUserId(caller);
    Steam_SendHTTPRequest(request, ReceiveHaveNotVoted, userid);
}

public ReceiveHaveNotVoted(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at RecivedHaveNotVoted (HTTP Code %d)", code);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:players = json_object_get(json, "players");
    decl p;

    new count=json_array_size(players);
    if(client)
    {
        CPrintToChat(client, "%t", "have_not_voted", count);
    }

    for(new i = 0; i < count; i++)
    {
        p = json_array_get_int(players, i);
        CallVoteOnClient(GetClientOfUserId(p));
    }
    CloseHandle(json);
    CloseHandle(players);
}

public UpdateMapPlayTime(start_time)
{
    new time_played = GetTime() - start_time;

    //Reject way off values
    if(time_played < 0 || time_played > 86400000)
    {
        LogError("[MapVotes] cannot update map time played; time_played=%d", time_played);
        return;
    }

    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));

    new HTTPRequestHandle:request = CreateMapVotesRequest(UPDATE_MAP_PLAY_TIME_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        LogError("[MapVotes] sm_map_votes_url invalid; cannot create HTTP request");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "time_played", time_played);
    Steam_SendHTTPRequest(request, ReceiveUpdateMapPlayTime);
}

public ReceiveUpdateMapPlayTime(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at ReceiveUpdateMapPlayTime (HTTP Code %d; success %d)", code, successful);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    Steam_ReleaseHTTPRequest(request);
}

public ViewMap(client)
{
    decl String:map[PLATFORM_MAX_PATH], String:url[256], String:base_url[128];
    GetCurrentMap(map, sizeof(map));
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));

    TrimString(base_url);
    new trim_length = strlen(base_url) - 1;

    if(base_url[trim_length] == '/')
    {
        strcopy(base_url, trim_length + 1, base_url);
    }

    Format(url, sizeof(url),
            "%s%s/%s", base_url, MAPS_ROUTE, map);

    ShowMOTDPanel(client, "Map Viewer", url, MOTDPANEL_TYPE_URL);

}

//TODO - delete everything below
public Action:Command_Test(client, args)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    decl String:map[PLATFORM_MAX_PATH];
    new player;
    new HTTPRequestHandle:request = CreateMapVotesRequest(HAVE_NOT_VOTED_ROUTE);

    GetCurrentMap(map, sizeof(map));
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);

    Steam_SetHTTPRequestGetOrPostParameter(request, "uids[0]", "76561198013926395");
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "players[0]", 1);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uids[1]", "76561198005175623");
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "players[1]", 2);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uids[2]", "76561198013600577");
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "players[2]", 6);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uids[3]", "76561198055205178");
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "players[3]", 17);

    new userid= client ? GetClientUserId(client) : 0;
    Steam_SendHTTPRequest(request, ReceiveTest, userid);

    return Plugin_Handled;
}
public ReceiveTest(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at RecivedHaveNotVoted (HTTP Code %d)", code);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));

    new Handle:json = json_load(data);
    new Handle:players = json_object_get(json, "players");
    decl p;

    new count=json_array_size(players);
    CPrintToChatAll("%t", "have_not_voted", count);
    CPrintToChatAll("%s", data);
    PrintToConsole(0, "%s", data);

    CloseHandle(json);
    CloseHandle(players);
    Steam_ReleaseHTTPRequest(request);
}

/**
 * vim: set ts=4 :
 * =============================================================================
 * map_votes
 * Interact with the Map Votes web api - https://github.com/CrimsonTautology/map_votes
 *
 * Copyright 2013 The Crimson Tautology
 * =============================================================================
 *
 */

#pragma semicolon 1

#include <sourcemod>
#include <steamtools>
#include <base64>
#include <smjansson>
#include <morecolors>

#define PLUGIN_VERSION "0.1"

public Plugin:myinfo = {
    name = "MapVotes",
    author = "CrimsonTautology",
    description = "Interact with the Map Votes web api",
    version = PLUGIN_VERSION,
    url = "https://github.com/CrimsonTautology/sm_map_votes"
};

#define CAST_VOTE_ROUTE "/v1/api/cast_vote"
#define WRITE_MESSAGE_ROUTE "/v1/api/write_message"
#define FAVORITE_ROUTE "/v1/api/favorite"
#define UNFAVORITE_ROUTE "/v1/api/unfavorite"
#define GET_FAVORITES_ROUTE "/v1/api/get_favorites"
#define HAVE_NOT_VOTED_ROUTE "/v1/api/have_not_voted"
#define SERVER_QUERY_ROUTE "/v1/api/server_query"
#define UPDATE_MAP_PLAY_TIME_ROUTE "/v1/api/update_map_play_time"
#define MAPS_ROUTE "/maps"

#define MAX_STEAMID_LENGTH 21
#define MAX_COMMUNITYID_LENGTH 18

new Handle:g_Cvar_MapVotesUrl = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesApiKey = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesVotingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesCommentingEnabled = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesNominationsName = INVALID_HANDLE;
new Handle:g_Cvar_MapVotesRequestCooldownTime = INVALID_HANDLE;

new bool:g_IsInCooldown[MAXPLAYERS+1];

new g_MapStartTimestamp = 0;

new g_MapFileSerial = -1;
new Handle:g_MapList = INVALID_HANDLE;
new Handle:g_MapTrie = INVALID_HANDLE;

new Handle:g_nominations = INVALID_HANDLE;
new Function:g_Handler_MapSelectMenu = INVALID_FUNCTION;

//Sourcemod older version support
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) {
    MarkNativeAsOptional("GetUserMessageType");
    return APLRes_Success;
}

public OnPluginStart()
{

    LoadTranslations("map_votes.phrases");
    g_Cvar_MapVotesUrl = CreateConVar("sm_map_votes_url", "", "URL to your Map Votes web page.  Remeber the \"http://\" part and to quote the entire url!");
    g_Cvar_MapVotesApiKey = CreateConVar("sm_map_votes_api_key", "", "The API key you generated to interact with the Map Votes web page");
    g_Cvar_MapVotesVotingEnabled = CreateConVar("sm_map_votes_voting_enabled", "1", "Whether players are allowed to vote on the current map");
    g_Cvar_MapVotesCommentingEnabled = CreateConVar("sm_map_votes_commenting_enabled", "1", "Whether players are allowed to comment on the current map");
    g_Cvar_MapVotesNominationsName = CreateConVar("sm_map_votes_nominations_plugin", "nominations.smx", "The nominations plugin used by the server");
    g_Cvar_MapVotesRequestCooldownTime = CreateConVar("sm_map_votes_request_cooldown_time", "2.0", "How long in seconds before a client can send another http request");

    RegConsoleCmd("sm_votemenu", Command_VoteMenu, "Bring up a menu to vote on the current map");
    RegConsoleCmd("sm_voteup", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_like", Command_VoteUp, "Vote that you like the current map");
    RegConsoleCmd("sm_votedown", Command_VoteDown, "Vote that you hate the current map");
    RegConsoleCmd("sm_hate", Command_VoteDown, "Vote that you hate the current map");

    RegConsoleCmd("sm_fav", Command_Favorite, "Add this map to your favorites");
    RegConsoleCmd("sm_unfav", Command_Unfavorite, "Remove this map to your favorites");
    RegConsoleCmd("sm_nomfav", Command_GetFavorites, "Nominate from a list of your favorites");

    RegConsoleCmd("sm_mapcomment", Command_MapComment, "Comment on the current map");
    RegConsoleCmd("sm_mc", Command_MapComment, "Comment on the current map");

    RegConsoleCmd("sm_viewmap", Command_ViewMap, "View the Map Votes web page for this map");

    RegAdminCmd("sm_have_not_voted", Command_HaveNotVoted, ADMFLAG_VOTE, "Popup a vote panel to every player on the server that has not yet voted on this map");

    new array_size = ByteCountToCells(PLATFORM_MAX_PATH);
    g_MapList = CreateArray(array_size);
    g_MapTrie = CreateTrie();
    BuildMapListAndTrie();
}

public OnMapStart()
{

    g_MapStartTimestamp = GetTime();
}

public OnMapEnd()
{
    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));
    UpdateMapPlayTime(g_MapStartTimestamp);
}

public OnConfigsExecuted()
{
    BuildMapListAndTrie();
}

public OnAllPluginsLoaded() {
    new String:noms[PLATFORM_MAX_PATH];
    GetConVarString(g_Cvar_MapVotesNominationsName, noms, sizeof(noms));
    g_nominations = FindPluginByFile(noms);

    //Check if nominations.smx is both available and currently running
    if(g_nominations == INVALID_HANDLE || GetPluginStatus(g_nominations) != Plugin_Running){
        SetFailState("[MapVotes] Error, nominations is currently not running");
    }
    else{
        //We should be clear to link the MapSelectMenu function
        g_Handler_MapSelectMenu = GetFunctionByName(g_nominations, "Handler_MapSelectMenu");
    }
}

public Action:Command_VoteMenu(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        CallVoteOnClient(client);
    }

    return Plugin_Handled;
}

public Action:Command_VoteUp(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        CastVote(client, 1);
    }

    return Plugin_Handled;
}

public Action:Command_VoteDown(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        CastVote(client, -1);
    }

    return Plugin_Handled;
}

public Action:Command_Favorite(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, true);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, FavoriteSearchHandler);
        }
    }

    return Plugin_Handled;
}
public Action:Command_Unfavorite(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        new String:map[PLATFORM_MAX_PATH];
        if (args <= 0)
        {
            GetCurrentMap(map, sizeof(map));
            Favorite(map, client, false);
        }else{
            GetCmdArg(1, map, sizeof(map));
            MapSearch(client, map, g_MapList, UnfavoriteSearchHandler);
        }

    }

    return Plugin_Handled;
}
public Action:Command_GetFavorites(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        GetFavorites(client);
    }

    return Plugin_Handled;
}

public Action:Command_ViewMap(client, args)
{
    if(client && IsClientAuthorized(client)){
        ViewMap(client);
    }

    return Plugin_Handled;
}

public Action:Command_MapComment(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if (!GetConVarBool(g_Cvar_MapVotesCommentingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    if (args < 1)
    {
        CReplyToCommand(client, "%t", "map_comment_usage");
        return Plugin_Handled;
    }

    if(client && IsClientAuthorized(client)){
        decl String:comment[256];
        GetCmdArgString(comment, sizeof(comment));
        WriteMessage(client, comment);
    }

    return Plugin_Handled;
}

public Action:Command_HaveNotVoted(client, args)
{
    if(IsClientInCooldown(client))
    {
        CReplyToCommand(client, "%t", "user_cooldown");
        return Plugin_Handled;
    }

    if(!GetConVarBool(g_Cvar_MapVotesVotingEnabled))
    {
        CReplyToCommand(client, "%t", "voting_not_enabled");
        return Plugin_Handled;
    }

    HaveNotVoted(client);

    return Plugin_Handled;
}

BuildMapListAndTrie()
{
    //Build the map list
    if (ReadMapList(g_MapList,
                g_MapFileSerial,
                "nominations",
                MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER)
            == INVALID_HANDLE)
    {
        if (g_MapFileSerial == -1)
        {
            SetFailState("Unable to create a valid map list.");
        }else{
            //Build the map trie; note we don't care about the value, just if the map exists in the trie
            ClearTrie(g_MapTrie);
            new String:map[PLATFORM_MAX_PATH];

            for (new i = 0; i < GetArraySize(g_MapList); i++)
            {
                GetArrayString(g_MapList, i, map, sizeof(map));
                SetTrieValue(g_MapTrie, map, 1);
            }
        }
    }
}

public SetAccessCode(&HTTPRequestHandle:request)
{
    //All MapVotes api calls require this code
    decl String:api_key[128];
    GetConVarString(g_Cvar_MapVotesApiKey, api_key, sizeof(api_key));
    Steam_SetHTTPRequestGetOrPostParameter(request, "access_token", api_key);
}

public HTTPRequestHandle:CreateMapVotesRequest(const String:route[])
{
    decl String:base_url[256], String:url[512];
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));
    TrimString(base_url);
    new trim_length = strlen(base_url) - 1;

    if(trim_length < 0)
    {
        //MapVotes Url not set
        return INVALID_HTTP_HANDLE;
    }

    //check for forward slash after base_url;
    if(base_url[trim_length] == '/')
    {
        strcopy(base_url, trim_length + 1, base_url);
    }

    Format(url, sizeof(url),
            "%s%s", base_url, route);

    new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
    SetAccessCode(request);

    return request;
}

public Steam_SetHTTPRequestGetOrPostParameterInt(&HTTPRequestHandle:request, const String:param[], value)
{
    new String:tmp[64];
    IntToString(value, tmp, sizeof(tmp));
    Steam_SetHTTPRequestGetOrPostParameter(request, param, tmp);
}

public StartCooldown(client)
{
    //Ignore the server console
    if (client == 0)
        return;

    g_IsInCooldown[client] = true;
    CreateTimer(GetConVarFloat(g_Cvar_MapVotesRequestCooldownTime), RemoveCooldown, client);
}

public bool:IsClientInCooldown(client)
{
    if(client == 0)
        return false;
    else
        return g_IsInCooldown[client];
}

public Action:RemoveCooldown(Handle:timer, any:client)
{
    g_IsInCooldown[client] = false;
}

public WriteMessage(client, String:message[256])
{
    //Encode the message to be url safe
    decl String:base64[256], String:base64_url[256];
    EncodeBase64(base64, sizeof(base64), message);
    Base64MimeToUrl(base64_url, sizeof(base64_url), base64);

    decl String:uid[MAX_COMMUNITYID_LENGTH];
    Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));

    new HTTPRequestHandle:request = CreateMapVotesRequest(WRITE_MESSAGE_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(client, "%t", "url_invalid");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    Steam_SetHTTPRequestGetOrPostParameter(request, "comment", base64_url);
    Steam_SetHTTPRequestGetOrPostParameter(request, "base64", "1");
    Steam_SendHTTPRequest(request, ReceiveWriteMessage, GetClientUserId(client));
    StartCooldown(client);
}

public ReceiveWriteMessage(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);

    if(client && successful)
    {
        CPrintToChat(client, "%t", "comment_added");
    }

    Steam_ReleaseHTTPRequest(request);
}

public CastVote(client, value)
{

    if(!(value == -1 || value == 0 || value == 1)){
        LogError("[MapVotes] invalid vote value %d (client: %d)", value, client);
    }else{
        decl String:map[PLATFORM_MAX_PATH];
        decl String:uid[MAX_COMMUNITYID_LENGTH];
        Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

        GetCurrentMap(map, sizeof(map));

        new HTTPRequestHandle:request = CreateMapVotesRequest(CAST_VOTE_ROUTE);

        if(request == INVALID_HTTP_HANDLE)
        {
            CReplyToCommand(client, "%t", "url_invalid");
            return;
        }

        Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
        Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "value", value);
        Steam_SendHTTPRequest(request, ReceiveCastVote, GetClientUserId(client));
        StartCooldown(client);
    }

}

public ReceiveCastVote(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);

    if(client && successful)
    {
        decl String:data[4096];
        Steam_GetHTTPResponseBodyData(request, data, sizeof(data));

        new Handle:json = json_load(data);
        new value = json_object_get_int(json, "value");
        CloseHandle(json);

        new String:name[64];
        GetClientName(client, name, sizeof(name));

        if (value == 1)
        {
            CPrintToChatAll("%t", "announce_up_vote", name);
        }else if(value == -1)
        {
            CPrintToChatAll("%t", "announce_down_vote", name);
        }else
        {
            CPrintToChat(client, "%t", "vote_cast");
        }
    }

    Steam_ReleaseHTTPRequest(request);
}

public Favorite(String:map[PLATFORM_MAX_PATH], client, bool:favorite)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

    new HTTPRequestHandle:request;
    if(favorite)
    {
        request = CreateMapVotesRequest(FAVORITE_ROUTE);
    }else{
        request = CreateMapVotesRequest(UNFAVORITE_ROUTE);
    }

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(client, "%t", "url_invalid");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    Steam_SendHTTPRequest(request, ReceiveFavorite, GetClientUserId(client));
    StartCooldown(client);
}

public ReceiveFavorite(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);

    if(client && successful)
    {
        decl String:data[4096];
        Steam_GetHTTPResponseBodyData(request, data, sizeof(data));

        new Handle:json = json_load(data);
        new bool:favorite = json_object_get_bool(json, "favorite");
        new String:map[PLATFORM_MAX_PATH];
        json_object_get_string(json, "map", map, sizeof(map));
        CloseHandle(json);

        new String:name[64];
        GetClientName(client, name, sizeof(name));

        if (favorite)
        {
            CPrintToChatAll("%t", "announce_favorite", name, map);
        }else
        {
            CPrintToChat(client, "%t", "updated_favorites");
        }
    }

    Steam_ReleaseHTTPRequest(request);
}

public MapSearch(client, String:search_key[PLATFORM_MAX_PATH], Handle:map_list, MenuHandler:handler)
{
    new String:map[PLATFORM_MAX_PATH];
    new Handle:menu = CreateMenu(handler, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);
    new bool:found = false;

    for(new i=0; i<GetArraySize(map_list); i++)
    {
        GetArrayString(map_list, i, map, sizeof(map));

        //If this map matches the search key, add it to the menu
        if(StrContains(map, search_key, false) >= 0){
            AddMenuItem(menu, map, map);
            found = true;
        }
    }

    if(found)
    {
        SetMenuTitle(menu, "Found Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }

}

public FavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, true);
    }
}

public UnfavoriteSearchHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new client=param1;
        new String:map[PLATFORM_MAX_PATH];
        GetMenuItem(menu, param2, map, sizeof(map));
        Favorite(map, client, false);
    }
}

public GetFavorites(client)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    Steam_GetCSteamIDForClient(client, uid, sizeof(uid));

    new HTTPRequestHandle:request = CreateMapVotesRequest(GET_FAVORITES_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(client, "%t", "url_invalid");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameterInt(request, "player", GetClientUserId(client));
    Steam_SetHTTPRequestGetOrPostParameter(request, "uid", uid);
    Steam_SendHTTPRequest(request, ReceiveGetFavorites, GetClientUserId(client));
    StartCooldown(client);
}

public ReceiveGetFavorites(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);
    if(!client)
    {
        //User logged off
        Steam_ReleaseHTTPRequest(request);
        return;
    }
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at RecivedGetFavorites (HTTP Code %d; success %d)", code, successful);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:maps = json_object_get(json, "maps");
    new String:map_buffer[PLATFORM_MAX_PATH];
    new junk;
    new bool:found;

    new Handle:menu = CreateMenu(NominateMapHandler, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem);

    for(new i = 0; i < json_array_size(maps); i++)
    {
        json_array_get_string(maps, i, map_buffer, sizeof(map_buffer));
        TrimString(map_buffer);
        found = GetTrieValue(g_MapTrie, map_buffer, junk);
        if(found)
        {
            AddMenuItem(menu, map_buffer, map_buffer);
        }
    }

    CloseHandle(json);
    CloseHandle(maps);

    //If no maps were found don't even bother displaying a menu
    if(GetMenuItemCount(menu) > 0){
        SetMenuTitle(menu, "Favorited Maps");
        DisplayMenu(menu, client, MENU_TIME_FOREVER);
    }else{
        CReplyToCommand(client, "%t", "no_favorited_maps");
    }
}

public NominateMapHandler(Handle:menu, MenuAction:action, param1, param2)
{

    decl result;

    // Start function call
    Call_StartFunction(g_nominations, g_Handler_MapSelectMenu);

    // Push parameters one at a time
    Call_PushCell(menu);
    Call_PushCell(action);
    Call_PushCell(param1);
    Call_PushCell(param2);

    // Finish the call, get the result
    Call_Finish(result);

    return result;
}

public CallVoteOnClient(client)
{
    new Handle:menu = CreateMenu(VoteMenuHandler);
    SetMenuTitle(menu, "Do you like this map?");
    AddMenuItem(menu, "1","Like it.");
    AddMenuItem(menu, "-1","Hate it.");
    AddMenuItem(menu, "0","I have no strong feelings one way or the other.");
    DisplayMenu(menu, client, 20);
}

public VoteMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
    if (action == MenuAction_End)
    {
        CloseHandle(menu);
    } else if (action == MenuAction_VoteCancel)
    {
    } else if (action == MenuAction_Select)
    {
        new String:info[32];
        GetMenuItem(menu, param2, info, sizeof(info));
        new value = StringToInt(info);

        CastVote(param1, value);
    }
}

public HaveNotVoted(caller)
{
    decl String:uid[MAX_COMMUNITYID_LENGTH];
    decl String:map[PLATFORM_MAX_PATH];
    new player;
    new HTTPRequestHandle:request = CreateMapVotesRequest(HAVE_NOT_VOTED_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        CReplyToCommand(caller, "%t", "url_invalid");
        return;
    }

    GetCurrentMap(map, sizeof(map));
    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);

    for (new client=1; client <= MaxClients; client++)
    {
        if (!IsClientInGame(client) || IsFakeClient(client))
        {
            continue;
        }
        Steam_GetCSteamIDForClient(client, uid, sizeof(uid));
        player = GetClientUserId(client);

        Steam_SetHTTPRequestGetOrPostParameter(request, "uids[]", uid);
        Steam_SetHTTPRequestGetOrPostParameterInt(request, "players[]", player);
    }

    new userid=GetClientUserId(caller);
    Steam_SendHTTPRequest(request, ReceiveHaveNotVoted, userid);
}

public ReceiveHaveNotVoted(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    new client = GetClientOfUserId(userid);
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at RecivedHaveNotVoted (HTTP Code %d)", code);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    decl String:data[4096];
    Steam_GetHTTPResponseBodyData(request, data, sizeof(data));
    Steam_ReleaseHTTPRequest(request);

    new Handle:json = json_load(data);
    new Handle:players = json_object_get(json, "players");
    decl p;

    new count=json_array_size(players);
    if(client)
    {
        CPrintToChat(client, "%t", "have_not_voted", count);
    }

    for(new i = 0; i < count; i++)
    {
        p = json_array_get_int(players, i);
        CallVoteOnClient(GetClientOfUserId(p));
    }
    CloseHandle(json);
    CloseHandle(players);
}

public UpdateMapPlayTime(start_time)
{
    new time_played = GetTime() - start_time;

    //Reject way off values
    if(time_played < 0 || time_played > 86400000)
    {
        LogError("[MapVotes] cannot update map time played; time_played=%d", time_played);
        return;
    }

    decl String:map[PLATFORM_MAX_PATH];
    GetCurrentMap(map, sizeof(map));

    new HTTPRequestHandle:request = CreateMapVotesRequest(UPDATE_MAP_PLAY_TIME_ROUTE);

    if(request == INVALID_HTTP_HANDLE)
    {
        LogError("[MapVotes] sm_map_votes_url invalid; cannot create HTTP request");
        return;
    }

    Steam_SetHTTPRequestGetOrPostParameter(request, "map", map);
    Steam_SetHTTPRequestGetOrPostParameterInt(request, "time_played", time_played);
    Steam_SendHTTPRequest(request, ReceiveUpdateMapPlayTime);
}

public ReceiveUpdateMapPlayTime(HTTPRequestHandle:request, bool:successful, HTTPStatusCode:code, any:userid)
{
    if(!successful || code != HTTPStatusCode_OK)
    {
        LogError("[MapVotes] Error at ReceiveUpdateMapPlayTime (HTTP Code %d; success %d)", code, successful);
        Steam_ReleaseHTTPRequest(request);
        return;
    }

    Steam_ReleaseHTTPRequest(request);
}

public ViewMap(client)
{
    decl String:map[PLATFORM_MAX_PATH], String:url[256], String:base_url[128];
    GetCurrentMap(map, sizeof(map));
    GetConVarString(g_Cvar_MapVotesUrl, base_url, sizeof(base_url));

    TrimString(base_url);
    new trim_length = strlen(base_url) - 1;

    if(base_url[trim_length] == '/')
    {
        strcopy(base_url, trim_length + 1, base_url);
    }

    Format(url, sizeof(url),
            "%s%s/%s", base_url, MAPS_ROUTE, map);

    ShowMOTDPanel(client, "Map Viewer", url, MOTDPANEL_TYPE_URL);

}


Oops, this is evilrocket

Oops, this is evilrocket

/**
* TheXeon
* ngs_evilrocket.sp
*
* Files:
* addons/sourcemod/plugins/ngs_evilrocket.smx
* cfg/sourcemod/plugin.ngs_evilrocket.cfg
*
* Dependencies:
* sourcemod.inc, sdktools.inc, adminmenu.inc, multicolors.inc, ngsutils.inc, ngsupdater.inc
*/
#pragma newdecls required
#pragma semicolon 1

#define LIBRARY_REMOVED_FUNC OnLibRemoved
#define CONTENT_URL "https://github.com/NGSNetwork/sm-plugins/raw/master/"
#define RELOAD_ON_UPDATE 1

#include <sourcemod>
#include <sdktools>
#undef REQUIRE_PLUGIN
#include <adminmenu>
#define REQUIRE_PLUGIN
#include <multicolors>
#include <ngsutils>
#include <ngsupdater>

Handle hAdminMenu;

ConVar Cvar_RocketMe;

int gametype = 0;
int g_Explosion;

int g_Ent[MAXPLAYERS + 1];
bool isInRocketMeMode[MAXPLAYERS + 1];
char GameName[64];

bool IsBonusRound = false;

// Functions
public Plugin myinfo = {
	name = "[NGS] Evil Admin - Rocket",
	author = "<eVa>Dog / TheXeon",
	description = "Make a rocket with a player.",
	version = "1.2.1",
	url = "https://www.neogenesisnetwork.net"
}

public void OnPluginStart()
{
	Cvar_RocketMe = CreateConVar("sm_rocketme_enabled", "1", " Allow players to suicide as a rocket");

	RegAdminCmd("sm_evilrocket", Command_EvilRocket, ADMFLAG_SLAY, "sm_evilrocket <#userid|name>");
	RegConsoleCmd("sm_rocketme", Command_RocketMe, "A 'fun' way to suicide!");

	LoadTranslations("common.phrases");

	GetGameFolderName(GameName, sizeof(GameName));

	if (StrEqual(GameName, "tf"))
	{
		HookEvent("teamplay_round_win", RoundWinEvent, EventHookMode_PostNoCopy);
		HookEvent("teamplay_round_active", RoundStartEvent, EventHookMode_PostNoCopy);
	}
	else if (StrEqual(GameName, "dod"))
	{
		HookEvent("dod_round_win", RoundWinEvent, EventHookMode_PostNoCopy);
		HookEvent("dod_round_active", RoundStartEvent, EventHookMode_PostNoCopy);
	}

	TopMenu topmenu;
	if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
	{
		OnAdminMenuReady(topmenu);
	}

	AutoExecConfig();
}

public Action RoundWinEvent(Event event, const char[] name, bool dontBroadcast)
{
	IsBonusRound = true;
}

public Action RoundStartEvent(Event event, const char[] name, bool dontBroadcast)
{
	IsBonusRound = false;
}

public void OnEventShutdown()
{
	if (StrEqual(GameName, "tf"))
	{
		UnhookEvent("teamplay_round_win", RoundWinEvent);
		UnhookEvent("teamplay_round_active", RoundStartEvent);
	}
	else if (StrEqual(GameName, "dod"))
	{
		UnhookEvent("dod_round_win", RoundWinEvent);
		UnhookEvent("dod_round_active", RoundStartEvent);
	}
}

public void OnMapStart()
{
	if (StrEqual(GameName, "tf"))
	{
		gametype = 1;
	}
	else if (StrEqual(GameName, "dod"))
	{
		gametype = 2;
	}
	else
	{
		gametype = 0;
	}

	g_Explosion = PrecacheModel("sprites/sprite_fire01.vmt");

	PrecacheSound("ambient/explosions/exp2.wav", true);
	PrecacheSound("npc/env_headcrabcanister/launch.wav", true);
	PrecacheSound("weapons/rpg/rocketfire1.wav", true);
}

public Action Command_EvilRocket(int client, int args)
{
	char target[65];
	char target_name[MAX_TARGET_LENGTH];
	int target_list[MAXPLAYERS];
	int target_count;
	bool tn_is_ml;

	if (args < 1)
	{
		ReplyToCommand(client, "[SM] Usage: sm_evilrocket <#userid|name>");
		return Plugin_Handled;
	}

	GetCmdArg(1, target, sizeof(target));

	if ((target_count = ProcessTargetString(
			target,
			client,
			target_list,
			MAXPLAYERS,
			0,
			target_name,
			sizeof(target_name),
			tn_is_ml)) <= 0)
	{
		ReplyToTargetError(client, target_count);
		return Plugin_Handled;
	}

	for (int i = 0; i < target_count; i++)
	{
		if (IsClientInGame(target_list[i]) && IsPlayerAlive(target_list[i]) && !isInRocketMeMode[target_list[i]])
		{
			PerformEvilRocket(client, target_list[i]);
		}
	}
	return Plugin_Handled;
}

void PerformEvilRocket(int client, int target)
{
	if (g_Ent[target] == 0)
	{
		if (client != -1)
		{
			LogAction(client, target, "\"%L\" sent \"%L\" into space", client, target);
			ShowActivity(client, "launched %N into space", target);

			if (gametype == 1)
			{
				AttachParticle(target, "rockettrail_!");
			}
			else if (gametype == 2)
			{
				AttachParticle(target, "rockettrail");
			}
			else
			{
				AttachFlame(target);
			}
			EmitSoundToAll("weapons/rpg/rocketfire1.wav", target, _, _, _, 0.8);
			CreateTimer(2.0, Launch, target);
			CreateTimer(3.5, Detonate, target);
		}
		else
		{
			if (gametype == 1)
			{
				AttachParticle(target, "rockettrail_!");
			}
			else if (gametype == 2)
			{
				AttachParticle(target, "rockettrail");
			}
			else
			{
				AttachFlame(target);
			}
			EmitSoundToAll("weapons/rpg/rocketfire1.wav", target, _, _, _, 0.8);
			CreateTimer(2.0, Launch, target);
			CreateTimer(3.5, Detonate, target);
		}
		if (!isInRocketMeMode[target]) isInRocketMeMode[target] = true;
	}
}

public Action Launch(Handle timer, any client)
{
	if (IsClientInGame(client))
	{
		float vVel[3];

		vVel[0] = 0.0;
		vVel[1] = 0.0;
		vVel[2] = 800.0;

		EmitSoundToAll("ambient/explosions/exp2.wav", client, _, _, _, 1.0);
		EmitSoundToAll("npc/env_headcrabcanister/launch.wav", client, _, _, _, 1.0);

		TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vVel);
		SetEntityGravity(client, 0.1);
	}

	return Plugin_Handled;
}

public Action Detonate(Handle timer, any client)
{
	if (IsClientInGame(client))
	{
		float vPlayer[3];
		GetClientAbsOrigin(client, vPlayer);

		if (gametype == 1)
		{
			DeleteParticle(g_Ent[client]);
			g_Ent[client] = 0;

			if (IsBonusRound)
			{
				float ClientOrigin[3];
				GetClientAbsOrigin(client, ClientOrigin);

				int g_ent = CreateEntityByName("env_explosion");
				DispatchKeyValue(g_ent, "iMagnitude", "2000");
				DispatchKeyValue(g_ent, "iRadiusOverride", "15");
				DispatchSpawn(g_ent);
				TeleportEntity(g_ent, ClientOrigin, NULL_VECTOR, NULL_VECTOR);
				AcceptEntityInput(g_ent, "Explode");
				CreateTimer(3.0, KillExplosion, g_ent);
			}
			else
			{
				FakeClientCommand(client, "Explode");
			}
		}
		else if (gametype == 2)
		{
			DeleteParticle(g_Ent[client]);
			g_Ent[client] = 0;

			FakeClientCommand(client, "Explode");
		}
		else
		{
			TE_SetupExplosion(vPlayer, g_Explosion, 10.0, 1, 0, 600, 5000);
			TE_SendToAll();
			g_Ent[client] = 0;

			ForcePlayerSuicide(client);
		}

		SetEntityGravity(client, 1.0);
		isInRocketMeMode[client] = false;
	}
	return Plugin_Handled;
}

public Action KillExplosion(Handle timer, any ent)
{
    if (IsValidEntity(ent))
    {
        char classname[256];
        GetEdictClassname(ent, classname, sizeof(classname));
        if (StrEqual(classname, "env_explosion", false))
        {
            RemoveEdict(ent);
        }
    }
}

public void OnLibRemoved(const char[] name)
{
	if (StrEqual(name, "adminmenu"))
	{
		delete hAdminMenu;
	}
}

public void OnAdminMenuReady(Handle topmenu)
{
	if (topmenu == hAdminMenu)
	{
		return;
	}

	hAdminMenu = topmenu;

	TopMenuObject player_commands = FindTopMenuCategory(hAdminMenu, ADMINMENU_PLAYERCOMMANDS);

	if (player_commands != INVALID_TOPMENUOBJECT)
	{
		AddToTopMenu(hAdminMenu,
			"sm_evilrocket",
			TopMenuObject_Item,
			AdminMenu_rocket,
			player_commands,
			"sm_evilrocket",
			ADMFLAG_SLAY);
	}
}

public int AdminMenu_rocket(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
	if (action == TopMenuAction_DisplayOption)
	{
		Format(buffer, maxlength, "Evil Rocket");
	}
	else if( action == TopMenuAction_SelectOption)
	{
		DisplayPlayerMenu(param);
	}
}

void DisplayPlayerMenu(int client)
{
	Handle menu = CreateMenu(MenuHandler_Players);

	char title[100];
	Format(title, sizeof(title), "Choose Player:");
	SetMenuTitle(menu, title);
	SetMenuExitBackButton(menu, true);

	AddTargetsToMenu(menu, client, true, true);

	DisplayMenu(menu, client, MENU_TIME_FOREVER);
}

public int MenuHandler_Players(Handle menu, MenuAction action, int param1, int param2)
{
	if (action == MenuAction_End)
	{
		CloseHandle(menu);
	}
	else if (action == MenuAction_Cancel)
	{
		if (param2 == MenuCancel_ExitBack && hAdminMenu != INVALID_HANDLE)
		{
			DisplayTopMenu(hAdminMenu, param1, TopMenuPosition_LastCategory);
		}
	}
	else if (action == MenuAction_Select)
	{
		char info[32];
		int userid, target;

		GetMenuItem(menu, param2, info, sizeof(info));
		userid = StringToInt(info);

		if ((target = GetClientOfUserId(userid)) == 0)
		{
			CPrintToChat(param1, "{GREEN}[SM]{DEFAULT} %s", "Player no longer available");
		}
		else if (!CanUserTarget(param1, target))
		{
			CPrintToChat(param1, "{GREEN}[SM]{DEFAULT} %s", "Unable to target");
		}
		else
		{
			PerformEvilRocket(param1, target);
		}

		/* Re-draw the menu if they're still valid */
		if (IsClientInGame(param1) && !IsClientInKickQueue(param1))
		{
			DisplayPlayerMenu(param1);
		}
	}
}

void AttachParticle(int ent, char[] particleType)
{
	int particle = CreateEntityByName("info_particle_system");

	char tName[128], pName[128];
	if (IsValidEdict(particle))
	{
		float pos[3];
		GetEntPropVector(ent, Prop_Send, "m_vecOrigin", pos);

		if (gametype == 1)
		{
			pos[2] += 10;
			TeleportEntity(particle, pos, NULL_VECTOR, NULL_VECTOR);
		}
		else if (gametype == 2)
		{
			pos[2] += 50;
			TeleportEntity(particle, pos, NULL_VECTOR, NULL_VECTOR);
		}

		Format(tName, sizeof(tName), "target%i", ent);
		DispatchKeyValue(ent, "targetname", tName);

		Format(pName, sizeof(pName), "particle%i", ent);
		DispatchKeyValue(particle, "targetname", pName);

		DispatchKeyValue(particle, "parentname", tName);
		DispatchKeyValue(particle, "effect_name", particleType);
		DispatchSpawn(particle);

		SetVariantString(tName);
		AcceptEntityInput(particle, "SetParent", particle, particle, 0);

		if (gametype == 1)
		{
			SetVariantString("flag");
			AcceptEntityInput(particle, "SetParentAttachment", particle, particle, 0);
		}
		ActivateEntity(particle);
		AcceptEntityInput(particle, "start");

		g_Ent[ent] = particle;
	}
}

void DeleteParticle(any particle)
{
	if (IsValidEntity(particle))
	{
		char classname[256];
		GetEdictClassname(particle, classname, sizeof(classname));
		if (StrEqual(classname, "info_particle_system", false))
		{
			RemoveEdict(particle);
		}
	}
}

void AttachFlame(int ent)
{
	char flame_name[128];
	Format(flame_name, sizeof(flame_name), "RocketFlame%i", ent);

	char tName[128];

	int flame = CreateEntityByName("env_steam");
	if (IsValidEdict(flame))
	{
		float pos[3];
		GetEntPropVector(ent, Prop_Send, "m_vecOrigin", pos);
		pos[2] += 30;

		float angles[3];
		angles[0] = 90.0;
		angles[1] = 0.0;
		angles[2] = 0.0;

		Format(tName, sizeof(tName), "target%i", ent);
		DispatchKeyValue(ent, "targetname", tName);

		DispatchKeyValue(flame, "targetname", flame_name);
		DispatchKeyValue(flame, "parentname", tName);
		DispatchKeyValue(flame, "SpawnFlags", "1");
		DispatchKeyValue(flame, "Type", "0");
		DispatchKeyValue(flame, "InitialState", "1");
		DispatchKeyValue(flame, "Spreadspeed", "10");
		DispatchKeyValue(flame, "Speed", "800");
		DispatchKeyValue(flame, "Startsize", "10");
		DispatchKeyValue(flame, "EndSize", "250");
		DispatchKeyValue(flame, "Rate", "15");
		DispatchKeyValue(flame, "JetLength", "400");
		DispatchKeyValue(flame, "RenderColor", "180 71 8");
		DispatchKeyValue(flame, "RenderAmt", "180");
		DispatchSpawn(flame);
		TeleportEntity(flame, pos, angles, NULL_VECTOR);
		SetVariantString(tName);
		AcceptEntityInput(flame, "SetParent", flame, flame, 0);

		CreateTimer(3.0, DeleteFlame, flame);

		g_Ent[ent] = flame;
	}
}

public Action DeleteFlame(Handle timer, any ent)
{
	if (IsValidEntity(ent))
    {
        char classname[256];
        GetEdictClassname(ent, classname, sizeof(classname));
        if (StrEqual(classname, "env_steam", false))
        {
            RemoveEdict(ent);
        }
    }
}

public Action Command_RocketMe(int client, int args)
{
	if (!IsValidClient(client)) return Plugin_Handled;
	if (CheckCommandAccess(client, "sm_rocketme_admin_override", ADMFLAG_VOTE))
	{
		if (IsClientInGame(client) && IsPlayerAlive(client) && !isInRocketMeMode[client])
		{
			PerformEvilRocket(-1, client);
			CreateTimer(3.0, MessageUs, client);
		}
	}
	else if (Cvar_RocketMe.BoolValue && CheckCommandAccess(client, "sm_rocketme_override", 0))
	{
		if (IsClientInGame(client) && IsPlayerAlive(client) && !isInRocketMeMode[client])
		{
			PerformEvilRocket(-1, client);
			CreateTimer(3.0, MessageUs, client);
		}
	}
	else
	{
		CReplyToCommand(client, "{GREEN}[SM]{DEFAULT} RocketMe is not enabled");
	}

	return Plugin_Handled;
}

public Action MessageUs(Handle timer, any client)
{
	CPrintToChatAll("{GREEN}[SM]{DEFAULT} {LIGHTGREEN}%N{DEFAULT} died in a rocket-related accident.", client);
}

/**
* TheXeon
* ngs_donor_toolkit.sp
*
* Files:
* addons/sourcemod/plugins/ngs_donor_toolkit.smx
* cfg/sourcemod/plugin.ngs_evilrocket.sp
*
* Dependencies:
* sourcemod.inc, tf2attributes.inc, multicolors.inc, ngsutils.inc, ngsupdater.inc
*/
#pragma newdecls required
#pragma semicolon 1

#define LIBRARY_REMOVED_FUNC OnLibRemoved
#define CONTENT_URL "https://github.com/NGSNetwork/sm-plugins/raw/master/"
#define RELOAD_ON_UPDATE 1

#include <sourcemod>
#include <sdktools>
#undef REQUIRE_PLUGIN
#include <adminmenu>
#define REQUIRE_PLUGIN
#include <multicolors>
#include <ngsutils>
#include <ngsupdater>

Handle hAdminMenu;

ConVar Cvar_RocketMe;

int gametype = 0;
int g_Explosion;

int g_Ent[MAXPLAYERS + 1];
bool isInRocketMeMode[MAXPLAYERS + 1];
char GameName[64];

bool IsBonusRound = false;

// Functions
public Plugin myinfo = {
	name = "[NGS] Evil Admin - Rocket",
	author = "<eVa>Dog / TheXeon",
	description = "Make a rocket with a player.",
	version = "1.2.1",
	url = "https://www.neogenesisnetwork.net"
}

public void OnPluginStart()
{
	Cvar_RocketMe = CreateConVar("sm_rocketme_enabled", "1", " Allow players to suicide as a rocket");

	RegAdminCmd("sm_evilrocket", Command_EvilRocket, ADMFLAG_SLAY, "sm_evilrocket <#userid|name>");
	RegConsoleCmd("sm_rocketme", Command_RocketMe, "A 'fun' way to suicide!");

	LoadTranslations("common.phrases");

	GetGameFolderName(GameName, sizeof(GameName));

	if (StrEqual(GameName, "tf"))
	{
		HookEvent("teamplay_round_win", RoundWinEvent, EventHookMode_PostNoCopy);
		HookEvent("teamplay_round_active", RoundStartEvent, EventHookMode_PostNoCopy);
	}
	else if (StrEqual(GameName, "dod"))
	{
		HookEvent("dod_round_win", RoundWinEvent, EventHookMode_PostNoCopy);
		HookEvent("dod_round_active", RoundStartEvent, EventHookMode_PostNoCopy);
	}

	TopMenu topmenu;
	if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
	{
		OnAdminMenuReady(topmenu);
	}

	AutoExecConfig();
}

public Action RoundWinEvent(Event event, const char[] name, bool dontBroadcast)
{
	IsBonusRound = true;
}

public Action RoundStartEvent(Event event, const char[] name, bool dontBroadcast)
{
	IsBonusRound = false;
}

public void OnEventShutdown()
{
	if (StrEqual(GameName, "tf"))
	{
		UnhookEvent("teamplay_round_win", RoundWinEvent);
		UnhookEvent("teamplay_round_active", RoundStartEvent);
	}
	else if (StrEqual(GameName, "dod"))
	{
		UnhookEvent("dod_round_win", RoundWinEvent);
		UnhookEvent("dod_round_active", RoundStartEvent);
	}
}

public void OnMapStart()
{
	if (StrEqual(GameName, "tf"))
	{
		gametype = 1;
	}
	else if (StrEqual(GameName, "dod"))
	{
		gametype = 2;
	}
	else
	{
		gametype = 0;
	}

	g_Explosion = PrecacheModel("sprites/sprite_fire01.vmt");

	PrecacheSound("ambient/explosions/exp2.wav", true);
	PrecacheSound("npc/env_headcrabcanister/launch.wav", true);
	PrecacheSound("weapons/rpg/rocketfire1.wav", true);
}

public Action Command_EvilRocket(int client, int args)
{
	char target[65];
	char target_name[MAX_TARGET_LENGTH];
	int target_list[MAXPLAYERS];
	int target_count;
	bool tn_is_ml;

	if (args < 1)
	{
		ReplyToCommand(client, "[SM] Usage: sm_evilrocket <#userid|name>");
		return Plugin_Handled;
	}

	GetCmdArg(1, target, sizeof(target));

	if ((target_count = ProcessTargetString(
			target,
			client,
			target_list,
			MAXPLAYERS,
			0,
			target_name,
			sizeof(target_name),
			tn_is_ml)) <= 0)
	{
		ReplyToTargetError(client, target_count);
		return Plugin_Handled;
	}

	for (int i = 0; i < target_count; i++)
	{
		if (IsClientInGame(target_list[i]) && IsPlayerAlive(target_list[i]) && !isInRocketMeMode[target_list[i]])
		{
			PerformEvilRocket(client, target_list[i]);
		}
	}
	return Plugin_Handled;
}

void PerformEvilRocket(int client, int target)
{
	if (g_Ent[target] == 0)
	{
		if (client != -1)
		{
			LogAction(client, target, "\"%L\" sent \"%L\" into space", client, target);
			ShowActivity(client, "launched %N into space", target);

			if (gametype == 1)
			{
				AttachParticle(target, "rockettrail_!");
			}
			else if (gametype == 2)
			{
				AttachParticle(target, "rockettrail");
			}
			else
			{
				AttachFlame(target);
			}
			EmitSoundToAll("weapons/rpg/rocketfire1.wav", target, _, _, _, 0.8);
			CreateTimer(2.0, Launch, target);
			CreateTimer(3.5, Detonate, target);
		}
		else
		{
			if (gametype == 1)
			{
				AttachParticle(target, "rockettrail_!");
			}
			else if (gametype == 2)
			{
				AttachParticle(target, "rockettrail");
			}
			else
			{
				AttachFlame(target);
			}
			EmitSoundToAll("weapons/rpg/rocketfire1.wav", target, _, _, _, 0.8);
			CreateTimer(2.0, Launch, target);
			CreateTimer(3.5, Detonate, target);
		}
		if (!isInRocketMeMode[target]) isInRocketMeMode[target] = true;
	}
}

public Action Launch(Handle timer, any client)
{
	if (IsClientInGame(client))
	{
		float vVel[3];

		vVel[0] = 0.0;
		vVel[1] = 0.0;
		vVel[2] = 800.0;

		EmitSoundToAll("ambient/explosions/exp2.wav", client, _, _, _, 1.0);
		EmitSoundToAll("npc/env_headcrabcanister/launch.wav", client, _, _, _, 1.0);

		TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vVel);
		SetEntityGravity(client, 0.1);
	}

	return Plugin_Handled;
}

public Action Detonate(Handle timer, any client)
{
	if (IsClientInGame(client))
	{
		float vPlayer[3];
		GetClientAbsOrigin(client, vPlayer);

		if (gametype == 1)
		{
			DeleteParticle(g_Ent[client]);
			g_Ent[client] = 0;

			if (IsBonusRound)
			{
				float ClientOrigin[3];
				GetClientAbsOrigin(client, ClientOrigin);

				int g_ent = CreateEntityByName("env_explosion");
				DispatchKeyValue(g_ent, "iMagnitude", "2000");
				DispatchKeyValue(g_ent, "iRadiusOverride", "15");
				DispatchSpawn(g_ent);
				TeleportEntity(g_ent, ClientOrigin, NULL_VECTOR, NULL_VECTOR);
				AcceptEntityInput(g_ent, "Explode");
				CreateTimer(3.0, KillExplosion, g_ent);
			}
			else
			{
				FakeClientCommand(client, "Explode");
			}
		}
		else if (gametype == 2)
		{
			DeleteParticle(g_Ent[client]);
			g_Ent[client] = 0;

			FakeClientCommand(client, "Explode");
		}
		else
		{
			TE_SetupExplosion(vPlayer, g_Explosion, 10.0, 1, 0, 600, 5000);
			TE_SendToAll();
			g_Ent[client] = 0;

			ForcePlayerSuicide(client);
		}

		SetEntityGravity(client, 1.0);
		isInRocketMeMode[client] = false;
	}
	return Plugin_Handled;
}

public Action KillExplosion(Handle timer, any ent)
{
    if (IsValidEntity(ent))
    {
        char classname[256];
        GetEdictClassname(ent, classname, sizeof(classname));
        if (StrEqual(classname, "env_explosion", false))
        {
            RemoveEdict(ent);
        }
    }
}

public void OnLibRemoved(const char[] name)
{
	if (StrEqual(name, "adminmenu"))
	{
		delete hAdminMenu;
	}
}

public void OnAdminMenuReady(Handle topmenu)
{
	if (topmenu == hAdminMenu)
	{
		return;
	}

	hAdminMenu = topmenu;

	TopMenuObject player_commands = FindTopMenuCategory(hAdminMenu, ADMINMENU_PLAYERCOMMANDS);

	if (player_commands != INVALID_TOPMENUOBJECT)
	{
		AddToTopMenu(hAdminMenu,
			"sm_evilrocket",
			TopMenuObject_Item,
			AdminMenu_rocket,
			player_commands,
			"sm_evilrocket",
			ADMFLAG_SLAY);
	}
}

public int AdminMenu_rocket(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
	if (action == TopMenuAction_DisplayOption)
	{
		Format(buffer, maxlength, "Evil Rocket");
	}
	else if( action == TopMenuAction_SelectOption)
	{
		DisplayPlayerMenu(param);
	}
}

void DisplayPlayerMenu(int client)
{
	Handle menu = CreateMenu(MenuHandler_Players);

	char title[100];
	Format(title, sizeof(title), "Choose Player:");
	SetMenuTitle(menu, title);
	SetMenuExitBackButton(menu, true);

	AddTargetsToMenu(menu, client, true, true);

	DisplayMenu(menu, client, MENU_TIME_FOREVER);
}

public int MenuHandler_Players(Handle menu, MenuAction action, int param1, int param2)
{
	if (action == MenuAction_End)
	{
		CloseHandle(menu);
	}
	else if (action == MenuAction_Cancel)
	{
		if (param2 == MenuCancel_ExitBack && hAdminMenu != INVALID_HANDLE)
		{
			DisplayTopMenu(hAdminMenu, param1, TopMenuPosition_LastCategory);
		}
	}
	else if (action == MenuAction_Select)
	{
		char info[32];
		int userid, target;

		GetMenuItem(menu, param2, info, sizeof(info));
		userid = StringToInt(info);

		if ((target = GetClientOfUserId(userid)) == 0)
		{
			CPrintToChat(param1, "{GREEN}[SM]{DEFAULT} %s", "Player no longer available");
		}
		else if (!CanUserTarget(param1, target))
		{
			CPrintToChat(param1, "{GREEN}[SM]{DEFAULT} %s", "Unable to target");
		}
		else
		{
			PerformEvilRocket(param1, target);
		}

		/* Re-draw the menu if they're still valid */
		if (IsClientInGame(param1) && !IsClientInKickQueue(param1))
		{
			DisplayPlayerMenu(param1);
		}
	}
}

void AttachParticle(int ent, char[] particleType)
{
	int particle = CreateEntityByName("info_particle_system");

	char tName[128], pName[128];
	if (IsValidEdict(particle))
	{
		float pos[3];
		GetEntPropVector(ent, Prop_Send, "m_vecOrigin", pos);

		if (gametype == 1)
		{
			pos[2] += 10;
			TeleportEntity(particle, pos, NULL_VECTOR, NULL_VECTOR);
		}
		else if (gametype == 2)
		{
			pos[2] += 50;
			TeleportEntity(particle, pos, NULL_VECTOR, NULL_VECTOR);
		}

		Format(tName, sizeof(tName), "target%i", ent);
		DispatchKeyValue(ent, "targetname", tName);

		Format(pName, sizeof(pName), "particle%i", ent);
		DispatchKeyValue(particle, "targetname", pName);

		DispatchKeyValue(particle, "parentname", tName);
		DispatchKeyValue(particle, "effect_name", particleType);
		DispatchSpawn(particle);

		SetVariantString(tName);
		AcceptEntityInput(particle, "SetParent", particle, particle, 0);

		if (gametype == 1)
		{
			SetVariantString("flag");
			AcceptEntityInput(particle, "SetParentAttachment", particle, particle, 0);
		}
		ActivateEntity(particle);
		AcceptEntityInput(particle, "start");

		g_Ent[ent] = particle;
	}
}

void DeleteParticle(any particle)
{
	if (IsValidEntity(particle))
	{
		char classname[256];
		GetEdictClassname(particle, classname, sizeof(classname));
		if (StrEqual(classname, "info_particle_system", false))
		{
			RemoveEdict(particle);
		}
	}
}

void AttachFlame(int ent)
{
	char flame_name[128];
	Format(flame_name, sizeof(flame_name), "RocketFlame%i", ent);

	char tName[128];

	int flame = CreateEntityByName("env_steam");
	if (IsValidEdict(flame))
	{
		float pos[3];
		GetEntPropVector(ent, Prop_Send, "m_vecOrigin", pos);
		pos[2] += 30;

		float angles[3];
		angles[0] = 90.0;
		angles[1] = 0.0;
		angles[2] = 0.0;

		Format(tName, sizeof(tName), "target%i", ent);
		DispatchKeyValue(ent, "targetname", tName);

		DispatchKeyValue(flame, "targetname", flame_name);
		DispatchKeyValue(flame, "parentname", tName);
		DispatchKeyValue(flame, "SpawnFlags", "1");
		DispatchKeyValue(flame, "Type", "0");
		DispatchKeyValue(flame, "InitialState", "1");
		DispatchKeyValue(flame, "Spreadspeed", "10");
		DispatchKeyValue(flame, "Speed", "800");
		DispatchKeyValue(flame, "Startsize", "10");
		DispatchKeyValue(flame, "EndSize", "250");
		DispatchKeyValue(flame, "Rate", "15");
		DispatchKeyValue(flame, "JetLength", "400");
		DispatchKeyValue(flame, "RenderColor", "180 71 8");
		DispatchKeyValue(flame, "RenderAmt", "180");
		DispatchSpawn(flame);
		TeleportEntity(flame, pos, angles, NULL_VECTOR);
		SetVariantString(tName);
		AcceptEntityInput(flame, "SetParent", flame, flame, 0);

		CreateTimer(3.0, DeleteFlame, flame);

		g_Ent[ent] = flame;
	}
}

public Action DeleteFlame(Handle timer, any ent)
{
	if (IsValidEntity(ent))
    {
        char classname[256];
        GetEdictClassname(ent, classname, sizeof(classname));
        if (StrEqual(classname, "env_steam", false))
        {
            RemoveEdict(ent);
        }
    }
}

public Action Command_RocketMe(int client, int args)
{
	if (!IsValidClient(client)) return Plugin_Handled;
	if (CheckCommandAccess(client, "sm_rocketme_admin_override", ADMFLAG_VOTE))
	{
		if (IsClientInGame(client) && IsPlayerAlive(client) && !isInRocketMeMode[client])
		{
			PerformEvilRocket(-1, client);
			CreateTimer(3.0, MessageUs, client);
		}
	}
	else if (Cvar_RocketMe.BoolValue && CheckCommandAccess(client, "sm_rocketme_override", 0))
	{
		if (IsClientInGame(client) && IsPlayerAlive(client) && !isInRocketMeMode[client])
		{
			PerformEvilRocket(-1, client);
			CreateTimer(3.0, MessageUs, client);
		}
	}
	else
	{
		CReplyToCommand(client, "{GREEN}[SM]{DEFAULT} RocketMe is not enabled");
	}

	return Plugin_Handled;
}

public Action MessageUs(Handle timer, any client)
{
	CPrintToChatAll("{GREEN}[SM]{DEFAULT} {LIGHTGREEN}%N{DEFAULT} died in a rocket-related accident.", client);
}


Add player method

Add player method

/**
 * vim: set ts=4 :
 * =============================================================================
 * in_game_audio
 * TODO - Add your project's description
 *
 * Copyright 2013 ???
 * =============================================================================
 *
 */

#pragma semicolon 1

#include <sourcemod>
#include <socket>
#include <base64>
#include <smjansson>

#define PLUGIN_VERSION "0.1"

public Plugin:myinfo =
{
    name = "",
    author = "",
    description = "",
    version = PLUGIN_VERSION,
    url = ""
};

#define SONG_INFO_ROUTE "/v1/api/song_info"
#define SONGS_ROUTE "/songs"

new Handle:g_Cvar_IGAApiKey = INVALID_HANDLE;
new Handle:g_Cvar_IGAUrl = INVALID_HANDLE;
new Handle:g_Cvar_IGAport = INVALID_HANDLE;
new Handle:g_Cvar_IGADonatorsOnly = INVALID_HANDLE;
new Handle:g_Cvar_IGAEnabled = INVALID_HANDLE;

public OnPluginStart()
{

    g_Cvar_IGAApiKey = CreateConVar(sm_iga_api_key, "", "API Key for your IGA webpage");
    g_Cvar_IGAUrl = CreateConVar(sm_iga_url, "", "URL to your IGA webpage");
    g_Cvar_IGAPort = CreateConVar("sm_iga_port", "80", "HTTP Port used");
    g_Cvar_IGADonatorsOnly = CreateConVar(sm_iga_donators_only, "1", "Whether or not only donators have access to pall");
    g_Cvar_IGAEnabled = CreateConVar(sm_iga_enabled, "1", "Whether or not pall is enabled");

    RegConsoleCmd("sm_p", Command_P, "Play a song for yourself");
    RegConsoleCmd("sm_pall", Command_Pall, "Play a song for everyone");
    RegConsoleCmd("sm_plist", Command_Plist, "Pop-up the song list");
    RegConsoleCmd("sm_stop", Command_Stop, "Stop the current song");
    RegConsoleCmd("sm_fstop", Command_Fstop, "[ADMIN] Stop the current pall for everyone");
    RegConsoleCmd("sm_fpall", Command_Fpall, "[ADMIN] Force everyone to listen to a song");
    RegConsoleCmd("sm_vol", Command_Vol, "Adjust your play volume");
    RegConsoleCmd("sm_nopall", Command_Nopall, "Turn off pall for yourself");
    RegConsoleCmd("sm_plast", Command_Plast, "Play the last played song for yourself");

    HookEvent("map_change", Event_MapChange);
    HookEvent("client_connect", Event_ClientConnect);
}

public OnSocketConnected(Handle:socket, any:headers_pack)
{
    decl String:request_string[1024];

    ResetPack(headers_pack);
    ReadPackString(headers_pack, request_string, sizeof(request_string));

    SocketSend(socket, request_string);
}

public OnSocketReceive(Handle:socket, String:receive_data[], const data_size, any:headers_pack) {
    //TODO parse JSON response
    //Used for data received back
    PrintToConsole(0,"%s", receive_data);//TODO
}

public OnSocketDisconnected(Handle:socket, any:headers_pack) {
    // Connection: close advises the webserver to close the connection when the transfer is finished
    // we're done here
    CloseHandle(headers_pack);
    CloseHandle(socket);
}

public OnSocketError(Handle:socket, const error_type, const error_num, any:headers_pack) {
    // a socket error occured
    if(error_type == EMPTY_HOST )
    {
        LogError("[IGA] Empty Host (errno %d)", error_num);
    } else if (error_type == NO_HOST )
    {
        LogError("[IGA] No Host (errno %d)", error_num);
    } else if (error_type == CONNECT_ERROR )
    {
        LogError("[IGA] Connection Error (errno %d)", error_num);
    } else if (error_type == SEND_ERROR )
    {
        LogError("[IGA] Send Error (errno %d)", error_num);
    } else if (error_type == BIND_ERROR )
    {
        LogError("[IGA] Bind Error (errno %d)", error_num);
    } else if (error_type == RECV_ERROR )
    {
        LogError("[IGA] Recieve Error (errno %d)", error_num);
    } else if (error_type == LISTEN_ERROR )
    {
        LogError("[IGA] Listen Error (errno %d)", error_num);
    } else
    {
        LogError("[IGA] socket error %d (errno %d)", errorType, error_num);
    }

    CloseHandle(headers_pack);
    CloseHandle(socket);
}

public Action:Command_P(client, args)
{
    //TODO
}

public Action:Command_Pall(client, args)
{
    //TODO
}

public Action:Command_Plist(client, args)
{
    //TODO
}

public Action:Command_Stop(client, args)
{
    //TODO
}

public Action:Command_Fstop(client, args)
{
    //TODO
}

public Action:Command_Fpall(client, args)
{
    //TODO
}

public Action:Command_Vol(client, args)
{
    //TODO
}

public Action:Command_Nopall(client, args)
{
    //TODO
}

public Action:Command_Plast(client, args)
{
    //TODO
}

public Event_MapChange(Handle:event, const String:name[], bool:dontBroadcast)
{
    //TODO
}

public Event_ClientConnect(Handle:event, const String:name[], bool:dontBroadcast)
{
    //TODO
}

public IGAApiCall(String:route[128], String:query_params[512])
{
    new port= GetConVarInt(g_Cvar_IGAPort);
    decl String:base_url[128], String:api_key[128];
    GetConVarString(g_Cvar_IGAUrl, base_url, sizeof(base_url));
    GetConVarString(g_Cvar_IGAApiKey, api_key, sizeof(api_key));

    ReplaceString(base_url, sizeof(base_url), "http://", "", false);
    ReplaceString(base_url, sizeof(base_url), "https://", "", false);

    Format(query_params, sizeof(query_params), "%s&access_token=%s", query_params, api_key);

    HTTPPost(base_url, route, query_params, port);
}

public HTTPPost(String:base_url[128], String:route[128], String:query_params[512], port)
{
    new Handle:socket = SocketCreate(SOCKET_TCP, OnSocketError);

    //This Formats the headers needed to make a HTTP/1.1 POST request.
    new String:request_string[1024];
    Format(request_string, sizeof(request_string), "POST %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\nContent-type: application/x-www-form-urlencoded\r\nContent-length: %d\r\n\r\n%s", route, base_url, strlen(headers), headers);

    new Handle:headers_pack = CreateDataPack();
    WritePackString(headers_pack, request_string);
    SocketSetArg(socket, headers_pack);

    SocketConnect(socket, OnSocketConnected, OnSocketReceive, OnSocketDisconnected, base_url, port);
}

public PlaySong(client, song[128])
{
        decl String:url[256], String:base_url[128];
        GetConVarString(g_Cvar_IGAUrl, base_url, sizeof(base_url));
        ReplaceString(base_url, sizeof(base_url), "http://", "", false);
        ReplaceString(base_url, sizeof(base_url), "https://", "", false);

        Format(url, sizeof(url),
                "http://%s%s/%s", base_url, SONGS_ROUTE, song);

        //TODO make popunder
        ShowMOTDPanel(client, "Song Player", url, MOTDPANEL_TYPE_URL);

}