Raised This Month: $32 Target: $400
 8% 

[L4D,L4D2,TF2] BuiltinVotes 0.5.8 (2013-01-29)


Post New Thread Reply   
 
Thread Tools Display Modes
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 07-30-2011 , 13:10   Re: [WIP] Builtin Votes
Reply With Quote #11

Quote:
Originally Posted by GoD-Tony View Post
Sorry if this has already been answered, but couldn't this be done in pawn (still providing natives) since it's based on usermessages?
It can, but in doing so, you lose:
  1. Handle type checking.
  2. The ability to return things via multi-dimensional arrays.
The decision to make this an extension was made by me in order to make it as close to a drop-in replacement for the current voting system as possible.

#2 causes huge problems because of how the current VoteHandler callback works, which is used with SetVoteResultsCallback. Specifically, it takes two multi-dimensional arrays. Which means the BuiltinVotes equivalent would have to return adt_arrays and the vote processing would have to be different.
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 07-30-2011 at 13:40.
Powerlord is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 07-31-2011 , 03:20   Re: [WIP] Builtin Votes
Reply With Quote #12

I made quite a bit of progress today.

I've eliminated every crash I've found so far. Some were stupid (got the wrong cvar names), while some were a bit trickier such as a value getting set in a handler that was no longer being called, but that value was necessary to avoid a crash.

Sadly, I have my plate full of non-crash bugs to deal with.

My current bug list is this:
1. Items are not showing in a multiple choice vote (TF2)
2. Vote timer never ends (all)

Sadly, these bugs are more or less keeping me from further testing of the success/fail screens.

There is also one known bug that I don't intend to fix at the moment:
There is currently no way to show the Map Extended success screen.

Anyway, I'll hopefully iron out some of these bugs tomorrow.

Side Note: I haven't tested the L4D/L4D2 logic at all, only the TF2 logic.
__________________
Not currently working on SourceMod plugin development.
Powerlord is offline
Mr. Zero
Veteran Member
Join Date: Jun 2009
Location: Denmark
Old 08-02-2011 , 11:58   Re: [WIP] Builtin Votes
Reply With Quote #13

Since you are doing so much progress more than me, here is my current shitty version of a l4d2 vote handler (far from done).

Have fun

PHP Code:
/**
 * =============================================================================
 * L4D2 Vote Manager (C)2011 Buster "Mr. Zero" Nielsen
 * =============================================================================
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 3.0, as published by the
 * Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * As a special exception, AlliedModders LLC gives you permission to link the
 * code of this program (as well as its derivative works) to "Half-Life 2," the
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 * by the Valve Corporation.  You must obey the GNU General Public License in
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
 * this exception to all derivative works.  AlliedModders LLC defines further
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 * or <http://www.sourcemod.net/license.php>.
 */

/*
 * ==================================================
 *                    Preprocessor
 * ==================================================
 */

#if defined DEBUG_CHANNEL_NAME
#undef DEBUG_CHANNEL_NAME
#endif
#define DEBUG_CHANNEL_NAME "VoteHandler"

#define L4D_MAX_VOTE_ISSUES 8

/**
 * Full list of vote issues.
 *
 * Typing "listissues" in console while playing on a server shows a full list
 * over available issues.
 */
enum L4DVoteIssue
{
    
L4DVoteIssue_None = -1,
    
L4DVoteIssue_ChangeDifficulty 0,
    
L4DVoteIssue_RestartGame 1,
    
L4DVoteIssue_Kick 2,
    
L4DVoteIssue_ChangeMission 3,
    
L4DVoteIssue_ReturnToLobby 4,
    
L4DVoteIssue_ChangeChapter 5,
    
L4DVoteIssue_ChangeAllTalk 6,
    
L4DVoteIssue_Custom 7,
};

enum L4DVote
{
    
L4DVote_None = -1,
    
L4DVote_No 0,
    
L4DVote_Yes 1
}

/*
 * ==================================================
 *                     Includes
 * ==================================================
 */

/*
 * ==================================================
 *                     Variables
 * ==================================================
 */

/*
 * --------------------
 *       Public
 * --------------------
 */

/*
 * --------------------
 *       Private
 * --------------------
 */

static            Handleg_hFwd_OnVoteStart// Used for internal modules
static            Handleg_hGFwd_OnVoteStart;
static            
Handleg_hGFwd_OnVoteStart_Post;
static            
Handle:    g_hGFwd_OnAddClientToVotePool;
static            
Handle:    g_hGFwd_OnAddClientToVotePool_P;
static            
Handleg_hGFwd_OnVoteRegister;
static            
Handleg_hGFwd_OnVoteRegister_Post;
static            
Handle:    g_hGFwd_OnVoteDisplay;
static            
Handle:    g_hGFwd_OnVoteDisplay_Post;
static            
Handle:    g_hGFwd_OnVotePassDisplay;
static            
Handle:    g_hGFwd_OnVotePassDisplay_Post;
static            
Handle:    g_hGFwd_OnVoteFailDisplay;
static            
Handle:    g_hGFwd_OnVoteEnd;
static            
Handle:    g_hGFwd_OnVoteEnd_Post;

static            
bool:    g_bIsVoteInProgress;
static            
bool:    g_bIsVoteInitiatedByConsole;
static            
bool:    g_bIsShowingVoteCaller;
static            
bool:    g_bIsVoteInCooldown;
static                    
g_iVoteInitiator;
static                    
g_iTeamVoting;
static            
L4DVoteIssue:g_iCurrentVoteIssue;
static            
Float:    g_fVoteStartTime;
static            
Float:    g_fVoteTime;
static            
Float:    g_fDefaultVoteTime;
static            
String:    g_sVoteIssue[256];
static            
String:    g_sVoteParam[256];
static            
String:    g_sVoteInitiatorName[128];
static            
String:    g_sVoteIssueTranslation[256];
static            
String:    g_sVoteParamTranslation[256];
static            
String:    g_sVotePassIssueTranslation[256];
static            
String:    g_sVotePassParamTranslation[256];

