Vote System
Overview
Vote System adds polls to your server. A poll is a vote definition: a title, a list of options, an optional end date and a message shown once the poll is closed. Each player can vote once per poll, from a TypeWriter action (a dialogue option, an NPC, a menu click) or with a command. The votes are stored in a TypeWriter artifact.
Results are available as PlaceholderAPI placeholders, so you can show them on a scoreboard, in a hologram or in a menu, and with a command for staff. This is a vote among your players, not a link to server-list voting sites.
Features
- Polls with several options — each option has its own text, which can contain placeholders and colours.
- One vote per player — a second vote on the same poll is refused, unless you set a revote
cooldownSecondsin Vote Config. - End date — close a poll automatically at a given date and time.
- Closed and blocked messages — tell late voters that the poll is over, and players who vote again too soon that they must wait. Both are MiniMessage.
- Vote action — cast a vote for the triggering player, and continue your sequence only when the vote was recorded.
- Status message — send a different message depending on whether the player already voted.
- Live results — totals, per-option counts and the option chosen by the player, as placeholders.
- Staff commands — vote for a player, read the results and reset a poll.
Installation
- Install PlaceholderAPI if you want to show results with placeholders.
- Put the Vote System JAR in
plugins/Typewriter/extensions/and restart the server.
Getting started
Run a poll on the next map theme:
- In the TypeWriter web panel, open a manifest page and add a Vote Data artifact. Keep the default
artifactId(vote_storage). - Add a Vote Definition. Set
displayNametoNext map theme, add three entries tooptions(for exampleDesert,Snow,Jungle) and link the artifact indata. - Set
endDateto a UTC date and time such as2026-12-31T23:00:00Z, andclosedMessageto a short text. LeaveendDateempty for a poll with no end. - Open a sequence page. Add a Vote Action for each option: link the
definitionand setoptionIndexto0,1or2. The option index starts at 0. - Connect each action to a dialogue option, an NPC interaction or a menu button, and put your thank-you message or reward in the action
triggers. They only run when the vote is recorded. - Optionally add a Vote Message before the options, to remind players who already voted.
- Publish. Use
%typewriter_vote_total_<definition id>%on a scoreboard to show the number of votes.
The votes of a poll are stored under a key derived from the entry id of its vote definition. If you delete the entry or change its id, its stored votes are no longer found.
Commands
All commands are sub-commands of /typewriter (alias /tw). <definition> is the id or the name of a vote definition.
| Command | Permission | Description |
|---|---|---|
/typewriter vote <definition> <option> [target] | typewriter.vote.cast | Vote for an option. Options start at 1. With target, vote for the selected players. |
/typewriter vote stats [definition] | typewriter.vote.stats | Show the votes per option and the total, for one poll or all of them. |
/typewriter vote reset <definition> | typewriter.vote.reset | Delete every vote of a poll. |
All of them also need typewriter.vote.
Editable texts
No text shown in game is fixed in the code. The messages of one poll are fields of its entries: closedMessage of the Vote Definition, and the two messages of Vote Message. What the /typewriter vote commands answer is a TypeWriter snippet, so you can reword, translate or recolour it without rebuilding anything. Open plugins/Typewriter/snippets.yml, find the key under content.vote, change the value and run /tw reload. The extension registers all its snippets with their default text when it starts, so they appear in the file after the first start. Keep the defaults and nothing changes.
Command answers are MiniMessage. They are sent with the Typewriter reply prefix (Typewriter »), which comes from the engine and is not part of these snippets. A {name} in a text is replaced by the value named in the table; a placeholder that does not exist stays as written, and a value is never searched for placeholders again. The server console log stays in English.
Snippet (under content.vote.) | Default | Placeholders |
|---|---|---|
usage | <gray>Usage: /tw vote <definition> <option> [target]</gray> | none |
option_unavailable | <red>Option {option} is not available for {definition}.</red> | {option} (the number typed), {definition} (the id of the poll) |
already_voted | <red>{player} cannot vote in {definition} yet.</red> | {player}, {definition} |
cast_option_fallback | option {number} | {number} (place of the option, from 1) |
recorded_for_voter | Your vote for <blue>{definition}</blue> has been recorded as <green>{option}</green>. | {definition}, {option} (the text of the option) |
recorded_by_other | Recorded vote for <blue>{player}</blue> in {definition}: {option}. | {player}, {definition}, {option} |
recorded_for_self | Vote registered for {definition}: {option}. | {definition}, {option} |
cast_failed | <red>Unable to register the vote for {player} in {definition}.</red> | {player}, {definition} |
reset_done | <green>Votes reset for {definition}.</green> | {definition} |
no_definitions | There are no active vote definitions. | none |
stats_line | Stats for {name} [{definition}]: {options} | total: {total} | {name} (the displayName, or the entry name when empty), {definition}, {options}, {total} |
stats_option | {number}: {votes} ({label}) | {number}, {votes}, {label} (the text of the option) |
stats_option_fallback | Option {number} | {number} |
stats_separator | , | none |
entry_not_found | Could not find entry {entry} | {entry} (what was typed) |
entry_rejected | Entry did not pass filter | none |
stats_option is repeated once per option and the results are joined with stats_separator to make {options} of stats_line. stats_option_fallback is the {label} of an option that has no text, and cast_option_fallback is the name of such an option in the messages sent when a vote is recorded. The last two snippets are command errors, shown in red by the game: they are plain text, without MiniMessage.
When the poll is closed, /tw vote shows the closedMessage of the poll to the staff member (cast_failed is used only when that message is empty). cast_failed also answers a missing data entry or an option out of range.
Permissions
| Permission | Grants |
|---|---|
typewriter.vote | Access to the /typewriter vote command. |
typewriter.vote.cast | Casting a vote with the command. |
typewriter.vote.stats | Reading results with the command. |
typewriter.vote.reset | Resetting a poll. |
The vote action does not check these permissions: it only needs the entry to run.
Placeholders
Replace <id> with the entry id of the vote definition. They need PlaceholderAPI.
| Placeholder | Returns |
|---|---|
%typewriter_vote_display_<id>% | The displayName of the poll. |
%typewriter_vote_option_<id>_<n>% | The text of option number n, starting at 1. |
%typewriter_vote_total_<id>% | The total number of votes. |
%typewriter_vote_stats_<id>% | The votes of every option, separated by commas. |
%typewriter_vote_stats_<id>_<n>% | The votes of option number n, starting at 1. |
%typewriter_vote_player_<id>% | The text of the option the player voted for, or nothing if they did not vote. |
%typewriter_vote_has_voted_<id>% | 1 if the player cannot vote again for now (they voted and the cooldown is not over), else 0. |
%typewriter_vote_closed_<id>% | 1 once the end date has passed, else 0. |
%typewriter_vote_remaining_<id>% | Whole seconds before the end date; 0 when there is no end date or it has passed. |
vote_votes_<id> works like vote_stats_<id>.
Entries
7 entries, generated from the extension source.
Actions2
- Vote ActionVote Action
- Vote MessageVote Message
Events1
- Vote Cast EventVote Cast Event
Facts1
- Vote FactVote Fact
Artifacts1
- Vote DataVote Data
Manifests2
- Vote ConfigVote System Configuration
- Vote DefinitionVote Definition
Vote Cast Event and Vote Fact exist, but nothing reads them yet: the event is never fired and the fact is not filled by votes. Vote Config is read: it sets the revote cooldown and the debug log.
Troubleshooting
The vote is not recorded. The action and the command fail silently when something is missing: the vote definition must have a data artifact linked, the option must exist, the player must not have voted already (or their cooldown must have elapsed, see Vote Config), and the poll must not be past its endDate. When the poll is closed, the closedMessage is sent to the player; when the player must still wait, the blockedMessage is sent. Turn debug on in Vote Config to log the reason of every refusal.
The wrong option gets the vote. The vote action counts options from 0, while the command and the placeholders count from 1.
The poll never closes. endDate must be an ISO-8601 instant in UTC ending with Z, such as 2026-12-31T23:00:00Z. A value in another format is ignored and the poll stays open.
The results are empty after I renamed the entry. Votes are tied to the entry id of the definition. Restore the old id, or reset the poll and start again.
A placeholder shows as is. Check that PlaceholderAPI is installed and that the id is the entry id of the definition. The extension also finds a definition when a placeholder is requested before the first vote.
Colours in a message look wrong. The status messages of Vote Message, the closedMessage and the blockedMessage are read as MiniMessage after their placeholders are resolved: write <red>text</red>, not &c codes.
Players can vote twice. With a cooldownSeconds above 0 in Vote Config, this is expected after the cooldown. Otherwise the check is per player and per poll. Using several vote definitions with the same options creates separate polls.