From f10720cac060eab4563ce679b8052d788edf1e2c Mon Sep 17 00:00:00 2001 From: Rowedahelicon Date: Mon, 17 Aug 2020 05:27:31 -0400 Subject: [PATCH 1/2] Update game.sp Fixed MvM mission votes from causing an Invalid forward handle due to not knowing how to handle vote overrides for mission changes. --- .../sourcemod/scripting/nativevotes/game.sp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 0f63128..a281d2f 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -151,7 +151,7 @@ // User vote to scramble teams. Can be immediate or end of round. #define TF2_VOTE_SCRAMBLE_IMMEDIATE_START "#TF_vote_scramble_teams" #define TF2_VOTE_SCRAMBLE_ROUNDEND_START "#TF_vote_should_scramble_round" -#define TF2_VOTE_SCRAMBLE_PASSED "#TF_vote_passed_scramble_teams" +#define TF2_VOTE_SCRAMBLE_PASSED "#TF_vote_passed_scramble_teams" // User vote to change MvM mission #define TF2_VOTE_CHANGEMISSION_START "#TF_vote_changechallenge" @@ -3223,6 +3223,10 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) { voteType = NativeVotesType_Extend; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) + { + voteType = NativeVotesType_ChgMission; } return voteType; @@ -3337,6 +3341,10 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over case NativeVotesOverride_Extend: { voteType = NativeVotesType_Extend; + } + case NativeVotesOverride_ChgMission: + { + voteType = NativeVotesType_ChgMission; } } @@ -3382,6 +3390,10 @@ static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteS else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) { overrideType = NativeVotesOverride_Extend; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) + { + overrideType = NativeVotesOverride_ChgMission; } #if defined LOG @@ -3447,6 +3459,11 @@ static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, case NativeVotesOverride_Extend: { strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); + } + + case NativeVotesOverride_ChgMission: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); } } @@ -3553,4 +3570,4 @@ stock bool Game_IsVoteTypeYesNo(NativeVotesType voteType) } return true; -} \ No newline at end of file +} From 248c2bf95fd43d5ccb9ef3d38b68c2ee62863edc Mon Sep 17 00:00:00 2001 From: Rowedahelicon Date: Sun, 4 Sep 2022 05:31:44 -0400 Subject: [PATCH 2/2] Add files via upload --- .../scripting/include/nativevotes.inc | 8 +- addons/sourcemod/scripting/nativevotes.sp | 127 ++++++++------- .../sourcemod/scripting/nativevotes/game.sp | 151 +++++++++++++++--- .../scripting/nativevotes_mapchooser.sp | 2 +- .../scripting/votedelay_changelevel.sp | 8 +- 5 files changed, 208 insertions(+), 88 deletions(-) diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index 506eaa5..426e6d1 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -238,6 +238,7 @@ enum NativeVotesOverride NativeVotesOverride_AutoBalance, NativeVotesOverride_ClassLimits, NativeVotesOverride_Extend, + NativeVotesOverride_Count, } methodmap NativeVote < KeyValues @@ -1282,7 +1283,8 @@ stock void NativeVotes_DisplayRawFailToAll(NativeVotesFailType reason=NativeVote players[total++] = i; } - return NativeVotes_DisplayRawFail(players, total, reason, team); + NativeVotes_DisplayRawFail(players, total, reason, team); + return; } /** @@ -1301,7 +1303,9 @@ stock void NativeVotes_DisplayRawFailToOne(int client, NativeVotesFailType reaso players[0] = client; - return NativeVotes_DisplayRawFail(players, 1, reason, team); + + NativeVotes_DisplayRawFail(players, 1, reason, team); + return; } /** diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 10df5a1..b5349ea 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -46,7 +46,7 @@ EngineVersion g_EngineVersion = Engine_Unknown; #include "nativevotes/data-keyvalues.sp" -#define VERSION "1.1.0 beta 8" +#define VERSION "1.1.1fix" #define LOGTAG "NV" @@ -72,7 +72,7 @@ EngineVersion g_EngineVersion = Engine_Unknown; #define MAX_CALLVOTE_SIZE 128 -//#define LOG +#define LOG //---------------------------------------------------------------------------- // Global Variables @@ -112,6 +112,8 @@ int g_ClientVotes[MAXPLAYERS+1]; bool g_bRevoting[MAXPLAYERS+1]; char g_LeaderList[1024]; +ConVar sv_vote_holder_may_vote_no; + // Map list stuffs #define STRINGTABLE_NAME "ServerMapCycle" @@ -124,19 +126,19 @@ StringMap g_MapOverrides; bool g_OverridesSet; bool g_OverrideNextCallVote[MAXPLAYERS + 1]; -enum CallVoteForwards +enum struct CallVoteForwards { - Handle:CallVote_Forward, - Handle:CallVote_Vis, + Handle CallVote_Forward; + Handle CallVote_Vis; } -enum CallVoteListData +enum struct CallVoteListData { - NativeVotesOverride:CallVoteList_VoteType, - bool:CallVoteList_VoteEnabled, + NativeVotesOverride CallVoteList_VoteType; + bool CallVoteList_VoteEnabled; } -int g_CallVotes[NativeVotesOverride][CallVoteForwards]; +CallVoteForwards g_CallVotes[NativeVotesOverride_Count]; #include "nativevotes/game.sp" @@ -262,6 +264,8 @@ public void OnPluginStart() AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive + sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); + // The new version of the CallVote system is TF2 only if (Game_AreVoteCommandsSupported()) { @@ -271,8 +275,8 @@ public void OnPluginStart() // As of 2015-09-28, there are 10 votes for a total of 20 private forwards created here. for (int i = 1; i < sizeof(g_CallVotes); i++) { - g_CallVotes[i][CallVote_Forward] = CreateForward(ET_Hook, Param_Cell, Param_Cell, Param_String, Param_Cell, Param_Cell); - g_CallVotes[i][CallVote_Vis] = CreateForward(ET_Hook, Param_Cell, Param_Cell); + g_CallVotes[i].CallVote_Forward = CreateForward(ET_Hook, Param_Cell, Param_Cell, Param_String, Param_Cell, Param_Cell); + g_CallVotes[i].CallVote_Vis = CreateForward(ET_Hook, Param_Cell, Param_Cell); } g_OverrideMaps = CreateGlobalForward("NativeVotes_OverrideMaps", ET_Hook, Param_Cell); @@ -416,7 +420,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) return Plugin_Handled; } - ArrayList hVoteTypes = new ArrayList(view_as(CallVoteListData)); // Stores arrays of CallVoteListData + ArrayList hVoteTypes = new ArrayList(sizeof(CallVoteListData)); // Stores arrays of CallVoteListData Game_AddDefaultVotes(hVoteTypes); @@ -424,32 +428,32 @@ public Action Command_CallVote(int client, const char[] command, int argc) bool overridesPresent = false; for (int i = 1; i < sizeof(g_CallVotes); i++) { - if (GetForwardFunctionCount(g_CallVotes[i][CallVote_Forward]) > 0) + if (GetForwardFunctionCount(g_CallVotes[i].CallVote_Forward) > 0) { #if defined LOG LogMessage("Found overrides for vote type: %d", i); #endif overridesPresent = true; - int voteType[CallVoteListData]; + CallVoteListData voteType; int pos = FindVoteInArray(hVoteTypes, view_as(i)); if (pos > -1) { - hVoteTypes.GetArray(pos, voteType[0], sizeof(voteType)); - voteType[CallVoteList_VoteEnabled] = true; + hVoteTypes.GetArray(pos, voteType, sizeof(CallVoteListData)); + voteType.CallVoteList_VoteEnabled = true; #if defined LOG LogMessage("Forcing vote type to visible: %d", i); #endif - hVoteTypes.SetArray(pos, voteType[0], sizeof(voteType)); + hVoteTypes.SetArray(pos, voteType); } else { #if defined LOG LogMessage("Creating override for vote type: %d", i); #endif - voteType[CallVoteList_VoteType] = view_as(i); - voteType[CallVoteList_VoteEnabled] = true; - hVoteTypes.PushArray(voteType[0], sizeof(voteType)); + voteType.CallVoteList_VoteType = view_as(i); + voteType.CallVoteList_VoteEnabled = true; + hVoteTypes.PushArray(voteType); } } } @@ -484,7 +488,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) char argument[PLATFORM_MAX_PATH]; - if (GetForwardFunctionCount(g_CallVotes[overrideType][CallVote_Forward]) == 0) + if (GetForwardFunctionCount(g_CallVotes[overrideType].CallVote_Forward) == 0) { if (g_MapOverrides != null && @@ -511,7 +515,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) #if defined LOG LogMessage("Calling visForward for %s", voteCommand); #endif - Call_StartForward(g_CallVotes[overrideType][CallVote_Vis]); + Call_StartForward(g_CallVotes[overrideType].CallVote_Vis); Call_PushCell(client); Call_PushCell(overrideType); Call_Finish(result); @@ -570,7 +574,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) LogMessage("Calling callVoteForward for %s", voteCommand); #endif - Call_StartForward(g_CallVotes[overrideType][CallVote_Forward]); + Call_StartForward(g_CallVotes[overrideType].CallVote_Forward); Call_PushCell(client); Call_PushCell(overrideType); Call_PushString(argument); @@ -590,10 +594,10 @@ stock int FindVoteInArray(ArrayList myArray, NativeVotesOverride value) int size = myArray.Length; for (int i = 0; i < size; i++) { - int voteData[CallVoteListData]; - myArray.GetArray(i, voteData[0]); + CallVoteListData voteData; + myArray.GetArray(i, voteData, sizeof(CallVoteListData)); - if (voteData[CallVoteList_VoteType] == value) + if (voteData.CallVoteList_VoteType == value) { return i; } @@ -607,9 +611,9 @@ stock bool IsVoteEnabled(ArrayList myArray, NativeVotesOverride value) int pos = FindVoteInArray(myArray, value); if (pos > -1) { - int voteType[CallVoteListData]; - myArray.GetArray(pos, voteType[0], sizeof(voteType)); - if (voteType[CallVoteList_VoteEnabled]) + CallVoteListData voteType; + myArray.GetArray(pos, voteType, sizeof(CallVoteListData)); + if (voteType.CallVoteList_VoteEnabled) return true; } return false; @@ -655,33 +659,36 @@ public void OnMapEnd() public Action Command_Vote(int client, const char[] command, int argc) { +#if defined LOG + char voteString[128]; + GetCmdArgString(voteString, sizeof(voteString)); + LogMessage("Client %N ran a vote command: %s", client, voteString); +#endif + // If we're not running a vote, return the vote control back to the server if (!Internal_IsVoteInProgress() || g_ClientVotes[client] != VOTE_PENDING) { return Plugin_Continue; } - char option[32]; - GetCmdArg(1, option, sizeof(option)); + char option[64]; + GetCmdArgString(option, sizeof(option)); int item = Game_ParseVote(option); - bool cancel; - - if (item == NATIVEVOTES_VOTE_INVALID) + // Make sure we don't go out of bounds on the vote + if (item == NATIVEVOTES_VOTE_INVALID || item > g_Items) { - cancel = true; + return Plugin_Handled; } + + bool cancel; if (Data_GetFlags(g_hCurVote) & MENUFLAG_BUTTON_NOVOTE && item == 0) { cancel = true; } - /* - * If they choose no vote or the vote is invalid (typed command), then - * treat it as no vote and adjust the numbers. - */ if (cancel) { OnCancel(g_hCurVote, client, MenuCancel_Exit); @@ -709,6 +716,8 @@ void OnVoteSelect(NativeVote vote, int client, int item) g_hVotes.Set(item, g_hVotes.Get(item) + 1); g_NumVotes++; + PrintToChatAll("OnVoteSelect hit"); + Game_UpdateClientCount(g_TotalClients); Game_UpdateVoteCounts(g_hVotes, g_TotalClients); if (g_Cvar_VoteChat.BoolValue || g_Cvar_VoteConsole.BoolValue || g_Cvar_VoteClientConsole.BoolValue) @@ -821,7 +830,7 @@ Action DoAction(NativeVote vote, MenuAction action, int param1, int param2, Acti { Action res = def_res; - Handle handler = Data_GetHandler(vote); + Handle handler = CloneHandle(Data_GetHandler(vote)); #if defined LOG LogMessage("Calling Menu forward for vote: %d, handler: %d, action: %d, param1: %d, param2: %d", vote, handler, action, param1, param2); #endif @@ -831,6 +840,7 @@ Action DoAction(NativeVote vote, MenuAction action, int param1, int param2, Acti Call_PushCell(param1); Call_PushCell(param2); Call_Finish(res); + delete handler; return res; } @@ -1118,6 +1128,10 @@ void EndVoting() NativeVote vote = g_hCurVote; Internal_Reset(); +#if defined LOG + LogMessage("Voting done"); +#endif + /* Send vote info */ OnVoteResults(vote, votes, num_votes, num_items, client_list, num_clients); OnEnd(vote, MenuEnd_VotingDone); @@ -1279,7 +1293,7 @@ void StartVoting() { int initiator = Data_GetInitiator(g_hCurVote); - if (initiator > 0 && initiator <= MaxClients && IsClientConnected(initiator) && Internal_IsClientInVotePool(initiator)) + if (initiator > 0 && initiator <= MaxClients && IsClientConnected(initiator) && Internal_IsClientInVotePool(initiator) && !sv_vote_holder_may_vote_no) { Game_VoteYes(initiator); } @@ -1486,22 +1500,22 @@ void PerformVisChecks(int client, ArrayList hVoteTypes) // Iterate backwards so we can safely remove items for (int i = hVoteTypes.Length - 1; i >= 0; i--) { - int voteData[CallVoteListData]; - hVoteTypes.GetArray(i, voteData[0]); + CallVoteListData voteData; + hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); Action hide = Plugin_Continue; #if defined LOG - LogMessage("Checking visibility forward for %d: %d", voteData[CallVoteList_VoteType], g_CallVotes[voteData[CallVoteList_VoteType]][CallVote_Vis]); + //LogMessage("Checking visibility forward for %d: %d", voteData.CallVoteList_VoteType., g_CallVotes[voteData.CallVoteList_VoteType].CallVote_Vis); #endif - Call_StartForward(g_CallVotes[voteData[CallVoteList_VoteType]][CallVote_Vis]); + Call_StartForward(g_CallVotes[voteData.CallVoteList_VoteType].CallVote_Vis); Call_PushCell(client); - Call_PushCell(voteData[CallVoteList_VoteType]); + Call_PushCell(voteData.CallVoteList_VoteType); Call_Finish(hide); if (hide >= Plugin_Handled) { #if defined LOG - LogMessage("Hiding vote type %d", voteData[CallVoteList_VoteType]); + LogMessage("Hiding vote type %d", voteData.CallVoteList_VoteType); #endif if (Game_AreDisabledIssuesHidden()) { @@ -1511,7 +1525,7 @@ void PerformVisChecks(int client, ArrayList hVoteTypes) else { // Arrays are pass by ref, so this should update the one inside the ArrayList - voteData[CallVoteList_VoteEnabled] = false; + voteData.CallVoteList_VoteEnabled = false; } } } @@ -2005,7 +2019,10 @@ public int Native_RedrawClientVote(Handle plugin, int numParams) if (!Internal_IsVoteInProgress()) { - ThrowNativeError(SP_ERROR_NATIVE, "No vote is in progress"); + // When revoting in TF2, NativeVotes_IsVoteInProgress always gets skipped because of Game_IsVoteInProgress() + // TF2s vote controller will stay alive a few seconds after the vote is complete + // If one tries to revote right as a vote completes, it will throw an error + LogError("No vote is in progress"); return false; } @@ -2412,7 +2429,7 @@ public int Native_RegisterVoteCommand(Handle plugin, int numParams) // This tosses an error so use the simplified version // if (view_as(overrideType) > sizeof(g_CallVotes)) - if (overrideType > NativeVotesOverride) + if (overrideType > NativeVotesOverride_Count) { ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); return; @@ -2424,11 +2441,11 @@ public int Native_RegisterVoteCommand(Handle plugin, int numParams) return; } - AddToForward(g_CallVotes[overrideType][CallVote_Forward], plugin, callVoteHandler); + AddToForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); if (visHandler != INVALID_FUNCTION) { - AddToForward(g_CallVotes[overrideType][CallVote_Vis], plugin, visHandler); + AddToForward(g_CallVotes[overrideType].CallVote_Vis, plugin, visHandler); } } @@ -2439,7 +2456,7 @@ public int Native_UnregisterVoteCommand(Handle plugin, int numParams) Function callVoteHandler = GetNativeFunction(2); Function visHandler = GetNativeFunction(3); - if (overrideType > NativeVotesOverride) + if (overrideType > NativeVotesOverride_Count) { ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); return; @@ -2451,11 +2468,11 @@ public int Native_UnregisterVoteCommand(Handle plugin, int numParams) return; } - RemoveFromForward(g_CallVotes[overrideType][CallVote_Forward], plugin, callVoteHandler); + RemoveFromForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); if (visHandler != INVALID_FUNCTION) { - RemoveFromForward(g_CallVotes[overrideType][CallVote_Vis], plugin, visHandler); + RemoveFromForward(g_CallVotes[overrideType].CallVote_Vis, plugin, visHandler); } } diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index a281d2f..78749b0 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -361,6 +361,14 @@ static ConVar g_Cvar_AutoBalance; static ConVar g_Cvar_HideDisabledIssues; +/** + * TODO(UPDATE): For now we only support one vote from NativeVotes at a time. + * + * Ideally we peek and poke at the game's `s_nVoteIdx` and increment it accordingly, but we also + * have to store an internal list of active votes. + */ +static int s_nNativeVoteIdx = 0; + bool Game_IsGameSupported(char[] engineName="", int maxlength=0) { g_EngineVersion = GetEngineVersion(); @@ -485,6 +493,10 @@ int Game_ParseVote(const char[] option) { int item = NATIVEVOTES_VOTE_INVALID; +#if defined LOG + LogMessage("Parsing vote option %s", option); +#endif + switch(g_EngineVersion) { case Engine_Left4Dead, Engine_Left4Dead2: @@ -492,9 +504,13 @@ int Game_ParseVote(const char[] option) item = L4DL4D2_ParseVote(option); } - case Engine_CSGO, Engine_TF2: + case Engine_CSGO: { - item = TF2CSGO_ParseVote(option); + item = CSGO_ParseVote(option); + } + case Engine_TF2: + { + item = TF2_ParseVote(option); } } @@ -730,7 +746,7 @@ void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client= default: { - L4D2_VotePass(translation, details, client, team); + L4D2_VotePass(translation, details, team, client); } } } @@ -821,6 +837,9 @@ void Game_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int ti void Game_ClientSelectedItem(NativeVote vote, int client, int item) { +#if defined LOG + LogMessage("Client %N selected item %d", client, item); +#endif switch(g_EngineVersion) { case Engine_Left4Dead, Engine_Left4Dead2: @@ -828,9 +847,13 @@ void Game_ClientSelectedItem(NativeVote vote, int client, int item) L4DL4D2_ClientSelectedItem(client, item); } - case Engine_CSGO, Engine_TF2: + case Engine_CSGO: { - TF2CSGO_ClientSelectedItem(vote, client, item); + CSGO_ClientSelectedItem(vote, client, item); + } + case Engine_TF2: + { + TF2_ClientSelectedItem(vote, client, item); } /* case Engine_Left4Dead: @@ -857,6 +880,7 @@ void Game_UpdateVoteCounts(ArrayList hVoteCounts, int totalClients) case Engine_CSGO, Engine_TF2: { + PrintToChatAll("TF2CSGO"); TF2CSGO_UpdateVoteCounts(hVoteCounts); } } @@ -941,6 +965,7 @@ public Action Game_ResetVote(Handle timer) TF2CSGO_ResetVote(); } } + return Plugin_Continue; } void Game_VoteYes(int client) @@ -952,10 +977,18 @@ void Game_VoteYes(int client) FakeClientCommand(client, "Vote Yes"); } - case Engine_CSGO, Engine_TF2: + case Engine_CSGO: { FakeClientCommand(client, "vote option1"); } + + // the update on 2022-06-22 changed the params passed to the `vote` command + // previously it was a single string "optionN", where N was the option to be selected + // now it's two arguments "X optionN", where X is the vote index being acted on + case Engine_TF2: + { + FakeClientCommand(client, "vote %i option1", s_nNativeVoteIdx); + } } } @@ -2102,7 +2135,7 @@ static bool L4D2_CheckVotePassType(NativeVotesPassType passType) // TF2 and CSGO functions are still together in case Valve moves TF2 to protobufs. // NATIVEVOTES_VOTE_INVALID means parse failed -static int TF2CSGO_ParseVote(const char[] option) +static int CSGO_ParseVote(const char[] option) { // option1 <-- 7 characters exactly if (strlen(option) != 7) @@ -2113,7 +2146,34 @@ static int TF2CSGO_ParseVote(const char[] option) return StringToInt(option[6]) - 1; } -static void TF2CSGO_ClientSelectedItem(NativeVote vote, int client, int item) +// NATIVEVOTES_VOTE_INVALID means parse failed +static int TF2_ParseVote(const char[] option) +{ + // the update on 2022-06-22 changed the params passed to the `vote` command + // previously it was a single string "optionN", where N was the option to be selected + // now it's two arguments "X optionN", where X is the vote index being acted on + + if (strlen(option) == 0 || GetCmdArgs() != 2) + { + return NATIVEVOTES_VOTE_INVALID; + } + + // int voteidx = GetCmdArgInt(1); + + char voteOption[16]; + GetCmdArg(2, voteOption, sizeof(voteOption)); + + // option1 <-- 7 characters exactly + // voteOption's last character should be numeric + if (strlen(voteOption) != 7 || !IsCharNumeric(voteOption[6])) + { + return NATIVEVOTES_VOTE_INVALID; + } + + return StringToInt(voteOption[6]) - 1; +} + +static void CSGO_ClientSelectedItem(NativeVote vote, int client, int item) { Event castEvent = CreateEvent("vote_cast"); @@ -2123,6 +2183,17 @@ static void TF2CSGO_ClientSelectedItem(NativeVote vote, int client, int item) castEvent.Fire(); } +static void TF2_ClientSelectedItem(NativeVote vote, int client, int item) +{ + Event castEvent = CreateEvent("vote_cast"); + + castEvent.SetInt("team", Data_GetTeam(vote)); + castEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE): this was added in 2022-06-22 - figure out what the client voted for + castEvent.SetInt("entityid", client); + castEvent.SetInt("vote_option", item); + castEvent.Fire(); +} + static void TF2CSGO_UpdateVoteCounts(ArrayList votes) { if (CheckVoteController()) @@ -2130,6 +2201,7 @@ static void TF2CSGO_UpdateVoteCounts(ArrayList votes) int size = votes.Length; for (int i = 0; i < size; i++) { + PrintToChatAll("VOTE OPTION COUNT UPDATED: %i", votes.Get(i)); SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i); } } @@ -2221,6 +2293,15 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); } + + // TODO(UPDATE): M-M-M-MULTIVOTE + // we need unique vote indices; HUD elements for previous votes aren't cleaned up (?) + // the game implements this as `this->m_nVoteIdx = s_nVoteIdx++` + s_nNativeVoteIdx = GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx"); +#if defined LOG + PrintToServer("Starting vote index: %d (controller: %d)", s_nNativeVoteIdx, GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx")); +#endif + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", s_nNativeVoteIdx + 1); // TODO(UPDATE) } // According to Source SDK 2013, vote_options is only sent for a multiple choice vote. @@ -2244,6 +2325,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) optionsEvent.SetString(option, display); } optionsEvent.SetInt("count", itemCount); + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) optionsEvent.Fire(); } @@ -2254,6 +2336,13 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); } + // required to allow the initiator to vote on their own issue + // ValveSoftware/Source-1-Games#3934 + if (sv_vote_holder_may_vote_no && vote.Initiator <= MaxClients) + { + sv_vote_holder_may_vote_no.ReplicateToClient(vote.Initiator, "1"); + } + MenuAction actions = Data_GetActions(vote); for (int i = 0; i < num_clients; ++i) @@ -2298,6 +2387,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { BfWrite bfStart = UserMessageToBfWrite(voteStart); bfStart.WriteByte(team); + bfStart.WriteNum(s_nNativeVoteIdx); bfStart.WriteByte(Data_GetInitiator(vote)); bfStart.WriteString(translation); if (bCustom && changeTitle == Plugin_Changed) @@ -2310,6 +2400,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } bfStart.WriteBool(bYesNo); } + EndMessage(); } @@ -2367,7 +2458,10 @@ static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) optionsEvent.SetString(option, display); } optionsEvent.SetInt("count", itemCount); + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) optionsEvent.FireToClient(client); + // FireToClient does not close the handle, so we call Cancel() to do that for us. + optionsEvent.Cancel(); } static void CSGO_VotePass(const char[] translation, const char[] details, int team, int client=0) @@ -2405,6 +2499,7 @@ static void TF2_VotePass(const char[] translation, const char[] details, int tea } votePass.WriteByte(team); + votePass.WriteNum(s_nNativeVoteIdx); votePass.WriteString(translation); votePass.WriteString(details); @@ -2426,6 +2521,7 @@ static void TF2_VoteFail(int[] clients, int numClients, int reason, int team) BfWrite voteFailed = UserMessageToBfWrite(StartMessage("VoteFailed", clients, numClients, USERMSG_RELIABLE)); voteFailed.WriteByte(team); + voteFailed.WriteNum(s_nNativeVoteIdx); // TODO(UPDATE) voteFailed.WriteByte(reason); EndMessage(); @@ -2461,10 +2557,10 @@ stock static void CSGO_DisplayVoteSetup(int client, ArrayList hVoteTypes) { char voteIssue[128]; - int voteData[CallVoteListData]; - hVoteTypes.GetArray(i, voteData[0]); + CallVoteListData voteData; + hVoteTypes.GetArray(i, voteData.CallVoteList_VoteType); - Game_OverrideTypeToVoteString(voteData[CallVoteList_VoteType], voteIssue, sizeof(voteIssue)); + Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); voteSetup.AddString("potential_issues", voteIssue); @@ -2485,17 +2581,17 @@ static void TF2_DisplayVoteSetup(int client, ArrayList hVoteTypes) { char voteIssue[128]; - int voteData[CallVoteListData]; - hVoteTypes.GetArray(i, voteData[0]); + CallVoteListData voteData; + hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); - Game_OverrideTypeToVoteString(voteData[CallVoteList_VoteType], voteIssue, sizeof(voteIssue)); + Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); char translation[128]; - Game_OverrideTypeToTranslationString(voteData[CallVoteList_VoteType], translation, sizeof(translation)); + Game_OverrideTypeToTranslationString(voteData.CallVoteList_VoteType, translation, sizeof(translation)); voteSetup.WriteString(voteIssue); voteSetup.WriteString(translation); - voteSetup.WriteByte(voteData[CallVoteList_VoteEnabled]); + voteSetup.WriteByte(voteData.CallVoteList_VoteEnabled); } EndMessage(); @@ -2506,7 +2602,10 @@ static void TF2CSGO_ResetVote() if (CheckVoteController()) { if (g_EngineVersion == Engine_TF2) + { SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); // TODO(UPDATE) + } for (int i = 0; i < 5; i++) { @@ -2826,14 +2925,14 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) static void VoteTypeSet(ArrayList hVoteTypes, bool bHideDisabledVotes, NativeVotesOverride voteType, bool bEnabled) { - int voteList[CallVoteListData]; + CallVoteListData voteList; if (bEnabled || !bHideDisabledVotes) { - voteList[CallVoteList_VoteType] = voteType; - voteList[CallVoteList_VoteEnabled] = bEnabled; + voteList.CallVoteList_VoteType = voteType; + voteList.CallVoteList_VoteEnabled = bEnabled; - hVoteTypes.PushArray(voteList[0]); + hVoteTypes.PushArray(voteList); } } @@ -3151,13 +3250,13 @@ static stock bool TF2_VoteTypeToVoteString(NativeVotesType voteType, char[] vote case NativeVotesType_ClassLimitsOn: { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT_ON); + strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); valid = true; } case NativeVotesType_ClassLimitsOff: { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT_OFF); + strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); valid = true; } @@ -3228,6 +3327,10 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) { voteType = NativeVotesType_ChgMission; } + else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) + { + voteType = NativeVotesType_ChgMission; + } return voteType; } @@ -3342,10 +3445,6 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over { voteType = NativeVotesType_Extend; } - case NativeVotesOverride_ChgMission: - { - voteType = NativeVotesType_ChgMission; - } } return voteType; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index f9a47d0..ffaccc0 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -287,7 +287,7 @@ public void OnConfigsExecuted() { if (g_Cvar_Bonusroundtime.FloatValue <= g_Cvar_VoteDuration.FloatValue) { - LogError("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); + LogMessage("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); } } } diff --git a/addons/sourcemod/scripting/votedelay_changelevel.sp b/addons/sourcemod/scripting/votedelay_changelevel.sp index b4d1a21..c370d30 100644 --- a/addons/sourcemod/scripting/votedelay_changelevel.sp +++ b/addons/sourcemod/scripting/votedelay_changelevel.sp @@ -93,10 +93,10 @@ public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) public OnPluginStart() { - CreateConVar("votedelay_changelevel_version", VERSION, "Vote Delay: Changelevel version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - g_Cvar_Enabled = CreateConVar("votedelay_changelevel_enable", "1", "Enable Vote Delay: Changelevel?", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0); - g_Cvar_FullRounds = CreateConVar("votedelay_changelevel_fullrounds", "1", "Full rounds only? Only applies to TF2.", FCVAR_PLUGIN, true, 0.0, true, 1.0); - g_Cvar_Rounds = CreateConVar("votedelay_changelevel_rounds", "4", "During what round should votes become available?", FCVAR_PLUGIN, true, 0.0, true, 10.0); + CreateConVar("votedelay_changelevel_version", VERSION, "Vote Delay: Changelevel version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); + g_Cvar_Enabled = CreateConVar("votedelay_changelevel_enable", "1", "Enable Vote Delay: Changelevel?", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0); + g_Cvar_FullRounds = CreateConVar("votedelay_changelevel_fullrounds", "1", "Full rounds only? Only applies to TF2.", FCVAR_NONE, true, 0.0, true, 1.0); + g_Cvar_Rounds = CreateConVar("votedelay_changelevel_rounds", "4", "During what round should votes become available?", FCVAR_NONE, true, 0.0, true, 10.0); g_bUserBuf = (GetUserMessageType() == UM_Protobuf);