Reki wrote:That's true, we can fix it so that when it gets to all-but-1, any less than that stays as such.I could knock this up in a few minutes really, and I think it'd be far better than the current method.
We're interested in finding when x - (1/4x + 3) =< 1. This is easily seen as x being less than 6 players.
(While we're on this discussion, let's deal with fractions in our 1/4 formula by rounding them.)
A 6-player server will require 4 other people to vote yes.
A 5-or-fewer server will require everyone but the person being voted to vote yes.
Actually this is all a bit too complicated, maybe we should stick with raw percentages.
The Problem with Votekick
-
rakiru
Coder
- Posts: 1349
- Joined: Sun Nov 11, 2012 12:26 pm
-
-
rakiru
Coder
- Posts: 1349
- Joined: Sun Nov 11, 2012 12:26 pm
-
Ok, I haven't tested this as I currently lack the ability to, but this should work, and if it doesn't, then hopefully only a couple of small bugs need fixed:
Code: Select all
Edit: It's also possible that I've missed something crucial and not realised.# This is based on the original votekick.py script as I don't have the
# luxury of a large server to test on, so I'll keep the same license.
# Oh, and since pyspades/pysnip is under the GPL, this is required to be
# under the same license, because it's cancerous like that. Anyway,
# original license below:
# Copyright (c) James Hofmann 2012.
# pyspades is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# pyspades 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 pyspades. If not, see <http://www.gnu.org/licenses/>.
# Modifications copyright (c) Sean Gordon 2013.
# Also, 80-char line length limits are stupid - are we living in the 80s?
from twisted.internet.reactor import seconds
from scheduler import Scheduler
from commands import name, add, get_player, InvalidPlayer
######################
# OPTIONS & MESSAGES #
######################
# Note: You should change these options in the config if possible. These are defaults!
O_REQUIRE_REASON = True
# If a player starts a bad votekick and the threshold (as defined below) of negative votes is met, kick that player
O_KICK_INSTIGATOR = True
# Broadcast votes in chat?
O_SHOW_VOTES = True
O_VOTE_DURATION = 2 * 60 # In seconds
O_VOTE_COOLDOWN = 3 * 60 # In seconds
O_BAN_TIME = 15 # In minutes, set to 0 to kick without banning
O_CHAT_UPDATE_TIME = 30 # In seconds
# Formula for calculating required votes: ROUND(MULTIPLIER * TOTAL_PLAYERS + CONSTANT) where ROUND() rounds to nearest whole number
O_FORMULA_MULTIPLIER = 1/4
O_FORMULA_CONSTANT = 3
# The count can be limited at TOTAL_PLAYERS - 1 to prevent impossible votekick scenarios (e.g. 3 players online, 4 votes required)
O_CAP_REQUIRED_VOTES = True
# Disallow voting if there are less than a certain number of players
O_MINIMUM_PLAYERS = 3
# The only messages you're likely to want to change, if any, are the first two
S_DEFAULT_REASON = "NO REASON GIVEN, BECAUSE I'M LAZY"
S_BAD_VOTEKICK_REASON = "INSTIGATED A BAD VOTEKICK"
S_NO_VOTEKICK = "No votekick in progress"
S_IN_PROGRESS = "Votekick already in progress"
S_SELF_VOTEKICK = "You can't votekick yourself"
S_IMMUNE = "This player can't be votekicked"
S_COOLING_DOWN = "You can't start another votekick yet"
S_REASON_REQUIRED = "You must provide a reason for the votekick"
S_NOT_ENOUGH_PLAYERS = "There aren't enough players to vote"
S_DID_NOT_START = "You didn't start this votekick"
S_VOTE_YES = "{player} voted YES"
S_VOTE_NO = "{player} voted NO"
S_ENDED = "Votekick for {target} has ended. {result}"
S_RESULT_TIMED_OUT = 'Votekick timed out'
S_RESULT_CANCELLED = 'Votekick cancelled'
S_RESULT_BANNED = "Banned by admin"
S_RESULT_KICKED = "Kicked by admin"
S_RESULT_INSTIGATOR_KICKED = "Instigator kicked by admin"
S_RESULT_TARGET_LEFT = "Target {target} left during votekick"
S_RESULT_INSTIGATOR_LEFT = "Instigator {instigator} left during votekick"
S_RESULT_INSTIGATOR_BANNED = "Instigator {instigator} kicked"
S_RESULT_TARGET_BANNED = 'Player {target} kicked'
S_ANNOUNCE = '{instigator} started a VOTEKICK against {target} - Vote by using /Y or /N'
S_ANNOUNCE_SELF = "You started a votekick against {target} - Say /CANCEL to stop it"
S_ANNOUNCE_IRC = "* {instigator} started a votekick against player {target} - Reason: {reason}"
S_UPDATE = '{instigator} is votekicking {target} - Vote by using /Y or /N..'
S_REASON = 'Reason: {reason}'
class VotekickException(Exception):
pass
############
# COMMANDS #
############
@name('votekick')
def start_votekick(connection, *args):
protocol = connection.protocol
if connection not in protocol.players:
raise KeyError()
player = connection
if not args:
if protocol.votekick:
# player requested votekick info
protocol.votekick.send_chat_update(player)
return
else:
raise ValueError()
value = args[0]
try:
target = get_player(protocol, '#' + value)
except InvalidPlayer:
target = get_player(protocol, value)
reason = " ".join(args[1:])
try:
votekick = Votekick.start(player, target, reason)
protocol.votekick = votekick
except VotekickException as err:
return str(err)
@name('cancel')
def cancel_votekick(connection):
protocol = connection.protocol
votekick = protocol.votekick
if not votekick:
return S_DID_NOT_START
if connection in protocol.players:
player = connection
if player is votekick.instigator or player.admin or player.rights.cancel:
#TODO: Check if instigator is losing vote, so they don't cancel it just to stop themselves being kicked
votekick.end(S_RESULT_CANCELLED)
else:
return S_CANT_CANCEL
@name('y')
def vote_yes(connection):
protocol = connection.protocol
if connection not in protocol.players:
raise KeyError()
player = connection
votekick = protocol.votekick
if not votekick:
return S_NO_VOTEKICK
else:
votekick.vote(player, True)
@name('n')
def vote_no(connection):
protocol = connection.protocol
if connection not in protocol.players:
raise KeyError()
player = connection
votekick = protocol.votekick
if not votekick:
return S_NO_VOTEKICK
else:
votekick.vote(player, False)
add(start_votekick)
add(cancel_votekick)
add(vote_yes)
add(vote_no)
class Votekick(object):
schedule = None
def _get_votes_remaining(self):
return self.protocol.get_required_votes() - len([a for a,b in d.iteritems() if b]) + 1
def _get_votes_remaining_no(self):
return self.protocol.get_required_votes() - len([a for a,b in d.iteritems() if not b) + 1
votes_remaining = property(_get_votes_remaining)
votes_remaining_no = property(_get_votes_remaining_no)
@classmethod
def start(cls, instigator, target, reason = None):
protocol = instigator.protocol
last_votekick = instigator.last_votekick
if reason then:
reason = reason.strip()
if protocol.votekick:
raise VotekickException(S_IN_PROGRESS)
elif instigator is target:
raise VotekickException(S_SELF_VOTEKICK)
elif not protocol.is_enough_players():
raise VotekickException(S_NOT_ENOUGH_PLAYERS)
elif target.admin or target.rights.cancel:
raise VotekickException(S_IMMUNE)
elif not instigator.admin and (last_votekick is not None and seconds() - last_votekick < cls.interval):
raise VotekickException(S_COOLING_DOWN)
elif self.require_reason and not reason:
raise VotekickException(S_REASON_REQUIRED)
result = protocol.on_votekick_start(instigator, target, reason)
if result is not None:
raise VotekickException(result)
reason = reason or S_DEFAULT_REASON
return cls(instigator, target, reason)
def __init__(self, instigator, target, reason):
self.protocol = protocol = instigator.protocol
self.instigator = instigator
self.target = target
self.reason = reason
self.votes = {instigator : True}
self.ended = False
protocol.irc_say(S_ANNOUNCE_IRC.format(instigator = instigator.name,
target = target.name, reason = self.reason))
protocol.send_chat(S_ANNOUNCE.format(instigator = instigator.name,
target = target.name), sender = instigator)
protocol.send_chat(S_REASON.format(reason = self.reason),
sender = instigator)
instigator.send_chat(S_ANNOUNCE_SELF.format(target = target.name))
schedule = Scheduler(protocol)
schedule.call_later(self.duration, self.end, S_RESULT_TIMED_OUT)
schedule.loop_call(self.chat_update_time, self.send_chat_update)
self.schedule = schedule
def vote(self, player, vote):
if self.target is player:
return
if self.public_votes:
self.protocol.send_chat((S_VOTE_YES if vote else S_VOTE_NO).format(player = player.name))
self.votes[player] = vote
if self.votes_remaining <= 0:
# vote passed, ban or kick accordingly
target = self.target
self.end(S_RESULT_TARGET_BANNED)
print '%s votekicked' % target.name
if self.ban_duration > 0.0:
target.ban(self.reason, self.ban_duration)
else:
target.kick(silent = True)
elif self.votes_remaining_no <= 0:
# vote failed badly, ban or kick accordingly
target = self.instigator
self.end(S_RESULT_INSTIGATOR_BANNED
print '%s votekicked' % target.name
if self.ban_duration > 0.0:
target.ban(S_BAD_VOTEKICK_REASON, self.ban_duration)
else:
target.kick(silent = True)
def release(self):
self.instigator = None
self.target = None
self.votes = None
if self.schedule:
self.schedule.reset()
self.schedule = None
self.protocol.votekick = None
def end(self, result):
self.ended = True
message = S_ENDED.format(target = self.target.name, result = result)
self.protocol.send_chat(message, irc = True)
if not self.instigator.admin:
self.instigator.last_votekick = seconds()
self.protocol.on_votekick_end()
self.release()
def send_chat_update(self, target = None):
# send only to target player if provided, otherwise broadcast to server
target = target or self.protocol
target.send_chat(S_UPDATE.format(instigator = self.instigator.name, target = self.target.name, needed = self.votes_remaining))
target.send_chat(S_REASON.format(reason = self.reason))
def apply_script(protocol, connection, config):
# Using the same config keys as the other version where possible
Votekick.require_reason = config.get('votekick_require_reason', O_REQUIRE_REASON)
Votekick.kick_instigator = config.get('votekick_kick_instigator', O_KICK_INSTIGATOR)
Votekick.show_votes = config.get('votekick_public_votes', O_SHOW_VOTES)
Votekick.vote_duration = config.get('votekick_vote_duration', O_VOTE_DURATION)
Votekick.cooldown = config.get('votekick_cooldown', O_VOTE_COOLDOWN)
Votekick.ban_duration = config.get('votekick_ban_duration', O_BAN_TIME)
Votekick.chat_update_time = config.get('votekick_chat_update_time', O_CHAT_UPDATE_TIME)
formula_multiplier = config.get('votekick_formula_multiplier', O_FORMULA_MULTIPLIER)
formula_constant = config.get('votekick_formula_constant', O_FORMULA_CONSTANT)
cap_required_votes = config.get('votekick_cap_required_votes', O_CAP_REQUIRED_VOTES)
minimum_players = config.get('votekick_minimum_players', O_MINIMUM_PLAYERS)
class VotekickProtocol(protocol):
votekick = None
def get_player_count(self):
return sum(not player.disconnected for player in self.players.itervalues())
def is_enough_players(self):
return self.get_player_count() >= self.minimum_players
def get_required_votes(self):
player_count = self.get_player_count
threshold = self.formula_multiplier * player_count + self.formula_constant
if self.cap_required_votes and threshold > player_count - 1:
threshold = player_count - 1
def on_map_leave(self):
if self.votekick:
self.votekick.release()
protocol.on_map_leave(self)
def on_ban(self, banee, reason, duration):
votekick = self.votekick
if votekick and votekick.target is self:
votekick.end(S_RESULT_BANNED)
protocol.on_ban(self, connection, reason, duration)
def on_votekick_start(self, instigator, target, reason):
pass
def on_votekick_end(self):
pass
class VotekickConnection(connection):
last_votekick = None
def on_disconnect(self):
votekick = self.protocol.votekick
if votekick:
if votekick.target is self:
# target leaves, gets votekick ban
reason = votekick.reason
votekick.end(S_RESULT_TARGET_LEFT.format(target = self.name))
self.ban(reason, Votekick.ban_duration)
elif votekick.instigator is self:
# instigator leaves, votekick is called off
s = S_RESULT_INSTIGATOR_LEFT.format(instigator = self.name)
votekick.end(s)
else:
# make sure we still have enough players
votekick.votes.pop(self, None)
if votekick.votes_remaining <= 0:
votekick.end(S_NOT_ENOUGH_PLAYERS)
connection.on_disconnect(self)
def kick(self, reason = None, silent = False):
votekick = self.protocol.votekick
if votekick:
if votekick.target is self:
votekick.end(S_RESULT_KICKED)
elif votekick.instigator is self:
votekick.end(S_RESULT_INSTIGATOR_KICKED)
connection.kick(self, reason, silent)
return VotekickProtocol, VotekickConnection
-
Triplefox
Scripter
- Posts: 32
- Joined: Thu Nov 22, 2012 5:28 am
Personally, I've concluded that votekick is a very complicated placebo. Even in games with dedicated UI for it, people are too distracted to make a good decision, and as it's made more effective it becomes a larger target for abuse.
What can be done instead? Have the game instantly elect temporary moderators - as many as we think is safe at any given time, each granted limited powers to do basic moderation tasks. The computer can create a heuristic for who is most likely to do a good job(if I were to implement it, I'd use time played with "real" admins/trusted), and a hierarchy based on that heuristic can be imposed to mitigate the possibility of takeovers. We can further supplement the system with a report/commend command that would give the admins some idea of temp-mod performance, and an "untrusted" blacklist if necessary.
I know this can work, because I've seen the model of "grant lots of powers" used in IRC very successfully. In that system a bot monitors everything and decides at its discretion to give ops, usually after someone has been visiting for many weeks. In general the channel is 70-80% ops at any one time. The last takeover attempt I can remember was about seven years ago, and drama is limited to personal issues.
This could also be further customized for each server, for example a bot quizzing the mod-elect on the server's rules before granting any powers.
What can be done instead? Have the game instantly elect temporary moderators - as many as we think is safe at any given time, each granted limited powers to do basic moderation tasks. The computer can create a heuristic for who is most likely to do a good job(if I were to implement it, I'd use time played with "real" admins/trusted), and a hierarchy based on that heuristic can be imposed to mitigate the possibility of takeovers. We can further supplement the system with a report/commend command that would give the admins some idea of temp-mod performance, and an "untrusted" blacklist if necessary.
I know this can work, because I've seen the model of "grant lots of powers" used in IRC very successfully. In that system a bot monitors everything and decides at its discretion to give ops, usually after someone has been visiting for many weeks. In general the channel is 70-80% ops at any one time. The last takeover attempt I can remember was about seven years ago, and drama is limited to personal issues.
This could also be further customized for each server, for example a bot quizzing the mod-elect on the server's rules before granting any powers.
-
jordan
Deuced Up - Posts: 97
- Joined: Thu Jan 03, 2013 12:45 pm
I thought about it more, maybe to make it more effective a small pop-up should come up in like the top left corner of the screen and you could just press yes or no.
-
Sasquatch
Mapper
- Posts: 141
- Joined: Wed Nov 14, 2012 5:18 pm
rakiru wrote:I just kinda made a thread about this. This is why coming together to make a universal decision is so critical. Sure, people get kicked for improper reasons sometimes, but that doesn't happen nearly as often as people hacking and griefing get away with what they do. And yes, the community is in part to blame for this, but note that a lot of players are kids, or foreigners who don't speak english, and tend to be oblivious to votekicking, or the chat entirely. And at any rate, if someone gets kicked for 24 hours (the way it should be), then there are still lots of other servers to play on, and the odds of being kicked twice on accident is pretty low. Now if you're a hacker, pretty much you're objective is going to be to ruin as many servers as possible, but once you've exhausted all of them, now you're left without places to play after that. I will never understand why people are conservative with kicking. It should not be discreet and should be for a long time. Period.jdrew wrote:If you want get a better script then make oneThat doesn't really work if you don't run a server and the server owners don't know about it or want/use it.
I make maps :3
My [url=aos://199772608:32887]dedicated server[/url] is NOT up and running... soz
-
rakiru
Coder
- Posts: 1349
- Joined: Sun Nov 11, 2012 12:26 pm
-
Triplefox wrote:Personally, I've concluded that votekick is a very complicated placebo. Even in games with dedicated UI for it, people are too distracted to make a good decision, and as it's made more effective it becomes a larger target for abuse.That sounds rather dangerous really, and there's no way to ensure the people selected would even user it (they could even miss the fact they have the power all together, since the only way to alert them is via a small chat box).
What can be done instead? Have the game instantly elect temporary moderators - as many as we think is safe at any given time, each granted limited powers to do basic moderation tasks. The computer can create a heuristic for who is most likely to do a good job(if I were to implement it, I'd use time played with "real" admins/trusted), and a hierarchy based on that heuristic can be imposed to mitigate the possibility of takeovers. We can further supplement the system with a report/commend command that would give the admins some idea of temp-mod performance, and an "untrusted" blacklist if necessary.
I know this can work, because I've seen the model of "grant lots of powers" used in IRC very successfully. In that system a bot monitors everything and decides at its discretion to give ops, usually after someone has been visiting for many weeks. In general the channel is 70-80% ops at any one time. The last takeover attempt I can remember was about seven years ago, and drama is limited to personal issues.
This could also be further customized for each server, for example a bot quizzing the mod-elect on the server's rules before granting any powers.
If you've seen it done before, I'll trust you that it can work, but I'm curious about the theme of the IRC channel you mentioned - how do the general users of it compare to the young, foreign, hacker-filled AoS players?
-
GreaseMonkey
Coder
- Posts: 733
- Joined: Tue Oct 30, 2012 11:07 pm
The "op party" trick works really well on IRC. However, this is because these channels are run by the sorts of people who go on IRC. I'm not sure how well it'll work with the sorts of people who play Ace of Spades.
Imagine someone in {EPIC} having tempmod powers. *shudder*
Imagine someone in {EPIC} having tempmod powers. *shudder*
-
Bigcheecho
Build and Shoot's 1st Birthday
- Posts: 582
- Joined: Sat Dec 22, 2012 3:43 pm
Reki wrote:Here are some ideas I proposed in a post ages ago (though revamped with examples this time!)
1) The threshold for successfully passing a kick shouldn't just be a fixed percentage of the total server population. Instead, add a constant term to create a curve.
Example: Someone (not me) proposed 1/4[Servertotal]+3.
4 people on: 1+3 = 4 votes needed (for such small populations, having everyone vote isn't hard)
8 people on: 2+3 = 5 votes needed (still small, quite easy to achieve majority)
20 people on: 5+3 = 8 votes needed (server's moderately filled, a bit too hard to go for 1/2)
32 people on: 8+3 = 11 votes needed (about 1/3 of the server)
Reki wrote:That's true, we can fix it so that when it gets to all-but-1, any less than that stays as such.I made a graph for your equation, designed for x=2-32. Click it to use zoom and all that fun stuff.
We're interested in finding when x - (1/4x + 3) =< 1. This is easily seen as x being less than 6 players.
(While we're on this discussion, let's deal with fractions in our 1/4 formula by rounding them.)
A 6-player server will require 4 other people to vote yes.
A 5-or-fewer server will require everyone but the person being voted to vote yes.
- x=players online
y=votes required
red line=actual value with decimal
black line=rounded value with Reki's rules of 5 or less

Last edited by Bigcheecho on Mon Jan 07, 2013 3:05 pm, edited 8 times in total.
-
xytor
Deuce - Posts: 5
- Joined: Sun Jan 06, 2013 11:58 pm
Votekick is a double-edged sword. I've never actually been kicked, but occasionally people accused me of hacking if I infiltrated an enemy base and headshot everybody who spawned. It's extremely easy to get headshots in this game... The only thing that saved me from being kicked is the high vote number.
-
GreaseMonkey
Coder
- Posts: 733
- Joined: Tue Oct 30, 2012 11:07 pm
xytor wrote:Votekick is a double-edged sword. I've never actually been kicked, but occasionally people accused me of hacking if I infiltrated an enemy base and headshot everybody who spawned. It's extremely easy to get headshots in this game... The only thing that saved me from being kicked is the high vote number.Is it safe to say we need to bring back /n?
-
Sasquatch
Mapper
- Posts: 141
- Joined: Wed Nov 14, 2012 5:18 pm
GreaseMonkey wrote: Is it safe to say we need to bring back /n?
Sasquatch wrote: ...Sure, people get kicked for improper reasons sometimes, but that doesn't happen nearly as often as people hacking and griefing get away with what they do. And yes, the community is in part to blame for this, but note that a lot of players are kids, or foreigners who don't speak english, and tend to be oblivious to votekicking, or the chat entirely. And at any rate, if someone gets kicked for 24 hours (the way it should be), then there are still lots of other servers to play on, and the odds of being kicked twice on accident is pretty low. Now if you're a hacker, pretty much you're objective is going to be to ruin as many servers as possible, but once you've exhausted all of them, now you're left without places to play after that. I will never understand why people are conservative with kicking. It should not be discreet and should be for a long time. Period.Honestly you cannot appeal to every case you hear where someone is kicked on accident. I've almost never experienced accusations of hacking and when I am accused, if you literally just say nothing instead of arguing, you're chances or being kicked become much lower. At that point most people will just check the death logs to see if your name has popped up four times in the last second with headshots, and if it hasn't, you're usually safe. Than and if you're not hacking the lack of getting snipped from outside of the fog range makes people tend to vote less.
I make maps :3
My [url=aos://199772608:32887]dedicated server[/url] is NOT up and running... soz
-
Bigcheecho
Build and Shoot's 1st Birthday
- Posts: 582
- Joined: Sat Dec 22, 2012 3:43 pm
rakiru wrote:Oh, OK. I was taught that >4 meant to round up. Sorry then, Reki.Bigcheecho wrote:Oh, and Reki, YOU ROUNDED 4.5 WRONG. Just saying.That depends on how you're rounding.
-
rakiru
Coder
- Posts: 1349
- Joined: Sun Nov 11, 2012 12:26 pm
-
Bigcheecho wrote:The usual way is >= .5 is up, although you can also take the ceiling (> 0) or floor (< 1) of the number too.rakiru wrote:Oh, OK. I was taught that >4 meant to round up. Sorry then, Reki.Bigcheecho wrote:Oh, and Reki, YOU ROUNDED 4.5 WRONG. Just saying.That depends on how you're rounding.
-
Bigcheecho
Build and Shoot's 1st Birthday
- Posts: 582
- Joined: Sat Dec 22, 2012 3:43 pm
rakiru wrote:And I assume that's how it's programmed on most scripting languages, which is fine with me.Bigcheecho wrote: Oh, OK. I was taught that >4 meant to round up. Sorry then, Reki.The usual way is >= .5 is up, although you can also take the ceiling (> 0) or floor (< 1) of the number too.
Thank god this didn't turn into a flame war about the rules of rounding, or then Dan would have to lock this thread, and I wouldn't be able to make more mathematical graphs for other suggested mathematical solutions for votekicking.
Who is online
Users browsing this forum: No registered users and 32 guests

