Difference between revisions of "TF2 Voting"
(Updated VoteSetup usermessage for today's change) |
(Valve no longer uses 255.) |
||
Line 51: | Line 51: | ||
== User Messages == | == User Messages == | ||
− | {{qnotice|These user messages use unsigned bytes for the team number. Since | + | {{qnotice|These user messages use unsigned bytes for the team number. Since the October 15, 2014 update, all teams is now represented by 0 (it used to be -1 / 255)}}<br> |
Line 112: | Line 112: | ||
{{begin-hl2msg|VoteStart|string}} | {{begin-hl2msg|VoteStart|string}} | ||
− | {{hl2msg|byte|team|Team index or | + | {{hl2msg|byte|team|Team index or 0 for all}} |
{{hl2msg|byte|ent_idx|Client index of person who started the vote, or 99 for the server.}} | {{hl2msg|byte|ent_idx|Client index of person who started the vote, or 99 for the server.}} | ||
{{hl2msg|string|disp_str|Vote issue translation string}} | {{hl2msg|string|disp_str|Vote issue translation string}} | ||
Line 139: | Line 139: | ||
{{begin-hl2msg|VotePass|string}} | {{begin-hl2msg|VotePass|string}} | ||
− | {{hl2msg|byte|team|Team index or | + | {{hl2msg|byte|team|Team index or 0 for all}} |
{{hl2msg|string|disp_str|Vote success translation string}} | {{hl2msg|string|disp_str|Vote success translation string}} | ||
{{hl2msg|string|details_str|Vote winner}} | {{hl2msg|string|details_str|Vote winner}} | ||
Line 161: | Line 161: | ||
{{begin-hl2msg|VoteFailed|string}} | {{begin-hl2msg|VoteFailed|string}} | ||
− | {{hl2msg|byte|team|Team index or | + | {{hl2msg|byte|team|Team index or 0 for all}} |
{{hl2msg|byte|reason|Failure reason code (0, 3-4)}} | {{hl2msg|byte|reason|Failure reason code (0, 3-4)}} | ||
{{end-hl2msg}} | {{end-hl2msg}} |
Revision as of 21:23, 15 October 2014
This is for SourceMod plugin development involving the TF2 Vote System. If you are looking into the details on configuring TF2 Voting on your TF2 server, see Voting on the Official Team Fortress Wiki.
Team Fortress 2 has a new VGUI voting system based on the Left 4 Dead Voting system and is controlled by Game Events and User Messages. You can use either a string from the resource file, or TF_playerid_noteam which will let you create any vote you want.
Contents
How voting works
- A client issues a callvote with the vote type and argument, or the server calls a vote (server uses entity index 99).
- The server sends a vote_options event.
- For Yes/No votes, the values it sends are Yes and No (in that order). For Multiple Choice votes, the actual vote options are sent.
- The VoteStart User Message is sent, the last argument determines the vote type.
- Clients use the "vote" command to register their votes (option1 through option5), after which the server sends a vote_cast event with a 0-based option number (so option1 = 0, option5 = 4). It also updates the vote_controller entity's vote counts.
- When the vote is complete, the server sends either a VotePass or VoteFailed User Message.
Server Entity
The server should update this as appropriate. Unfortunately, the valid values for m_iActiveIssueIndex is unknown.
Name: | vote_controller (CVoteController) | |||||||||||||||
Structure: |
|
Console Commands
callvote
Note: This command can take 0 arguments. If it does, a VoteSetup usermessage is returned to that user.
Name: | callvote | ||||||
Structure: |
|
kicktype_string is one of these (at least in the English version):
- other
- cheating
- idle
- scamming
This data is primarily there if you want to intercept these messages in a plugin.
CallVoteFailed UserMessage is sent back if a vote cannot be called unless a vote is already being displayed.
vote
Note: This command is only valid when a vote is ongoing.
Name: | vote | |||
Structure: |
|
Note that these are 1-based, but the vote_cast event is 0-based.
User Messages
Note: These user messages use unsigned bytes for the team number. Since the October 15, 2014 update, all teams is now represented by 0 (it used to be -1 / 255)
The User Messages that exist in the TF2 Voting system are:
VoteSetup
Note: Sent to a player when they load the Call Vote screen (which sends the callvote command to the server), lists what votes are allowed on the server
{{qnotice|This changed in the October 15, 2014 update
Name: | VoteSetup | ||||||
Structure: |
|
Name: | issue | |||||||||
Structure: |
|
There is one string sent for each supported issue. Valid strings are:
- Kick
- RestartGame
- ChangeLevel
- NextLevel
- ScrambleTeams
- ChangeMission
Disabled issues can be either omitted or followed by " (Disabled on Server)". The default is to omit them (sv_vote_ui_hide_disabled_issues 1).
CallVoteFailed
Note: Sent to a player when they attempt to call a vote and fail.
Name: | CallVoteFailed | ||||||
Structure: |
|
Valid Failure Codes are:
1 - VOTE_FAILED_TRANSITIONING_PLAYERS - Cannot call vote while other players are still loading. This appears to be a holdover from L4D2; use code 10 instead.
2 - VOTE_FAILED_RATE_EXCEEDED - You called a vote recently and cannot call another one for X seconds (second argument to CallVoteFailed specifies the number of seconds)
5 - VOTE_FAILED_ISSUE_DISABLED - Server has disabled that issue.
6 - VOTE_FAILED_MAP_NOT_FOUND - That map does not exist.
7 - VOTE_FAILED_MAP_NAME_REQUIRED - You must specify a map name
8 - VOTE_FAILED_FAILED_RECENTLY - This vote failed recently
9 - VOTE_FAILED_TEAM_CANT_CALL - Your team cannot call this vote
10 - VOTE_FAILED_WAITINGFORPLAYERS - Voting not allowed while Waiting for Players
11 - VOTE_FAILED_PLAYERNOTFOUND - Doesn't appear to work
12 - VOTE_FAILED_CANNOT_KICK_ADMIN - Can't Kick Server Admin
13 - VOTE_FAILED_SCRAMBLE_IN_PROGRESS - Vote Scramble is pending
14 - VOTE_FAILED_SPECTATOR - Spectators can't vote
15 - VOTE_FAILED_NEXTLEVEL_SET - Next level already set
16 - VOTE_FAILED_MAP_NOT_VALID - Map is not in the map list
17 - VOTE_FAILED_CANNOT_KICK_FOR_TIME - Cannot kick yet. Used for MvM
18 - VOTE_FAILED_CANNOT_KICK_DURING_ROUND - Cannot kick during round. Used for MvM
19 - VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE - Modification is already active, used by Eternaween
VoteStart
Note: Sent to all players currently online. The default implementation also sends it to bots.
Name: | VoteStart | |||||||||||||||
Structure: |
|
Valid issue strings:
- #TF_vote_kick_player_other - Generic Kick vote. param1 is player name.
- #TF_vote_kick_player_idle - Idler Kick vote. param1 is player name.
- #TF_vote_kick_player_cheating - Cheater Kick vote. param1 is player name.
- #TF_vote_kick_player_scamming - Scammer Kick vote. param1 is player name.
- #TF_vote_restart_game - Restart map vote. param1 ignored.
- #TF_vote_changelevel - Change map vote. param1 is map name.
- #TF_vote_nextlevel - Set next level vote. param1 is map name.
- #TF_vote_nextlevel_choices - End of map map vote. param1 ignored. This vote is not in the user vote menu.
- #TF_vote_scramble_teams - Scramble teams vote. param1 ignored.
- #TF_vote_should_scramble_round - Scramble teams at round end vote. param1 ignored. This vote is not in the user vote menu.
- #TF_vote_td_start_round - Start the round? param1 ignored. This vote is not in the user vote menu.
- #TF_vote_changechallenge - Change MvM mission? param1 is mission. This vote is in the menu on MvM maps.
- #TF_vote_eternaween - Activate Halloween mode? param1 ignored. This vote is activated by someone using the Eternaween item.
- #TF_playerid_noteam - Unofficially used for a custom vote. param1 is custom vote issue.
VotePass
Note: Sent to all players after a vote passes.
Name: | VotePass | |||||||||
Structure: |
|
Valid success strings:
- #TF_vote_passed_kick_player - Kick vote passed. param1 is player name.
- #TF_vote_passed_restart_game - Restart vote passed. param1 ignored.
- #TF_vote_passed_changelevel - Change level vote passed. param1 is map name.
- #TF_vote_passed_nextlevel - Set next level vote passed. param1 is map name.
- #TF_vote_passed_nextlevel_extend - Current map has been extended. param1 ignored.
- #TF_vote_passed_scramble_teams - Team Scramble vote passed. param1 is ignored.
- #TF_vote_passed_td_start_round - Round start vote passed. param1 is ignored.
- #TF_vote_passed_changechallenge - MvM mission change passed. param1 is new mission.
- #TF_vote_passed_eternaween - Eternaween vote passed. param1 ignored.
- #TF_playerid_noteam - Unofficially used for a custom success string. param1 is custom success string.
VoteFailed
Note: Sent to all players after a vote fails.
Name: | VoteFailed | ||||||
Structure: |
|
Valid Failure codes are:
0 - VOTE_FAILED_GENERIC - Generic "Vote Failed" message
3 - VOTE_FAILED_YES_MUST_EXCEED_NO - Yes votes must outnumber No votes
4 - VOTE_FAILED_QUORUM_FAILURE - Not Enough Votes
Events
Server to Client Events
vote_cast
Note: Sent to all players when a player chooses a vote option (or more specifically, the server receives a vote command)
Name: | vote_cast | |||||||||
Structure: |
|
This event is unusual as it uses the client's entity ID instead of its userid.
vote_options
Note: Sent to players before VoteStart UserMessage to populate choices for a multiple choice vote
Name: | vote_options | ||||||||||||||||||
Structure: |
|
If sv_vote_issue_nextlevel_allowextend is set to 1, option5 is "Extend current Map"
Client-only Events
These events are passed between the TF2 client and the client's VGUI voting panels. They have no server interaction and the server should not be touching them in any way.
vote_ended
Name: | vote_ended | |
Structure: |
|
vote_started
Name: | vote_started | ||||||||||||
Structure: |
|
vote_changed
Note: These values are read from the vote_controller entity on the server
Name: | vote_changed | ||||||||||||||||||
Structure: |
|
vote_passed
Name: | vote_passed | |||||||||
Structure: |
|
vote_failed
Name: | vote_failed | |||
Structure: |
|
Example voting plugin
This is a basic plugin that starts a vote, "Is gaben fat?". It does not ensure the same client does not vote multiple times, nor does it actually kick the user.
#include <sourcemod> // TF2's internal map vote uses client index 99 for the server #define TF2_SERVER_CLIENT_INDEX 99 #define TF2_TEAM_ALL -1 new yesvotes; new novotes; #define MAX_VOTES 4 public Plugin:myinfo = { name = "Test Yes/No Vote", author = "Powerlord", description = "A test vote plugin for the AlliedMods wiki", version = "1.0", url = "http://wiki.alliedmods.net/TF2_Voting" } public OnPluginStart() { RegConsoleCmd("testvote",Callvote_Handler); RegConsoleCmd("vote", vote); } public Action:Callvote_Handler(client, args) { new Handle:bf = StartMessageAll("VoteStart", USERMSG_RELIABLE); BfWriteByte(bf, TF2_TEAM_ALL); BfWriteByte(bf, TF2_SERVER_CLIENT_INDEX); BfWriteString(bf, "#TF_playerid_noteam"); BfWriteString(bf, "Is gaben fat?"); BfWriteBool(bf, true); EndMessage(); yesvotes = 0; novotes = 0; return Plugin_Handled; } UpdateVotes() { if (yesvotes+novotes >= MAX_VOTES) { PrintToServer("voting complete!"); if (yesvotes > novotes) { new Handle:bf = StartMessageAll("VotePass"); BfWriteByte(bf, TF2_TEAM_ALL); BfWriteString(bf, "#TF_playerid_noteam"); BfWriteString(bf, "Gaben is fat"); EndMessage(); } else { new Handle:bf = StartMessageAll("VoteFailed"); BfWriteByte(bf, TF2_TEAM_ALL); // Check list of failure reasons BfWriteByte(bf, 3); EndMessage(); } } } // If the TF2 vote system is running (sv_allow_votes 1), this needs to be a command listener // because TF2 registers the vote command only when a vote is ongoing, and thus hooking it using RegConsoleCmd doesn't work. public Action:vote(client, args) { new String:arg[8]; new option = 0; GetCmdArg(1,arg,8); PrintToServer("Got vote %s from %i",arg,client); if (strcmp(arg,"option1",true) == 0) { yesvotes++; option = 0; } else if (strcmp(arg,"option2",true) == 0) { novotes++; option = 1; } new Handle:msg = CreateEvent("vote_cast"); SetEventInt(msg, "entityid", client); SetEventInt(msg, "team", -1); SetEventInt(msg, "vote_option", option); FireEvent(msg); UpdateVotes(); return Plugin_Continue; }
See the following images for examples what this looks like:
See Also
- Left 4 Voting
- Left 4 Voting 2
- BuiltinVotes, a SourceMod extension that exposes a voting API that uses this voting system.