diff --git a/.travis.yml b/.travis.yml
index 016b78a..2dddf25 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,15 @@
# TravisCI config file. See http://travis-ci.org
language: python
-python:
- - 2.6
- - 2.7
+python: 2.7
+
+env:
+ - B3_REPO='git+git://github.com/BigBrotherBot/big-brother-bot.git@master#egg=b3'
+ - B3_REPO='git+git://github.com/BigBrotherBot/big-brother-bot.git@release-1.10#egg=b3'
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install:
- - pip install -r tests/requirements.txt --use-mirrors
- - pip install -e git+git://github.com/courgette/big-brother-bot.git#egg=b3 --use-mirrors
+ - pip install -r poweradminurt/tests/requirements.txt --use-mirrors
+ - pip install -e ${B3_REPO} --use-mirrors
# command to run tests, e.g. python setup.py test
-script: PYTHONPATH=extplugins nosetests --where=tests
+script: PYTHONPATH=extplugins nosetests --where=poweradminurt/tests
diff --git a/INSTALL.TXT b/INSTALL.TXT
deleted file mode 100644
index 58cce7a..0000000
--- a/INSTALL.TXT
+++ /dev/null
@@ -1,30 +0,0 @@
---------------------------------------------------------------------------------
-Installation of PowerAdminUrt for B3 v1.1.3+
---------------------------------------------------------------------------------
-* Read all in this zip file included readme's prior to installation.
-* This version of PowerAdminUrt is in development for use with the latest
- version of B3: v1.1.3.
- How to get this version see here:
- http://www.bigbrotherbot.com/forums/index.php?topic=353.0
- You can also grab a B3 package from my development branch, this version
- might be unstable though:
- http://xlr8or.snt.utwente.nl/hg/b3?ca=9f36620020ea;type=gz
-* Then Copy the content of this zip file into the corresponding b3
- (sub)directories.
-
---------------------------------------------------------------------------------
-Support for the basic Bot:
---------------------------------------------------------------------------------
-Many questions are answered in the manual, both for installation and usage.
-* Installation: http://b3.python-hosting.com/wiki/BotInstall
-* General documentation: http://b3.python-hosting.com/wiki/BotManual
-
---------------------------------------------------------------------------------
-Support for the UrT addon:
---------------------------------------------------------------------------------
-* Post your issues here: http://bigbrotherbot.com/forums/index.php?board=3.0
-
---------------------------------------------------------------------------------
-Please check if your problem is not already discussed in other threads before
-starting a new one.
-* http://bigbrotherbot.com/forums/index.php?action=search
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..94e502c
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,588 @@
+Power Admin Urban Terror for Big Brother Bot
+============================================
+
+http://www.bigbrotherbot.net
+
+
+Description
+-----------
+
+This plugin brings Urban Terror 4.1 and 4.2 specific features to Bigbrotherbot.
+
+
+-----
+
+*NOTE: since B3 v1.10.1 beta this plugin has been included in the standard plugins set, thus all patches and updates will be performed in the official B3 repository.*
+
+-----
+
+
+Requirements
+------------
+
+- requires B3 v1.9.0 or later
+
+
+Installation
+------------
+
+- copy the directory `poweradminurt/` into your `b3/extplugins/` directory
+- copy the files from the `poweradminurt/conf` folder into the folder that contains your main b3.ini (or b3.xml) config file
+- add to the plugins section of your main b3 config file::
+
+**b3.xml**
+
+.. code:: xml
+
+
+
+
+**b3.ini**
+
+.. code:: ini
+
+ [plugins]
+ poweradminurt: @conf/plugin_poweradminurt.ini
+
+
+Commands
+--------
+
+
+!paadvise (!advise)
+ report team skill balance, and give advice if teams are unfair
+
+!paunskuffle (!unsk)
+ create unbalanced teams. Used to test !paskuffle and !pabalance.
+
+!paskuffle (!sk)
+ shuffle players to balanced teams by numbers and skill
+
+ Lock players are also moved. Use `!force all free` to unlock them.
+
+!pabalance (!bal)
+ move as few players as needed to create teams balanced by numbers AND skill.
+
+ Lock players are not moved. Use `!force all free` to unlock them.
+
+!paautoskuffle (!ask) [new mode]
+ display current auto skuffle mode
+
+ If *new mode* is provided, switch to that new auto skuffle mode.
+
+ Skuffle modes are :
+
+ +-----+--------------+
+ | 0 | none |
+ +-----+--------------+
+ | 1 | advise |
+ +-----+--------------+
+ | 2 | auto balance |
+ +-----+--------------+
+ | 3 | auto skuffle |
+ +-----+--------------+
+
+!paswap (!swap) [player2]
+ swap two teams for 2 players. If *player2* is not specified, the admin using the command is swapped with player1
+
+ Doesn't work with spectators (exception for calling admin).
+
+!pateams (!teams)
+ balance teams evenly (read *teambalancer* below)
+
+!pavote
+ switch voting (cvar *g_allowvote*)
+
+ When choosing *reset* set the voting mode as it was before B3 started
+
+!paversion (!paver)
+ display the version of PowerAdminUrt
+
+!paexec
+ execute a server given server config file
+
+!pacyclemap (!cyclemap)
+ tell the game server to load the next map
+
+!pamaprestart (!maprestart)
+ tell the game server to restart the current map
+
+!pamapreload (!mapreload)
+ tell the game server to reload the current map
+
+!paset
+ set a server cvar to a certain value
+
+!paget
+ display a server cvar value
+
+!pabigtext (!bigtext)
+ print a bold message on the center of players' screen
+
+!pamute (!mute) []
+ mute a player
+
+ if *seconds* is provided, mute the player for this amount of time.
+
+ if *seconds* is 0, unmute the player
+
+!papause (!pause)
+ tell the game server to pause the current game
+
+!paslap (!slap) []
+ (multi) slap a player
+
+!panuke (!nuke) []
+ (multi) Nuke a player
+
+!paveto
+ veto currently running Vote.
+
+!paforce (!force) [lock]
+ force a client to red/blue/spec or release the player from a forced team (free).
+
+ You can use the first letter of a team instead of the full team name.
+
+ Adding *'lock'* will lock the player where it is forced to.
+
+ Using *'all free'* wil release all locks.
+
+ **Usage examples**
+
+ Moving Bill to the red team :
+ - `!force bill red`
+ - `!force bill r`
+
+ Forcing Bill to stay in the red team :
+ `!force bill r lock`
+
+ Allowing Bill to go whereever he wants :
+ `!force bill free`
+
+!paswapteams (!swapteams)
+ tell the game server to swap teams
+
+!pashuffleteams (!shuffleteams)
+ tell the game server to shuffle teams
+
+!pamoon (!moon)
+ activate Moon mode... low gravity
+
+ Set the values for normal/low gravity in the plugin config file under section *moonmode*
+
+!papublic (!public)
+ set the server to public or private mode
+
+ In private mode players need a password to enter the server.
+
+ When putting the server in private mode, a password will be picked depending on the settings from section *publicmode*.
+
+!pamatch (!match)
+ tell the game server to switch of match mode (cvar *g_matchmode*)
+
+ When switching to match mode B3 plugins set in the config section *matchmode/plugins_disable* will be disabled.
+
+ Then will be re-enabled when you will use `!match off`. Also the team balancer, name checker, spec checker, heashot
+ counter will be disabled.
+
+!pagear (!gear) *for UrT 4.1*
+ set allowed/disallowed weapon groups
+
+ *all* allow all weapons
+
+ *none* will only allow the knife
+
+ *reset* will put back the settings as they were before B3 started
+
+ Use *+* before a weapon group to allow it
+
+ Use *-* before a weapon group to disallow it
+
+ If you want to disable only one weapon/item instead of weapon group, have a look at the plugins weaponcontrolurt and weaponcontrolurt42. They allow to disallow smoke grenades while allowing HE for instance, or can disallow the kelvar vest.
+
+
+!pagear (!gear) *for UrT 4.2*
+ set allowed/disallowed weapons or items
+
+ *all* allow all weapons
+
+ *none* will only allow the knife
+
+ *reset* will put back the settings as they were before B3 started
+
+ Use *+* before a weapon/item to allow it
+
+ Use *-* before a weapon/item to disallow it
+
+ Accepted *weapon* and *item* names are what you would expect. I.E. *spas* for the SPAS, *de* for Desert Eagle .50, etc.
+
+ Accepted *group* names are stricly one of:
+
+ - *all_nades*: for all HE and Smoke grenades
+ - *all_snipers*: for SR8 and PSG1
+ - *all_pistols*: for Beretta 92FS, .50 Desert Eagle, Glock and Colt1911
+ - *all_auto*: for MPK5, LR300ML, Colt M4, MAC11, UMP45, G36, AK.103 and Negev LMG
+
+ For instance, you can make your server a SR8 only with the following command::
+
+ !gear none +sr8
+
+ If you want to only allow any sniper rifles::
+
+ !gear none +all_snipers
+
+ or maybe you just want to forbid smoke grenades::
+
+ !gear all -smoke
+
+
+!paffa (!ffa)
+ switch to gametype *Free For All*
+
+!patdm (!tdm)
+ switch to gametype *Team Death Match*
+
+!pats (!ts)
+ switch to gametype *Team Survivor*
+
+!paftl (!ftl)
+ switch to gametype *Follow The Leader*
+
+!pacah (!cah)
+ switch to gametype *Capture And Hold*
+
+!pactf (!ctf)
+ switch to gametype *Capture The Flag*
+
+!pabomb (!bomb)
+ switch to gametype *Bomb Mode*
+
+!paident (!id)
+ print a player's B3-id, Guid and IP to screen
+
+!pawaverespawns (!waverespawns)
+ tell the game server to respawn players by wave (cvar *g_waverespawns*)
+
+!pasetnextmap (!setnextmap)
+ tell the game server what will be the next map (cvar *g_nextmap*)
+
+ You can use a partial map name, B3 will do its best to guess the correct name
+
+!parespawngod (!respawngod)
+ set the respawn protection in seconds (cvar *g_respawnProtection*)
+
+!parespawndelay (!respawndelay)
+ set the respawn delay in seconds (cvar *g_respawnDelay*)
+
+!pacaplimit (!caplimit)
+ set the amount of flagcaps before map is over (cvar *capturelimit*)
+
+!patimelimit (!timelimit)
+ set the minutes before map is over (cvar *timelimit*)
+
+!pafraglimit (!fraglimit)
+ set the amount of points to be scored before map is over (cvar *fraglimit*)
+
+!pabluewave (!bluewave)
+ set the blue wave respawn time (cvar *g_bluewave*)
+
+!paredwave (!redwave)
+ set the red wave respawn time (cvar *g_redwave*)
+
+!pasetwave (!setwave)
+ set the wave respawn time for both teams (cvars *g_bluewave* and *g_redwave*)
+
+!pahotpotato (!hotpotato)
+ set the flag explode time (cvar *g_hotpotato*)
+
+!pasetgravity (!setgravity)
+ set the gravity value. default = 800 (less means less gravity) (cvar *g_gravity*)
+ Also see command *!pamoon*
+
+
+
+Commands specific to Urban Terror 4.2
+-------------------------------------
+
+!pakill (!kill)
+ kill a player
+
+!palms (!lms)
+ change game type to *Last Man Standing*
+
+!pajump (!jump)
+ change game type to *Jump Mode*
+
+!pafreeze (!freeze)
+ change game type to *Freeze Tag*
+
+!pagoto (!goto)
+ activate/deactivate the *goto* (Jump mode feature)
+
+!paskins (!skins)
+ activate/deactivate the use of client skins
+
+!pafunstuff (!funstuff)
+ activate/deactivate the use of funstuff
+
+!pastamina (!stamina)
+ set the stamina behavior (Jump mode feature)
+
+!pacaptain (!captain)
+ set the given client as the captain for its team (only in match mode)
+
+!pasub (!sub)
+ set the given client as a substitute for its team (only in match mode)
+
+
+Other features
+--------------
+
+Autobalancer
+~~~~~~~~~~~~
+
+When active the autobalancer makes sure the teams will always be balanced. When a player joins a team that is already
+outnumbering the other team B3 will immediately correct the player to the right team. The balancer also checks on
+(configurable) intervals if balancing is needed. In that case it will balance the player with the least teamtime, so
+the player that joined the team last will be force to the other team.
+
+
+Namechecker
+~~~~~~~~~~~
+
+When active it checks for unwanted playernames. This is a simple function and warns players using duplicate names, the
+name 'all' or 'New UrT Player' depending on the config. Three warnings without a responding rename action will result
+in a kick.
+
+
+Vote Delayer
+~~~~~~~~~~~~
+
+You can disable voting during the first minutes of a round. Set the number of minutes in the config and voting will be
+disabled for that amount of time.
+
+
+Spec Checker
+~~~~~~~~~~~~
+
+Controls how long a player may stay in spec before being warned. All parameters are configurable.
+
+**Important!**
+
+In order to make Spec checker work it is crucial you edit *b3/conf/plugin_admin.xml*
+
+Open the file with your favorite text editor and look for the next line:
+ `5m, ^7spectator too long on full server`
+Change it to:
+ `1h, ^7spectator too long on full server`
+
+
+Bot Support
+~~~~~~~~~~~
+
+This will crash your server. I've put it in here as a challenge for you programmers out there to fix us a stable version.
+
+
+Headshot counter
+~~~~~~~~~~~~~~~~
+
+Broadcasts headshots made by players.
+
+
+RotationManager
+~~~~~~~~~~~~~~~
+
+Switches between different mapcycles, based on the playercount.
+
+
+
+
+Support
+-------
+
+Support is only provided on www.bigbrotherbot.net forums on the following topic :
+http://forum.bigbrotherbot.net/xlr-releases/poweradminurt-1-4-0-for-urban-terror!/
+
+
+
+Changelog
+---------
+
+09/06/2008 - Courgette
+ - add commands pagear (to change allowed weapons)
+ - add commands paffa, patdm, pats, paftl, pacah, pactf, pabomb (to change g_gametype)
+ - now namecheck is disabled during match mode
+ - _smaxplayers is now set taking care of private slots (this is for speccheck)
+09/07/2008 - Courgette
+ - add command !paident : show date / ip / guid of player. Useful when moderators make demo of cheaters
+17/08/2008 - xlr8or
+ - added counter for max number of allowed client namechanges per map before being kicked
+1.4.0b8 - 20/10/2008 - xlr8or
+ - fixed a bug where balancing failed and disabled itself on rcon socket failure.
+1.4.0b9 - 10/21/2008 - mindriot
+ - added team_change_force_balance_enable to control force balance on client team change
+1.4.0b10 - 10/22/2008 - mindriot
+ -added autobalance_gametypes to specify which gametypes to autobalance
+1.4.0b11 - 10/22/2008 - mindriot
+ - if client does not have teamtime, provide new one
+1.4.0b12 - 10/23/2008 - mindriot
+ - onTeamChange is disabled during matchmode
+1.4.0b13 - 10/28/2008 - mindriot
+ - fixed teambalance to set newteam if dominance switches due to clients voluntarily switching teams during balance
+1.4.0b14 - 10/28/2008 - mindriot
+ - teambalance verbose typo
+1.4.0b15 - 12/07/2008 - xlr8or
+ - teamswitch-stats-harvest exploit penalty -> non legit switches become suicides
+1.4.0b16 - 2/9/2009 - xlr8or
+ - added locking mechanism to paforce. !paforce
+1.4.0b17 - 2/9/2009 - xlr8or
+ - Fixed a default value onLoad for maximum teamdiff setting
+03/15/09 by FSK405|Fear
+ - added more rcon cmds:
+ - !waverespawns Turn waverespawns on/off
+ - !bluewave Set the blue team wave respawn delay
+ - !redwave Set the red team wave respawn delay
+ - !setnextmap Set the nextmap
+ - !respawngod Set the respawn protection
+ - !respawndelay Set the respawn delay
+ - !caplimit
+ - !timelimit
+ - !fraglimit
+ - !hotpotato
+1.4.0b18 - 4/4/2009 - xlr8or
+ - Fixed locked force to stick and not continue with balancing
+ - Helmet and Kevlar messages only when connections < 20
+1.4.0 - 28/6/2009 - xlr8or
+ - Time to leave beta
+ - Teambalance raises warning instead of error
+1.4.1 - 10/8/2009 - naixn
+ - Improved forceteam locking mechanism and messaging
+1.4.2 - 10/8/2009 - xlr8or
+ - Added TeamLock release command '!paforce all free' and release on gameExit
+1.4.3 - 09/07/2009 - SGT
+ - add use of dictionary for private password (papublic)
+1.5.0 - 27/10/2009 - Courgette
+ - /!\ REQUIRES B3 v1.2.1 /!\
+ - add !pamap which works with partial map names
+ - update !pasetnextmap to work with partial map names
+1.5.1 - 27/10/2009 - Courgette
+ - debug !pamap and !pasetnextmap
+ - debug dictionnary use for !papublic
+ - !papublic can now use randnum even if dictionnary is not used
+1.5.2 - 31/01/2010 - xlr8or
+ - added ignore Set and Check functions for easier implementation in commands
+ - added ignoreSet(30) to swapteams and shuffleteams to temp disable auto checking functions
+ - Note: this will be overridden by the ignoreSet(60) when the new round starts after swapping/shuffling!
+ - Send rcon result to client on !paexec
+1.5.3 - 13/03/2010 - xlr8or
+ - fixed headshotcounter reset. now able to set it to 'no', 'round', or 'map'
+1.5.4 - 19/03/2010 - xlr8or
+ - fixed endless loop in ignoreCheck()
+1.5.5 - 30/06/2010 - xlr8or
+ - no longer set bot_enable var to 0 on startup when botsupport is disabled.
+1.5.6 - 20/09/2010 - Courgette
+ - debug !paslap and !panuke
+ - add tests
+1.5.7 - 20/09/2010 - BlackMamba
+ - fix !pamute - http://www.bigbrotherbot.net/forums/xlr-releases/poweradminurt-1-4-0-for-urban-terror!/msg15296/#msg15296
+1.5.8 - 20/09/2011 - SGT
+ - minor fix for b3 1.7 compatibility
+ - fix method onKillTeam
+1.5.9 - 25/09/2011 - xlr8or
+ - Code reformat by convention
+1.6 - 25/07/2012 - Courgette
+ - prepare separation of poweradmin plugin for UrT4.1 and UrT4.2
+ - change default config file from xml to ini format
+ - change the way to load from the config the list of plugins to disable in matchmode. See section 'matchmode' in config file
+ - gracefully fallback on default value if cannot read publicmode/usedic from config file
+ - UrT4.2: implement command !kill
+1.6.1 - 25/08/2012 - Courgette
+ - fix checkunknown feature
+ - name checker: provide exact reason for warning in log
+ - fix plugin version since UrT 4.1/4.2 split
+1.6.2 - 13/09/2012 - Courgette
+ - UrT42: fix feedback message on missing parameter for the !pakill command
+1.6.3 - 05/10/2012 - Courgette
+ - UrT42: fix the headshot counter by introducing hit location constants
+1.7 - 06/10/2012 - Courgette
+ - UrT42: add the radio spam protection feature
+1.8 - 21/10/2012 - Courgette
+ - UrT42: change: update to new rcon mute command behavior introduced in UrT 4.2.004
+1.9 - 27/10/2012 - Courgette
+ - change: remove command pamap now that the B3 admin plugin map command can provide suggestions and does fuzzy matching
+ - change: command !setnextmap now gets map suggestions from the B3 parser
+1.10 - 28/10/2012 - Courgette
+ - merge from xlr8or/master
+1.11 - 09/11/2012 - Courgette
+ - new: add command !jump to change the server to the jump gametype
+1.12 - 07/04/2013 - Courgette
+ - the spec check won't be ignored at game/round start for 30s anymore
+1.13 - 07/07/2013 - Fenix
+ - added command !pagoto
+ - added command !paskins
+ - added command !pafunstuff
+ - added command !pastamina
+ - updated hitlocation codes to match the last UrT release (4.2.013)
+1.14 - 14/07/2013 - Courgette
+ - hitlocation codes are provided by the B3 parser if available
+1.14.1 - 17/09/2013 - Fenix
+ - !pasetnextmap command displays at most 5 map suggestions
+1.15 - 09/10/2013 - Courgette
+ - !paident command now shows the auth name from the Frozen Sand account (UrT 4.2 only)
+1.16 - 10/11/2013 - Fenix
+ - refactored plugin syntax to match PEP8 coding style guide
+ - more verbose logging on plugin configuration
+ - catch all raised exception instead of discarding them
+ - correctly use config getFloat method when needed
+ - log message consistency (used same pattern for plugin configuration log messages)
+ - declare missing attributes
+ - flagged some attributes with correct scope (protected)
+ - correctly declare lists as lists instead of dictionaries
+ - renamed variables using python reserved symbols
+ - fixed forceteam %s spectate: spectate is not interpreted by the gamecode
+ - added missing command descriptions
+ - fixed some in-game message spelling
+ - replaced color code ^9 with ^1 -> red in both 4.1 and 4.2
+ - make use of self.console.setCvar when possible
+1.16.1 - 2014/01/26 - Courgette
+ - fix !paset when used with no cvar value
+1.17 - 2014/01/27 - Fenix
+ - updated !pagear command for iourt42 game: it now works with weapon letters instead of bitmask
+1.18 - 2014/01/28 - Courgette
+ - !pagear command for iourt42 game accept weapon groups as parameter (all_snipers, all_nades, all_pistols, all_auto)
+1.19 - 2014/02/09 - Fenix
+ - code cleanup
+1.20 - 2014/02/09 - Courgette
+ - !pagear accepts multiple parameters
+1.21 - 2014/05/11 - Fenix
+ - fixed unresolved reference for EVT_CLIENT_RADIO
+ - removed some warnings in iourt41.py module
+1.22 - 2014/09/19 - Fenix
+ - added !pafreeze command: change gametype to Freeze Tag
+1.23 - 2014/12/04 - Fenix
+ - added command !pacaptain: set the captain status on the given client
+ - added command !pasub: set the substitute status on the given client
+ - overridden command !paswap in iourt42 module: game server now provides a swap rcon command
+ - updated printGear method (iourt42 module) to use the new getWrap implementation
+1.24 - 2015/05/13 - Fenix
+ - fixed invalid plugin class reference 'requiresParsers' which was crashing B3 on startup
+
+
+Credit
+------
+
+Original author : xlr8or
+Contributors : Courgette, mindriot, FSK405|Fear, naixn, BlackMamba, SGT, Fenix
+
+
+Contrib
+-------
+
+- *features* can be discussed on the `B3 forums `_
+- documented and reproducible *bugs* can be reported on the `issue tracker `_
+- *patches* are welcome. Send me a `pull request `_. It is best if your patch provides tests.
+
+.. image:: https://travis-ci.org/thomasleveil/b3-plugin-poweradminurt.png?branch=master
+ :alt: Build Status
+ :target: http://travis-ci.org/thomasleveil/b3-plugin-poweradminurt
+
diff --git a/extplugins/poweradminurt/__init__.py b/extplugins/poweradminurt/__init__.py
deleted file mode 100644
index 3d99987..0000000
--- a/extplugins/poweradminurt/__init__.py
+++ /dev/null
@@ -1,153 +0,0 @@
-#
-# PowerAdmin Plugin for BigBrotherBot(B3) (www.bigbrotherbot.net)
-# Copyright (C) 2008 Mark Weirath (xlr8or@xlr8or.com)
-#
-# This program 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 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#
-
-# Changelog:
-#
-# 01:49 09/06/2008 by Courgette
-# * add commands pagear (to change allowed weapons)
-# * add commands paffa, patdm, pats, paftl, pacah, pactf, pabomb (to change g_gametype)
-# * now namecheck is disabled during match mode
-# * _smaxplayers is now set taking care of private slots (this is for speccheck)
-# * fixes in Mark's code (I suppose I checkout an unstable version)
-# for LoadRotationManager code, I'm not sure what you meant with all those successives "try:"
-# maybe this is a python 2.5 syntax ? wasn't working for me
-# 01:21 09/07/2008 - Courgette
-# * add command !paident : show date / ip / guid of player. Useful when moderators make demo of cheaters
-# 17/08/2008 - xlr8or
-# * added counter for max number of allowed client namechanges per map before being kicked
-# 20/10/2008 - xlr8or (v1.4.0b8)
-# * fixed a bug where balancing failed and disabled itself on rcon socket failure.
-# 10/21/2008 - 1.4.0b9 - mindriot
-# * added team_change_force_balance_enable to control force balance on client team change
-# 10/22/2008 - 1.4.0b10 - mindriot
-# * added autobalance_gametypes to specify which gametypes to autobalance
-# 10/22/2008 - 1.4.0b11 - mindriot
-# * if client does not have teamtime, provide new one
-# 10/23/2008 - 1.4.0b12 - mindriot
-# * onTeamChange is disabled during matchmode
-# 10/28/2008 - 1.4.0b13 - mindriot
-# * fixed teambalance to set newteam if dominance switches due to clients voluntarily switching teams during balance
-# 10/28/2008 - 1.4.0b14 - mindriot
-# * teambalance verbose typo
-# 12/07/2008 - 1.4.0b15 - xlr8or
-# * teamswitch-stats-harvest exploit penalty -> non legit switches become suicides
-# 2/9/2009 - 1.4.0b16 - xlr8or
-# * added locking mechanism to paforce. !paforce
-# 2/9/2009 - 1.4.0b17 - xlr8or
-# * Fixed a default value onLoad for maximum teamdiff setting
-# 02:33 3/15/09 by FSK405|Fear
-# added more rcon cmds:
-# !waverespawns Turn waverespawns on/off
-# !bluewave Set the blue team wave respawn delay
-# !redwave Set the red team wave respawn delay
-# !setnextmap Set the nextmap
-# !respawngod Set the respawn protection
-# !respawndelay Set the respawn delay
-# !caplimit
-# !timelimit
-# !fraglimit
-# !hotpotato
-# 4/4/2009 - 1.4.0b18 - xlr8or
-# * Fixed locked force to stick and not continue with balancing
-# * Helmet and Kevlar messages only when connections < 20
-# 28/6/2009 - 1.4.0 - xlr8or
-# * Time to leave beta
-# * Teambalance raises warning instead of error
-# 10/8/2009 - 1.4.1 - naixn
-# * Improved forceteam locking mechanism and messaging
-# 10/8/2009 - 1.4.2 - xlr8or
-# * Added TeamLock release command '!paforce all free' and release on gameExit
-# 09/07/2009 - 1.4.3 by SGT
-# add use of dictionary for private password (papublic)
-# 27/10/2009 - 1.5.0 - Courgette
-# /!\ REQUIRES B3 v1.2.1 /!\
-# * add !pamap which works with partial map names
-# * update !pasetnextmap to work with partial map names
-# 27/10/2009 - 1.5.1 - Courgette
-# * debug !pamap and !pasetnextmap
-# * debug dictionnary use for !papublic
-# * !papublic can now use randnum even if dictionnary is not used
-# 31/01/2010 - 1.5.2 - xlr8or
-# * added ignore Set and Check functions for easier implementation in commands
-# * added ignoreSet(30) to swapteams and shuffleteams to temp disable auto checking functions
-# Note: this will be overridden by the ignoreSet(60) when the new round starts after swapping/shuffling!
-# * Send rcon result to client on !paexec
-# 13/03/2010 - 1.5.3 - xlr8or
-# * fixed headshotcounter reset. now able to set it to 'no', 'round', or 'map'
-# 19/03/2010 - 1.5.4 - xlr8or
-# * fixed endless loop in ignoreCheck()
-# 30/06/2010 - 1.5.5 - xlr8or
-# * no longer set bot_enable var to 0 on startup when botsupport is disabled.
-# 20/09/2010 - 1.5.6 - Courgette
-# * debug !paslap and !panuke
-# * add tests
-# 20/09/2010 - 1.5.7 - BlackMamba
-# * fix !pamute - http://www.bigbrotherbot.net/forums/xlr-releases/poweradminurt-1-4-0-for-urban-terror!/msg15296/#msg15296
-# 20/09/2011 - 1.5.8 - SGT
-# * minor fix for b3 1.7 compatibility
-# * fix method onKillTeam
-# 25/09/2011 - 1.5.9 - xlr8or
-# * Code reformat by convention
-# 25/07/2012 - 1.6 - Courgette
-# * prepare separation of poweradmin plugin for UrT4.1 and UrT4.2
-# * change default config file from xml to ini format
-# * change the way to load from the config the list of plugins to disable in matchmode. See section 'matchmode' in config file
-# * gracefully fallback on default value if cannot read publicmode/usedic from config file
-# * UrT4.2: implement command !kill
-# 25/08/2012 - 1.6.1 - Courgette
-# * fix checkunknown feature
-# * name checker: provide exact reason for warning in log
-# * fix plugin version since UrT 4.1/4.2 split
-# 13/09/2012 - 1.6.2 - Courgette
-# * UrT42: fix feedback message on missing parameter for the !pakill command
-# 05/10/2012 - 1.6.3 - Courgette
-# * UrT42: fix the headshot counter by introducing hit location constants
-# 06/10/2012 - 1.7 - Courgette
-# * UrT42: add the radio spam protection feature
-# 21/10/2012 - 1.8 - Courgette
-# * UrT42: change: update to new rcon mute command behavior introduced in UrT 4.2.004
-# 27/10/2012 - 1.9 - Courgette
-# * change: remove command pamap now that the B3 admin plugin map command can provide suggestions and does fuzzy matching
-# * change: command !setnextmap now gets map suggestions from the B3 parser
-# 28/10/2012 - 1.10 - Courgette
-# * merge from xlr8or/master
-#
-__version__ = '1.10'
-__author__ = 'xlr8or, courgette'
-
-
-"""
-
-Depending on the B3 parser loaded, this module will either act as the plugin for
-UrT4.1 or the plugin for UrT4.2
-
-"""
-
-class PoweradminurtPlugin(object):
-
- def __new__(cls, *args, **kwargs):
- console, plugin_config = args
- if console.gameName == 'iourt41':
- from poweradminurt.iourt41 import Poweradminurt41Plugin
- return Poweradminurt41Plugin(*args, **kwargs)
- elif console.gameName == 'iourt42':
- from poweradminurt.iourt42 import Poweradminurt42Plugin
- return Poweradminurt42Plugin(*args, **kwargs)
- else:
- raise AssertionError("Poweradminurt plugin can only work with Urban Terror 4.1 or 4.2")
\ No newline at end of file
diff --git a/extplugins/poweradminurt/iourt41.py.orig b/extplugins/poweradminurt/iourt41.py.orig
deleted file mode 100644
index ee21f15..0000000
--- a/extplugins/poweradminurt/iourt41.py.orig
+++ /dev/null
@@ -1,2026 +0,0 @@
-#
-# PowerAdmin Plugin for BigBrotherBot(B3) (www.bigbrotherbot.net)
-# Copyright (C) 2008 Mark Weirath (xlr8or@xlr8or.com)
-#
-# This program 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 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#
-import b3, time, thread, threading, re
-import b3.events
-import b3.plugin
-import b3.cron
-from b3.functions import soundex, levenshteinDistance
-
-import os
-import random
-import string
-import traceback
-import ConfigParser
-
-#--------------------------------------------------------------------------------------------------
-class Poweradminurt41Plugin(b3.plugin.Plugin):
-
- # ClientUserInfo and ClientUserInfoChanged lines return different names, unsanitized and sanitized
- # this regexp designed to make sure either one is sanitized before namecomparison in onNameChange()
- _reClean = re.compile(r'(\^.)|[\x00-\x20]|[\x7E-\xff]', re.I)
-
- _adminPlugin = None
- _ignoreTill = 0
- _checkdupes = False
- _checkunknown = False
- _checkbadnames = False
- _checkchanges = False
- _checkallowedchanges = 0
- _ncronTab = None
- _tcronTab = None
- _scronTab = None
- _ninterval = 0
- _tinterval = 0
- _sinterval = 0
- _teamred = 0
- _teamblue = 0
- _teamdiff = 0
- _balancing = False
- _origvote = 0
- _lastvote = 0
- _votedelay = 0
- _smaxspectime = 0
- _smaxlevel = 0
- _smaxplayers = 0
- _sv_maxclients = 0
- _g_maxGameClients = 0
- _teamsbalanced = False
- _matchmode = False
- _botenable = False
- _botskill = 4
- _botminplayers = 4
- _botmaps = {}
- _hsenable = False
- _hsresetvars = True
- _hsbroadcast = True
- _hsall = True
- _hspercent = True
- _hspercentmin = 20
- _hswarnhelmet = True
- _hswarnhelmetnr = 7
- _hswarnkevlar = True
- _hswarnkevlarnr = 50
- _rmenable = False
- _dontcount = 0
- _mapchanged = False
- _playercount = -1
- _oldplayercount = None
- _currentrotation = 0
- _switchcount1 = 12
- _switchcount2 = 24
- _hysteresis = 0
- _rotation_small = ''
- _rotation_medium = ''
- _rotation_large = ''
- _gamepath = ''
- _origgear = 0
- _team_change_force_balance_enable = True
- _teamLocksPermanent = False
- _autobalance_gametypes = 'tdm'
- _autobalance_gametypes_array = []
- _max_dic_size = 512000 #max dictionary size in bytes
-
- def startup(self):
- """\
- Initialize plugin settings
- """
-
- # get the admin plugin so we can register commands
- self._adminPlugin = self.console.getPlugin('admin')
- if not self._adminPlugin:
- # something is wrong, can't start without admin plugin
- self.error('Could not find admin plugin')
- return False
-
- # register our commands
- if 'commands' in self.config.sections():
- for cmd in self.config.options('commands'):
- level = self.config.get('commands', cmd)
- sp = cmd.split('-')
- alias = None
- if len(sp) == 2:
- cmd, alias = sp
-
- func = self.getCmd(cmd)
- if func:
- self._adminPlugin.registerCommand(self, cmd, level, func, alias)
- self._adminPlugin.registerCommand(self, 'paversion', 0, self.cmd_paversion, 'paver')
-
- # Register our events
- self.verbose('Registering events')
- self.registerEvent(b3.events.EVT_GAME_ROUND_START)
- self.registerEvent(b3.events.EVT_GAME_EXIT)
- #self.registerEvent(b3.events.EVT_CLIENT_JOIN)
- self.registerEvent(b3.events.EVT_CLIENT_AUTH)
- self.registerEvent(b3.events.EVT_CLIENT_DISCONNECT)
- self.registerEvent(b3.events.EVT_CLIENT_TEAM_CHANGE)
- self.registerEvent(b3.events.EVT_CLIENT_DAMAGE)
- self.registerEvent(b3.events.EVT_CLIENT_NAME_CHANGE)
-
- # don't run cron-checks on startup
- self.ignoreSet(60)
- self._balancing = False
-
- # save original vote settings
- self._origvote = self.console.getCvar('g_allowvote').getInt()
- # if by any chance on botstart g_allowvote is 0, we'll use the default UrT value
- if self._origvote == 0:
- self._origvote = 536871039
- self._lastvote = self._origvote
-
- # how many players are allowed and if g_maxGameClients != 0 we will disable specchecking
- self._sv_maxclients = self.console.getCvar('sv_maxclients').getInt()
- self._g_maxGameClients = self.console.getCvar('g_maxGameClients').getInt()
-
- # save original gear settings
- try:
- self._origgear = self.console.getCvar('g_gear').getInt()
- except:
- self._origgear = 0 # allow all weapons
-
- self.debug('Started')
-
-
- def onLoadConfig(self):
- self.LoadNameChecker()
- self.LoadTeamBalancer()
- self.LoadVoteDelayer()
- self.LoadSpecChecker()
- self.LoadMoonMode()
- self.LoadPublicMode()
- self.LoadMatchMode()
- self.LoadBotSupport()
- self.LoadHeadshotCounter()
- self.LoadRotationManager()
-
- def LoadNameChecker(self):
- # NAMECHECKING SETUP
- try:
- self._ninterval = self.config.getint('namechecker', 'ninterval')
- except:
- self._ninterval = 0
- self.debug('Using default value (%s) for Names Interval', self._ninterval)
-
- # set a max interval for namechecker
- if self._ninterval > 59:
- self._ninterval = 59
-
- try:
- self._checkdupes = self.config.getboolean('namechecker', 'checkdupes')
- except:
- self._checkdupes = True
- self.debug('Using default value (%s) for checkdupes', self._checkdupes)
- try:
- self._checkunknown = self.config.getboolean('namechecker', 'checkunknown')
- except:
- self._checkunknown = True
- self.debug('Using default value (%s) for checkunknown', self._checkunknown)
- try:
- self._checkbadnames = self.config.getboolean('namechecker', 'checkbadnames')
- except:
- self._checkbadnames = True
- self.debug('Using default value (%s) for checkbadnames', self._checkbadnames)
- try:
- self._checkchanges = self.config.getboolean('namechecker', 'checkchanges')
- except:
- self._checkchanges = True
- self.debug('Using default value (%s) for checkchanges', self._checkchanges)
- try:
- self._checkallowedchanges = self.config.getboolean('namechecker', 'checkallowedchanges')
- except:
- self._checkallowedchanges = 7
- self.debug('Using default value (%s) for checkallowedchanges', self._checkallowedchanges)
-
- self.debug('Names Interval: %s' %(self._ninterval))
- self.debug('Check badnames: %s' %(self._checkbadnames))
- self.debug('Dupechecking: %s' %(self._checkdupes))
- self.debug('Check unknowns: %s' %(self._checkunknown))
- self.debug('Check namechanges: %s' %(self._checkchanges))
- self.debug('Max. allowed namechanges per game: %s' %(self._checkallowedchanges))
-
- def LoadTeamBalancer(self):
- # TEAMBALANCER SETUP
- try:
- self._tinterval = self.config.getint('teambalancer', 'tinterval')
- except:
- self._tinterval = 0
- self.debug('Using default value (%s) for Teambalancer Interval', self._tinterval)
-
- # set a max interval for teamchecker
- if self._tinterval > 59:
- self._tinterval = 59
-
- try:
- self._teamdiff = self.config.getint('teambalancer', 'teamdifference')
- except:
- self._teamdiff = 1
- self.debug('Using default value (%s) for teamdiff', self._teamdiff)
- # set a minimum/maximum teamdifference
- if self._teamdiff < 1:
- self._teamdiff = 1
- if self._teamdiff > 9:
- self._teamdiff = 9
-
- try:
- self._tmaxlevel = self.config.getint('teambalancer', 'maxlevel')
- except:
- self._tmaxlevel = 20
- self.debug('Using default value (%s) for tmaxlevel', self._tmaxlevel)
- try:
- self._announce = self.config.getint('teambalancer', 'announce')
- except:
- self._announce = 2
- self.debug('Using default value (%s) for announce', self._announce)
-
- self.debug('TeamsInterval: %s' %(self._tinterval))
- self.debug('Teambalance Difference: %s' %(self._teamdiff))
- self.debug('Teambalance Maxlevel: %s' %(self._tmaxlevel))
- self.debug('Teambalance Announce: %s' %(self._announce))
-
- # 10/21/2008 - 1.4.0b9 - mindriot
- try:
- self._team_change_force_balance_enable = self.config.getboolean('teambalancer', 'team_change_force_balance_enable')
- except:
- self._team_change_force_balance_enable = True
- self.debug('Using default value (%s) for team_change_force_balance_enable', self._team_change_force_balance_enable)
-
- # 10/22/2008 - 1.4.0b10 - mindriot
- try:
- self._autobalance_gametypes = self.config.get('teambalancer', 'autobalance_gametypes')
- except:
- self._autobalance_gametypes = 'tdm'
- self.debug('Using default value (%s) for autobalance_gametypes', self._autobalance_gametypes)
-
- self._autobalance_gametypes = self._autobalance_gametypes.lower()
- self._autobalance_gametypes_array = re.split(r'[\s\,]+', self._autobalance_gametypes)
-
- try:
- self._teamLocksPermanent = self.config.getboolean('teambalancer', 'teamLocksPermanent')
- except:
- self._teamLocksPermanent = False
- self.debug('Using default value (%s) for teamLocksPermanent', self._teamLocksPermanent)
-
-
-
- def LoadVoteDelayer(self):
- #VOTEDELAYER SETUP
- try:
- self._votedelay = self.config.getint('votedelay', 'votedelay')
- except:
- self._votedelay = 0
- self.debug('Using default value (%s) for Vote delayer', self._votedelay)
- # set a max delay, setting it larger than timelimit would be foolish
- timelimit = self.console.getCvar('timelimit').getInt()
- if timelimit == 0 and self._votedelay != 0:
- # endless map or frag limited settings
- self._votedelay = 10
- elif self._votedelay >= timelimit - 1:
- # don't overlap rounds
- self._votedelay = timelimit - 1
- self.debug('Vote delay: %s' %(self._votedelay))
-
- def LoadSpecChecker(self):
- # SPECTATOR CHECK SETUP
- try:
- self._sinterval = self.config.getint('speccheck', 'sinterval')
- except:
- self._sinterval = 0
- self.debug('Using default value (%s) for speccheck interval', self._sinterval)
- try:
- self._smaxspectime = self.config.getint('speccheck', 'maxspectime')
- except:
- self._smaxspectime = 0
- self.debug('Using default value (%s) for speccheck smaxspectime', self._smaxspectime)
- try:
- self._smaxlevel = self.config.getint('speccheck', 'maxlevel')
- except:
- self._smaxlevel = 0
- self.debug('Using default value (%s) for speccheck maxlevel', self._smaxlevel)
- try:
- self._smaxplayers = self.config.getint('speccheck', 'maxplayers')
- except:
- #self._smaxplayers = 10
- #self.debug('Using default value (%s) for speccheck maxplayers', self._smaxplayers)
- self._smaxplayers = self.console.getCvar('sv_maxclients').getInt() - self.console.getCvar('sv_privateClients').getInt()
- self.debug('Using default server value (sv_maxclients - sv_privateClients = %s) for speccheck maxplayers', self._smaxplayers)
-
- self.debug('Speccheck interval: %s' %(self._sinterval))
- self.debug('Max Spectime: %s' %(self._smaxspectime))
- self.debug('Speccheck Maxlevel: %s' %(self._smaxlevel))
- self.debug('Maxplayers: %s' %(self._smaxplayers))
-
- def LoadMoonMode(self):
- #MOON MODE SETUP
- try:
- self._moon_on_gravity = self.config.getint('moonmode', 'gravity_on')
- except:
- self._moon_on_gravity = 100
- self.debug('Using default value (%s) for moon mode ON', self._moon_on_gravity)
- try:
- self._moon_off_gravity = self.config.getint('moonmode', 'gravity_off')
- except:
- self._moon_off_gravity = 800
- self.debug('Using default value (%s) for moon mode OFF', self._moon_off_gravity)
-
- self.debug('Moon ON gravity: %s' % self._moon_on_gravity)
- self.debug('Moon OFF gravity: %s' % self._moon_off_gravity)
-
- def LoadPublicMode(self):
- # PUBLIC MODE SETUP
- try:
- self.randnum = self.config.getint('publicmode','randnum')
- except:
- self.randnum = 0
-
- try:
- self.pass_lines = None
- try:
- padic = self.config.getboolean('publicmode','usedic')
- except ConfigParser.NoOptionError:
- self.warning("cannot find publicmode/usedic in config file, using default : no")
- padic = False
- except ValueError, err:
- self.error("cannot read publicmode/usedic in config file, using default : no")
- self.debug(err)
- padic = False
-
- if padic:
- padicfile = self.config.getpath('publicmode','dicfile')
- self.debug('trying to use password dictionnary %s' % padicfile)
- if os.path.exists(padicfile):
- stinfo = os.stat(padicfile)
- if stinfo.st_size > self._max_dic_size:
- self.warning('The dictionary file is too big. Switching to default.')
- else:
- dicfile = open(padicfile)
- text = dicfile.read().strip()
- dicfile.close()
- if text == "":
- self.warning('Dictionary file is empty. Switching to default.')
- else:
- self.pass_lines = text.splitlines()
- self.debug('Using dictionary password.')
- else:
- self.warning('Dictionary is enabled but the file doesn\'t exists. Switching to default.')
- except:
- traceback.print_exc()
- self.debug('Cannot load dictionary config. Using default')
-
- try:
- self._papublic_password = self.config.get('publicmode', 'g_password')
- if self._papublic_password is None:
- self.warning('Can\'t setup papublic command because there is no password set in config')
- except:
- self._papublic_password = None
- self.debug('Can\'t setup papublic command because there is no password set in config')
- self.debug('papublic password set to : %s' %(self._papublic_password))
-
- def LoadMatchMode(self):
- # MATCH MODE SETUP
- self.match_plugin_disable = []
- try:
- self.debug('matchmode/plugins_disable : %s' %self.config.get('matchmode', 'plugins_disable'))
- self.match_plugin_disable = [x for x in re.split('\W+', self.config.get('matchmode', 'plugins_disable')) if x]
- except ConfigParser.NoOptionError:
- self.warning(r"cannot find option 'matchmode/plugins_disable' in your config file")
-
-
- def LoadBotSupport(self):
- # BOT SUPPORT SETUP
- try:
- self._botenable = self.config.getboolean('botsupport', 'bot_enable')
- except:
- self._botenable = False
- self.debug('Using default value (%s) for bot enable', self._botenable)
- try:
- self._botskill = self.config.getint('botsupport', 'bot_skill')
- if self._botskill > 5:
- self._botskill = 5
- elif self._botskill < 1:
- self._botskill = 1
- except:
- self._botskill = 4
- self.debug('Using default value (%s) for bot skill', self._botskill)
- try:
- self._botminplayers = self.config.getint('botsupport', 'bot_minplayers')
- if self._botminplayers > 16:
- self._botminplayers = 16
- elif self._botminplayers < 0:
- self._botminplayers = 0
- except:
- self._botminplayers = 4
- self.debug('Using default value (%s) for bot minimum players', self._botminplayers)
- try:
- maps = self.config.get('botsupport', 'bot_maps')
- maps = maps.split(' ')
- self._botmaps = maps
- except:
- self._botmaps = {}
- self.debug('No maps for botsupport...')
-
- if self._botenable:
- # if it isn't enabled already it takes a mapchange to activate
- self.console.write('set bot_enable 1')
- # set the correct botskill anyway
- self.console.write('set g_spskill %s' %(self._botskill))
-
- self.debug('Bot enable: %s' %(self._botenable))
- self.debug('Bot skill: %s' %(self._botskill))
- self.debug('Bot minplayers: %s' %(self._botminplayers))
- self.debug('Bot maps: %s' %(self._botmaps))
-
- # first check for botsupport
- self.botsupport()
-
- def LoadHeadshotCounter(self):
- # HEADSHOT COUNTER SETUP
- try:
- self._hsenable = self.config.getboolean('headshotcounter', 'hs_enable')
- except:
- self._hsenable = False
- self.debug('Using default value (%s) for hs_enable', self._hsenable)
- try:
- self._hsresetvars = self.config.get('headshotcounter', 'reset_vars')
- if not self._hsresetvars in ['no', 'map', 'round']:
- raise Exception('Config setting not valid.')
- except:
- self._hsresetvars = 'map'
- self.debug('Using default value (%s) for reset_vars', self._hsresetvars)
- try:
- self._hsbroadcast = self.config.getboolean('headshotcounter', 'broadcast')
- except:
- self._hsbroadcast = True
- self.debug('Using default value (%s) for broadcast', self._hsbroadcast)
- try:
- self._hsall = self.config.getboolean('headshotcounter', 'announce_all')
- except:
- self._hsall = True
- self.debug('Using default value (%s) for announce_all', self._hsall)
- try:
- self._hspercent = self.config.getboolean('headshotcounter', 'announce_percentages')
- except:
- self._hspercent = True
- self.debug('Using default value (%s) for announce_percentages', self._hspercent)
- try:
- self._hspercentmin = self.config.getint('headshotcounter', 'percent_min')
- except:
- self._hspercentmin = 20
- self.debug('Using default value (%s) for percent_min', self._hspercentmin)
- try:
- self._hswarnhelmet = self.config.getboolean('headshotcounter', 'warn_helmet')
- except:
- self._hswarnhelmet = True
- self.debug('Using default value (%s) for warn_helmet', self._hswarnhelmet)
- try:
- self._hswarnhelmetnr = self.config.getint('headshotcounter', 'warn_helmet_nr')
- except:
- self._hswarnhelmetnr = 7
- self.debug('Using default value (%s) for warn_helmet_nr', self._hswarnhelmetnr)
- try:
- self._hswarnkevlar = self.config.getboolean('headshotcounter', 'warn_kevlar')
- except:
- self._hswarnkevlar = True
- self.debug('Using default value (%s) for warn_kevlar', self._hswarnkevlar)
- try:
- self._hswarnkevlarnr = self.config.getint('headshotcounter', 'warn_kevlar_nr')
- except:
- self._hswarnkevlarnr = 50
- self.debug('Using default value (%s) for warn_kevlar_nr', self._hswarnkevlarnr)
- # making shure loghits is enabled to count headshots
- if self._hsenable:
- self.console.write('set g_loghits 1')
-
- self.debug('Headshotcounter enable: %s' %(self._hsenable))
- self.debug('Broadcasting: %s' %(self._hsbroadcast))
- self.debug('Announce all: %s' %(self._hsall))
- self.debug('Announce percentages: %s' %(self._hspercent))
- self.debug('Minimum percentage: %s' %(self._hspercentmin))
- self.debug('Warn to use helmet: %s' %(self._hswarnhelmet))
- self.debug('Warn after nr of hits in the head: %s' %(self._hswarnhelmetnr))
- self.debug('Warn to use kevlar: %s' %(self._hswarnkevlar))
- self.debug('Warn after nr of hits in the torso: %s' %(self._hswarnkevlarnr))
-
- def LoadRotationManager(self):
- # ROTATION MANAGER SETUP
- try:
- self._rmenable = self.config.getboolean('rotationmanager', 'rm_enable')
- except:
- pass
- if self._rmenable:
- try:
- self._switchcount1 = self.config.getint('rotationmanager', 'switchcount1')
- except:
- pass
- try:
- self._switchcount2 = self.config.getint('rotationmanager', 'switchcount2')
- except:
- pass
- try:
- self._hysteresis = self.config.getint('rotationmanager', 'hysteresis')
- except:
- pass
- try:
- self._rotation_small = self.config.get('rotationmanager', 'smallrotation')
- except:
- pass
- try:
- self._rotation_medium = self.config.get('rotationmanager', 'mediumrotation')
- except:
- pass
- try:
- self._rotation_large = self.config.get('rotationmanager', 'largerotation')
- except:
- pass
- try:
- self._gamepath = self.config.get('rotationmanager', 'gamepath')
- except:
- pass
-
- self.debug('Rotation Manager is enabled')
- self.debug('Switchcount 1: %s' %(self._switchcount1))
- self.debug('Switchcount 2: %s' %(self._switchcount2))
- self.debug('Hysteresis: %s' %(self._hysteresis))
- self.debug('Rotation small: %s' %(self._rotation_small))
- self.debug('Rotation medium: %s' %(self._rotation_medium))
- self.debug('Rotation large: %s' %(self._rotation_large))
- else:
- self.debug('Rotation Manager is disabled')
-
- # CRONTABS INSTALLATION
- # Cleanup and Create the crontabs
- if self._ncronTab:
- # remove existing crontab
- self.console.cron - self._ncronTab
- if self._tcronTab:
- # remove existing crontab
- self.console.cron - self._tcronTab
- if self._scronTab:
- # remove existing crontab
- self.console.cron - self._scronTab
- if self._ninterval > 0:
- self._ncronTab = b3.cron.PluginCronTab(self, self.namecheck, 0, '*/%s' % (self._ninterval))
- self.console.cron + self._ncronTab
- if self._tinterval > 0:
- self._tcronTab = b3.cron.PluginCronTab(self, self.teamcheck, 0, '*/%s' % (self._tinterval))
- self.console.cron + self._tcronTab
- if self._sinterval > 0:
- self._scronTab = b3.cron.PluginCronTab(self, self.speccheck, 0, '*/%s' % (self._sinterval))
- self.console.cron + self._scronTab
-
-
- def getCmd(self, cmd):
- cmd = 'cmd_%s' % cmd
- if hasattr(self, cmd):
- func = getattr(self, cmd)
- return func
-
- return None
-
-
- def onEvent(self, event):
- """\
- Handle intercepted events
- """
- if event.type == b3.events.EVT_CLIENT_DISCONNECT:
- if self._rmenable and self.console.time() > self._dontcount and self._mapchanged:
- self._playercount -= 1
- self.debug('PlayerCount: %s' % (self._playercount))
- self.adjustrotation(-1)
- elif event.type == b3.events.EVT_CLIENT_AUTH:
- if self._hsenable:
- self.setupVars(event.client)
- if self._rmenable and self.console.time() > self._dontcount and self._mapchanged:
- self._playercount += 1
- self.debug('PlayerCount: %s' % (self._playercount))
- self.adjustrotation(+1)
- elif event.type == b3.events.EVT_CLIENT_TEAM_CHANGE:
- self.onTeamChange(event.data, event.client)
- elif event.type == b3.events.EVT_CLIENT_DAMAGE:
- self.headshotcounter(event.client, event.target, event.data)
- elif event.type == b3.events.EVT_GAME_EXIT:
- self._mapchanged = True
- if self._botenable:
- self.botsdisable()
- self.ignoreSet(60)
- # reset headshotcounter (per map) if applicable
- if self._hsresetvars == 'map':
- self.resetVars()
- # reset number of Namechanges per client
- self.resetNameChanges()
- if not self._teamLocksPermanent:
- # release TeamLocks
- self.resetTeamLocks()
- #Setup timer for recounting players
- if self._rmenable:
- time = 60
- self._dontcount = self.console.time() + time
- t2 = threading.Timer(time, self.recountplayers)
- self.debug('Starting RecountPlayers Timer: %s seconds' % (time))
- t2.start()
- elif event.type == b3.events.EVT_GAME_ROUND_START:
- # check for botsupport
- if self._botenable:
- self.botsdisable()
- self.botsupport()
- # reset headshotcounter (per round) if applicable
- if self._hsresetvars == 'round':
- self.resetVars()
- # ignore teambalance checking for 1 minute
- self.ignoreSet(60)
- self._teamred = 0
- self._teamblue = 0
- # vote delay init
- if self._votedelay > 0 and self.console.getCvar('g_allowvote').getInt() != 0:
- # delay voting
- data = 'off'
- self.votedelay(data)
- # re-enable voting
- time = self._votedelay * 60
- t1 = threading.Timer(time, self.votedelay)
- self.debug('Starting Vote delay Timer: %s seconds' % (time))
- t1.start()
- # recount players
- elif event.type == b3.events.EVT_CLIENT_NAME_CHANGE:
- self.onNameChange(event.data, event.client)
-
- else:
- self.dumpEvent(event)
-
- def dumpEvent(self, event):
- self.debug('poweradminurt.dumpEvent -- Type %s, Client %s, Target %s, Data %s',
- event.type, event.client, event.target, event.data)
-
-#--Commands implementation ------------------------------------------------------------------------
-# /rcon commands:
-# slap
-# nuke
-# forceteam
-# veto (vote cancellen)
-# mute
-# pause
-# swapteams
-# shuffleteams
-
- def cmd_pateams(self ,data , client, cmd=None):
- """\
- Force teambalancing (all gametypes!)
- The player with the least time in a team will be switched.
- """
- if self.teambalance():
- if self._teamsbalanced:
- client.message('^7Teams are already balanced.')
- else:
- client.message('^7Teams are now balanced.')
- self._teamsbalanced = True
- else:
- client.message('^7Teambalancing failed, please try a again in a few moments.')
- return None
-
- def cmd_pavote(self, data, client=None, cmd=None):
- """\
- - Set voting on, off or reset to original value at bot start.
- Setting vote on will set the vote back to the value when it was set off.
- """
- if not data:
- if client:
- client.message('^7Invalid or missing data, try !help pavote')
- else:
- self.debug('No data sent to cmd_pavote')
- return False
- else:
- if data in ('on', 'off', 'reset'):
- if client:
- client.message('^7Voting: ^1%s' % (data))
- else:
- self.debug('Voting: %s' % (data))
- else:
- if client:
- client.message('^7Invalid data, try !help pavote')
- else:
- self.debug('Invalid data sent to cmd_pavote')
- return False
-
- if data == 'off':
- curvalue = self.console.getCvar('g_allowvote').getInt()
- if curvalue != 0:
- self._lastvote = curvalue
- self.console.setCvar( 'g_allowvote', '0' )
- elif data == 'on':
- self.console.setCvar( 'g_allowvote', '%s' % self._lastvote )
- elif data == 'reset':
- self.console.setCvar( 'g_allowvote', '%s' % self._origvote )
- else:
- return False
-
- return True
-
- def cmd_paversion(self, data, client, cmd=None):
- """\
- This command identifies PowerAdminUrt version and creator.
- """
- cmd.sayLoudOrPM(client, 'I am PowerAdminUrt version %s by %s' % (__version__, __author__))
- return None
-
- def cmd_paexec(self, data, client, cmd=None):
- """\
- - Execute a server configfile.
- (You must use the command exactly as it is! )
- """
- if not data:
- client.message('^7Invalid or missing data, try !help paexec')
- return False
- else:
- if re.match('^[a-z0-9_.]+.cfg$', data, re.I):
- self.debug('Executing configfile = [%s]', data)
- result = self.console.write('exec %s' % data)
- cmd.sayLoudOrPM(client, result)
- else:
- self.error('%s is not a valid configfile', data)
-
- return True
-
- def cmd_pacyclemap(self, data, client, cmd=None):
- """\
- Cycle to the next map.
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('cyclemap')
- return True
-
- def cmd_pamaprestart(self, data, client, cmd=None):
- """\
- Restart the current map.
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('map_restart')
- return True
-
- def cmd_pamapreload(self, data, client, cmd=None):
- """\
- Reload the current map.
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('reload')
- return True
-
- def cmd_paset(self, data, client, cmd=None):
- """\
- - Set a server cvar to a certain value.
- (You must use the command exactly as it is! )
- """
- if not data:
- client.message('^7Invalid or missing data, try !help paset')
- return False
- else:
- # are we still here? Let's write it to console
- input = data.split(' ',1)
- cvarName = input[0]
- value = input[1]
- self.console.setCvar( cvarName, value )
-
- return True
-
- def cmd_paget(self, data, client, cmd=None):
- """\
- - Returns the value of a servercvar.
- (You must use the command exactly as it is! )
- """
- if not data:
- client.message('^7Invalid or missing data, try !help paget')
- return False
- else:
- # are we still here? Let's write it to console
- getcvar = data.split(' ')
- getcvarvalue = self.console.getCvar( '%s' % getcvar[0] )
- cmd.sayLoudOrPM(client, '%s' % getcvarvalue)
-
- return True
-
- def cmd_pabigtext(self, data, client, cmd=None):
- """\
- - Print a Bold message on the center of all screens.
- (You can safely use the command without the 'pa' at the beginning)
- """
- if not data:
- client.message('^7Invalid or missing data, try !help pabigtext')
- return False
- else:
- # are we still here? Let's write it to console
- self.console.write( 'bigtext "%s"' % data )
-
- return True
-
- def cmd_pamute(self, data, client, cmd=None):
- """\
- [] - Mute a player.
- (You can safely use the command without the 'pa' at the beginning)
- """
- # this will split the player name and the message
- input = self._adminPlugin.parseUserCmd(data)
- if input:
- # input[0] is the player id
- sclient = self._adminPlugin.findClientPrompt(input[0], client)
- if not sclient:
- # a player matchin the name was not found, a list of closest matches will be displayed
- # we can exit here and the user will retry with a more specific player
- return False
- else:
- client.message('^7Invalid data, try !help pamute')
- return False
-
- if input[1] is not None and re.match('^([0-9]+)\s*$', input[1]):
- duration = int(input[1])
- else:
- duration = ''
-
- # are we still here? Let's write it to console
- self.console.write('mute %s %s' % (sclient.cid, duration))
-
- return True
-
- def cmd_papause(self, data, client, cmd=None):
- """\
- - Pause the game. Type again to resume
- """
- result = self.console.write('pause')
- cmd.sayLoudOrPM(client, result)
-
- return True
-
- def cmd_paslap(self, data, client, cmd=None):
- """\
- [] - (multi)Slap a player.
- (You can safely use the command without the 'pa' at the beginning)
- """
- # this will split the player name and the message
- input = self._adminPlugin.parseUserCmd(data)
- if input:
- # input[0] is the player id
- sclient = self._adminPlugin.findClientPrompt(input[0], client)
- if not sclient:
- # a player matchin the name was not found, a list of closest matches will be displayed
- # we can exit here and the user will retry with a more specific player
- return False
- else:
- client.message('^7Invalid data, try !help paslap')
- return False
-
- if input[1]:
- try:
- x = int(input[1])
- except:
- client.message('^7Invalid data, try !help paslap')
- return False
- if x in range(1, 26):
- thread.start_new_thread(self.multipunish, (x, sclient, client, 'slap'))
- else:
- client.message('^7Number of punishments out of range, must be 1 to 25')
- else:
- self.debug('Performing single slap...')
- self.console.write('slap %s' % (sclient.cid))
-
- return True
-
- def cmd_panuke(self, data, client, cmd=None):
- """\
- [] - (multi)Nuke a player.
- (You can safely use the command without the 'pa' at the beginning)
- """
- # this will split the player name and the message
- input = self._adminPlugin.parseUserCmd(data)
- if input:
- # input[0] is the player id
- sclient = self._adminPlugin.findClientPrompt(input[0], client)
- if not sclient:
- # a player matchin the name was not found, a list of closest matches will be displayed
- # we can exit here and the user will retry with a more specific player
- return False
- else:
- client.message('^7Invalid data, try !help panuke')
- return False
-
- if input[1]:
- try:
- x = int(input[1])
- except:
- client.message('^7Invalid data, try !help panuke')
- return False
- if x in range(1, 26):
- thread.start_new_thread(self.multipunish, (x, sclient, client, 'nuke'))
- else:
- client.message('^7Number of punishments out of range, must be 1 to 25')
- else:
- self.debug('Performing single nuke...')
- self.console.write('nuke %s' % (sclient.cid))
-
- return True
-
- def multipunish(self, x, sclient, client, cmd):
- self.debug('Entering multipunish...')
- #self.debug('x: %s, sclient.cid: %s, client.cid: %s, cmd: %s' %(x, sclient.cid, client.cid, cmd))
- c = 0
- while c < x:
- self.console.write('%s %s' % (cmd, sclient.cid))
- time.sleep(1)
- c += 1
-
- def cmd_paveto(self, data, client, cmd=None):
- """\
- Veto current running Vote.
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('veto')
-
- return True
-
- def cmd_paforce(self, data, client, cmd=None):
- """\
- - Force a client to red/blue/spec or release the force (free)
- adding 'lock' will lock the player where it is forced to, default this is off.
- using 'all free' wil release all locks.
- (You can safely use the command without the 'pa' at the beginning)
- """
- # this will split the player name and the message
- input = self._adminPlugin.parseUserCmd(data)
- if input:
- # check if all Locks should be released
- if input[0] == "all" and input[1] == "free":
- self.resetTeamLocks()
- self.console.say('All TeamLocks were released')
- return None
-
- # input[0] is the player id
- sclient = self._adminPlugin.findClientPrompt(input[0], client)
- if not sclient:
- # a player matchin the name was not found, a list of closest matches will be displayed
- # we can exit here and the user will retry with a more specific player
- return False
- else:
- client.message('^7Invalid data, try !help paforce')
- return False
-
- if not len(input[1]):
- client.message('^7Missing data, try !help paforce')
- return False
-
- tdata = input[1].split(' ')
- team = tdata[0]
-
- try:
- if tdata[1] == 'lock':
- lock = True
- except:
- lock = False
-
- if team == 'spec' or team == 'spectator':
- team = 's'
- if team == 'b':
- team = 'blue'
- if team == 'r':
- team = 'red'
-
-
- if team == 's':
- teamname = 'spectator'
- else:
- teamname = team
-
- if team == 'free':
- if sclient.isvar(self, 'paforced'):
- sclient.message('^3Your are released by the admin')
- client.message('^7%s ^3was released.' % (sclient.name))
- sclient.delvar(self, 'paforced')
- return False
- else:
- client.message('^3There was no lock on ^7%s' %(sclient.name))
- elif team in ('red','blue','s') and lock:
- sclient.message('^3Your are forced and locked to: ^7%s' % (teamname))
- elif team in ('red','blue','s'):
- sclient.message('^3Your are forced to: ^7%s' % (teamname))
- else:
- client.message('^7Invalid or missing data, try !help paforce')
- return False
-
- if lock:
- sclient.setvar(self, 'paforced', team) # s, red or blue
- else:
- sclient.delvar(self, 'paforced')
-
- # are we still here? Let's write it to console
- self.console.write('forceteam %s %s' % (sclient.cid, team))
-
- return True
-
- def cmd_paswapteams(self, data, client, cmd=None):
- """\
- Swap teams.
- (You can safely use the command without the 'pa' at the beginning)
- """
- # Ignore automatic checking before giving the command
- self.ignoreSet(30)
- self.console.write('swapteams')
-
- return True
-
- def cmd_pashuffleteams(self, data, client, cmd=None):
- """\
- Shuffle teams.
- (You can safely use the command without the 'pa' at the beginning)
- """
- # Ignore automatic checking before giving the command
- self.ignoreSet(30)
- self.console.write('shuffleteams')
-
- return True
-
- def cmd_pamoon(self, data, client, cmd=None):
- """\
- Set moon mode
- (You can safely use the command without the 'pa' at the beginning)
- """
- if not data or data not in ('on','off'):
- client.message('^7Invalid or missing data, try !help pamoon')
- return False
- else:
- if data == 'on':
- self.console.setCvar( 'g_gravity', self._moon_on_gravity )
- self.console.say('^7Moon mode: ^2ON')
- elif data == 'off':
- self.console.setCvar( 'g_gravity', self._moon_off_gravity )
- self.console.say('^7Moon mode: ^9OFF')
- return True
-
- def cmd_papublic(self, data, client, cmd=None):
- """\
- Set server public mode on/off
- (You can safely use the command without the 'pa' at the beginning)
- """
- if not data or data not in ('on','off'):
- client.message('^7Invalid or missing data, try !help papublic')
- return False
- else:
- if data == 'on':
- self.console.setCvar( 'g_password', '' )
- self.console.say('^7public mode: ^2ON')
- elif data == 'off':
- newpassword = self._papublic_password
- if self.pass_lines is not None:
- i = random.randint(0,len(self.pass_lines)-1)
- newpassword = self.pass_lines[i]
-
- for i in range(0,self.randnum):
- newpassword += str(random.randint(1,9))
-
- self.debug('Private password set to: %s' % newpassword)
-
- if newpassword is None:
- client.message('^4ERROR :^7 can\'t set public mode off because there is no password specified in the config file')
- return False
- else:
- self.console.setCvar( 'g_password', '%s' % (newpassword) )
- self.console.say('^7public mode: ^9OFF')
- client.message('^7password is \'^4%s^7\''% (newpassword))
- client.message('^7type ^5!mapreload^7 to apply change')
- self.console.write('bigtext "^7Server going ^3PRIVATE^7 soon !!"')
- return True
-
- def cmd_pamatch(self, data, client, cmd=None):
- """\
- Set server match mode on/off
- (You can safely use the command without the 'pa' at the beginning)
- """
- if not data or data not in ('on','off'):
- client.message('^7Invalid or missing data, try !help pamatch')
- return False
- else:
- if data == 'on':
- self._matchmode = True
- self.console.setCvar( 'g_matchmode', '1' )
- self.console.say('^7match mode: ^2ON')
- self.console.write('bigtext "^7MATCH starting soon !!"')
- for e in self.match_plugin_disable:
- self.debug('Disabling plugin %s' %e)
- plugin = self.console.getPlugin(e)
- if plugin:
- plugin.disable()
- client.message('^7plugin %s disabled' % e)
- client.message('^7type ^5!mapreload^7 to apply change')
- self.console.write('bigtext "^7MATCH starting soon !!"')
-
- elif data == 'off':
- self._matchmode = False
- self.console.setCvar( 'g_matchmode', '0' )
- self.console.say('^7match mode: ^9OFF')
- client.message('^7type ^5!mapreload^7 to apply change')
- for e in self.match_plugin_disable:
- self.debug('enabling plugin %s' %e)
- plugin = self.console.getPlugin(e)
- if plugin:
- plugin.enable()
- client.message('^7plugin %s enabled' % e)
-
- return True
-
-
- def cmd_pagear(self, data, client=None, cmd=None):
- """\
- - Set allowed weapons.
- """
- cur_gear = self.console.getCvar('g_gear').getInt()
- if not data:
- if client:
- nade = (cur_gear & 1) != 1
- snipe = (cur_gear & 2) != 2
- spas = (cur_gear & 4) != 4
- pist = (cur_gear & 8) != 8
- auto = (cur_gear & 16) != 16
- nege = (cur_gear & 32) != 32
-
- self.console.write('^7current gear: %s (Nade:%d, Sniper:%d, Spas:%d, Pistol:%d, Auto:%d, Negev:%d)' %
- (cur_gear, nade, snipe, spas, pist, auto, nege) )
- return False
- else:
- if not data[:5] in ('all', 'none', 'reset',
- '+nade', '+snip', '+spas', '+pist', '+auto', '+nege',
- '-nade', '-snip', '-spas', '-pist', '-auto', '-nege'):
- if client:
- client.message('^7Invalid data, try !help pagear')
- else:
- self.debug('Invalid data sent to cmd_pagear')
- return False
-
- if data[:5] == 'all':
- self.console.setCvar( 'g_gear', '0' )
- elif data[:5] == 'none':
- self.console.setCvar( 'g_gear', '63' )
- elif data[:5] == 'reset':
- self.console.setCvar( 'g_gear', '%s' % self._origgear )
- else:
- if data[1:5] == 'nade':
- bit=1
- elif data[1:5] == 'snip':
- bit=2
- elif data[1:5] == 'spas':
- bit=4
- elif data[1:5] == 'pist':
- bit=8
- elif data[1:5] == 'auto':
- bit=16
- elif data[1:5] == 'nege':
- bit=32
- else:
- return False
-
- if data[:1] == '+':
- self.console.setCvar( 'g_gear', '%s' % (cur_gear & (63-bit)) )
- elif data[:1] == '-':
- self.console.setCvar( 'g_gear', '%s' % (cur_gear | bit) )
- else:
- return False
-
- return True
-
- def cmd_paffa(self, data, client, cmd=None):
- """\
- Change game type to Free For All
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 0')
- if client:
- client.message('^7game type changed to ^4Free For All')
-
- def cmd_patdm(self, data, client, cmd=None):
- """\
- Change game type to Team Death Match
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 3')
- if client:
- client.message('^7game type changed to ^4Team Death Match')
-
- def cmd_pats(self, data, client, cmd=None):
- """\
- Change game type to Team Survivor
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 4')
- if client:
- client.message('^7game type changed to ^4Team Survivor')
-
- def cmd_paftl(self, data, client, cmd=None):
- """\
- Change game type to Follow The Leader
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 5')
- if client:
- client.message('^7game type changed to ^4Follow The Leader')
-
- def cmd_pacah(self, data, client, cmd=None):
- """\
- Change game type to Capture And Hold
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 6')
- if client:
- client.message('^7game type changed to ^4Capture And Hold')
-
- def cmd_pactf(self, data, client, cmd=None):
- """\
- Change game type to Capture The Flag
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 7')
- if client:
- client.message('^7game type changed to ^4Capture The Flag')
-
- def cmd_pabomb(self, data, client, cmd=None):
- """\
- Change game type to Bomb
- (You can safely use the command without the 'pa' at the beginning)
- """
- self.console.write('g_gametype 8')
- if client:
- client.message('^7game type changed to ^4Bomb')
-
-
- def cmd_paident(self, data, client=None, cmd=None):
- """\
- - show the ip and guid of a player
- (You can safely use the command without the 'pa' at the beginning)
- """
- input = self._adminPlugin.parseUserCmd(data)
- if not input:
- client.message('^7Invalid data, try !help paident')
- return False
- else:
- # input[0] is the player id
- sclient = self._adminPlugin.findClientPrompt(input[0], client)
- if not sclient:
- # a player matchin the name was not found, a list of closest matches will be displayed
- # we can exit here and the user will retry with a more specific player
- return False
-
- cmd.sayLoudOrPM(client, '%s ^2%s ^2%s^7 ^4%s' % (self.console.formatTime(time.time()), sclient.exactName, sclient.ip, sclient.guid))
- return True
-
-#---Teambalance Mechanism--------------------------------------------------------------------------
- """\
- /g_redteamlist en /g_blueteamlist
- they return which clients are in the red or blue team
- not with numbers but characters (clientnum 0 = A, clientnum 1 = B, etc.)
- """
- def onTeamChange(self, team, client):
- #store the time of teamjoin for autobalancing purposes
- client.setvar(self, 'teamtime', self.console.time())
- self.verbose('Client variable teamtime set to: %s' % client.var(self, 'teamtime').value)
-
- if not self._matchmode and client.isvar(self, 'paforced'):
- forcedTeam = client.var(self, 'paforced').value
- if team != b3.TEAM_UNKNOWN and team != self.console.getTeam(forcedTeam):
- self.console.write('forceteam %s %s' % (client.cid, forcedTeam))
- client.message('^1You are LOCKED! You are NOT allowed to switch!')
- self.verbose('%s was locked and forced back to %s' %(client.name, forcedTeam))
- # Break out of this function, nothing more to do here
- return None
-
- # 10/21/2008 - 1.4.0b9 - mindriot
- # 10/23/2008 - 1.4.0b12 - mindriot
- if self._team_change_force_balance_enable and not self._matchmode:
-
- # if the round just started, don't do anything
- if self.ignoreCheck():
- return None
-
- if self.isEnabled() and not self._balancing:
- # set balancing flag
- self._balancing = True
- self.verbose('Teamchanged cid: %s, name: %s, team: %s' % (client.cid, client.name, team))
-
- # are we supposed to be balanced?
- if client.maxLevel >= self._tmaxlevel:
- # done balancing
- self._balancing = False
- return None
-
- # did player join spectators?
- if team == b3.TEAM_SPEC:
- self.verbose('Player joined specs')
- # done balancing
- self._balancing = False
- return None
- elif team == b3.TEAM_UNKNOWN:
- self.verbose('Team is unknown')
- # done balancing
- self._balancing = False
- return None
-
- # check if player was allowed to join this team
- if not self.countteams():
- self._balancing = False
- self.error('Aborting teambalance. Counting teams failed!')
- return False
- if abs(self._teamred - self._teamblue) <= self._teamdiff:
- # teams are balanced
- self.verbose('Teams are balanced, red: %s, blue: %s' %(self._teamred, self._teamblue))
- # done balancing
- self._balancing = False
- return None
- else:
- # teams are not balanced
- self.verbose('Teams are NOT balanced, red: %s, blue: %s' %(self._teamred, self._teamblue))
-
- # switch is not allowed, so this should be a client suicide, not a legit switch.
- # added as anti stats-harvest-exploit measure. One suicide is added as extra penalty for harvesting.
- if self.console:
- self.verbose('Applying Teamswitch penalties')
- self.console.queueEvent(b3.events.Event(b3.events.EVT_CLIENT_SUICIDE, (100, 'penalty', 'body', 'Team_Switch_Penalty'), client, client))
- self.console.queueEvent(b3.events.Event(b3.events.EVT_CLIENT_SUICIDE, (100, 'penalty', 'body', 'Team_Switch_Penalty'), client, client))
- plugin = self.console.getPlugin('xlrstats')
- if plugin:
- client.message('Switching made teams unfair. Points where deducted from your stats as a penalty!')
-
- if self._teamred > self._teamblue:
- # join the blue team
- self.verbose('Forcing %s to the Blue team' % client.name)
- self.console.write('forceteam %s blue' % client.cid)
- else:
- # join the red team
- self.verbose('Forcing %s to the Red team' % client.name)
- self.console.write('forceteam %s red' % client.cid)
-
- # done balancing
- self._balancing = False
-
- else:
- self.debug('onTeamChange DISABLED')
-
- return None
-
- def countteams(self):
- try:
- self._teamred = len(self.console.getCvar('g_redteamlist').getString())
- self._teamblue = len(self.console.getCvar('g_blueteamlist').getString())
- return True
- except:
- return False
-
- def teamcheck(self):
- # g_gametype //0=FreeForAll=dm, 3=TeamDeathMatch=tdm, 4=Team Survivor=ts,
- # 5=Follow the Leader=ftl, 6=Capture and Hold=cah, 7=Capture The Flag=ctf, 8=Bombmode=bm
-
- # 10/22/2008 - 1.4.0b10 - mindriot
- # if gametype is unknown when B3 is started in the middle of a game
- if self.console.game.gameType == None:
- try:
- # find and set current gametype
- self.console.game.gameType = self.console.defineGameType( self.console.getCvar('g_gametype').getString() )
- self.debug('Current gametype found - changed to (%s)', self.console.game.gameType)
- except:
- self.debug('Unable to determine current gametype - remains at (%s)', self.console.game.gameType)
-
- # run teambalance only if current gametype is in autobalance_gametypes list
- try:
- self._autobalance_gametypes_array.index(self.console.game.gameType)
- except:
- self.debug('Current gametype (%s) is not specified in autobalance_gametypes - teambalancer disabled', self.console.game.gameType)
- return None
-
- if self.console.time() > self._ignoreTill:
- self.teambalance()
-
- return None
-
- def teambalance(self):
- if self.isEnabled() and not self._balancing and not self._matchmode:
- #set balancing flag
- self._balancing = True
- self.verbose('Checking for balancing')
-
- if not self.countteams():
- self._balancing = False
- self.warning('Aborting teambalance. Counting teams failed!')
- return False
-
- if abs(self._teamred - self._teamblue) <= self._teamdiff:
- #teams are balanced
- self._teamsbalanced = True
- self.verbose('Teambalance: Teams are balanced, red: %s, blue: %s (diff: %s)' %(self._teamred, self._teamblue, self._teamdiff))
- #done balancing
- self._balancing = False
- return True
- else:
- #teams are not balanced
- self._teamsbalanced = False
- self.verbose('Teambalance: Teams are NOT balanced, red: %s, blue: %s (diff: %s)' %(self._teamred, self._teamblue, self._teamdiff))
- if self._announce == 1:
- self.console.write('say Autobalancing Teams!')
- elif self._announce == 2:
- self.console.write('bigtext "Autobalancing Teams!"')
-
- if self._teamred > self._teamblue:
- newteam = 'blue'
- oldteam = b3.TEAM_RED
- else:
- newteam = 'red'
- oldteam = b3.TEAM_BLUE
- self.verbose('Smaller team is: %s' % newteam )
-
- #endless loop protection
- count = 25
- while abs(self._teamred - self._teamblue) > self._teamdiff and count > 0:
- stime = self.console.upTime()
- self.verbose('Uptime bot: %s' % stime)
- forceclient = None
- clients = self.console.clients.getList()
- for c in clients:
- if not c.isvar(self, 'teamtime'):
- self.debug('client has no variable teamtime')
- # 10/22/2008 - 1.4.0b11 - mindriot
- # store the time of teamjoin for autobalancing purposes
- c.setvar(self, 'teamtime', self.console.time())
- self.verbose('Client variable teamtime set to: %s' % c.var(self, 'teamtime').value)
-
- if self.console.time() - c.var(self, 'teamtime').value < stime and c.team == oldteam and c.maxLevel < self._tmaxlevel and not c.isvar(self, 'paforced'):
- forceclient = c.cid
- stime = self.console.time() - c.var(self, 'teamtime').value
-
- if forceclient:
- if newteam:
- self.verbose('Forcing client: %s to team: %s' % (forceclient, newteam))
- self.console.write('forceteam %s %s' % (forceclient, newteam))
- else:
- self.debug('No new team to force to')
- else:
- self.debug('No client to force')
- count -= 1
- #recount the teams... do we need to balance once more?
- if not self.countteams():
- self._balancing = False
- self.error('Aborting teambalance. Counting teams failed!')
- return False
-
- # 10/28/2008 - 1.4.0b13 - mindriot
- self.verbose('Teambalance: red: %s, blue: %s (diff: %s)' %(self._teamred, self._teamblue, self._teamdiff))
- if self._teamred > self._teamblue:
- newteam = 'blue'
- oldteam = b3.TEAM_RED
- else:
- newteam = 'red'
- oldteam = b3.TEAM_BLUE
- self.verbose('Smaller team is: %s' % newteam )
-
- #done balancing
- self._balancing = False
- return True
-
- def resetTeamLocks(self):
- if self.isEnabled():
- clients = self.console.clients.getList()
- for c in clients:
- if c.isvar(self, 'paforced'):
- c.delvar(self, 'paforced')
- self.debug('TeamLocks Released')
- return None
-
-#---Dupes/Forbidden Names Mechanism----------------------------------------------------------------
- def namecheck(self):
- if self._matchmode:
- return None
-
- self.debug('Checking Names')
- d = {}
- if self.isEnabled() and self.console.time() > self._ignoreTill:
- for player in self.console.clients.getList():
- if not d.has_key(player.name):
- d[player.name] = [player.cid]
- else:
- #l = d[player.name]
- #l.append(cid)
- #d[player.name]=l
- d[player.name].append(player.cid)
-
- for pname, cidlist in d.items():
- if self._checkdupes and len(cidlist) > 1:
- self.info("Warning Players %s for using the same name" % (", ".join(["%s <%s> @%s" % (c.exactName, c.cid, c.id) for c in map(self.console.clients.getByCID, cidlist)])))
- for cid in cidlist:
- client = self.console.clients.getByCID(cid)
- self._adminPlugin.warnClient(client, 'badname')
-
- if self._checkunknown and pname == self.console.stripColors('New UrT Player'):
- for cid in cidlist:
- client = self.console.clients.getByCID(cid)
- self.info("Warning Player %s <%s> @%s for using forbidden name 'New UrT Player'" % (client.exactName, client.cid, client.id))
- self._adminPlugin.warnClient(client, 'badname')
-
- if self._checkbadnames and pname == 'all':
- for cid in cidlist:
- client = self.console.clients.getByCID(cid)
- self.info("Warning Player %s <%s> @%s for using forbidden name 'all'" % (client.exactName, client.cid, client.id))
- self._adminPlugin.warnClient(client, 'badname')
-
- return None
-
- def onNameChange(self, name, client):
- if self.isEnabled() and self._checkchanges and client.maxLevel < 9 :
- if not client.isvar(self, 'namechanges'):
- client.setvar(self, 'namechanges', 0)
- client.setvar(self, 'savedname', self.clean(client.exactName))
-
- cleanedname = self.clean(client.exactName)
- ## also check if the name is ending with '_' (happens with clients having deconnections)
- if cleanedname.endswith('_'+str(client.cid)):
- cleanedname = cleanedname[:-len('_'+str(client.cid))]
-
- if cleanedname != client.var(self, 'savedname').value:
- n = client.var(self, 'namechanges').value + 1
- oldname = client.var(self, 'savedname').value
- client.setvar(self, 'savedname', cleanedname)
- self.debug('%s changed name %s times. His name was %s' %(cleanedname, n, oldname))
- if n > self._checkallowedchanges:
- client.kick('Too many namechanges!')
- else:
- client.setvar(self, 'namechanges', n)
- if self._checkallowedchanges - n < 4:
- r = self._checkallowedchanges - n
- client.message('^1WARNING:^7 ^2%s^7 more namechanges allowed during this map!' % r )
-
- return None
-
- def resetNameChanges(self):
- if self.isEnabled() and self._checkchanges:
- clients = self.console.clients.getList()
- for c in clients:
- if c.isvar(self, 'namechanges'):
- c.setvar(self, 'namechanges', 0)
- self.debug('Namechanges Reset')
- return None
-
-
-#---Vote delayer at round start--------------------------------------------------------------------
- def votedelay(self, data=None):
- if not data:
- data = 'on'
- self.cmd_pavote(data)
-
-
-#---Spectator Checking-----------------------------------------------------------------------------
- def speccheck(self):
- self.debug('Checking for idle Spectators')
- if self.isEnabled() and (self.console.time() > self._ignoreTill) and self._g_maxGameClients == 0 and not self._matchmode:
- clients = self.console.clients.getList()
- if len(clients) < self._smaxplayers:
- self.verbose('Clients online (%s) < maxplayers (%s), ignoring' % (len(clients), self._smaxplayers))
- return None
-
- for c in clients:
- if not c.isvar(self, 'teamtime'):
- self.debug('client has no variable teamtime')
- # 10/22/2008 - 1.4.0b11 - mindriot
- # store the time of teamjoin for autobalancing purposes
- c.setvar(self, 'teamtime', self.console.time())
- self.verbose('Client variable teamtime set to: %s' % c.var(self, 'teamtime').value)
-
- if c.maxLevel >= self._smaxlevel:
- self.debug('%s is allowed to idle in spec.' % c.name)
- continue
- elif c.isvar(self, 'paforced'):
- self.debug('%s is forced by an admin.' % c.name)
- continue
- elif c.team == b3.TEAM_SPEC and ( self.console.time() - c.var(self, 'teamtime').value ) > ( self._smaxspectime * 60 ):
- self.debug('Warning %s for speccing on full server.' % c.name)
- self._adminPlugin.warnClient(c, 'spec')
-
- return None
-
-
-#---Bot support------------------------------------------------------------------------------------
- def botsupport(self, data=None):
- self.debug('Checking for bot support')
- if self.isEnabled() and not self._matchmode:
- try:
- test = self.console.game.mapName
- except:
- self.debug('mapName not yet available')
- return None
-
- if not self._botenable:
- return None
- for m in self._botmaps:
- if m == self.console.game.mapName:
- # we got ourselves a winner
- self.debug('Enabling bots for this map')
- self.botsenable()
-
- return None
-
- def botsdisable(self):
- self.debug('Disabling the bots')
- self.console.write('set bot_minplayers 0')
- return None
-
- def botsenable(self):
- self.debug('Enabling the bots')
- self.console.write('set bot_minplayers %s' %(self._botminplayers))
- return None
-
-
-#---Headshot Counter-------------------------------------------------------------------------------
- def setupVars(self, client):
- if not client.isvar(self, 'totalhits'):
- client.setvar(self, 'totalhits', 0.00)
- if not client.isvar(self, 'totalhitted'):
- client.setvar(self, 'totalhitted', 0.00)
- if not client.isvar(self, 'headhits'):
- client.setvar(self, 'headhits', 0.00)
- if not client.isvar(self, 'headhitted'):
- client.setvar(self, 'headhitted', 0.00)
- if not client.isvar(self, 'helmethits'):
- client.setvar(self, 'helmethits', 0.00)
- if not client.isvar(self, 'torsohitted'):
- client.setvar(self, 'torsohitted', 0.00)
- client.setvar(self, 'hitvars', True)
- self.debug('ClientVars set up for %s' % client.name)
-
- def resetVars(self):
- if self.isEnabled() and self._hsenable:
- clients = self.console.clients.getList()
- for c in clients:
- if c.isvar(self, 'hitvars'):
- c.setvar(self, 'totalhits', 0.00)
- c.setvar(self, 'totalhitted', 0.00)
- c.setvar(self, 'headhits', 0.00)
- c.setvar(self, 'headhitted', 0.00)
- c.setvar(self, 'helmethits', 0.00)
- c.setvar(self, 'torsohitted', 0.00)
- self.debug('ClientVars Reset')
- return None
-
- def headshotcounter(self, attacker, victim, data):
- if self.isEnabled() and self._hsenable and attacker.isvar(self, 'hitvars') and victim.isvar(self, 'hitvars') and not self._matchmode:
-
- headshots = 0
- #damage = int(data[0])
- weapon = int(data[1])
- hitloc = int(data[2])
-
- # set totals
- t = attacker.var(self, 'totalhits').value + 1
- attacker.setvar(self, 'totalhits', t)
- t = victim.var(self, 'totalhitted').value + 1
- victim.setvar(self, 'totalhitted', t)
-
- # headshots... no helmet!
- if hitloc == 0:
- t = attacker.var(self, 'headhits').value + 1
- attacker.setvar(self, 'headhits', t)
- t = victim.var(self, 'headhitted').value + 1
- victim.setvar(self, 'headhitted', t)
-
- # helmethits
- elif hitloc == 1:
- t = attacker.var(self, 'helmethits').value + 1
- attacker.setvar(self, 'helmethits', t)
-
- # torso... no kevlar!
- elif hitloc == 2:
- t = victim.var(self, 'torsohitted').value + 1
- victim.setvar(self, 'torsohitted', t)
-
- # announce headshots
- if self._hsall == True and (hitloc == 0 or hitloc == 1):
- headshots = attacker.var(self, 'headhits').value + attacker.var(self, 'helmethits').value
- hstext = 'headshots'
- if headshots == 1:
- hstext = 'headshot'
-
- percentage = int(headshots / attacker.var(self, 'totalhits').value * 100)
- if self._hspercent == True and headshots > 5 and percentage > self._hspercentmin:
- message = ('^2%s^7: %s %s! ^7(%s percent)' %(attacker.name, int(headshots), hstext, percentage))
- else:
- message = ('^2%s^7: %s %s!' %(attacker.name, int(headshots), hstext))
-
- if self._hsbroadcast == True:
- self.console.write(message)
- else:
- self.console.say(message)
-
- # wear a helmet!
- if self._hswarnhelmet == True and victim.connections < 20 and victim.var(self, 'headhitted').value == self._hswarnhelmetnr and hitloc == 0:
- victim.message('You were hit in the head %s times! Consider wearing a helmet!' % self._hswarnhelmetnr)
-
- # wear kevlar!
- if self._hswarnkevlar == True and victim.connections < 20 and victim.var(self, 'torsohitted').value == self._hswarnkevlarnr and hitloc == 2:
- victim.message('You were hit in the torso %s times! Wearing kevlar will reduce your number of deaths!' % self._hswarnkevlarnr)
-
- return None
-
-#---Rotation Manager-------------------------------------------------------------------------------
- def adjustrotation(self, delta):
- # if the round just started, don't do anything
- if self.console.time() < self._dontcount:
- return None
-
- if delta == +1:
- if self._playercount > (self._switchcount2 + self._hysteresis):
- self.setrotation(3)
- elif self._playercount > (self._switchcount1 + self._hysteresis):
- self.setrotation(2)
- else:
- self.setrotation(1)
-
- elif delta == -1 or delta == 0:
- if self._playercount < (self._switchcount1 + (delta * self._hysteresis)):
- self.setrotation(1)
- elif self._playercount < (self._switchcount2 + (delta * self._hysteresis)):
- self.setrotation(2)
- else:
- self.setrotation(3)
-
- else:
- self.error('Error: Invalid delta passed to adjustrotation')
-
- return None
-
- def setrotation(self, newrotation):
- if not self._gamepath or not self._rotation_small or not self._rotation_medium or not self._rotation_large or not self._mapchanged:
- return None
-
- if newrotation == self._currentrotation:
- return None
-
- if newrotation == 1:
- rotname = "small"
- rotation = self._rotation_small
- elif newrotation == 2:
- rotname = "medium"
- rotation = self._rotation_medium
- elif newrotation == 3:
- rotname = "large"
- rotation = self._rotation_large
- else:
- self.error('Error: Invalid newrotation passed to setrotation.')
- return None
-
- self.debug('Adjusting to %s mapRotation' % rotname)
- self.console.setCvar('g_mapcycle', rotation)
- self._currentrotation = newrotation
-
- def recountplayers(self):
- # reset, recount and set a rotation
- self._oldplayercount = self._playercount
- self._playercount = 0
-
- for p in self.console.clients.getList():
- self._playercount += 1
-
- self.debug('Initial PlayerCount: %s' % (self._playercount))
-
- if self._oldplayercount == -1:
- self.adjustrotation(0)
- elif self._playercount > self._oldplayercount:
- self.adjustrotation(+1)
- elif self._playercount < self._oldplayercount:
- self.adjustrotation(-1)
- else:
- pass
-
-#--Support Functions------------------------------------------------------------------------------
-
- def clean(self, data):
- return re.sub(self._reClean, '', data)[:20]
-
- def ignoreSet(self, data=60):
- """
- Sets the ignoreflag for an amount of seconds
- self._ignoreTill is a plugin flag that holds a time which ignoreCheck checks against
- """
- self._ignoreTill = self.console.time() + data
- return None
-
- def ignoreDel(self):
- self._ignoreTill = 0
- return None
-
- def ignoreCheck(self):
- """
- Tests if the ignore flag is set, to disable certain automatic functions when unwanted
- Returns True if the functionality should be ignored
- """
- if self._ignoreTill - self.console.time() > 0:
- return True
- else:
- return False
-
-
-#--Rcon commands------by:FSK405|Fear--------------------------------------------------------------
-# setnextmap
-# respawngod
-# respawndelay
-# caplimit
-# fraglimit
-# waverespawns
-# bluewave
-# redwave
-# timelimit
-# hotpotato
-
- def cmd_pawaverespawns(self, data, client, cmd=None):
- """\
- - Set waverespawns on, or off.
- """
- if not data or data not in ('on','off'):
- client.message('^7Invalid or missing data, try !help waverespawns')
- return False
- else:
- if data == 'on':
- self.console.setCvar( 'g_waverespawns','1' )
- self.console.say('^7Wave Respawns: ^2ON')
- elif data == 'off':
- self.console.setCvar( 'g_waverespawns','0' )
- self.console.say('^7Wave Respawns: ^9OFF')
- return True
-
- def cmd_pasetnextmap(self, data, client=None, cmd=None):
- """\
- - Set the nextmap (partial map name works)
- """
- if not data:
- client.message('^7Invalid or missing data, try !help setnextmap')
- return False
- else:
- match = self.getMapsSoundingLike(data)
- if len(match) > 1:
- client.message('do you mean : %s ?' % string.join(match,', '))
- return True
- if len(match) == 1:
- mapname = match[0]
- self.console.write('g_nextmap %s' % mapname)
- if client:
- client.message('^7nextmap set to %s' % mapname)
- else:
- client.message('^7cannot find any map like [^4%s^7].' % data)
- return False
-
- def cmd_parespawngod(self, data, client, cmd=None):
- """\
- - Set the respawn protection in seconds.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help respawngod')
- return False
- else:
- self.console.write( 'g_respawnProtection "%s"' % data )
- return True
-
- def cmd_parespawndelay(self, data, client, cmd=None):
- """\
- - Set the respawn delay in seconds.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help respawndelay')
- return False
- else:
- self.console.write( 'g_respawnDelay "%s"' % data )
- return True
-
- def cmd_pacaplimit(self, data, client, cmd=None):
- """\
- - Set the ammount of flagcaps before map is over.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help caplimit')
- return False
- else:
- self.console.write( 'capturelimit "%s"' % data )
- return True
-
- def cmd_patimelimit(self, data, client, cmd=None):
- """\
- - Set the minutes before map is over.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help timelimit')
- return False
- else:
- self.console.write( 'timelimit "%s"' % data )
- return True
-
- def cmd_pafraglimit(self, data, client, cmd=None):
- """\
- - Set the ammount of points to be scored before map is over.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help fraglimit')
- return False
- else:
- self.console.write( 'fraglimit "%s"' % data )
- return True
-
- def cmd_pabluewave(self, data, client, cmd=None):
- """\
- - Set the blue wave respawn time.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help bluewave')
- return False
- else:
- self.console.write( 'g_bluewave "%s"' % data )
- return True
-
- def cmd_paredwave(self, data, client, cmd=None):
- """\
- - Set the red wave respawn time.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help redwave')
- return False
- else:
- self.console.write( 'g_redwave "%s"' % data )
- return True
-
- def cmd_pahotpotato(self, data, client, cmd=None):
- """\
- - Set the flag explode time.
- """
- if not data:
- client.message('^7Invalid or missing data, try !help hotpotato')
- return False
- else:
- self.console.write( 'g_hotpotato "%s"' % data )
- return True
-
-
-
- def cmd_pamap(self, data, client, cmd=None):
- """\
-