static    const            
VOTE_INITIATOR_CONSOLE 99// Initiator index when started by console
static    const    String:    VOTE_INITIATOR_CONSOLE_NAME[] = "Console";
static    const    
Float:    VOTE_COOLDOWN_TIME 6.0// 6 seconds for vote pass/fail to disappear

static            bool:    g_bCanVote[MAXPLAYERS 1];
static            
bool:    g_bChangedVoteIncomming[MAXPLAYERS 1];
static            
bool:    g_bChangedVoteStartIncomming[MAXPLAYERS 1];
static            
bool:    g_bChangedVotePassIncomming[MAXPLAYERS 1];
static            
L4DVote:g_iVote[MAXPLAYERS 1];
static                    
g_iTotalVoters;
static                    
g_iYesVotes;
static                    
g_iNoVotes;

static            
UserMsg:g_umVoteStart;
static            
UserMsg:g_umVotePass;
static            
UserMsg:g_umVoteFail;
static            
UserMsg:g_umVoteRegistered;

static            
Handle:    g_hVoteCreationTimer_Cvar;
static            
Handle:    g_hVoteFailedMapLimit_Cvar;
static            
Handle:    g_hVoteShowCaller_Cvar;
static            
Handle:    g_hVoteTimerDuration_Cvar;

static    const    
String:    VOTE_CAST_YES[] = "Yes";
static    const    
String:    VOTE_CAST_NO[] = "No";

static    const            
MAX_FAILED_VOTE_MAP_LIMIT 999999// Any high arbitrary number will do to allow "unlimited" voting

/* Custom vote translation string.
 * An empty translation string that contains nothing but %s1 to allow us
 * to fill in whatever we desire via the param text. Can be found in 
 * resource/left4dead2_english.txt at line 729 */
static    const    String:    VOTE_ISSUE_CUSTOM[] = "#L4D_TargetID_Player";

static    const            
VOTE_TEAMBYTE_SIGNED_ALL_TEAMS 255// Sourcemod can't read signed bytes from usermessages right now. The value is really -1.

/* List over call vote issue strings, must match L4DVoteIssue enum. */
static    const    String:    CALLVOTE_ISSUE_STRINGS[][] =
{
    
"",
    
"ChangeDifficulty",
    
"RestartGame",
    
"Kick",
    
"ChangeMission",
    
"ReturnToLobby",
    
"ChangeChapter",
    
"ChangeAllTalk",
    
"Custom"
};

/*
 * ==================================================
 *                     Forwards
 * ==================================================
 */

/**
 * Called on plugin start.
 *
 * @noreturn
 */
