It's really been bugging me for a while not that some people are using the plainly terrible default values when running their own server. Aimbots are slowly becoming a problem again and votekicking doesn't do any good considering 50% of the people who play games are often either oblivious on how to use votekicks or oblivious to typing in the chat entirely. I think that in the default config file for pyspades or pysnip or whatever we're using now should be changed to something like 20%.
On top of that, I think we should discuss here as a community what some of the other default features should be. Votekick percentages is just issue #1. I'll propose a couple things which I think will help solve problems the community has been facing for a while...
1. Don't add any maps initially to the cycle for map rotation in the configuration file. That way we can avoid having dozens of servers up at a time that are only playing bridge or anticbridge or whatever. Also, hosters will have to be more aware of the maps that are available and might make more conscious choices as to what's actually being played.
2. Extend time limit. Simple enough, it's a real pain when you're excited that you're team is just taking the lead and the score is 6 to 5 or something, and then you get a warning that there are like three minutes left. I remember around the time of v 2.1- 2.5 when maps would be kept up for days at a time and still stayed full. It wouldn't kill is to extend time limits by a few hours so that when someone joins a server with a map they like only to find out it's changing in a few moments.
I know these issues would be avoided if people took the time to read and go through each of the options that pyspades/pysnip offers, but let's be honest, most of them don't.
Changing some of the default features in pyspades/pysnip
-
GreaseMonkey
Coder
- Posts: 733
- Joined: Tue Oct 30, 2012 11:07 pm
3. Drop the SMG headshot damage to 33. That way we won't have to flame people so much.
-
Sasquatch
Mapper
- Posts: 141
- Joined: Wed Nov 14, 2012 5:18 pm
GreaseMonkey wrote:3. Drop the SMG headshot damage to 33. That way we won't have to flame people so much.Umm, that's more of a programmed feature. I'm just talking about the default values in the config files
I make maps :3
My [url=aos://199772608:32887]dedicated server[/url] is NOT up and running... soz
-
VladVP
Post Demon
- Posts: 1425
- Joined: Fri Dec 14, 2012 10:48 pm
This could be a good set of tips for people starting up new servers...
We might as well make this thread sticky...
We might as well make this thread sticky...
It's a Christmas miracle!
-
rakiru
Coder
- Posts: 1349
- Joined: Sun Nov 11, 2012 12:26 pm
-
As much as I'd love to just say "Well, if they don't bother setting up their server, it'll be shit and no-one will play on it, meaning it will die quickly.", I feel that you're right. Here's my suggestion for a default config:
For a start, I'd recommend moving the name away from the top of the file, in an attempt to make people read the rest of it.
For a start, I'd recommend moving the name away from the top of the file, in an attempt to make people read the rest of it.
-
Sasquatch
Mapper
- Posts: 141
- Joined: Wed Nov 14, 2012 5:18 pm
rakiru wrote:As much as I'd love to just say "Well, if they don't bother setting up their server, it'll be shit and no-one will play on it, meaning it will die quickly.", I feel that you're right. Here's my suggestion for a default config:xD Might not be a bad idea. However, the map name and rules text shows up right after that. Noobs might see that and not think of it as much of a tutorial. While default values are used a lot of the time in these servers, I almost never see them named just pyspades server/pysnip server... anymore. Maybe the next release should come with a config help text file?
For a start, I'd recommend moving the name away from the top of the file, in an attempt to make people read the rest of it.
also w00t we agree on something! ;) sorry about my rock headed argument yesterday.
cough
I make maps :3
My [url=aos://199772608:32887]dedicated server[/url] is NOT up and running... soz
-
GreaseMonkey
Coder
- Posts: 733
- Joined: Tue Oct 30, 2012 11:07 pm
Sasquatch wrote:1. Don't add any maps initially to the cycle for map rotation in the configuration file. That way we can avoid having dozens of servers up at a time that are only playing bridge or anticbridge or whatever.I want to punch whoever decided that anticbridge should be a default map. That map SUCKS.
rakiru wrote:For a start, I'd recommend moving the name away from the top of the file, in an attempt to make people read the rest of it.If you can require that people RTFM, that definitely helps. It means you can teach them important stuff, like "don't add anticbridge to the rotation".
Sasquatch wrote:Maybe the next release should come with a config help text file?If it's one of the files that aren't pure JSON (as in, they're actually Python), I guess you could slip in some comments. However, I have a sneaking suspicion this is the one place where they actually use a proper JSON parser.
-
rakiru
Coder
- Posts: 1349
- Joined: Sun Nov 11, 2012 12:26 pm
-
GreaseMonkey wrote:If it's one of the files that aren't pure JSON (as in, they're actually Python), I guess you could slip in some comments. However, I have a sneaking suspicion this is the one place where they actually use a proper JSON parser.The config is indeed actual JSON.
I say we set the default values for things like votekick to good ones, as that's something that's hard to judge without experience, but at the same time, remove all of the default maps (yes, all of them), and perhaps instead of clearing out the map list, change it to something like:
Code: Select all
so the server will simply not work if they haven't bothered learning how to set it up."maps" : ["map1", "map2", "read-the-readme"]
-
Sasquatch
Mapper
- Posts: 141
- Joined: Wed Nov 14, 2012 5:18 pm
rakiru wrote:GreaseMonkey wrote:If it's one of the files that aren't pure JSON (as in, they're actually Python), I guess you could slip in some comments. However, I have a sneaking suspicion this is the one place where they actually use a proper JSON parser.The config is indeed actual JSON.
I say we set the default values for things like votekick to good ones, as that's something that's hard to judge without experience, but at the same time, remove all of the default maps (yes, all of them), and perhaps instead of clearing out the map list, change it to something like:Code: Select allso the server will simply not work if they haven't bothered learning how to set it up."maps" : ["map1", "map2", "read-the-readme"]

I make maps :3
My [url=aos://199772608:32887]dedicated server[/url] is NOT up and running... soz
-
Cajun Style
Deuced Up - Posts: 145
- Joined: Fri Dec 07, 2012 11:04 am
The admin password should have the same feature: don't run unless changed. dynfog and mapextensions should be standard too.
Maps planned: Z-Fighting, Hallway Lite, Chaos Redux, Random Maze, FHQ Infiltration, Greece Push, Dracula's Castle, New York.
-
izzy
Head Admin / Co-founder
- Posts: 474
- Joined: Tue Oct 09, 2012 8:16 pm
A default successful votekick of 20% is way too low and will get abused often on a public server. Even 50% gets abused. The current default of 25% is already low. How about increasing it to 35%?
The default map rotation has already been reduced to only 'random' and 'classicgen' in PySnip.
These are the defaults I'd like:
config.txt.default
The default map rotation has already been reduced to only 'random' and 'classicgen' in PySnip.
These are the defaults I'd like:
config.txt.default
Code: Select all
commands.py
{
"name" : "A Default Server",
"motd" : [
"Welcome to %(server_name)s",
"Map: %(map_name)s by %(map_author)s",
"Game mode: Capture The Flag",
"Server powered by PySnip and BuildAndShoot.com"
],
"help" : [
"Server name: %(server_name)s",
"Map: %(map_name)s by %(map_author)s",
"Game mode: Capture The Flag",
"/STREAK Shows how many kills in a row you got without dying",
"/INTEL Tells you who's got the enemy intel",
"/VOTEKICK Start a vote to temporarily ban a disruptive player",
"/TIME Remaining time until forced map reset"
],
"tips" : [
"You are playing Capture The Flag on %(server_name)s",
"Type /help for info & commands"
],
"tip_frequency" : 5,
"rules" : [
"Cheating isn't welcome. Griefing is frowned upon. Have fun!"
],
"master" : true,
"max_players" : 32,
"max_connections_per_ip" : 3,
"port" : 32887,
"network_interface" : "",
"game_mode" : "ctf",
"cap_limit" : 10,
"default_time_limit" : 120,
"advance_on_win" : true,
"maps" : ["classicgen", "random"],
"random_rotation" : false,
"respawn_time" : 16,
"respawn_waves" : true,
"friendly_fire" : "on_grief",
"grief_friendly_fire_time" : 5,
"spade_teamkills_on_grief" : false,
"balanced_teams" : 2,
"teamswitch_interval" : 0,
"speedhack_detect" : false,
"votekick_percentage" : 35,
"votekick_ban_duration" : 30,
"votekick_public_votes" : true,
"votemap_public_votes" : true,
"votemap_extension_time" : 15,
"votemap_player_driven" : false,
"votemap_autoschedule" : false,
"votemap_time" : 120,
"votemap_percentage" : 80,
"melee_damage" : 80,
"fall_damage" : true,
"user_blocks_only" : false,
"set_god_build" : false,
"server_prefix" : "",
"time_announcements" : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 60, 120, 180,
240, 300, 600, 900, 1200, 1800, 2400, 3000],
"login_retries" : 3,
"default_ban_duration" : 1440,
"logfile" : "./logs/log.txt",
"rotate_daily" : true,
"debug_log" : false,
"profile" : false,
"team1" : {
"name" : "Blue",
"color" : [0, 0, 255]
},
"team2" : {
"name" : "Green",
"color" : [0, 255, 0]
},
"passwords" : {
"admin" : ["adminpass1", "adminpass2", "adminpass3"],
"moderator" : ["modpass"],
"guard" : ["guardpass"],
"trusted" : ["trustedpass"]
},
"rights" : {
"moderator" : ["advance", "cancel", "dban", "fog", "from", "goto", "hackinfo", "hban", "invisible", "ip", "kick", "kickafk", "kill", "map", "master", "move", "mute", "resetgame", "switch", "teleport", "teleport_other", "tpsilent", "togglebuild", "togglekill", "togglevotekick", "trust", "undoban", "unmute", "unstick", "where", "whowas"],
"guard" : ["cancel", "fog", "from", "goto", "hackinfo", "hban", "ip", "kick", "kickafk", "kill", "move", "mute", "switch", "teleport", "teleport_other", "togglebuild", "togglekill", "togglevotekick", "trust", "unmute", "unstick", "where", "whowas"]
},
"ssh" : {
"enabled" : false,
"port" : 32887,
"users" : {
"user" : "ssh_pass_change_this"
}
},
"status_server" : {
"enabled" : false,
"port" : 32886
},
"ban_publish" : {
"enabled" : false,
"port" : 32885
},
"ban_subscribe" : {
"enabled" : true,
"urls" : [
["http://www.blacklist.spadille.net/subscribe.json", []]
]
},
"irc" : {
"enabled" : false,
"nickname" : "PySnip",
"username" : "PySnip",
"realname" : "PySnip",
"server" : "irc.quakenet.org",
"port" : 6667,
"channel" : "#MyServerChannel",
"password" : "",
"commandprefix" : "!",
"chatprefix" : "."
},
"scripts" : [
"rollback",
"protect",
"map_extensions",
"disco",
"votekick",
"trusted",
"ratio",
"passreload",
"blockinfo",
"afk"
],
"squad_respawn_time" : 32,
"squad_size" : 4,
"auto_squad" : false,
"load_saved_map" : false,
"rollback_on_game_end" : false,
"afk_time_limit" : 30
}Code: Select all
disco.py
# Copyright (c) Mathias Kaerlev 2011-2012.
# This file is part of pyspades.
# 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/>.
import math
from random import choice
from pyspades.constants import *
from pyspades.common import prettify_timespan
from pyspades.server import parse_command
from twisted.internet import reactor
from map import check_rotation
import inspect
commands = {}
aliases = {}
rights = {}
class InvalidPlayer(Exception):
pass
class InvalidSpectator(InvalidPlayer):
pass
class InvalidTeam(Exception):
pass
def add_rights(func_name, *user_types):
for user_type in user_types:
if user_type in rights:
rights[user_type].add(func_name)
else:
rights[user_type] = set([func_name])
def restrict(func, *user_types):
def new_func(connection, *arg, **kw):
return func(connection, *arg, **kw)
new_func.func_name = func.func_name
new_func.user_types = user_types
new_func.argspec = inspect.getargspec(func)
return new_func
def has_rights(f, connection):
return not hasattr(f, 'user_types') or f.func_name in connection.rights
def admin(func):
return restrict(func, 'admin')
def name(name):
def dec(func):
func.func_name = name
return func
return dec
def alias(name):
def dec(func):
try:
func.aliases.append(name)
except AttributeError:
func.aliases = [name]
return func
return dec
def get_player(protocol, value, spectators = True):
ret = None
try:
if value.startswith('#'):
value = int(value[1:])
ret = protocol.players[value]
else:
players = protocol.players
try:
ret = players[value]
except KeyError:
value = value.lower()
for player in players.values():
name = player.name.lower()
if name == value:
return player
if name.count(value):
ret = player
except (KeyError, IndexError, ValueError):
pass
if ret is None:
raise InvalidPlayer()
elif not spectators and ret.world_object is None:
raise InvalidSpectator()
return ret
def get_team(connection, value):
value = value.lower()
if value == 'blue':
return connection.protocol.blue_team
elif value == 'green':
return connection.protocol.green_team
elif value == 'spectator':
return connection.protocol.spectator_team
raise InvalidTeam()
def join_arguments(arg, default = None):
if not arg:
return default
return ' '.join(arg)
def parse_maps(pre_maps):
maps = []
for n in pre_maps:
if n[0]=="#" and len(maps)>0:
maps[-1] += " "+n
else:
maps.append(n)
return maps, ', '.join(maps)
@admin
def kick(connection, value, *arg):
reason = join_arguments(arg)
player = get_player(connection.protocol, value)
player.kick(reason)
def get_ban_arguments(connection, arg):
duration = None
if len(arg):
try:
duration = int(arg[0])
arg = arg[1:]
except (IndexError, ValueError):
pass
if duration is None:
if len(arg)>0 and arg[0] == "perma":
arg = arg[1:]
else:
duration = connection.protocol.default_ban_time
reason = join_arguments(arg)
return duration, reason
@admin
def ban(connection, value, *arg):
duration, reason = get_ban_arguments(connection, arg)
player = get_player(connection.protocol, value)
player.ban(reason, duration)
@admin
def hban(connection, value, *arg):
duration = int(60)
reason = join_arguments(arg)
player = get_player(connection.protocol, value)
player.ban(reason, duration)
@admin
def dban(connection, value, *arg):
duration = int(1440)
reason = join_arguments(arg)
player = get_player(connection.protocol, value)
player.ban(reason, duration)
@admin
def wban(connection, value, *arg):
duration = int(10080)
reason = join_arguments(arg)
player = get_player(connection.protocol, value)
player.ban(reason, duration)
@admin
def banip(connection, ip, *arg):
duration, reason = get_ban_arguments(connection, arg)
try:
connection.protocol.add_ban(ip, reason, duration)
except ValueError:
return 'Invalid IP address/network'
reason = ': ' + reason if reason is not None else ''
duration = duration or None
if duration is None:
return 'IP/network %s permabanned%s' % (ip, reason)
else:
return 'IP/network %s banned for %s%s' % (ip,
prettify_timespan(duration * 60), reason)
@admin
def unban(connection, ip):
try:
connection.protocol.remove_ban(ip)
return 'IP unbanned'
except KeyError:
return 'IP not found in ban list'
@name('undoban')
@admin
def undo_ban(connection, *arg):
if len(connection.protocol.bans)>0:
result = connection.protocol.undo_last_ban()
return ('Ban for %s undone' % result[0])
else:
return 'No bans to undo!'
@admin
def say(connection, *arg):
value = ' '.join(arg)
connection.protocol.send_chat(value)
connection.protocol.irc_say(value)
add_rights('kill', 'admin')
def kill(connection, value = None):
if value is None:
player = connection
else:
if not connection.rights.kill:
return "You can't use this command"
player = get_player(connection.protocol, value, False)
player.kill()
if connection is not player:
message = '%s killed %s' % (connection.name, player.name)
connection.protocol.send_chat(message, irc = True)
@admin
def heal(connection, player = None):
if player is not None:
player = get_player(connection.protocol, player, False)
message = '%s was healed by %s' % (player.name, connection.name)
else:
if connection not in connection.protocol.players:
raise ValueError()
player = connection
message = '%s was healed' % (connection.name)
player.refill()
connection.protocol.send_chat(message, irc = True)
def rules(connection):
if connection not in connection.protocol.players:
raise KeyError()
lines = connection.protocol.rules
if lines is None:
return
connection.send_lines(lines)
def help(connection):
"""
This help
"""
if connection.protocol.help is not None and not connection.admin:
connection.send_lines(connection.protocol.help)
else:
names = [command.func_name for command in command_list
if command.func_name in connection.rights]
return 'Available commands: %s' % (', '.join(names))
def login(connection, password):
"""
Login as a user type
"""
if connection not in connection.protocol.players:
raise KeyError()
for user_type, passwords in connection.protocol.passwords.iteritems():
if password in passwords:
if user_type in connection.user_types:
return "You're already logged in as %s" % user_type
return connection.on_user_login(user_type, True)
if connection.login_retries is None:
connection.login_retries = connection.protocol.login_retries - 1
else:
connection.login_retries -= 1
if not connection.login_retries:
connection.kick('Ran out of login attempts')
return
return 'Invalid password - you have %s tries left' % (
connection.login_retries)
def pm(connection, value, *arg):
player = get_player(connection.protocol, value)
message = join_arguments(arg)
player.send_chat('PM from %s: %s' % (connection.name, message))
return 'PM sent to %s' % player.name
@name('admin')
def to_admin(connection, *arg):
protocol = connection.protocol
message = join_arguments(arg)
if not message:
return "Enter a message you want to send, like /admin I'm stuck"
prefix = '(TO ADMINS)'
irc_relay = protocol.irc_relay
if irc_relay:
if irc_relay.factory.bot and irc_relay.factory.bot.colors:
prefix = '\x0304' + prefix + '\x0f'
irc_relay.send(prefix + ' <%s> %s' % (connection.name, message))
for player in protocol.players.values():
if player.admin and player is not connection:
player.send_chat('To ADMINS from %s: %s' %
(connection.name, message))
return 'Message sent to admins'
def streak(connection):
if connection not in connection.protocol.players:
raise KeyError()
return ('Your current kill streak is %s. Best is %s kills' %
(connection.streak, connection.best_streak))
@admin
def lock(connection, value):
team = get_team(connection, value)
team.locked = True
connection.protocol.send_chat('%s team is now locked' % team.name)
connection.protocol.irc_say('* %s locked %s team' % (connection.name,
team.name))
@admin
def unlock(connection, value):
team = get_team(connection, value)
team.locked = False
connection.protocol.send_chat('%s team is now unlocked' % team.name)
connection.protocol.irc_say('* %s unlocked %s team' % (connection.name,
team.name))
@admin
def switch(connection, player = None, team = None):
protocol = connection.protocol
if player is not None:
player = get_player(protocol, player)
elif connection in protocol.players:
player = connection
else:
raise ValueError()
if player.team.spectator:
player.send_chat("The switch command can't be used on a spectating player.")
return
if team is None:
new_team = player.team.other
else:
new_team = get_team(connection, team)
if player.invisible:
old_team = player.team
player.team = new_team
player.on_team_changed(old_team)
player.spawn(player.world_object.position.get())
player.send_chat('Switched to %s team' % player.team.name)
if connection is not player and connection in protocol.players:
connection.send_chat('Switched %s to %s team' % (player.name,
player.team.name))
protocol.irc_say('* %s silently switched teams' % player.name)
else:
player.respawn_time = protocol.respawn_time
player.set_team(new_team)
protocol.send_chat('%s switched teams' % player.name, irc = True)
@name('setbalance')
@admin
def set_balance(connection, value):
try:
value = int(value)
except ValueError:
return 'Invalid value %r. Use 0 for off, 1 and up for on' % value
protocol = connection.protocol
protocol.balanced_teams = value
protocol.send_chat('Balanced teams set to %s' % value)
connection.protocol.irc_say('* %s set balanced teams to %s' % (
connection.name, value))
@name('togglebuild')
@alias('tb')
@admin
def toggle_build(connection, player = None):
if player is not None:
player = get_player(connection.protocol, player)
value = not player.building
player.building = value
msg = '%s can build again' if value else '%s is disabled from building'
connection.protocol.send_chat(msg % player.name)
connection.protocol.irc_say('* %s %s building for %s' % (connection.name,
['disabled', 'enabled'][int(value)], player.name))
return
value = not connection.protocol.building
connection.protocol.building = value
on_off = ['OFF', 'ON'][int(value)]
connection.protocol.send_chat('Building has been toggled %s!' % on_off)
connection.protocol.irc_say('* %s toggled building %s' % (connection.name,
on_off))
@name('togglekill')
@alias('tk')
@admin
def toggle_kill(connection, player = None):
if player is not None:
player = get_player(connection.protocol, player)
value = not player.killing
player.killing = value
msg = '%s can kill again' if value else '%s is disabled from killing'
connection.protocol.send_chat(msg % player.name)
connection.protocol.irc_say('* %s %s killing for %s' % (connection.name,
['disabled', 'enabled'][int(value)], player.name))
return
value = not connection.protocol.killing
connection.protocol.killing = value
on_off = ['OFF', 'ON'][int(value)]
connection.protocol.send_chat('Killing has been toggled %s!' % on_off)
connection.protocol.irc_say('* %s toggled killing %s' % (connection.name,
on_off))
@name('toggleteamkill')
@admin
def toggle_teamkill(connection):
value = not connection.protocol.friendly_fire
connection.protocol.friendly_fire = value
on_off = ['OFF', 'ON'][int(value)]
connection.protocol.send_chat('Friendly fire has been toggled %s!' % on_off)
connection.protocol.irc_say('* %s toggled friendly fire %s' % (
connection.name, on_off))
@admin
def mute(connection, value):
player = get_player(connection.protocol, value)
if player.mute:
return '%s is already muted' % player.name
player.mute = True
message = '%s has been muted by %s' % (player.name, connection.name)
connection.protocol.send_chat(message, irc = True)
@admin
def unmute(connection, value):
player = get_player(connection.protocol, value)
if not player.mute:
return '%s is not muted' % player.name
player.mute = False
message = '%s has been unmuted by %s' % (player.name, connection.name)
connection.protocol.send_chat(message, irc = True)
def deaf(connection, value = None):
if value is not None:
if not connection.admin and not connection.rights.deaf:
return 'No administrator rights!'
connection = get_player(connection.protocol, value)
message = '%s deaf' % ('now' if not connection.deaf else 'no longer')
connection.protocol.irc_say('%s is %s' % (connection.name, message))
message = "You're " + message
if connection.deaf:
connection.deaf = False
connection.send_chat(message)
else:
connection.send_chat(message)
connection.deaf = True
@name('globalchat')
@admin
def global_chat(connection):
connection.protocol.global_chat = not connection.protocol.global_chat
connection.protocol.send_chat('Global chat %s' % ('enabled' if
connection.protocol.global_chat else 'disabled'), irc = True)
@alias('tp')
@admin
def teleport(connection, player1, player2 = None, silent = False):
player1 = get_player(connection.protocol, player1)
if player2 is not None:
if connection.admin or connection.rights.teleport_other:
player, target = player1, get_player(connection.protocol, player2)
silent = silent or player.invisible
message = ('%s ' + ('silently ' if silent else '') + 'teleported '
'%s to %s')
message = message % (connection.name, player.name, target.name)
else:
return 'No administrator rights!'
else:
if connection not in connection.protocol.players:
raise ValueError()
player, target = connection, player1
silent = silent or player.invisible
message = '%s ' + ('silently ' if silent else '') + 'teleported to %s'
message = message % (player.name, target.name)
player.set_location(target.get_location())
if silent:
connection.protocol.irc_say('* ' + message)
else:
connection.protocol.send_chat(message, irc = True)
@admin
def unstick(connection, player = None):
if player is not None:
player = get_player(connection.protocol, player)
else:
player = connection
connection.protocol.send_chat("%s unstuck %s" %
(connection.name, player.name), irc = True)
player.set_location_safe(player.get_location())
@alias('tps')
@admin
def tpsilent(connection, player1, player2 = None):
teleport(connection, player1, player2, silent = True)
from pyspades.common import coordinates, to_coordinates
@name('goto')
@admin
def go_to(connection, value):
if connection not in connection.protocol.players:
raise KeyError()
move(connection, connection.name, value, silent = connection.invisible)
@admin
def move(connection, player, value, silent = False):
player = get_player(connection.protocol, player)
x, y = coordinates(value)
x += 32
y += 32
player.set_location((x, y, connection.protocol.map.get_height(x, y) - 2))
if connection is player:
message = ('%s ' + ('silently ' if silent else '') + 'teleported to '
'location %s')
message = message % (player.name, value.upper())
else:
message = ('%s ' + ('silently ' if silent else '') + 'teleported %s '
'to location %s')
message = message % (connection.name, player.name, value.upper())
if silent:
connection.protocol.irc_say('* ' + message)
else:
connection.protocol.send_chat(message, irc = True)
@admin
def where(connection, value = None):
if value is not None:
connection = get_player(connection.protocol, value)
elif connection not in connection.protocol.players:
raise ValueError()
x, y, z = connection.get_location()
return '%s is in %s (%s, %s, %s)' % (connection.name,
to_coordinates(x, y), int(x), int(y), int(z))
@admin
def god(connection, value = None):
if value is not None:
connection = get_player(connection.protocol, value)
elif connection not in connection.protocol.players:
raise ValueError()
connection.god = not connection.god
if connection.protocol.set_god_build:
connection.god_build = connection.god
else:
connection.god_build = False
if connection.god:
message = '%s entered GOD MODE!' % connection.name
else:
message = '%s returned to being a mere human' % connection.name
connection.protocol.send_chat(message, irc = True)
@name('godbuild')
@admin
def god_build(connection, player = None):
protocol = connection.protocol
if player is not None:
player = get_player(protocol, player)
elif connection in protocol.players:
player = connection
else:
raise ValueError()
if not player.god:
return 'Placing god blocks is only allowed in god mode'
player.god_build = not player.god_build
message = ('now placing god blocks' if player.god_build else
'no longer placing god blocks')
player.send_chat("You're %s" % message)
if connection is not player and connection in protocol.players:
connection.send_chat('%s is %s' % (player.name, message))
protocol.irc_say('* %s is %s' % (player.name, message))
@admin
def fly(connection, player = None):
protocol = connection.protocol
if player is not None:
player = get_player(protocol, player)
elif connection in protocol.players:
player = connection
else:
raise ValueError()
player.fly = not player.fly
message = 'now flying' if player.fly else 'no longer flying'
player.send_chat("You're %s" % message)
if connection is not player and connection in protocol.players:
connection.send_chat('%s is %s' % (player.name, message))
protocol.irc_say('* %s is %s' % (player.name, message))
from pyspades.contained import KillAction
from pyspades.server import create_player, set_tool, set_color, input_data, weapon_input
from pyspades.common import make_color
@alias('invis')
@alias('inv')
@admin
def invisible(connection, player = None):
protocol = connection.protocol
if player is not None:
player = get_player(protocol, player)
elif connection in protocol.players:
player = connection
else:
raise ValueError()
player.invisible = not player.invisible
player.filter_visibility_data = player.invisible
player.god = player.invisible
player.god_build = False
player.killing = not player.invisible
if player.invisible:
player.send_chat("You're now invisible")
protocol.irc_say('* %s became invisible' % player.name)
kill_action = KillAction()
kill_action.kill_type = choice([GRENADE_KILL, FALL_KILL])
kill_action.player_id = kill_action.killer_id = player.player_id
reactor.callLater(1.0 / NETWORK_FPS, protocol.send_contained,
kill_action, sender = player)
else:
player.send_chat("You return to visibility")
protocol.irc_say('* %s became visible' % player.name)
x, y, z = player.world_object.position.get()
create_player.player_id = player.player_id
create_player.name = player.name
create_player.x = x
create_player.y = y
create_player.z = z
create_player.weapon = player.weapon
create_player.team = player.team.id
world_object = player.world_object
input_data.player_id = player.player_id
input_data.up = world_object.up
input_data.down = world_object.down
input_data.left = world_object.left
input_data.right = world_object.right
input_data.jump = world_object.jump
input_data.crouch = world_object.crouch
input_data.sneak = world_object.sneak
input_data.sprint = world_object.sprint
set_tool.player_id = player.player_id
set_tool.value = player.tool
set_color.player_id = player.player_id
set_color.value = make_color(*player.color)
weapon_input.primary = world_object.primary_fire
weapon_input.secondary = world_object.secondary_fire
protocol.send_contained(create_player, sender = player, save = True)
protocol.send_contained(set_tool, sender = player)
protocol.send_contained(set_color, sender = player, save = True)
protocol.send_contained(input_data, sender = player)
protocol.send_contained(weapon_input, sender = player)
if connection is not player and connection in protocol.players:
if player.invisible:
return '%s is now invisible' % player.name
else:
return '%s is now visible' % player.name
@admin
def ip(connection, value = None):
if value is None:
if connection not in connection.protocol.players:
raise ValueError()
player = connection
else:
player = get_player(connection.protocol, value)
return 'The IP of %s is %s' % (player.name, player.address[0])
@name('whowas')
@admin
def who_was(connection, value):
value = value.lower()
ret = None
exact_match = False
for name, ip in connection.protocol.player_memory:
name_lower = name.lower()
if name_lower == value:
ret = (name, ip)
exact_match = True
elif not exact_match and name_lower.count(value):
ret = (name, ip)
if ret is None:
raise InvalidPlayer()
return "%s's most recent IP was %s" % ret
@name('resetgame')
@admin
def reset_game(connection):
resetting_player = connection
# irc compatibility
if resetting_player not in connection.protocol.players:
for player in connection.protocol.players.values():
resetting_player = player
if player.admin:
break
if resetting_player is connection:
return
connection.protocol.reset_game(resetting_player)
connection.protocol.on_game_end()
connection.protocol.send_chat('Game has been reset by %s' % connection.name,
irc = True)
from map import Map
import itertools
@name('map')
@admin
def change_planned_map(connection, *pre_maps):
name = connection.name
protocol = connection.protocol
# parse seed numbering
maps, map_list = parse_maps(pre_maps)
if not maps:
return 'Invalid map name'
map = maps[0]
protocol.planned_map = check_rotation([map])[0]
protocol.send_chat('%s changed next map to %s' % (name, map), irc = True)
@name('rotation')
@admin
def change_rotation(connection, *pre_maps):
name = connection.name
protocol = connection.protocol
maps, map_list = parse_maps(pre_maps)
if len(maps) == 0:
return 'Usage: /rotation <map1> <map2> <map3>...'
ret = protocol.set_map_rotation(maps, False)
if not ret:
return 'Invalid map in map rotation (%s)' % ret.map
protocol.send_chat("%s changed map rotation to %s." %
(name, map_list), irc=True)
@name('rotationadd')
@admin
def rotation_add(connection, *pre_maps):
name = connection.name
protocol = connection.protocol
new_maps, map_list = parse_maps(pre_maps)
maps = connection.protocol.get_map_rotation()
map_list = ", ".join(maps) + map_list
maps.extend(new_maps)
ret = protocol.set_map_rotation(maps, False)
if not ret:
return 'Invalid map in map rotation (%s)' % ret.map
protocol.send_chat("%s added %s to map rotation." %
(name, " ".join(pre_maps)), irc=True)
@name('showrotation')
def show_rotation(connection):
return ", ".join(connection.protocol.get_map_rotation())
@name('revertrotation')
@admin
def revert_rotation(connection):
protocol = connection.protocol
maps = protocol.config['maps']
protocol.set_map_rotation(maps, False)
protocol.irc_say("* %s reverted map rotation to %s" % (name, maps))
def mapname(connection):
return 'Current map: ' + connection.protocol.map_info.name
@admin
def advance(connection):
connection.protocol.advance_rotation('Map advance forced.')
@name('timelimit')
@admin
def set_time_limit(connection, value):
value = float(value)
protocol = connection.protocol
protocol.set_time_limit(value)
protocol.send_chat('Time limit set to %s' % value, irc = True)
@name('time')
def get_time_limit(connection):
advance_call = connection.protocol.advance_call
if advance_call is None:
return 'No time limit set'
left = int(math.ceil((advance_call.getTime() - reactor.seconds()) / 60.0))
return 'There are %s minutes left' % left
@name('servername')
@admin
def server_name(connection, *arg):
name = join_arguments(arg)
protocol = connection.protocol
protocol.config['name'] = name
protocol.update_format()
message = "%s changed servername to to '%s'" % (connection.name, name)
print message
connection.protocol.irc_say("* " + message)
if connection in connection.protocol.players:
return message
@name('master')
@admin
def toggle_master(connection):
protocol = connection.protocol
protocol.set_master_state(not protocol.master)
message = ("toggled master broadcast %s" % ['OFF', 'ON'][
int(protocol.master)])
protocol.irc_say("* %s " % connection.name + message)
if connection in connection.protocol.players:
return ("You " + message)
def ping(connection, value = None):
if value is None:
if connection not in connection.protocol.players:
raise ValueError()
player = connection
else:
player = get_player(connection.protocol, value)
ping = player.latency
if value is None:
return ('Your ping is %s ms. Lower ping is better!' % ping)
return "%s's ping is %s ms" % (player.name, ping)
def intel(connection):
if connection not in connection.protocol.players:
raise KeyError()
flag = connection.team.other.flag
if flag.player is not None:
if flag.player is connection:
return "You have the enemy intel, return to base!"
else:
return "%s has the enemy intel!" % flag.player.name
return "Nobody in your team has the enemy intel"
def version(connection):
return 'Server version is "%s"' % connection.protocol.server_version
@name('server')
def server_info(connection):
protocol = connection.protocol
msg = 'You are playing on "%s"' % protocol.name
if protocol.identifier is not None:
msg += ' at %s' % protocol.identifier
return msg
def scripts(connection):
scripts = connection.protocol.config.get('scripts', [])
return 'Scripts enabled: %s' % (', '.join(scripts))
@admin
def fog(connection, r, g, b):
r = int(r)
g = int(g)
b = int(b)
connection.protocol.set_fog_color((r, g, b))
def weapon(connection, value):
player = get_player(connection.protocol, value)
if player.weapon_object is None:
name = '(unknown)'
else:
name = player.weapon_object.name
return '%s has a %s' % (player.name, name)
command_list = [
help,
pm,
to_admin,
login,
kick,
intel,
ip,
who_was,
fog,
ban,
hban,
dban,
wban,
banip,
unban,
undo_ban,
mute,
unmute,
deaf,
global_chat,
say,
kill,
heal,
lock,
unlock,
switch,
set_balance,
rules,
toggle_build,
toggle_kill,
toggle_teamkill,
teleport,
tpsilent,
go_to,
move,
unstick,
where,
god,
god_build,
fly,
invisible,
streak,
reset_game,
toggle_master,
change_planned_map,
change_rotation,
revert_rotation,
show_rotation,
rotation_add,
advance,
set_time_limit,
get_time_limit,
server_name,
ping,
version,
server_info,
scripts,
weapon,
mapname
]
def add(func, name = None):
"""
Function to add a command from scripts
"""
if name is None:
name = func.func_name
name = name.lower()
if not hasattr(func, 'argspec'):
func.argspec = inspect.getargspec(func)
add_rights(name, *getattr(func, 'user_types', ()))
commands[name] = func
try:
for alias in func.aliases:
aliases[alias.lower()] = name
except AttributeError:
pass
for command_func in command_list:
add(command_func)
# optional commands
try:
import pygeoip
database = pygeoip.GeoIP('./data/GeoLiteCity.dat')
@admin
@name('from')
def where_from(connection, value = None):
if value is None:
if connection not in connection.protocol.players:
raise ValueError()
player = connection
else:
player = get_player(connection.protocol, value)
record = database.record_by_addr(player.address[0])
if record is None:
return 'Player location could not be determined.'
items = []
for entry in ('country_name', 'city', 'region_name'):
# sometimes, the record entries are numbers or nonexistent
try:
value = record[entry]
int(value) # if this raises a ValueError, it's not a number
continue
except KeyError:
continue
except ValueError:
pass
items.append(value)
return '%s is from %s' % (player.name, ', '.join(items))
add(where_from)
except ImportError:
print "('from' command disabled - missing pygeoip)"
except (IOError, OSError):
print "('from' command disabled - missing data/GeoLiteCity.dat)"
def handle_command(connection, command, parameters):
command = command.lower()
try:
command = aliases[command]
except KeyError:
pass
try:
command_func = commands[command]
except KeyError:
return # 'Invalid command'
mn = len(command_func.argspec.args) - 1 - len(command_func.argspec.defaults or ())
mx = len(command_func.argspec.args) - 1 if command_func.argspec.varargs is None else None
lp = len(parameters)
if lp < mn or mx is not None and lp > mx:
return 'Invalid number of arguments for %s' % command
try:
if not has_rights(command_func, connection):
return "You can't use this command"
return command_func(connection, *parameters)
except KeyError:
return # 'Invalid command'
except TypeError, t:
print 'Command', command, 'failed with args:', parameters
print t
return 'Command failed'
except InvalidPlayer:
return 'No such player'
except InvalidTeam:
return 'Invalid team specifier'
except ValueError:
return 'Invalid parameters'
def debug_handle_command(connection, command, parameters):
# use this when regular handle_command eats errors
if connection in connection.protocol.players:
connection.send_chat("Commands are in DEBUG mode")
command = command.lower()
try:
command = aliases[command]
except KeyError:
pass
try:
command_func = commands[command]
except KeyError:
return # 'Invalid command'
if not has_rights(command_func, connection):
return "You can't use this command"
return command_func(connection, *parameters)
# handle_command = debug_handle_command
def handle_input(connection, input):
# for IRC and console
return handle_command(connection, *parse_command(input))Code: Select all
votekick.py
"""
Ever wanted a disco in Ace of Spades?
Maintainer: mat^2
"""
from twisted.internet.task import LoopingCall
from twisted.internet.reactor import callLater
import random
import commands
DISCO_ON_GAME_END = True
# Time is in seconds
DISCO_ON_GAME_END_DURATION = 10.0
@commands.name('disco')
@commands.admin
def toggle_disco(connection):
connection.protocol.toggle_disco(True)
commands.add(toggle_disco)
DISCO_COLORS = set([
(235, 64, 0),
(128, 232, 121),
(220, 223, 12),
(43, 72, 228),
(216, 94, 231),
(255, 255, 255)
])
def apply_script(protocol, connection, config):
class DiscoProtocol(protocol):
current_colors = None
disco = False
old_fog_color = None
def __init__(self, *arg, **kw):
protocol.__init__(self, *arg, **kw)
self.disco_loop = LoopingCall(self.update_color)
def update_color(self):
if not self.current_colors:
self.current_colors = DISCO_COLORS.copy()
color = self.current_colors.pop()
self.set_fog_color(color)
def on_game_end(self):
if not self.disco and DISCO_ON_GAME_END:
self.toggle_disco(False)
callLater(DISCO_ON_GAME_END_DURATION, self.stop_disco)
return protocol.on_game_end(self)
def stop_disco(self):
if self.disco:
self.toggle_disco(False)
def toggle_disco(self, message = False):
self.disco = not self.disco
if self.disco:
self.old_fog_color = self.fog_color
self.disco_loop.start(0.3)
if message:
self.send_chat('DISCO PARTY MODE ENABLED!')
else:
self.disco_loop.stop()
if self.old_fog_color is not None:
self.set_fog_color(self.old_fog_color)
if message:
self.send_chat('The party has been stopped.')
return DiscoProtocol, connectionCode: Select all
passreload.py
# maintained by triplefox
# Copyright (c) James Hofmann 2012.
# This file is part of pyspades.
# 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/>.
from twisted.internet.reactor import seconds
from scheduler import Scheduler
from commands import name, add, get_player, join_arguments, InvalidPlayer, admin, alias
REQUIRE_REASON = True
S_NO_VOTEKICK = 'No votekick in progress'
S_DEFAULT_REASON = 'NO REASON GIVEN'
S_IN_PROGRESS = 'Votekick already in progress'
S_SELF_VOTEKICK = "You can't votekick yourself"
S_NOT_ENOUGH_PLAYERS = "There aren't enough players to vote"
S_VOTEKICK_IMMUNE = "You can't votekick this player"
S_NOT_YET = "You can't start another votekick yet!"
S_NEED_REASON = 'You must provide a reason for the votekick'
S_CANT_CANCEL = "You didn't start the votekick!"
S_YES = '{player} voted YES'
S_ENDED = 'Votekick for {victim} has ended. {result}'
S_RESULT_TIMED_OUT = 'Votekick timed out'
S_RESULT_CANCELLED = 'Cancelled'
S_RESULT_BANNED = 'Banned by admin'
S_RESULT_KICKED = 'Kicked by admin'
S_RESULT_INSTIGATOR_KICKED = 'Instigator kicked by admin'
S_RESULT_LEFT = '{victim} left during votekick'
S_RESULT_INSTIGATOR_LEFT = 'Instigator {instigator} left'
S_RESULT_PASSED = 'Player kicked'
S_ANNOUNCE_IRC = '* {instigator} started a votekick against player {victim}. ' \
'Reason: {reason}'
S_ANNOUNCE = '{instigator} started a VOTEKICK against {victim}. Say /Y to agree'
S_ANNOUNCE_SELF = 'You started a votekick against {victim}. Say /CANCEL to ' \
'stop it'
S_UPDATE = '{instigator} is votekicking {victim}. /Y to vote ({needed} left)'
S_REASON = 'Reason: {reason}'
class VotekickFailure(Exception):
pass
@name('votekick')
def start_votekick(connection, *args):
protocol = connection.protocol
if connection not in protocol.players:
raise KeyError()
player = connection
if protocol.votekick_enabled == False:
return "Votekicking disabled"
if player.votekick_enabled == False:
return "You are not allowed to initiate a votekick."
if not args:
if protocol.votekick:
# player requested votekick info
protocol.votekick.send_chat_update(player)
return
raise ValueError()
value = args[0]
try:
# vanilla aos behavior
victim = get_player(protocol, '#' + value)
except InvalidPlayer:
victim = get_player(protocol, value)
reason = join_arguments(args[1:])
try:
# attempt to start votekick
votekick = Votekick.start(player, victim, reason)
protocol.votekick = votekick
except VotekickFailure as err:
return str(err)
@name('cancel')
def cancel_votekick(connection):
protocol = connection.protocol
votekick = protocol.votekick
if not votekick:
return S_NO_VOTEKICK
if connection in protocol.players:
player = connection
if (player is not votekick.instigator and not player.admin and
not player.rights.cancel):
return S_CANT_CANCEL
votekick.end(S_RESULT_CANCELLED)
@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
votekick.vote(player)
@alias('tvk')
@admin
def togglevotekick(connection, *args):
protocol = connection.protocol
if len(args) == 0:
protocol.votekick_enabled = not protocol.votekick_enabled
return "Votekicking globally %s." % ['disabled', 'enabled'][protocol.votekick_enabled]
try:
player = get_player(protocol, '#' + args[0])
except InvalidPlayer:
player = get_player(protocol, args[0])
player.votekick_enabled = not player.votekick_enabled
return "Votekicking is %s for %s." % (['disabled', 'enabled'][player.votekick_enabled], player.name)
add(start_votekick)
add(cancel_votekick)
add(vote_yes)
add(togglevotekick)
class Votekick(object):
duration = 120.0 # 2 minutes
interval = 2 * 60.0 # 3 minutes
ban_duration = 15.0
public_votes = True
schedule = None
def _get_votes_remaining(self):
return self.protocol.get_required_votes() - len(self.votes) + 1
votes_remaining = property(_get_votes_remaining)
@classmethod
def start(cls, instigator, victim, reason = None):
protocol = instigator.protocol
last_votekick = instigator.last_votekick
reason = reason.strip() if reason else None
if protocol.votekick:
raise VotekickFailure(S_IN_PROGRESS)
elif instigator is victim:
raise VotekickFailure(S_SELF_VOTEKICK)
elif protocol.get_required_votes() <= 0:
raise VotekickFailure(S_NOT_ENOUGH_PLAYERS)
elif victim.admin or victim.rights.cancel:
raise VotekickFailure(S_VOTEKICK_IMMUNE)
elif not instigator.admin and (last_votekick is not None and
seconds() - last_votekick < cls.interval):
raise VotekickFailure(S_NOT_YET)
elif REQUIRE_REASON and not reason:
raise VotekickFailure(S_NEED_REASON)
result = protocol.on_votekick_start(instigator, victim, reason)
if result is not None:
raise VotekickFailure(result)
reason = reason or S_DEFAULT_REASON
return cls(instigator, victim, reason)
def __init__(self, instigator, victim, reason):
self.protocol = protocol = instigator.protocol
self.instigator = instigator
self.victim = victim
self.reason = reason
self.votes = {instigator : True}
self.ended = False
protocol.irc_say(S_ANNOUNCE_IRC.format(instigator = instigator.name,
victim = victim.name, reason = self.reason))
protocol.send_chat(S_ANNOUNCE.format(instigator = instigator.name,
victim = victim.name), sender = instigator)
protocol.send_chat(S_REASON.format(reason = self.reason),
sender = instigator)
instigator.send_chat(S_ANNOUNCE_SELF.format(victim = victim.name))
schedule = Scheduler(protocol)
schedule.call_later(self.duration, self.end, S_RESULT_TIMED_OUT)
schedule.loop_call(30.0, self.send_chat_update)
self.schedule = schedule
def vote(self, player):
if self.victim is player:
return
elif player in self.votes:
return
if self.public_votes:
self.protocol.send_chat(S_YES.format(player = player.name))
self.votes[player] = True
if self.votes_remaining <= 0:
# vote passed, ban or kick accordingly
victim = self.victim
self.end(S_RESULT_PASSED)
print '%s votekicked' % victim.name
if self.ban_duration > 0.0:
victim.ban(self.reason, self.ban_duration)
else:
victim.kick(silent = True)
def release(self):
self.instigator = None
self.victim = 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(victim = self.victim.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,
victim = self.victim.name, needed = self.votes_remaining))
target.send_chat(S_REASON.format(reason = self.reason))
def apply_script(protocol, connection, config):
Votekick.ban_duration = config.get('votekick_ban_duration', 15.0)
Votekick.public_votes = config.get('votekick_public_votes', True)
required_percentage = config.get('votekick_percentage', 25.0)
class VotekickProtocol(protocol):
votekick = None
votekick_enabled = True
def get_required_votes(self):
# votekicks are invalid if this returns <= 0
player_count = sum(not player.disconnected for player in
self.players.itervalues()) - 1
return int(player_count / 100.0 * required_percentage)
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.victim is self:
votekick.end(S_RESULT_BANNED)
protocol.on_ban(self, connection, reason, duration)
def on_votekick_start(self, instigator, victim, reason):
pass
def on_votekick_end(self):
pass
class VotekickConnection(connection):
last_votekick = None
votekick_enabled = True
def on_disconnect(self):
votekick = self.protocol.votekick
if votekick:
if votekick.victim is self:
# victim leaves, gets votekick ban
reason = votekick.reason
votekick.end(S_RESULT_LEFT.format(victim = 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.victim 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, VotekickConnectionCode: Select all
# passreload.py
# written by Danke
import commands
from commands import add, admin
import json
@admin
def reloadconfig(connection):
new_config = {}
try:
new_config = json.load(open('config.txt', 'r'))
if not isinstance(new_config, dict):
raise ValueError('config.txt is not a mapping type')
except ValueError, v:
print 'Error reloading config:', v
return 'Error reloading config. Check pyspades log for details.'
connection.protocol.config.update(new_config)
connection.protocol.reload_passes()
return 'Config reloaded!'
add(reloadconfig)
def apply_script(protocol, connection, config):
class PassreloadProtocol(protocol):
def reload_passes(self):
self.passwords = config.get('passwords', {})
for password in self.passwords.get('admin', []):
if password == 'replaceme':
print 'REMEMBER TO CHANGE THE DEFAULT ADMINISTRATOR PASSWORD!'
elif not password:
self.everyone_is_admin = True
commands.rights.update(config.get('rights', {}))
return PassreloadProtocol, connection-
Sasquatch
Mapper
- Posts: 141
- Joined: Wed Nov 14, 2012 5:18 pm
izzy wrote:A default successful votekick of 20% is way too low and will get abused often on a public server. Even 50% gets abused. The current default of 25% is already low. How about increasing it to 35%?Nooooo are you out of your mind? Also did you read the rest of the thread? This doesn't have to be entirely about scripts.
Cajun Style wrote:The admin password should have the same feature: don't run unless changed. dynfog and mapextensions should be standard too.I think map_extensions is. But dynfog would be a smart choice since the pyspades fog is so ugly.
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
-
Cajun Style wrote:The admin password should have the same feature: don't run unless changed. dynfog and mapextensions should be standard too.No no no no no... If they can't even bother setting up their server, then why should they be protected from their own stupidity and laziness?
izzy wrote:A default successful votekick of 20% is way too low and will get abused often on a public server. Even 50% gets abused. The current default of 25% is already low. How about increasing it to 35%?See the votekick thread for discussion on this.
-
izzy
Head Admin / Co-founder
- Posts: 474
- Joined: Tue Oct 09, 2012 8:16 pm
Sasquatch wrote:I'm saying this from my personal experience of monitoring many AoS servers for a long time; a low % will get abused and legit players will get kicked waaay more often than "bad" players will get removed. Here's just one example that happens all the time at 50%: http://buildandshoot.com/viewtopic.php?f=18&t=821#p7567.izzy wrote:A default successful votekick of 20% is way too low and will get abused often on a public server. Even 50% gets abused. The current default of 25% is already low. How about increasing it to 35%?Nooooo are you out of your mind? Also did you read the rest of the thread? This doesn't have to be entirely about scripts.
My suggested config.txt.default includes additional or modified scripts in the script list, that's why I included the actual scripts as well. passreload.py allows admins to refresh config passwords without restarting the server, for example.
-
Ki11aWi11
Mapper
- Posts: 106
- Joined: Tue Nov 20, 2012 12:39 am
Would you guys mind if I added some of my maps to the original pyspades map repository, just a select few such as trenches, none of the Arena maps or infiltration maps.
Who is online
Users browsing this forum: No registered users and 3 guests