public _VoteHandler_OnPluginStart()
{
    
g_hFwd_OnVoteStart CreateForward(ET_HookParam_CellParam_CellParam_String);

    
/* Global forwards */
    
g_hGFwd_OnVoteStart                CreateGlobalForward("L4D_OnVoteStart"ET_HookParam_CellParam_CellParam_String);
    
g_hGFwd_OnVoteStart_Post        CreateGlobalForward("L4D_OnVoteStart_Post"ET_IgnoreParam_CellParam_CellParam_StringParam_ArrayParam_CellParam_Float);
    
g_hGFwd_OnAddClientToVotePool    CreateGlobalForward("L4D_OnAddClientToVotePool"ET_HookParam_Cell);
    
g_hGFwd_OnAddClientToVotePool_P    CreateGlobalForward("L4D_OnAddClientToVotePool_Post"ET_IgnoreParam_Cell);
    
g_hGFwd_OnVoteRegister            CreateGlobalForward("L4D_OnVoteRegister"ET_HookParam_CellParam_CellByRef);
    
g_hGFwd_OnVoteRegister_Post        CreateGlobalForward("L4D_OnVoteRegister_Post"ET_IgnoreParam_CellParam_Cell);
    
g_hGFwd_OnVoteDisplay            CreateGlobalForward("L4D_OnVoteDisplay"ET_HookParam_CellParam_StringParam_CellParam_StringParam_CellParam_StringParam_Cell);
    
g_hGFwd_OnVoteDisplay_Post        CreateGlobalForward("L4D_OnVoteDisplay_Post"ET_IgnoreParam_CellParam_StringParam_StringParam_String);
    
g_hGFwd_OnVotePassDisplay        CreateGlobalForward("L4D_OnVotePassDisplay"ET_HookParam_CellParam_StringParam_CellParam_StringParam_Cell);
    
g_hGFwd_OnVotePassDisplay_Post    CreateGlobalForward("L4D_OnVotePassDisplay_Post"ET_IgnoreParam_CellParam_StringParam_String);
    
g_hGFwd_OnVoteFailDisplay        CreateGlobalForward("L4D_OnVoteFailDisplay"ET_IgnoreParam_Cell);

    
/* User messages */
    
g_umVoteStart GetUserMessageId("VoteStart");
    
HookUserMessage(g_umVoteStart_VH_VoteStart_UserMsgtrue);
    
g_umVotePass GetUserMessageId("VotePass");
    
HookUserMessage(g_umVotePass_VH_VotePass_UserMsgtrue);
    
g_umVoteFail GetUserMessageId("VoteFail");
    
HookUserMessage(g_umVoteFail_VH_VoteFail_UserMsg);
    
g_umVoteRegistered GetUserMessageId("VoteRegistered");
    
HookUserMessage(g_umVoteRegistered_VH_VoteRegistered_UserMsg);

    
/* Vote command listeners */
    
AddCommandListener(_VH_OnCallVote_Command"callvote");
    
AddCommandListener(_VH_OnVote_Command"vote");

    
/* Remove vote creation timer */
    
g_hVoteCreationTimer_Cvar FindConVar("sv_vote_creation_timer");
    if (
g_hVoteCreationTimer_Cvar == INVALID_HANDLESetFailState("Unable to find sv_vote_creation_timer cvar.");
    
SetConVarFloat(g_hVoteCreationTimer_Cvar0.0);
    
HookConVarChange(g_hVoteCreationTimer_Cvar_VH_VoteCvars_CvarChange);

    
/* Remove failed vote limit */
    
g_hVoteFailedMapLimit_Cvar FindConVar("sv_vote_plr_map_limit");
    if (
g_hVoteFailedMapLimit_Cvar == INVALID_HANDLESetFailState("Unable to find sv_vote_plr_map_limit cvar.");
    
SetConVarInt(g_hVoteFailedMapLimit_CvarMAX_FAILED_VOTE_MAP_LIMIT);
    
HookConVarChange(g_hVoteFailedMapLimit_Cvar_VH_VoteCvars_CvarChange);

    
/* Get vote show caller value */
    
g_hVoteShowCaller_Cvar FindConVar("sv_vote_show_caller");
    if (
g_hVoteShowCaller_Cvar == INVALID_HANDLESetFailState("Unable to find sv_vote_show_caller cvar.");
    
g_bIsShowingVoteCaller GetConVarBool(g_hVoteShowCaller_Cvar);
    
HookConVarChange(g_hVoteShowCaller_Cvar_VH_VoteCvars_CvarChange);

    
/* Get vote time */
    
g_hVoteTimerDuration_Cvar FindConVar("sv_vote_timer_duration");
    if (
g_hVoteTimerDuration_Cvar == INVALID_HANDLESetFailState("Unable to find sv_vote_timer_duration cvar.");
    
g_fDefaultVoteTime GetConVarFloat(g_hVoteTimerDuration_Cvar);
    
HookConVarChange(g_hVoteTimerDuration_Cvar_VH_VoteCvars_CvarChange);

    
Debug_PrintTextEx("Module start");
}

/**
 * Called on plugin end.
 *
 * @noreturn
 */
public _VoteHandler_OnPluginEnd()
{
    
ResetConVar(g_hVoteCreationTimer_Cvar);
    
ResetConVar(g_hVoteFailedMapLimit_Cvar);

    
Debug_PrintTextEx("Module end");
}

/**
 * Called on vote cvars changed.
 *
 * @param convar        Handle to the convar that was changed.
 * @param oldValue        String containing the value of the convar before it was changed.
 * @param newValue        String containing the new value of the convar.
 * @noreturn
 */
public _VH_VoteCvars_CvarChange(Handle:convar, const String:oldValue[], const String:newValue[])
{
    
SetConVarFloat(g_hVoteCreationTimer_Cvar0.0);
    
SetConVarInt(g_hVoteFailedMapLimit_CvarMAX_FAILED_VOTE_MAP_LIMIT);
    
g_bIsShowingVoteCaller GetConVarBool(g_hVoteShowCaller_Cvar);
    
g_fDefaultVoteTime GetConVarFloat(g_hVoteTimerDuration_Cvar);
}

/**
 * Called on map start.
 *
 * @noreturn
 */
public _VH_OnMapStart()
{
    
g_bIsVoteInProgress false;
    
g_bIsVoteInCooldown false;
    for (new 
0<= MAXPLAYERSi++)
    {
        
g_bCanVote[i] = false;
        
g_iVote[i] = L4DVote_None;
        
g_bChangedVoteIncomming[i] = false;
        
g_bChangedVoteStartIncomming[i] = false;
        
g_bChangedVotePassIncomming[i] = false;
    }
}

/**
 * Called on client disconnect.
 *
 * @parma client        Client index.
 * @noreturn
 */
public _VH_OnClientDisconnect(client)
{
    
g_bCanVote[client] = false;
    
g_iVote[client] = L4DVote_None;
    
g_bChangedVoteIncomming[client] = false;
    
g_bChangedVoteStartIncomming[client] = false;
    
g_bChangedVotePassIncomming[client] = false;
    
//UpdateVote();
}

/**
 * On callvote command.
 *
 * @param client        Client, or 0 for server. Client will be connected but
 *                      not necessarily in game.
 * @param command       Command name, lower case. To get name as typed, use
 *                      GetCmdArg() and specify argument 0.
 * @param argc          Argument count.
 * @return                Returning Plugin_Handled or Plugin_Stop will prevent
 *                        the original, baseline code from running.
 */ 
public Action:_VH_OnCallVote_Command(client, const String:command[], argc)
{
    if (
argc 1) return Plugin_Continue;

    
decl String:callvote[64];
    
GetCmdArg(1callvotesizeof(callvote));
    
TrimString(callvote);

    new 
L4DVoteIssue:issueId;
    
issueId L4D_GetVoteIssueIdOfCallVote(callvote);

    if (
issueId == L4DVoteIssue_None) return Plugin_Handled;

    if (
client == 0)
    {
        if (
issueId != L4DVoteIssue_Custom)
        {
            
PrintToServer("[SM] %T""Server can only start custom votes"LANG_SERVER);
            return 
Plugin_Handled;
        }
        else if (
g_bIsVoteInProgress)
        {
            
PrintToServer("[SM] %T""Another vote is already in progress"LANG_SERVER);
            return 
Plugin_Handled;
        }
        else if (
g_bIsVoteInCooldown)
        {
            
PrintToServer("[SM] %T""Voting is in cooldown"LANG_SERVER);
            return 
Plugin_Handled;
        }
    }

    if (
g_bIsVoteInProgress || g_bIsVoteInCooldown) return Plugin_Handled;

    
g_iCurrentVoteIssue issueId;
    
strcopy(g_sVoteIssuesizeof(g_sVoteIssue), callvote);

    
decl String:param[256];
    
GetCmdArg(2paramsizeof(param));
    
TrimString(param);

    
/* Internal forwarding */
    
Debug_PrintTextEx("Internal forwarding of OnVoteStart. Params %d, %d, %s"clientissueIdparam);
    new 
Action:result Plugin_Continue;
    
Call_StartForward(g_hFwd_OnVoteStart);
    
Call_PushCell(client);
    
Call_PushCell(issueId);
    
Call_PushString(param);
    
Call_Finish(result);

    
Debug_PrintTextEx("result %d"result);

    if (
result == Plugin_Handled)
    {
        
Debug_PrintTextEx("Internal module returned handled, stopping vote.");
        return 
Plugin_Handled;
    }

    
/* Global forwarding */
    
Debug_PrintTextEx("Global forwarding of OnVoteStart. Params %d, %d, %s"clientissueIdparam);
    
result Plugin_Continue;
    
Call_StartForward(g_hGFwd_OnVoteStart);
    
Call_PushCell(client);
    
Call_PushCell(issueId);
    
Call_PushString(param);
    
Call_Finish(result);

    
Debug_PrintTextEx("result %d"result);

    if (
result == Plugin_Handled)
    {
        
Debug_PrintTextEx("Global forward returned handled, stopping vote.");
        return 
Plugin_Handled;
    }

    
strcopy(g_sVoteParam256param);
    if (
client != 0)
    {
        
g_bIsVoteInitiatedByConsole false;
        
g_iVoteInitiator GetClientUserId(client);
        
GetClientName(clientg_sVoteInitiatorNamesizeof(g_sVoteInitiatorName));
    }
    else
    {
        
g_bIsVoteInitiatedByConsole true;
        
g_iVoteInitiator VOTE_INITIATOR_CONSOLE;
        
strcopy(g_sVoteInitiatorNamesizeof(g_sVoteInitiatorName), VOTE_INITIATOR_CONSOLE_NAME);
    }

    if (
issueId == L4DVoteIssue_Custom)
    {
        new 
numClients 0;
        new 
clients[MaxClients];
        for (new 
1<= MaxClientsi++)
        {
            if (!
IsClientInGame(i) || IsFakeClient(i)) continue;
            
clients[numClients++] = i;
        }
        
//StartCustomVote(clients, numClients, param, _, client);

        
if (client == 0)
        {
            
PrintToServer("[SM] %T""Custom vote started"LANG_SERVER);
        }

        
LogAction(client, -1"%L started a custom vote with the issue of \"%s\""clientparam);

        return 
Plugin_Handled;
    }

    return 
Plugin_Continue;
}

/**
 * Callback for vote command listener.
 *
 * @param client        Client, or 0 for server.
 * @param command       Command name, lower case.
 * @param argc          Argument count.
 * @return                Plugin_Handled to stop command from running, anything
 *                        else to allow command.
 */
public Action:_VH_OnVote_Command(client, const String:command[], argc)
{
    if (!
g_bIsVoteInProgress || argc != 1) return Plugin_Continue;

    if (!
g_bCanVote[client] || g_iVote[client] != L4DVote_None) return Plugin_Handled// Client have already voted or cannot vote

    
decl String:voteRaw[8];
    
GetCmdArg(1voteRawsizeof(voteRaw));
    new 
L4DVote:vote;

    if (
StrEqual(voteRawVOTE_CAST_YESfalse))
    {
        
vote L4DVote_Yes;
    }
    else if (
StrEqual(voteRawVOTE_CAST_NOfalse))
    {
        
vote L4DVote_No;
    }
    else
    {
        return 
Plugin_Handled;
    }

    if (!
g_bChangedVoteIncomming[client])
    {
        
Debug_PrintTextEx("Client %N (%d) casted their vote. Voted %s."clientclient, (vote == L4DVote_Yes VOTE_CAST_YES VOTE_CAST_NO));

        new 
L4DVote:refVote vote;
        new 
Action:result Plugin_Continue;

        
Call_StartForward(g_hGFwd_OnVoteRegister);
        
Call_PushCell(client);
        
Call_PushCellRef(refVote);
        
Call_Finish(result);

        if (
result == Plugin_Changed && refVote != vote)
        {
            
Debug_PrintTextEx("Forward return changed vote. New vote casted %s.", (vote == L4DVote_Yes VOTE_CAST_YES VOTE_CAST_NO));
            
vote refVote;
            
g_bChangedVoteIncomming[client] = true;
            
FakeClientCommand(client"Vote %s", (vote == L4DVote_Yes VOTE_CAST_YES VOTE_CAST_NO));
            return 
Plugin_Handled;
        }
        else if (
result == Plugin_Handled)
        {
            
Debug_PrintTextEx("Forward return handled, stopping registering vote.");
            return 
Plugin_Handled;
        }
    }

    
g_iVote[client] = vote;
    
g_bChangedVoteIncomming[client] = false;

    return 
Plugin_Continue;
}

/**
 * Called when vote start is hooked
 *
 * @param msg_id        Message index.
 * @param bf            Handle to the input bit buffer of the message.
 * @param players        Array containing player indexes.
 * @param playersNum    Number of players in the array.
 * @param reliable        True if message is reliable, false otherwise.
 * @param init            True if message is an initmsg, false otherwise.
 * @return                Plugin_Handled blocks the message from being sent, and
 *                        Plugin_Continue resumes normal functionality.
 */
public Action:_VH_VoteStart_UserMsg(UserMsg:msg_idHandle:bf, const players[], playersNumbool:reliablebool:init)
{
    
/**
     * VoteStart Structure
     * - Byte      Team index voting
     * - Byte      Vote initiator index
     * - String    Vote issue id
     * - String    Vote issue text
     * - String    Vote initiator name
     */

    /* To prevent endless loop when re-sending the modified vote start to
     * client */
    
if (g_bIsVoteInProgress && playersNum == && g_bChangedVoteStartIncomming[players[0]])
    {
        
g_bChangedVoteStartIncomming[players[0]] = false;
        return 
Plugin_Continue;
    }

    if (
g_bIsVoteInProgress || g_bIsVoteInCooldown)
    {
        return 
Plugin_Handled;
    }

    
g_bIsVoteInProgress true;
    
g_iTeamVoting BfReadByte(bf);
    
BfReadByte(bf); // Ignore initiator, already got that
    
BfReadString(bfg_sVoteIssueTranslationsizeof(g_sVoteIssueTranslation), true);
    
BfReadString(bfg_sVoteParamTranslationsizeof(g_sVoteParamTranslation), true);

    
g_fVoteStartTime GetEngineTime();
    if (
g_iCurrentVoteIssue != L4DVoteIssue_Custom)
    {
        
g_fVoteTime g_fDefaultVoteTime;
    }
    else
    {
        
g_fVoteTime g_fDefaultVoteTime;
    }

    
g_iTotalVoters 0;
    
g_iNoVotes 0;
    
g_iYesVotes 0;

    for (new 
0<= MAXPLAYERSi++)
    {
        
g_bCanVote[i] = false;
        
g_iVote[i] = L4DVote_None;
    }

    new 
Handle:pack CreateDataPack();

    new 
client;
    new 
voters[MAXPLAYERS 1];
    for (new 
0playersNumi++)
    {
        
client players[i];
        if (
client == || !IsClientInGame(client) || IsFakeClient(client)) continue;
        
voters[g_iTotalVoters++] = client;
    }

    
WritePackCell(packg_iTotalVoters);
    for (new 
0g_iTotalVotersi++)
    {
        
WritePackCell(packvoters[i]);
    }

    
/* Resend VoteStart usermessage in next frame. */
    
CreateTimer(0.0_VH_VoteStart_TimerpackTIMER_FLAG_NO_MAPCHANGE TIMER_DATA_HNDL_CLOSE);

    
LogAction((g_bIsVoteInitiatedByConsole g_iVoteInitiator), -1"%L started a %s vote with the param of %s", (g_bIsVoteInitiatedByConsole g_iVoteInitiator), g_sVoteIssueg_sVoteParam);

    
Call_StartForward(g_hGFwd_OnVoteStart_Post);
    
Call_PushCell((g_bIsVoteInitiatedByConsole g_iVoteInitiator));
    
Call_PushCell(g_iCurrentVoteIssue);
    
Call_PushString(g_sVoteParam);
    
Call_PushArray(playersplayersNum);
    
Call_PushCell(playersNum);
    
Call_PushFloat(g_fVoteTime);
    
Call_Finish();

    return 
Plugin_Handled;
}

/**
 * Called when vote start timer interval has elapsed.
 * 
 * @param timer            Handle to the timer object.
 * @param hndl            Handle to data pack.
 * @return                Plugin_Stop.
 */
public Action:_VH_VoteStart_Timer(Handle:timerHandle:pack)
{
    
ResetPack(pack);

    new 
totalVoters ReadPackCell(pack);
    
decl Handle:bf;
    new 
client;

    
decl String:initiatorName[128], String:voteIssue[256], String:voteParam[256];
    
decl String:refInitiatorName[128], String:refVoteIssue[256], String:refVoteParam[256];
    new 
Action:result;

    for (new 
0totalVotersi++)
    {
        
client ReadPackCell(pack);
        if (
client == || !IsClientInGame(client) || IsFakeClient(client)) continue;

        
result Plugin_Continue;
        
Call_StartForward(g_hGFwd_OnAddClientToVotePool);
        
Call_PushCell(client);
        
Call_Finish(result);

        if (
result == Plugin_Handled)
        {
            continue;
        }

        
g_bCanVote[client] = true;
        
g_iVote[client] = L4DVote_None;

        
Call_StartForward(g_hGFwd_OnAddClientToVotePool_P);
        
Call_PushCell(client);
        
Call_Finish();

        
/* Reset */
        
strcopy(initiatorName128g_sVoteInitiatorName);
        
strcopy(voteIssue256g_sVoteIssueTranslation);
        
strcopy(voteParam256g_sVoteParamTranslation);
        
strcopy(refInitiatorName128g_sVoteInitiatorName);
        
strcopy(refVoteIssue256g_sVoteIssueTranslation);
        
strcopy(refVoteParam256g_sVoteParamTranslation);

        
result Plugin_Continue;
        
Call_StartForward(g_hGFwd_OnVoteDisplay);
        
Call_PushCell(client);
        
Call_PushStringEx(refInitiatorNamesizeof(refInitiatorName), SM_PARAM_STRING_UTF8 SM_PARAM_STRING_COPYSM_PARAM_COPYBACK);
        
Call_PushCell(sizeof(refInitiatorName));
        
Call_PushStringEx(refVoteIssuesizeof(refVoteIssue), SM_PARAM_STRING_UTF8 SM_PARAM_STRING_COPYSM_PARAM_COPYBACK);
        
Call_PushCell(sizeof(refVoteIssue));
        
Call_PushStringEx(refVoteParamsizeof(refVoteParam), SM_PARAM_STRING_UTF8 SM_PARAM_STRING_COPYSM_PARAM_COPYBACK);
        
Call_PushCell(sizeof(refVoteParam));
        
Call_Finish(result);

        if (
result == Plugin_Changed)
        {
            
strcopy(initiatorName128refInitiatorName);
            
strcopy(voteIssue256refVoteIssue);
            
strcopy(voteParam256refVoteParam);
        }

        
Call_StartForward(g_hGFwd_OnVoteDisplay_Post);
        
Call_PushCell(client);
        
Call_PushString(initiatorName);
        
Call_PushString(voteIssue);
        
Call_PushString(voteParam);
        
Call_Finish();

        
g_bChangedVoteStartIncomming[client] = true;
        
bf StartMessageExOne(g_umVoteStartclientUSERMSG_RELIABLE);
        
BfWriteByte(bfg_iTeamVoting);
        
BfWriteByte(bfg_iVoteInitiator);
        
BfWriteString(bfvoteIssue);
        
BfWriteString(bfvoteParam);
        
BfWriteString(bfinitiatorName);
        
EndMessage();
    }

    return 
Plugin_Stop;
}

/**
 * Called when vote pass is hooked
 *
 * @param msg_id        Message index.
 * @param bf            Handle to the input bit buffer of the message.
 * @param players        Array containing player indexes.
 * @param playersNum    Number of players in the array.
 * @param reliable        True if message is reliable, false otherwise.
 * @param init            True if message is an initmsg, false otherwise.
 * @return                Plugin_Handled blocks the message from being sent, and
 *                        Plugin_Continue resumes normal functionality.
 */
public Action:_VH_VotePass_UserMsg(UserMsg:msg_idHandle:bf, const players[], playersNumbool:reliablebool:init)
{
    
/**
     * VotePass Structure
     * - Byte      Team index voting
     * - String    Vote issue id
     * - String    Vote issue text
     */

    /* To prevent endless loop when re-sending the modified vote pass to
     * client */
    
if (!g_bIsVoteInProgress && playersNum == && g_bChangedVotePassIncomming[players[0]])
    {
        
g_bChangedVotePassIncomming[players[0]] = false;
        return 
Plugin_Continue;
    }

    
BfReadByte(bf); // Ignore team index, already got that
    
BfReadString(bfg_sVotePassIssueTranslationsizeof(g_sVotePassIssueTranslation), true);
    
BfReadString(bfg_sVotePassParamTranslationsizeof(g_sVotePassParamTranslation), true);

    new 
Handle:pack CreateDataPack();

    new 
client;
    new 
voters[MAXPLAYERS 1];
    new 
count 0;
    for (new 
0playersNumi++)
    {
        
client players[i];
        if (
client == || !IsClientInGame(client) || IsFakeClient(client) || !g_bCanVote[client]) continue;
        
voters[count++] = client;
    }

    
WritePackCell(packcount);
    for (new 
0counti++)
    {
        
WritePackCell(packvoters[i]);
    }

    
/* Resend VotePass usermessage in next frame. */
    
CreateTimer(0.0_VH_VotePass_TimerpackTIMER_FLAG_NO_MAPCHANGE TIMER_DATA_HNDL_CLOSE);

    
g_bIsVoteInProgress false;
    if (!
g_bIsVoteInCooldown)
    {
        
CreateTimer(VOTE_COOLDOWN_TIME_VH_VoteCooldown_Timer_TIMER_FLAG_NO_MAPCHANGE);
    }
    
g_bIsVoteInCooldown true;

    return 
Plugin_Handled;
}

/**
 * Called when vote pass timer interval has elapsed.
 * 
 * @param timer            Handle to the timer object.
 * @param hndl            Handle to data pack.
 * @return                Plugin_Stop.
 */
public Action:_VH_VotePass_Timer(Handle:timerHandle:pack)
{
    
ResetPack(pack);

    new 
totalVoters ReadPackCell(pack);
    
decl Handle:bf;
    new 
client;

    
decl String:voteIssue[256], String:voteParam[256];
    
decl String:refVoteIssue[256], String:refVoteParam[256];
    new 
Action:result;

    for (new 
0totalVotersi++)
    {
        
client ReadPackCell(pack);
        if (
client == || !IsClientInGame(client) || IsFakeClient(client)) continue;

        
/* Reset */
        
strcopy(voteIssue256g_sVotePassIssueTranslation);
        
strcopy(voteParam256g_sVotePassParamTranslation);
        
strcopy(refVoteIssue256g_sVotePassIssueTranslation);
        
strcopy(refVoteParam256g_sVotePassParamTranslation);

        
result Plugin_Continue;
        
Call_StartForward(g_hGFwd_OnVotePassDisplay);
        
Call_PushCell(client);
        
Call_PushStringEx(refVoteIssuesizeof(refVoteIssue), SM_PARAM_STRING_UTF8 SM_PARAM_STRING_COPYSM_PARAM_COPYBACK);
        
Call_PushCell(sizeof(refVoteIssue));
        
Call_PushStringEx(refVoteParamsizeof(refVoteParam), SM_PARAM_STRING_UTF8 SM_PARAM_STRING_COPYSM_PARAM_COPYBACK);
        
Call_PushCell(sizeof(refVoteParam));
        
Call_Finish(result);

        if (
result == Plugin_Changed)
        {
            
strcopy(voteIssue256refVoteIssue);
            
strcopy(voteParam256refVoteParam);
        }

        
Call_StartForward(g_hGFwd_OnVotePassDisplay_Post);
        
Call_PushCell(client);
        
Call_PushString(voteIssue);
        
Call_PushString(voteParam);
        
Call_Finish();

        
g_bChangedVotePassIncomming[client] = true;
        
bf StartMessageExOne(g_umVotePassclientUSERMSG_RELIABLE);
        
BfWriteByte(bfg_iTeamVoting);
        
BfWriteString(bfvoteIssue);
        
BfWriteString(bfvoteParam);
        
EndMessage();
    }

    return 
Plugin_Stop;
}

/**
 * Called when vote fail is hooked
 *
 * @param msg_id        Message index.
 * @param bf            Handle to the input bit buffer of the message.
 * @param players        Array containing player indexes.
 * @param playersNum    Number of players in the array.
 * @param reliable        True if message is reliable, false otherwise.
 * @param init            True if message is an initmsg, false otherwise.
 * @return                Plugin_Continue.
 */
public Action:_VH_VoteFail_UserMsg(UserMsg:msg_idHandle:bf, const players[], playersNumbool:reliablebool:init)
{
    
/**
     * VoteFail Structure
     * - Byte      Team index voting
     */

    
g_bIsVoteInProgress false;
    if (!
g_bIsVoteInCooldown)
    {
        
CreateTimer(VOTE_COOLDOWN_TIME_VH_VoteCooldown_Timer_TIMER_FLAG_NO_MAPCHANGE);
    }
    
g_bIsVoteInCooldown true;

    new 
client;
    for (new 
0playersNumi++)
    {
        
client players[i];
        if (!
g_bCanVote[client]) continue;

        
Call_StartForward(g_hGFwd_OnVoteFailDisplay);
        
Call_PushCell(client);
        
Call_Finish();
    }

    return 
Plugin_Continue;
}

/**
 * Called when vote registered is hooked
 *
 * @param msg_id        Message index.
 * @param bf            Handle to the input bit buffer of the message.
 * @param players        Array containing player indexes.
 * @param playersNum    Number of players in the array.
 * @param reliable        True if message is reliable, false otherwise.
 * @param init            True if message is an initmsg, false otherwise.
 * @return                Plugin_Continue.
 */
public Action:_VH_VoteRegistered_UserMsg(UserMsg:msg_idHandle:bf, const players[], playersNumbool:reliablebool:init)
{
    
/**
     * VoteRegistered Structure
     * - Byte      Vote casted. 0: No, 1: Yes
     *
     * Note: Only the voting client gets this usermessage which causes their
     * HUD to fade out the voting choices.
     */

    
new L4DVote:vote L4DVote:BfReadByte(bf);
    new 
client players[0];

    
Call_StartForward(g_hGFwd_OnVoteRegister_Post);
    
Call_PushCell(client);
    
Call_PushCell(vote);
    
Call_Finish();

    return 
Plugin_Continue;
}

/**
 * Called when vote cooldown timer interval has elapsed.
 * 
 * @param timer            Handle to the timer object.
 * @return                Plugin_Stop.
 */
public Action:_VH_VoteCooldown_Timer(Handle:timer)
{
    
g_bIsVoteInCooldown false;
    return 
Plugin_Stop;
}

/*
 * ==================================================
 *                     Public API
 * ==================================================
 */

/**
 * Returns whether a vote is in progress.
 *
 * @return                True if a vote is in progress, false otherwise.
 */
stock bool:L4D_IsVoteInProgress()
{
    return 
g_bIsVoteInProgress;
}

/**
 * Returns whether vote is in cooldown.
 *
 * @return                True if vote is in cooldown, false otherwise.
 */
stock bool:L4D_IsVoteInCooldown()
{
    return 
g_bIsVoteInCooldown;
}

/**
 * Retrives current vote issue id.
 *
 * @return                Current vote issue id.
 */
stock L4DVoteIssue:L4D_GetVoteIssueId()
{
    if (!
g_bIsVoteInProgress) return L4DVoteIssue_None;
    return 
g_iCurrentVoteIssue;
}

/**
 * Retrives vote issue of issue name.
 *
 * @param callVoteString Call vote string of issue.
 * @return                Issue id of name.
 */
stock L4DVoteIssue:L4D_GetVoteIssueIdOfCallVote(const String:callVoteString[])
{
    for (new 
0sizeof(CALLVOTE_ISSUE_STRINGS); i++)
    {
        if (
StrEqual(callVoteStringCALLVOTE_ISSUE_STRINGS[i], false)) return L4DVoteIssue:(1);
    }
    return 
L4DVoteIssue_None;
}

/**
 * Retrives current no votes for the issue.
 *
 * @return                Count of no votes, -1 if no vote is in progress.
 */
stock L4D_GetNoVotes()
{
    if (!
g_bIsVoteInProgress) return -1;
    return 
g_iNoVotes;
}

/** 
 * Retrives current yes votes for the issue.
 *
 * @return                Count of yes votes, -1 if no vote is in progress.
 */
stock L4D_GetYesVotes()
{
    if (!
g_bIsVoteInProgress) return -1;
    return 
g_iYesVotes;
}

/**
 * Retrives total voters that can vote.
 *
 * @return                Count of all voters in vote, -1 if no vote is in
 *                        progress.
 */
stock L4D_GetTotalVoters()
{
    if (!
g_bIsVoteInProgress) return -1;
    return 
g_iTotalVoters;
}

/**
 * Returns whether player is able to vote.
 *
 * @param client        Client index.
 * @return                True if player can vote, false otherwise.
 */
stock bool:L4D_IsPlayerAbleToVote(client)
{
    if (!
g_bIsVoteInProgress) return false;
    return 
g_bCanVote[client];
}

/**
 * Retrives the players vote.
 *
 * @param client        Client index.
 * @return                Players vote.
 */
stock L4DVote:L4D_GetPlayerVote(client)
{
    if (!
g_bIsVoteInProgress) return L4DVote_None;
    return 
g_iVote[client];
}

/**
 * Set the players vote.
 *
 * @param clients        Client index.
 * @param vote            Players vote.
 * @noreturn
 */
stock L4D_SetPlayerVote(clientL4DVote:vote)
{
    if (!
g_bIsVoteInProgress) return;
    
g_iVote vote;
}

/**
 * Returns whether vote caller is being shown.
 *
 * @return                True if vote caller is being shown, false otherwise.
 */
stock bool:L4D_IsShowingVoteCaller()
{
    return 
g_bIsShowingVoteCaller;
}

/**
 * Retrives vote start in engine time.
 *
 * @return                Vote start time. -1.0 if no vote is active.
 */
stock Float:L4D_GetVoteStartTime()
{
    if (!
g_bIsVoteInProgress) return -1.0;
    return 
g_fVoteStartTime;
}

/**
 * Retrives the remaining voting time for current vote, in engine time.
 *
 * @return                Remaining vote time, -1.0 if no vote is active.
 */
stock Float:L4D_GetRemainingVoteTime()
{
    if (!
g_bIsVoteInProgress) return -1.0;
    return 
g_fVoteTime - (GetEngineTime() - g_fVoteStartTime);
}

/**
 * Copies the vote issue string to destination string.
 *
 * @param dest            Destination string buffer to copy to.
 * @param destlen        Destination buffer length (includes null terminator).
 * @return                Number of cells written, -1 if no vote is in progress.
 */
stock L4D_GetVoteIssueString(String:dest[], destlen)
{
    if (!
g_bIsVoteInProgress) return -1;
    return 
strcopy(destdestleng_sVoteIssueTranslation);
}

/**
 * Copies the vote param string to destination string.
 *
 * @param dest            Destination string buffer to copy to.
 * @param destlen        Destination buffer length (includes null terminator).
 * @return                Number of cells written, -1 if no vote is in progress.
 */
stock L4D_GetVoteParamString(String:dest[], destlen)
{
    if (!
g_bIsVoteInProgress) return -1;
    return 
strcopy(destdestleng_sVoteParamTranslation);
}

/**
 * Copies the vote initiator string to destination string.
 *
 * @param dest            Destination string buffer to copy to.
 * @param destlen        Destination buffer length (includes null terminator).
 * @return                Number of cells written, -1 if no vote is in progress.
 */
stock L4D_GetVoteInitiatorString(String:dest[], destlen)
{
    if (!
g_bIsVoteInProgress) return -1;
    return 
strcopy(destdestleng_sVoteInitiatorName);
}

/**
 * Hooks the function to the internal OnVoteStart forward.
 *
 * @param func            The function to add.
 * @return                True on success, false otherwise.
 */
stock bool:HookOnVoteStart(Function:func)
{
    return 
AddToForward(g_hFwd_OnVoteStartINVALID_HANDLEfunc);
}

/**
 * Unhooks the function from the internal OnVoteStart forward.
 *
 * @param func            The function to remove.
 * @return                True on success, false otherwise.
 */
stock bool:UnhookOnVoteStart(Function:func)
{
    return 
RemoveFromForward(g_hFwd_OnVoteStartINVALID_HANDLEfunc);
}

/*
 * ==================================================
 *                    Private API
 * ==================================================
 */

/**
 * Starts a simpler usermessage (network message) for one client.
 * @note See StartMessage or StartMessageEx().
 *
 * @param msg            Message index to start.
 * @param client        Client to send to.
 * @param flags            Optional flags to set.
 * @return                A handle to a bf_write bit packing structure, or
 *                        INVALID_HANDLE on failure.
 */
static Handle:StartMessageExOne(UserMsg:msgclientflags=0)
{
    new 
players[1];

    
players[0] = client;

    return 
StartMessageEx(msgplayers1flags);
}

/**
 * Wrapper for Debug_PrintText without having to define debug channel.
 *
 * @param format        Formatting rules.
 * @param ...            Variable number of format parameters.
 * @noreturn
 */
static Debug_PrintTextEx(const String:format[], any:...)
{
#if !defined DEBUG
#pragma unused format
    
return;

#else
    
static channel = -1;
    if (
channel == -1channel Debug_AddChannel(DEBUG_CHANNEL_NAME);

    
decl String:buffer[256];
    
VFormat(buffersizeof(buffer), format2);
    
Debug_PrintText(channelbuffer);
#endif

Attached Files
File Type: zip L4D2 Vote Manager.zip (885.6 KB, 293 views)
Mr. Zero is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 08-02-2011 , 14:17   Re: [WIP] Builtin Votes
Reply With Quote #14

@Mr. Zero:
Yeah, sorry, I've had some things come up and I haven't had a chance to do anything more since the weekend.

I got some more work done on Sunday, although most of it was in the "this is why X doesn't work" type, but hasn't actually been changed yet in the code.

I discovered that I stupidly moved the vote tabulation code to the global level (extension.cpp), while they need to actually be at the local level (Votes.cpp). I ultimately discovered this was a problem when I went to fix the code that displays the current vote leader list and realized it can't see the votes from the vote object. Whoops.

I discovered that the SourceMod vote system uses two timers. One is a normal timer that runs for the entire vote time and then ends the vote. The other is a repeating timer that ticks once a second and updates the hint box. I only implemented the latter. I'm not sure if I want to duplicate the two timer system, or just add a counter to count how many times the existing timer has ticked and consider that the vote time.

I think I also want to fix the issue where you can only store what's actually displayed in my menu rather than the data stored for it. There's not reason it can't be done like the existing menu system.
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 08-02-2011 at 14:40.
Powerlord is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 08-19-2011 , 11:57   Re: [WIP] Builtin Votes
Reply With Quote #15

For those of you who didn't know, my desktop PC died a few weeks back... and it's kind of a pain working on this on my work PC... that and I have real work to do when using it.

I'm also cleaning up a design mistake I made when making the plugin simpler... I moved things that should be in the vote handler into the global handler (whoops).

I plan on working on this over the weekend, after I set my development environment back up on my desktop.
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 08-19-2011 at 11:59.
Powerlord is offline
Nightbox
Senior Member
Join Date: Apr 2008
Old 09-05-2011 , 18:11   Re: [WIP] Builtin Votes
Reply With Quote #16

Hey Powerlond, any news about this ? This stuff is amazing
__________________
Nightbox is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 09-05-2011 , 18:45   Re: [WIP] Builtin Votes
Reply With Quote #17

Quote:
Originally Posted by Nightbox View Post
Hey Powerlond, any news about this ? This stuff is amazing
I had an outbreak of real life. Hopefully more news soon.
__________________
Not currently working on SourceMod plugin development.
Powerlord is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 09-22-2011 , 12:51   Re: [WIP] Builtin Votes
Reply With Quote #18

Just so people don't think this is completely dead, I'm rebuilding this from the ground up based on the SourceMod menu system. Last time, I used it for inspiration while copying parts of it. Turns out some of the features I'd skipped need to be present.

Right now, it's more or less converting the various parts of the existing voting system over, then I'll implement the game-specific code in their respective vote objects. (Last time, I completely ignored the MenuStyle classes, which do the actual vote handling, and instead wrote my own).

Luckily, some techniques I used before copy over to the new system. I've even found some bugs from my old code that I'm now fixing (seriously, it wasn't logging certain errors at all, just ignoring them. Whoops.)
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 09-22-2011 at 13:00.
Powerlord is offline
jasonfrog
Senior Member
Join Date: Mar 2008
Old 09-22-2011 , 13:12   Re: [WIP] Builtin Votes
Reply With Quote #19

Thanks for the update. Looking forward to this immensely!
jasonfrog is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 10-10-2011 , 16:58   Re: [WIP] Builtin Votes
Reply With Quote #20

So, the rewrite is basically finished for TF2 support. All functionality *should* be present, and it compiles on Windows with no errors.

I'm going to be doing testing of a new version of this sometime this week, and we'll see if any bugs or runtime errors crop up.

Sadly, the Linux compiler is pickier, and I don't have access to it where I am right now, so I'll have to do some work getting it to compile under Linux tonight before I really start testing it.

P.S. I know there is some unnecessary code in this version, which people will see once I post the source code after I sort out any crashing issues.

Edit: I did have to remove the DisplayCallVoteFail support. It requires you to have a vote object first, which doesn't make sense in context. Maybe it will reappear in a later version. DisplayVoteFail is also theoretically not vote dependent, but t least you know you've already had a vote before that appears.
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 10-10-2011 at 17:10.
Powerlord is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 16:00.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode