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): - """\ - - switch current map - """ - if not data: - client.message('^7You must supply a map to change to.') - return - 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] - else: - client.message('^7cannot find any map like [^4%s^7].' % data) - return False - - self.console.say('^7Changing map to %s' % mapname) - time.sleep(1) - self.console.write('map %s' % mapname) - return True - - - def getMapsSoundingLike(self, mapname): - maplist = self.console.getMaps() - data = mapname.strip() - - soundex1 = soundex(string.replace(string.replace(data, 'ut4_',''), 'ut_','')) - #self.debug('soundex %s : %s' % (data, soundex1)) - - match = [] - if data in maplist: - match = [data] - else: - for m in maplist: - s = soundex(string.replace(string.replace(m, 'ut4_',''), 'ut_','')) - #self.debug('soundex %s : %s' % (m, s)) - if s == soundex1: - #self.debug('probable map : %s', m) - match.append(m) - - if len(match) == 0: - # suggest closest spellings - shortmaplist = [] - for m in maplist: - if m.find(data) != -1: - shortmaplist.append(m) - if len(shortmaplist) > 0: - shortmaplist.sort(key=lambda map: levenshteinDistance(data, string.replace(string.replace(map.strip(), 'ut4_',''), 'ut_',''))) - self.debug("shortmaplist sorted by distance : %s" % shortmaplist) - match = shortmaplist[:3] - else: - maplist.sort(key=lambda map: levenshteinDistance(data, string.replace(string.replace(map.strip(), 'ut4_',''), 'ut_',''))) - self.debug("maplist sorted by distance : %s" % maplist) - match = maplist[:3] - return match diff --git a/extplugins/poweradminurt/iourt42.py b/extplugins/poweradminurt/iourt42.py deleted file mode 100644 index f3cdf19..0000000 --- a/extplugins/poweradminurt/iourt42.py +++ /dev/null @@ -1,187 +0,0 @@ -# -# PowerAdmin Plugin for BigBrotherBot(B3) (www.bigbrotherbot.net) -# Copyright (C) 2012 Thomas LEVEIL self.getTime(): - self.debug("ignoring radio event") - return - - points = 0 - data = repr(event.data) - last_message_data = client.var(self, 'last_radio_data').value - - now = self.getTime() - last_radio_time = client.var(self, 'last_radio_time', None).value - gap = None - if last_radio_time is not None: - gap = now - last_radio_time - if gap < 20: - points += 1 - if gap < 2: - points += 1 - if data == last_message_data: - points += 3 - if gap < 1: - points += 3 - - spamins = client.var(self, 'radio_spamins', 0).value + points - - # apply natural points decrease due to time - if gap is not None: - spamins -= int(gap / self._rsp_falloffRate) - - if spamins < 1: - spamins = 0 - - # set new values - self.verbose("radio_spamins for %s : %s" % (client.name, spamins)) - client.setvar(self, 'radio_spamins', spamins) - client.setvar(self, 'last_radio_time', now) - client.setvar(self, 'last_radio_data', data) - - # should we warn ? - if spamins >= self._rsp_maxSpamins: - self.console.writelines(["mute %s %s" % (client.cid, self._rsp_mute_duration)]) - client.setvar(self, 'radio_spamins', int(self._rsp_maxSpamins / 2.0)) - client.setvar(self, 'radio_ignore_till', int(self.getTime() + self._rsp_mute_duration - 1)) - - - ############################################################################################### - # - # commands - # - ############################################################################################### - - def cmd_pakill(self, data, client, cmd=None): - """\ - - kill a player. - (You can safely use the command without the 'pa' at the beginning) - """ - if not data: - client.message('^7Invalid data, try !help pakill') - return - else: - sclient = self._adminPlugin.findClientPrompt(data, 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 - - self.console.write('smite %s' % sclient.cid) - - - - def cmd_palms(self, data, client, cmd=None): - """\ - Change game type to Last Man Standing - (You can safely use the command without the 'pa' at the beginning) - """ - self.console.write('g_gametype 1') - if client: - client.message('^7game type changed to ^4Last Man Standing') - self.set_configmode('lms') - - - - ############################################################################################### - # - # Other methods - # - ############################################################################################### - - def getTime(self): - """ just to ease automated tests """ - return self.console.time() \ No newline at end of file diff --git a/parser-documentation.txt b/parser-documentation.txt deleted file mode 100644 index fe1c5c0..0000000 --- a/parser-documentation.txt +++ /dev/null @@ -1,80 +0,0 @@ -================================================================================ -Damage --------------------------------------------------------------------------------- -#Hit: 13 10 0 8: Grover hit jacobdk92 in the Head -#Hit: VictimCid AttackerCid hitLoc WeaponIndex: - -hitLoc index: --------------------------------------------------------------------------------- -head 0 -helmet 1 -torso 2 -kevlar 3 -arms 4 -legs 5 -... 6 - - -Weaponindex: --------------------------------------------------------------------------------- -KNIFE 1 -BERETTA 2 -DEAGLE 3 -SPAS12 4 -MP5K 5 -UMP45 6 -HK69 7 -M4 8 -G36 9 -PSG1 10 -GRENADE_HE 11 -NEGEV 17 -GRENADE_SMOKE 13 -SR8 14 -AK103 15 - -================================================================================ -Kills --------------------------------------------------------------------------------- -#Kill: 14 4 21: Qst killed Leftovercrack by UT_MOD_PSG1 -#kill: AtackerCiD VictimCid WeaponIndex: - -WeaponIndex: --------------------------------------------------------------------------------- -Unknown 0 -Drowning 1 -Got Slimed 2 -Meltdown 3 -Crushed 4 -Telefragged 5 -Doing the Lemming thing 6 -Suicide 7 -Laser Target 8 -Damage by triggers 9 -Changing Team 10 -Cut by Knife 12 -Thrown Knife 13 -Beretta 14 -Desert Eagle 15 -Spas 12 16 -UMP 45 17 -MP5K 18 -LR300 19 -G36 20 -PSG1 21 -HK 69 11 -Excessive Bloodloss 23 -HK 69 hit 37 -High Explosive Grenade 25 -Flash Grenade 26 -Smoke Grenade 27 -SR8 28 -Sacrificed his life 29 -AK 103 30 -Exploded 31 -Bitchslapped 32 -Negev 35 -M4 38 -Got kicked 24 -================================================================================ - diff --git a/poweradminurt/__init__.py b/poweradminurt/__init__.py new file mode 100644 index 0000000..0c94aa4 --- /dev/null +++ b/poweradminurt/__init__.py @@ -0,0 +1,45 @@ +# +# 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 +# + +__version__ = '1.24' +__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): + + requiresConfigFile = True + requiresPlugins = [] + requiresParsers = ['iourt41', 'iourt42'] + + 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/conf/poweradminurt.ini b/poweradminurt/conf/plugin_poweradminurt.ini similarity index 90% rename from extplugins/conf/poweradminurt.ini rename to poweradminurt/conf/plugin_poweradminurt.ini index 8f9b3f1..ed9eabb 100644 --- a/extplugins/conf/poweradminurt.ini +++ b/poweradminurt/conf/plugin_poweradminurt.ini @@ -20,7 +20,7 @@ paexec: 100 pateams-teams: 2 paskuffle-sk: 20 paunskuffle-unsk: 60 -paadvise-adv: 2 +paadvise-advise: 2 pabalance-bal: 2 paautoskuffle-ask: 60 paswap-swap: 20 @@ -61,8 +61,15 @@ pasetgravity-setgravity: 40 #### commands below only work with UrT 4.2 #### pakill-kill: 20 ; kill a player -palms-lms: 20 ; change game type to Last Man Standing - +palms-lms: 60 ; change game type to Last Man Standing +pajump-jump: 60 ; change game type to Jump +pafreeze-freeze: 60 ; change game type to Freeze Tag +pagoto-goto: 60 ; set the goto +paskins-skins: 60 ; set the use of client skins +pafunstuff-funstuff: 60 ; set the use of funstuff +pastamina-stamina: 60 ; set the stamina behavior +pacaptain-captain: 60 ; set the given client as the captain for its team +pasub-sub: 60 ; set the given client as a substitute for its team [namechecker] @@ -128,6 +135,7 @@ maxplayers: 14 # maxlevel and above won't be handled so they may idle in spec maxlevel: 20 + [moonmode] # gravity_on : the gravity to swith to in moon mode (default:100) gravity_on: 100 @@ -152,6 +160,7 @@ dicfile: @conf/dicfile.txt # Plugins that need to be disabled during matchmode (separated by a comma) plugins_disable: adv, tk, pingwatch, voting2g, censor, spamcontrol, follow, flagstats, spree + [matchmode_configs] # name of config files to run when match config is set to on/off matchon: match_config_on.cfg @@ -159,6 +168,8 @@ matchoff: match_config_off.cfg # config files for different game types mode_ctf: config_ctf.cfg mode_ts: config_ts.cfg +mode_jump: config_jump.cfg ; the jump mode is only available for UrT 4.2.006+ + [botsupport] # BOTSUPPORT IS VERY UNSTABLE! IT MAY CRASH YOUR SERVER PLENTY! diff --git a/extplugins/dicfile.txt b/poweradminurt/dicfile.txt similarity index 100% rename from extplugins/dicfile.txt rename to poweradminurt/dicfile.txt diff --git a/extplugins/poweradminurt/iourt41.py b/poweradminurt/iourt41.py similarity index 57% rename from extplugins/poweradminurt/iourt41.py rename to poweradminurt/iourt41.py index b5e6c67..760b036 100644 --- a/extplugins/poweradminurt/iourt41.py +++ b/poweradminurt/iourt41.py @@ -15,20 +15,25 @@ # 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 import b3.events import b3.plugin import b3.cron -from poweradminurt import __version__, __author__ +from poweradminurt import __version__ +from poweradminurt import __author__ + +import time +import thread +import threading +import re 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 @@ -37,11 +42,11 @@ class Poweradminurt41Plugin(b3.plugin.Plugin): _adminPlugin = None _ignoreTill = 0 - _checkdupes = False - _checkunknown = False - _checkbadnames = False - _checkchanges = False - _checkallowedchanges = 0 + _checkdupes = True + _checkunknown = True + _checkbadnames = True + _checkchanges = True + _checkallowedchanges = 7 _ncronTab = None _tcronTab = None _scronTab = None @@ -50,16 +55,20 @@ class Poweradminurt41Plugin(b3.plugin.Plugin): _tinterval = 0 _sinterval = 0 _skinterval = 0 - _minbalinterval = 0 # minimum time in minutes between !bal or !sk for non-mods - _lastbal = 0 # time since last !bal or !sk + _minbalinterval = 2 # minimum time in minutes between !bal or !sk for non-mods + _lastbal = 0 # time since last !bal or !sk _oldadv = (None, None, None) _teamred = 0 _teamblue = 0 - _teamdiff = 0 + _teamdiff = 1 + _skilldiff = 0.5 + _skill_balance_mode = 0 _balancing = False _origvote = 0 _lastvote = 0 _votedelay = 0 + _tmaxlevel = 20 + _announce = 2 _smaxspectime = 0 _smaxlevel = 0 _smaxplayers = 0 @@ -70,9 +79,9 @@ class Poweradminurt41Plugin(b3.plugin.Plugin): _botenable = False _botskill = 4 _botminplayers = 4 - _botmaps = {} + _botmaps = [] _hsenable = False - _hsresetvars = True + _hsresetvars = 'map' _hsbroadcast = True _hsall = True _hspercent = True @@ -95,25 +104,61 @@ class Poweradminurt41Plugin(b3.plugin.Plugin): _rotation_large = '' _gamepath = '' _origgear = 0 + _randnum = 0 + _pass_lines = None + _papublic_password = None + _match_plugin_disable = [] + _gameconfig = {} _team_change_force_balance_enable = True _teamLocksPermanent = False _autobalance_gametypes = 'tdm' _autobalance_gametypes_array = [] - _max_dic_size = 512000 #max dictionary size in bytes - _slapSafeLevel = None + _max_dic_size = 512000 # max dictionary size in bytes + _moon_on_gravity = 100 + _moon_off_gravity = 800 + _slapSafeLevel = 60 _ignorePlus = 30 - _full_ident_level = 20 + _full_ident_level = 60 + _killhistory = [] + _hitlocations = {} + + def __init__(self, console, config=None): + + b3.plugin.Plugin.__init__(self, console, config) + if self.console.gameName != 'iourt41': + self.critical("unsupported game : %s" % self.console.gameName) + raise SystemExit(220) + + ### hit location constants ### - # hit locations - _hitloc_head = 0 - _hitloc_helmet = 1 - _hitloc_torso = 2 + try: + self._hitlocations['HL_HEAD'] = self.console.HL_HEAD + except AttributeError, e: + self._hitlocations['HL_HEAD'] = '0' + self.warning("could not get HL_HEAD value from B3 parser: %s" % e) + + self.debug("HL_HEAD is %s" % self._hitlocations['HL_HEAD']) + + try: + self._hitlocations['HL_HELMET'] = self.console.HL_HELMET + except AttributeError, e: + self._hitlocations['HL_HELMET'] = '1' + self.warning("could not get HL_HELMET value from B3 parser: %s" % e) + + self.debug("HL_HELMET is %s" % self._hitlocations['HL_HELMET']) + + try: + self._hitlocations['HL_TORSO'] = self.console.HL_TORSO + except AttributeError, e: + self._hitlocations['HL_TORSO'] = '2' + self.warning("could not get HL_TORSO value from B3 parser: %s" % e) - def startup(self): - """\ + self.debug("HL_TORSO is %s" % self._hitlocations['HL_TORSO']) + + def onStartup(self): + """ Initialize plugin settings """ - # get the admin plugin so we can register commands self._adminPlugin = self.console.getPlugin('admin') if not self._adminPlugin: @@ -133,12 +178,13 @@ def startup(self): 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 + # register our events self.registerEvents() - # Create event + # create event self.createEvent('EVT_CLIENT_PUBLIC', 'Server Public Mode Changed') # don't run cron-checks on startup @@ -146,518 +192,737 @@ def startup(self): self._balancing = False self._killhistory = [] - # save original vote settings try: + # save original vote settings self._origvote = self.console.getCvar('g_allowvote').getInt() - except: - self._origvote = 0 # no votes + except ValueError, e: + self.warning("could not retrieve g_allowvote CVAR value: %s" % e) + self._origvote = 0 # no votes - # if by any chance on botstart g_allowvote is 0, we'll use the default UrT value + # 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: + # save original gear settings self._origgear = self.console.getCvar('g_gear').getInt() - except: - self._origgear = 0 # allow all weapons + except ValueError, e: + if self.console.gameName == 'iourt41': + # if the game is iourt42 don't log since the above cvar retrieval + # is going to raise an exception everytime: iourt42 uses a gear + # string instead of gear bitmask so int casting will raise a ValueError + self.warning("could not retrieve g_gear CVAR value: %s" % e) + self._origgear = 0 # allow all weapons self.installCrontabs() self.debug('Started') - def registerEvents(self): + """ + Register events needed + """ 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) - self.registerEvent(b3.events.EVT_CLIENT_KILL) - self.registerEvent(b3.events.EVT_CLIENT_KILL_TEAM) - self.registerEvent(b3.events.EVT_CLIENT_ACTION) - + self.registerEvent(self.console.getEventID('EVT_GAME_ROUND_START')) + self.registerEvent(self.console.getEventID('EVT_GAME_EXIT')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_AUTH')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_DISCONNECT')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_TEAM_CHANGE')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_DAMAGE')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_NAME_CHANGE')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_KILL')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_KILL_TEAM')) + self.registerEvent(self.console.getEventID('EVT_CLIENT_ACTION')) + self.registerEvent(self.console.getEventID('EVT_GAME_MAP_CHANGE')) def onLoadConfig(self): - self.LoadNameChecker() - self.LoadTeamBalancer() - self.LoadVoteDelayer() - self.LoadSpecChecker() - self.LoadSkillBalancer() - self.LoadMoonMode() - self.LoadPublicMode() - self.LoadMatchMode() - self.LoadBotSupport() - self.LoadHeadshotCounter() - self.LoadRotationManager() - self.LoadSpecial() - - def LoadNameChecker(self): - # NAMECHECKING SETUP + """ + Load the plugin configuration + """ + self.loadNameChecker() + self.loadTeamBalancer() + self.loadVoteDelayer() + self.loadSpecChecker() + self.loadSkillBalancer() + self.loadMoonMode() + self.loadPublicMode() + self.loadMatchMode() + self.loadBotSupport() + self.loadHeadshotCounter() + self.loadRotationManager() + self.loadSpecial() + + def loadNameChecker(self): + """ + Setup the name checker + """ try: self._ninterval = self.config.getint('namechecker', 'ninterval') - except: - self._ninterval = 0 - self.debug('Using default value (%s) for Names Interval', self._ninterval) + except ConfigParser.NoOptionError: + self.warning('Could not find namechecker/ninterval in config file, using default: %s' % self._ninterval) + except ValueError, e: + self.error('Could not load namechecker/ninterval config value: %s' % e) + self.debug('Using default value (%s) for namechecker/ninterval' % self._ninterval) - # set a max interval for namechecker + # clamp name checker interval 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) + except ConfigParser.NoOptionError: + self.warning('Could not find namechecker/checkdupes in config file, using default: %s' % self._checkdupes) + except ValueError, e: + self.error('Could not load namechecker/checkdupes config value: %s' % e) + self.debug('Using default value (%s) for namechecker/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) + except ConfigParser.NoOptionError: + self.warning('Could not find namechecker/checkunknown in config file, using default: %s' % + self._checkunknown) + except ValueError, e: + self.error('Could not load namechecker/checkunknown config value: %s' % e) + self.debug('Using default value (%s) for namechecker/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) + except ConfigParser.NoOptionError: + self.warning('Could not find namechecker/checkbadnames in config file, using default: %s' % + self._checkbadnames) + except ValueError, e: + self.error('Could not load namechecker/checkbadnames config value: %s' % e) + self.debug('Using default value (%s) for namechecker/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) + except ConfigParser.NoOptionError: + self.warning('Could not find namechecker/checkchanges in config file, using default: %s' % + self._checkchanges) + except ValueError, e: + self.error('Could not load namechecker/checkchanges config value: %s' % e) + self.debug('Using default value (%s) for namechecker/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 + self._checkallowedchanges = self.config.getint('namechecker', 'checkallowedchanges') + except ConfigParser.NoOptionError: + self.warning('Could not find namechecker/checkallowedchanges in config file, using default: %s' % + self._checkallowedchanges) + except ValueError, e: + self.error('Could not load namechecker/checkallowedchanges config value: %s' % e) + self.debug('Using default value (%s) for namechecker/checkallowedchanges' % self._checkallowedchanges) + + self.debug('Name checker interval: %s' % self._ninterval) + self.debug('Check bad names: %s' % self._checkbadnames) + self.debug('Check duplicate names: %s' % self._checkdupes) + self.debug('Check unknown names: %s' % self._checkunknown) + self.debug('Check name changes: %s' % self._checkchanges) + self.debug('Max. allowed name changes per game: %s' % self._checkallowedchanges) + + def loadTeamBalancer(self): + """ + Setup the teambalancer + """ try: self._tinterval = self.config.getint('teambalancer', 'tinterval') - except: - self._tinterval = 0 - self.debug('Using default value (%s) for Teambalancer Interval', self._tinterval) + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/tinterval in config file, using default: %s' % self._tinterval) + except ValueError, e: + self.error('Could not load teambalancer/tinterval config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/tinterval' % self._tinterval) - # set a max interval for teamchecker + # clamp team balancer interval 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) + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/teamdifference in config file, using default: %s' % + self._teamdiff) + except ValueError, e: + self.error('Could not load teambalancer/teamdifference config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/teamdifference' % self._teamdiff) + # set a minimum/maximum teamdifference if self._teamdiff < 1: self._teamdiff = 1 - if self._teamdiff > 9: + elif 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) + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/maxlevel in config file, using default: %s' % self._tmaxlevel) + except ValueError, e: + self.error('Could not load teambalancer/maxlevel config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/maxlevel' % 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)) + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/announce in config file, using default: %s' % self._announce) + except ValueError, e: + self.error('Could not load teambalancer/announce config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/announce' % 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/21/2008 - 1.4.0b9 - mindriot + self._team_change_force_balance_enable = self.config.getboolean('teambalancer', + 'team_change_force_balance_enable') + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/team_change_force_balance_enable in config file, \ + using default: %s' % self._team_change_force_balance_enable) + except ValueError, e: + self.error('Could not load teambalancer/team_change_force_balance_enable config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/team_change_force_balance_enable' % + self._team_change_force_balance_enable) - # 10/22/2008 - 1.4.0b10 - mindriot try: + # 10/22/2008 - 1.4.0b10 - mindriot 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) + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/autobalance_gametypes in config file, ' + 'using default: %s' % 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) + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/teamLocksPermanent in config file, ' + 'using default: %s' % self._teamLocksPermanent) + except ValueError, e: + self.error('Could not load teambalancer/teamLocksPermanent config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/teamLocksPermanent' % self._teamLocksPermanent) try: self._ignorePlus = self.config.getint('teambalancer', 'timedelay') - except: - self.debug('Using default value (%s) for timedelay', self._ignorePlus) - - def LoadSkillBalancer(self): - # SKILLBALANCER SETUP + except ConfigParser.NoOptionError: + self.warning('Could not find teambalancer/timedelay in config file, using default: %s' % self._ignorePlus) + except ValueError, e: + self.warning('Could not load teambalancer/timedelay config value: %s' % e) + self.debug('Using default value (%s) for teambalancer/timedelay' % self._ignorePlus) + + self.debug('Teambalance interval: %s' % self._tinterval) + self.debug('Teambalance difference: %s' % self._teamdiff) + self.debug('Teambalance max level: %s' % self._tmaxlevel) + self.debug('Teambalance announce: %s' % self._announce) + self.debug('Team change force balance enable: %s' % self._team_change_force_balance_enable) + self.debug('Team locks permanent: %s' % self._teamLocksPermanent) + self.debug('Time delay: %s' % self._ignorePlus) + self.debug('Autobalance gametypes: %s' % self._autobalance_gametypes) + + def loadSkillBalancer(self): + """ + Setup the skill balancer + """ try: self._skinterval = self.config.getint('skillbalancer', 'interval') - except: - self._skinterval = 0 - self.debug('Using default value (%s) for Skillbalancer Interval', self._skinterval) + except ConfigParser.NoOptionError: + self.warning('Could not find skillbalancer/interval in config file, using default: %s' % self._skinterval) + except ValueError, e: + self.error('Could not load skillbalancer/interval config value: %s' % e) + self.debug('Using default value (%s) for skillbalancer/interval' % self._skinterval) - # set a max interval for skillchecker + # clamp skill balancer interval if self._skinterval > 59: self._skinterval = 59 try: - self._skilldiff = self.config.getint('skillbalancer', 'difference') - except: - self._skilldiff = 0.5 - self.debug('Using default value (%s) for skilldiff', self._skilldiff) - # set a minimum/maximum teamdifference + self._skilldiff = self.config.getfloat('skillbalancer', 'difference') + except ConfigParser.NoOptionError: + self.warning('Could not find skillbalancer/difference in config file, using default: %s' % self._skilldiff) + except ValueError, e: + self.error('Could not load skillbalancer/difference config value: %s' % e) + self.debug('Using default value (%s) for skillbalancer/difference' % self._skilldiff) + + # clamp skill difference if self._skilldiff < 0.1: self._skilldiff = 0.1 - if self._skilldiff > 9: + elif self._skilldiff > 9: self._skilldiff = 9 try: self._skill_balance_mode = self.config.getint('skillbalancer', 'mode') - except: - self._skill_balance_mode = 0 - self.debug('Using default value (%s) for skill_balance_mode', self._skill_balance_mode) + except ConfigParser.NoOptionError: + self.warning('Could not find skillbalancer/mode in config file, using default: %s' % + self._skill_balance_mode) + except ValueError, e: + self.error('Could not load skillbalancer/mode config value: %s' % e) + self.debug('Using default value (%s) for skillbalancer/mode' % self._skill_balance_mode) try: self._minbalinterval = self.config.getint('skillbalancer', 'min_bal_interval') - except: - self._minbalinterval = 2 - self.debug('Using default value (%s) for Skillbalancer Manual Balance Interval', self._minbalinterval) - - def LoadVoteDelayer(self): - #VOTEDELAYER SETUP + except ConfigParser.NoOptionError: + self.warning('Could not find skillbalancer/min_bal_interval in config file, using default: %s' % + self._minbalinterval) + except ValueError, e: + self.warning('Could not load skillbalancer/min_bal_interval config value: %s' % e) + self.debug('Using default value (%s) for skillbalancer/min_bal_interval' % self._minbalinterval) + + self.debug('Skillbalance interval: %s' % self._skinterval) + self.debug('Skillbalance difference: %s' % self._skilldiff) + self.debug('Skillbalance mode: %s' % self._skill_balance_mode) + self.debug('Minimum skillbalance interval: %s' % self._minbalinterval) + + def loadVoteDelayer(self): + """ + Setup the vote delayer + """ try: self._votedelay = self.config.getint('votedelay', 'votedelay') - except: - self._votedelay = 0 - self.debug('Using default value (%s) for Vote delayer', self._votedelay) + except ConfigParser.NoOptionError: + self.warning('Could not find votedelay/votedelay in config file, using default: %s' % self._votedelay) + except ValueError, e: + self.warning('Could not load votedelay/votedelay config value: %s' % e) + self.debug('Using default value (%s) for votedelay/votedelay' % 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 + self.debug('Vote delay: %s' % self._votedelay) + + def loadSpecChecker(self): + """ + Setup the spec checker + """ try: self._sinterval = self.config.getint('speccheck', 'sinterval') - except: - self._sinterval = 0 - self.debug('Using default value (%s) for speccheck interval', self._sinterval) + except ConfigParser.NoOptionError: + self.warning('Could not find speccheck/sinterval in config file, using default: %s' % self._sinterval) + except ValueError, e: + self.error('Could not load speccheck/sinterval config value: %s' % e) + self.debug('Using default value (%s) for speccheck/sinterval' % 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) + except ConfigParser.NoOptionError: + self.warning('Could not find speccheck/maxspectime in config file, using default: %s' % self._smaxspectime) + except ValueError, e: + self.error('Could not load speccheck/maxspectime e config value: %s' % e) + self.debug('Using default value (%s) for speccheck/maxspectime ' % self._smaxspectime) + try: + # loading spec max level configuration value self._smaxlevel = self.config.getint('speccheck', 'maxlevel') - except: - self._smaxlevel = 0 - self.debug('Using default value (%s) for speccheck maxlevel', self._smaxlevel) + except ConfigParser.NoOptionError: + self.warning('Could not find speccheck/maxlevel in config file, using default: %s' % self._smaxlevel) + except ValueError, e: + self.error('Could not load speccheck/maxlevel config value: %s' % e) + 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 + except (ConfigParser.NoOptionError, ValueError), e: + if isinstance(e, ConfigParser.NoOptionError): + self.warning('Could not find speccheck/maxplayers in config file') + elif isinstance(e, ValueError): + self.error('Could not load speccheck/maxplayers config value: %s' % e) + + # load default value according to server configuration + maxclients = self.console.getCvar('sv_maxclients').getInt() + pvtclients = self.console.getCvar('sv_privateClients').getInt() + self._smaxplayers = maxclients - pvtclients + 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 spec time: %s' % self._smaxspectime) + self.debug('Speccheck max level: %s' % self._smaxlevel) + self.debug('Spec max players: %s' % self._smaxplayers) + + def loadMoonMode(self): + """ + Setup the moon mode + """ 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) + except ConfigParser.NoOptionError: + self.warning('Could not find moonmode/gravity_on in config file, using default: %s' % self._moon_on_gravity) + except ValueError, e: + self.error('Could not load moonmode/gravity_on config value: %s' % e) + self.debug('Using default value (%s) for moonmode/gravity_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) + except ConfigParser.NoOptionError: + self.warning('Could not find moonmode/gravity_off in config file, using default: %s' % + self._moon_off_gravity) + except ValueError, e: + self.error('Could not load moonmode/gravity_off config value: %s' % e) + self.debug('Using default value (%s) for moonmode/gravity_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 + def loadPublicMode(self): + """ + Setup the public mode + """ try: - self.randnum = self.config.getint('publicmode','randnum') - except: - self.randnum = 0 + self._randnum = self.config.getint('publicmode', 'randnum') + except ConfigParser.NoOptionError: + self.warning('Could not find publicmode/randnum in config file, using default: %s' % self._randnum) + except ValueError, e: + self.error('Could not load publicmode/randnum config value: %s' % e) + self.debug('Using default value (%s) for publicmode/randnum' % self._randnum) try: - self.pass_lines = None + try: - padic = self.config.getboolean('publicmode','usedic') + 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) + self.warning('Could not find publicmode/usedic in config file, using default: %s' % padic) + except ValueError, e: padic = False + self.error('Could not load publicmode/usedic config value: %s' % e) + self.debug('Using default value (%s) for publicmode/usedic' % padic) if padic: - padicfile = self.config.getpath('publicmode','dicfile') + + 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.') + self.warning('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.') + self.warning('dictionary file is empty: switching to default') else: - self.pass_lines = text.splitlines() - self.debug('Using dictionary password.') + 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') + self.warning('dictionary is enabled but the file doesn\'t exists: switching to default') + + except Exception, e: + self.error('Could not load dictionary config: %s' % e) + self.debug('using default dictionary') 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 = [] + self.warning('could not setup papublic command because there is no password set in config') + + except ConfigParser.NoOptionError: + self.debug('could not setup papublic command because there is no password set in config') + + self.debug('papublic password set to : %s' % self._papublic_password) + + def loadMatchMode(self): + """ + Setup the match mode + """ try: + # load a list of plugins to be disabled/enabled upon match mode enabled/disabled 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] + 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") + self.warning('Could not find matchmode/plugins_disable in config file') - self.gameconfig = {} try: + + # load all the configuration files into a dict for key, value in self.config.items('matchmode_configs'): - self.gameconfig[key] = value - except: - self.warning('Can\'t read matchmode_configs') + self._gameconfig[key] = value + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, KeyError), e: + self.warning('could not read matchmode configs: %s' % e) - def LoadBotSupport(self): - # BOT SUPPORT SETUP + def loadBotSupport(self): + """ + Setup the bot support + """ try: self._botenable = self.config.getboolean('botsupport', 'bot_enable') - except: - self._botenable = False - self.debug('Using default value (%s) for bot enable', self._botenable) + except ConfigParser.NoOptionError: + self.warning('Could not find botsupport/bot_enable in config file, using default: %s' % self._botenable) + except ValueError, e: + self.error('Could not load botsupport/bot_enable config value: %s' % e) + self.debug('Using default value (%s) for botsupport/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) + except ConfigParser.NoOptionError: + self.warning('Could not find botsupport/bot_skill in config file, using default: %s' % self._botskill) + except ValueError, e: + self.error('Could not load botsupport/bot_skill config value: %s' % e) + self.debug('Using default value (%s) for botsupport/bot_skill' % self._botskill) + + # clamp botskill value + if self._botskill > 5: + self._botskill = 5 + elif self._botskill < 1: + self._botskill = 1 + 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) + except ConfigParser.NoOptionError: + self.warning('Could not find botsupport/bot_minplayers in config file, ' + 'using default: %s' % self._botminplayers) + except ValueError, e: + self.error('Could not load botsupport/bot_minplayers config value: %s' % e) + self.debug('Using default value (%s) for botsupport/bot_minplayers' % self._botminplayers) + + # clamp botminplayers value + if self._botminplayers > 16: + self._botminplayers = 16 + elif self._botminplayers < 0: + self._botminplayers = 0 + try: maps = self.config.get('botsupport', 'bot_maps') maps = maps.split(' ') self._botmaps = maps - except: - self._botmaps = {} - self.debug('No maps for botsupport...') + except ConfigParser.NoOptionError: + self.debug('no map specified 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.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)) + 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 + def loadHeadshotCounter(self): + """ + Setup the headshot counter + """ try: self._hsenable = self.config.getboolean('headshotcounter', 'hs_enable') - except: - self._hsenable = False - self.debug('Using default value (%s) for hs_enable', self._hsenable) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/hs_enable in config file, using default: %s' % self._hsenable) + except ValueError, e: + self.error('Could not load headshotcounter/hs_enable config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + raise KeyError('configuration setting not valid') + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/reset_vars in config file, using default: %s' % + self._hsresetvars) + except KeyError, e: + self.error('Could not load headshotcounter/reset_vars config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/broadcast in config file, using default: %s' % + self._hsbroadcast) + except ValueError, e: + self.error('Could not load headshotcounter/broadcast config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/announce_all in config file, using default: %s' % self._hsall) + except ValueError, e: + self.error('Could not load headshotcounter/announce_all config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/announce_percentages in config file, using default: %s' % + self._hspercent) + except ValueError, e: + self.error('Could not load headshotcounter/announce_percentages config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/percent_min in config file, using default: %s' % + self._hspercentmin) + except ValueError, e: + self.error('Could not load headshotcounter/percent_min config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/warn_helmet in config file, using default: %s' % + self._hswarnhelmet) + except ValueError, e: + self.error('Could not load headshotcounter/warn_helmet config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/warn_helmet_nr in config file, using default: %s' % + self._hswarnhelmetnr) + except ValueError, e: + self.error('Could not load headshotcounter/warn_helmet_nr config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/warn_kevlar in config file, using default: %s' % + self._hswarnkevlar) + except ValueError, e: + self.error('Could not load headshotcounter/warn_kevlar config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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) + except ConfigParser.NoOptionError: + self.warning('Could not find headshotcounter/warn_kevlar_nr in config file, using default: %s' % + self._hswarnkevlarnr) + except ValueError, e: + self.error('Could not load headshotcounter/warn_kevlar_nr config value: %s' % e) + self.debug('Using default value (%s) for headshotcounter/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 + 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): + """ + Setup the rotation manager + """ try: self._rmenable = self.config.getboolean('rotationmanager', 'rm_enable') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/rm_enable in config file, using default: %s' % self._rmenable) + except ValueError, e: + self.error('Could not load rotationmanager/rm_enable config value: %s' % e) + self.debug('Using default value (%s) for rotationmanager/rm_enable' % self._rmenable) + if self._rmenable: + try: self._switchcount1 = self.config.getint('rotationmanager', 'switchcount1') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/switchcount1 in config file, using default: %s' % + self._switchcount1) + except ValueError, e: + self.error('Could not load rotationmanager/switchcount1 config value: %s' % e) + self.debug('Using default value (%s) for rotationmanager/switchcount1' % self._switchcount1) + try: - self._switchcount2 = self.config.getint('rotationmanager', 'switchcount2') - except: - pass + self._switchcount2 = self.config.getint('rotationmanager', 'switchcount3') + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/switchcount2 in config file, using default: %s' % + self._switchcount2) + except ValueError, e: + self.error('Could not load rotationmanager/switchcount2 config value: %s' % e) + self.debug('Using default value (%s) for rotationmanager/switchcount2' % self._switchcount2) + try: self._hysteresis = self.config.getint('rotationmanager', 'hysteresis') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/hysteresis in config file, using default: %s' % + self._hysteresis) + except ValueError, e: + self.error('Could not load rotationmanager/hysteresis config value: %s' % e) + self.debug('Using default value (%s) for rotationmanager/hysteresis' % self._hysteresis) + try: self._rotation_small = self.config.get('rotationmanager', 'smallrotation') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/smallrotation in config file') + try: self._rotation_medium = self.config.get('rotationmanager', 'mediumrotation') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/mediumrotation in config file') + try: self._rotation_large = self.config.get('rotationmanager', 'largerotation') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/largerotation in config file') + try: self._gamepath = self.config.get('rotationmanager', 'gamepath') - except: - pass + except ConfigParser.NoOptionError: + self.warning('Could not find rotationmanager/gamepath in config file') 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)) + 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) + self.debug('Game path: %s' % self._gamepath) else: self.debug('Rotation Manager is disabled') - - def LoadSpecial(self): + def loadSpecial(self): + """ + Setup special configs + """ try: self._slapSafeLevel = self.config.getint('special', 'slap_safe_level') - except Exception, err: - self._slapSafeLevel = 60 - self.warning('Using default value (%s) for slap_safe_level. %s' % (self._slapSafeLevel, err)) + except ConfigParser.NoOptionError: + self.warning('Could not find special/slap_safe_level in config file, using default: %s' % + self._slapSafeLevel) + except ValueError, e: + self.error('Could not load special/slap_safe_level config value: %s' % e) + self.debug('Using default value (%s) for special/slap_safe_level' % self._slapSafeLevel) try: self._full_ident_level = self.config.getint('special', 'paident_full_level') - except Exception, err: - self._full_ident_level = 60 - self.warning('Using default value (%s) for paident_full_level. %s' % (self._full_ident_level, err)) - - + except ConfigParser.NoOptionError: + self.warning('Could not find special/paident_full_level in config file, using default: %s' % + self._full_ident_level) + except ValueError, e: + self.error('Could not load special/paident_full_level config value: %s' % e) + self.debug('Using default value (%s) for special/paident_full_level' % self._full_ident_level) def installCrontabs(self): - # CRONTABS INSTALLATION - # Cleanup and Create the crontabs + """ + CRONTABS INSTALLATION + Cleanup and Create the crontabs + """ if self._ncronTab: # remove existing crontab self.console.cron - self._ncronTab @@ -671,16 +936,16 @@ def installCrontabs(self): # remove existing crontab self.console.cron - self._skcronTab if self._ninterval > 0: - self._ncronTab = b3.cron.PluginCronTab(self, self.namecheck, 0, '*/%s' % (self._ninterval)) + 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._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._scronTab = b3.cron.PluginCronTab(self, self.speccheck, 0, '*/%s' % self._sinterval) self.console.cron + self._scronTab if self._skinterval > 0: - self._skcronTab = b3.cron.PluginCronTab(self, self.skillcheck, 0, '*/%s' % (self._skinterval)) + self._skcronTab = b3.cron.PluginCronTab(self, self.skillcheck, 0, '*/%s' % self._skinterval) self.console.cron + self._skcronTab def getCmd(self, cmd): @@ -691,81 +956,100 @@ def getCmd(self, cmd): return None - def onEvent(self, event): - """\ + """ Handle intercepted events """ - if event.type == b3.events.EVT_CLIENT_DISCONNECT: + if event.type == self.console.getEventID('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.debug('PlayerCount: %s' % self._playercount) self.adjustrotation(-1) - elif event.type == b3.events.EVT_CLIENT_AUTH: + + elif event.type == self.console.getEventID('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.debug('PlayerCount: %s' % self._playercount) self.adjustrotation(+1) - elif event.type == b3.events.EVT_CLIENT_TEAM_CHANGE: + + elif event.type == self.console.getEventID('EVT_CLIENT_TEAM_CHANGE'): self.onTeamChange(event.data, event.client) - elif event.type == b3.events.EVT_CLIENT_DAMAGE: + + elif event.type == self.console.getEventID('EVT_CLIENT_DAMAGE'): self.headshotcounter(event.client, event.target, event.data) - elif event.type == b3.events.EVT_GAME_EXIT: + + elif event.type == self.console.getEventID('EVT_GAME_EXIT'): self._mapchanged = True if self._botenable: self.botsdisable() + self.ignoreSet(self._ignorePlus) + # reset headshotcounter (per map) if applicable if self._hsresetvars == 'map': self.resetVars() - # reset number of Namechanges per client + + # reset number of name changes per client self.resetNameChanges() if not self._teamLocksPermanent: # release TeamLocks self.resetTeamLocks() - #Setup timer for recounting players + + # 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)) + tm = 60 + self._dontcount = self.console.time() + tm + t2 = threading.Timer(tm, self.recountplayers) + self.debug('Starting RecountPlayers Timer: %s seconds' % tm) t2.start() - elif event.type == b3.events.EVT_GAME_ROUND_START: + + elif event.type == self.console.getEventID('EVT_GAME_ROUND_START'): self._forgetTeamContrib() self._killhistory = [] self._lastbal = self.console.time() + # 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(self._ignorePlus) 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)) + tm = self._votedelay * 60 + t1 = threading.Timer(tm, self.votedelay) + self.debug('Starting Vote delay Timer: %s seconds' % tm) t1.start() - # recount players - elif event.type == b3.events.EVT_CLIENT_NAME_CHANGE: + + elif event.type == self.console.getEventID('EVT_CLIENT_NAME_CHANGE'): self.onNameChange(event.data, event.client) - elif event.type == b3.events.EVT_CLIENT_KILL: + + elif event.type == self.console.getEventID('EVT_CLIENT_KILL'): self.onKill(event.client, event.target, int(event.data[0])) - elif event.type == b3.events.EVT_CLIENT_KILL_TEAM: + + elif event.type == self.console.getEventID('EVT_CLIENT_KILL_TEAM'): self.onKillTeam(event.client, event.target, int(event.data[0])) - elif event.type == b3.events.EVT_CLIENT_ACTION: + + elif event.type == self.console.getEventID('EVT_CLIENT_ACTION'): self.onAction(event.client, event.data) + + elif event.type == self.console.getEventID('EVT_GAME_MAP_CHANGE'): + self._matchmode = self.console.getCvar('g_matchmode').getBoolean() + else: self.dumpEvent(event) @@ -788,10 +1072,13 @@ def onAction(self, client, actiontype): def dumpEvent(self, event): self.debug('poweradminurt.dumpEvent -- Type %s, Client %s, Target %s, Data %s', - event.type, event.client, event.target, event.data) + event.type, event.client, event.target, event.data) def _teamvar(self, client, var): - # return how much variable has changed since player joined its team + """ + Return how much variable has changed + since player joined its team + """ old = client.var(self, 'prev_' + var, 0).value new = client.var(self, var, 0).value return new - old @@ -817,13 +1104,13 @@ def _getScores(self, clients, usexlrstats=True): deaths = max(0, self._teamvar(c, 'deaths')) teamkills = max(0, self._teamvar(c, 'teamkills')) hs = self._teamvar(c, 'headhits') + self._teamvar(c, 'helmethits') - hsratio = min(1.0, hs / (1.0 + kills)) # hs can be greater than kills + hsratio = min(1.0, hs / (1.0 + kills)) # hs can be greater than kills killratio = kills / (1.0 + deaths + teamkills) teamcontrib = (kills - deaths - teamkills) / (age + 1.0) - flag_taken = int(bool(c.var(self, 'flag_taken', 0).value)) # one-time bonus + flag_taken = int(bool(c.var(self, 'flag_taken', 0).value)) # one-time bonus flag_captured = self._teamvar(c, 'flag_captured') flag_returned = self._teamvar(c, 'flag_returned') - flagperf = 10*flag_taken + 20*flag_captured + flag_returned + flagperf = 10 * flag_taken + 20 * flag_captured + flag_returned bomb_planted = self._teamvar(c, 'bomb_planted') bomb_defused = self._teamvar(c, 'bomb_defused') bombperf = bomb_planted + bomb_defused @@ -835,8 +1122,9 @@ def _getScores(self, clients, usexlrstats=True): 'teamcontrib': teamcontrib, 'flagperf': flagperf, 'bombperf': bombperf, - } - stats = xlrstats and xlrstats.get_PlayerStats(c) + } + + stats = xlrstats.get_PlayerStats(c) if xlrstats else None if stats: playerstats[c.id]['xkillratio'] = stats.ratio head = xlrstats.get_PlayerBody(playerid=c.cid, bodypartid=0).kills @@ -851,6 +1139,7 @@ def _getScores(self, clients, usexlrstats=True): maxstats[key] = playerstats[c.id][key] if key not in minstats or minstats[key] > playerstats[c.id][key]: minstats[key] = playerstats[c.id][key] + scores = {} weights = { 'killratio': 1.0, @@ -861,28 +1150,31 @@ def _getScores(self, clients, usexlrstats=True): # weight score for mission objectives higher 'flagperf': 3.0, 'bombperf': 3.0, - } + } + weightsum = sum(weights[key] for key in keys) - self.debug("score: maxstats=%s" % maxstats) - self.debug("score: minstats=%s" % minstats) + self.debug("score: maxstats=%s" % str(maxstats)) + self.debug("score: minstats=%s" % str(minstats)) for c in clients: score = 0.0 - T = min(1.0, playerstats[c.id]['age'] / 5.0) # reduce score for players who just joined + tm = min(1.0, playerstats[c.id]['age'] / 5.0) # reduce score for players who just joined msg = [] for key in keys: denom = maxstats[key] - minstats[key] - if denom < 0.0001: # accurate at ne nimis + if denom < 0.0001: # accurate at ne nimis continue msg.append("%s=%.3f" % (key, playerstats[c.id][key])) keyscore = weights[key] * (playerstats[c.id][key] - minstats[key]) / denom if key in ('killratio', 'teamcontrib', 'hsratio'): - score += T * keyscore + score += tm * keyscore else: score += keyscore + score /= weightsum self.debug('score: %s %s score=%.3f age=%.2f %s' % (c.team, c.name, score, playerstats[c.id]['age'], ' '.join(msg))) scores[c.id] = score + return scores def _getRandomTeams(self, clients, checkforced=False): @@ -899,6 +1191,7 @@ def _getRandomTeams(self, clients, checkforced=False): red.append(c) else: nonforced.append(c) + # distribute nonforced players random.shuffle(nonforced) n = (len(nonforced) + len(blue) + len(red)) / 2 - len(blue) @@ -922,54 +1215,49 @@ def _getTeamScoreDiffForAdvise(self, minplayers=None): blue = [c for c in clients if c.team == b3.TEAM_BLUE] red = [c for c in clients if c.team == b3.TEAM_RED] self.debug("advise: numblue=%d numred=%d" % (len(blue), len(red))) + if minplayers and len(blue) + len(red) < minplayers: self.debug('advise: too few players') return None, None + diff = self._getTeamScoreDiff(blue, red, scores) + if tdm: bs, rs = self._getAvgKillsRatios(blue, red) avgdiff = bs - rs - self.debug('advise: TDM blue=%.2f red=%.2f avgdiff=%.2f skilldiff=%.2f' %\ - (bs, rs, avgdiff, diff)) + self.debug('advise: TDM blue=%.2f red=%.2f avgdiff=%.2f skilldiff=%.2f' % (bs, rs, avgdiff, diff)) else: - # Just looking at kill ratios doesn't work well for CTF, so we base - # the balance diff on the skill diff for now. - sinceLast = self.console.time() - self._lastbal - damping = min(1.0, sinceLast/(1.0+60.0*self._minbalinterval)) + # just looking at kill ratios doesn't work well for CTF, so we base + # the balance diff on the skill diff for now + sincelast = self.console.time() - self._lastbal + damping = min(1.0, sincelast / (1.0 + 60.0 * self._minbalinterval)) avgdiff = 1.21*diff*damping - self.debug('advise: CTF/BOMB avgdiff=%.2f skilldiff=%.2f damping=%.2f' %\ - (avgdiff, diff, damping)) - return avgdiff, diff + self.debug('advise: CTF/BOMB avgdiff=%.2f skilldiff=%.2f damping=%.2f' % (avgdiff, diff, damping)) - def cmd_paadvise(self, data, client, cmd=None): - """\ - Report team skill balance, and give advice if teams are unfair - """ - avgdiff, diff = self._getTeamScoreDiffForAdvise() - self.console.say('Avg kill ratio diff is %.2f, skill diff is %.2f' %\ - (avgdiff, diff)) - self._advise(avgdiff, 1) + return avgdiff, diff - def _getRecentKills(self, T): - t0 = self.console.time() - T - i = len(self._killhistory)-1 + def _getRecentKills(self, tm): + t0 = self.console.time() - tm + i = len(self._killhistory) - 1 while i >= 0: t, team = self._killhistory[i] if t < t0: break + i -= 1 yield t, team def _getAvgKillsRatios(self, blue, red): if not blue or not red: return 0.0, 0.0 - Tmin = 2.0 - Tmax = 4.0 + + tmin = 2.0 + tmax = 4.0 totkpm = len(list((self._getRecentKills(60)))) - T = max(Tmin, Tmax-0.1*totkpm) - self.debug('recent: totkpm=%d T=%.2f' % (totkpm, T)) + tm = max(tmin, tmax - 0.1 * totkpm) + self.debug('recent: totkpm=%d tm=%.2f' % (totkpm, tm)) recentcontrib = {} - t0 = self.console.time() - T * 60 + t0 = self.console.time() - tm * 60 for c in blue + red: hist = c.var(self, 'teamcontribhist', []).value k = 0 @@ -981,7 +1269,8 @@ def _getAvgKillsRatios(self, blue, red): elif s < 0: d += 1 recentcontrib[c.id] = k/(1.0+d) - self.debug('recent: %s' % recentcontrib) + + self.debug('recent: %s' % str(recentcontrib)) def contribcmp(a, b): return cmp(recentcontrib[b.id], recentcontrib[a.id]) @@ -991,9 +1280,10 @@ def contribcmp(a, b): n = min(len(blue), len(red)) if n > 3: n = 3 + int((n-3)/2) - bs = float(sum(recentcontrib[c.id] for c in blue[:n]))/n/T - rs = float(sum(recentcontrib[c.id] for c in red[:n]))/n/T - self.debug('recent: n=%d T=%.2f %.2f %.2f' % (n, T, bs, rs)) + + bs = float(sum(recentcontrib[c.id] for c in blue[:n])) / n / tm + rs = float(sum(recentcontrib[c.id] for c in red[:n])) / n / tm + self.debug('recent: n=%d tm=%.2f %.2f %.2f' % (n, tm, bs, rs)) return bs, rs def _forgetTeamContrib(self): @@ -1003,57 +1293,6 @@ def _forgetTeamContrib(self): c.setvar(self, 'teamcontribhist', []) self._saveTeamvars(c) - def cmd_paunskuffle(self, data, client, cmd=None): - """\ - Create unbalanced teams. Used to test !paskuffle and !pabalance. - """ - self._balancing = True - clients = self.console.clients.getList() - scores = self._getScores(clients) - decorated = [(scores.get(c.id, 0), c) for c in clients - if c.team in (b3.TEAM_BLUE, b3.TEAM_RED)] - decorated.sort() - players = [c for score, c in decorated] - n = len(players) / 2 - blue = players[:n] - red = players[n:] - self.console.write('bigtext "Unskuffling! Noobs beware!"') - self._move(blue, red) - self._forgetTeamContrib() - self._balancing = False - - def cmd_paskuffle(self, data=None, client=None, cmd=None): - """\ - Skill shuffle. Shuffle players to balanced teams by numbers and skill. - Locked players are also moved. - """ - now = self.console.time() - sinceLast = now - self._lastbal - if client and client.maxLevel < 20 and self.ignoreCheck() and sinceLast < 60*self._minbalinterval: - client.message('Teams changed recently, please wait a while') - return None - self._balancing = True - olddiff, bestdiff, blue, red, scores = self._randTeams(100, 0.1) - if client: - if (client.team == b3.TEAM_BLUE and\ - client.cid not in [c.cid for c in blue]) or\ - (client.team == b3.TEAM_RED and\ - client.cid not in [c.cid for c in red]): - # don't move player who initiated skuffle - blue, red = red, blue - moves = 0 - if bestdiff is not None: - self.console.write('bigtext "Skill Shuffle in Progress!"') - moves = self._move(blue, red, scores) - if moves: - self.console.say('^4Team skill difference was ^1%.2f^4, is now ^1%.2f' % ( - olddiff, bestdiff)) - else: - self.console.say('^1Cannot improve team balance!') - self._forgetTeamContrib() - self._balancing = False - self._lastbal = now - def _countSnipers(self, team): n = 0 for c in team: @@ -1067,6 +1306,7 @@ def _countSnipers(self, team): gear = getattr(c, 'gear', '') if 'Z' in gear or 'N' in gear: n += 1 + return n def _move(self, blue, red, scores=None): @@ -1104,7 +1344,7 @@ def _move(self, blue, red, scores=None): if blue and numblue == numred: random.shuffle(blue) spec = blue.pop() - self.console.write('forceteam %s spectate' % spec.cid) + self.console.write('forceteam %s spectator' % spec.cid) numred -= 1 moves -= 1 self.debug('move: moved %s from red to spec' % spec.name) @@ -1113,6 +1353,7 @@ def _move(self, blue, red, scores=None): for _ in xrange(moves): newteam = None + if (blue and numblue < numred) or (blue and not red): c = blue.pop() newteam = 'blue' @@ -1127,6 +1368,7 @@ def _move(self, blue, red, scores=None): numblue -= 1 numred += 1 self.debug('move: moved %s to red' % c.name) + if newteam and scores: if newteam == "red": colorpfx = '^1' @@ -1139,29 +1381,35 @@ def _move(self, blue, red, scores=None): "You were moved because %n team needs more noobs.", "I wanted to move the best player but settled for you instead.", "If you learnt to aim before you shoot I wouldn't have to move you!", - ] + ] else: messages = [ "%n team needs your help! Try not to die too many times...", "You have new friends now. Try not to kill them...", "You have no friends now but try to kill %o team anyway...", "You were moved to %n team for balance.", - ] + ] + msg = random.choice(messages) team = None + if '%n' in msg: team = newteam msg = msg.replace('%n', '%s') + if '%o' in msg: team = oldteam msg = msg.replace('%o', '%s') + if msg.startswith('%'): team = team.capitalize() + if '%s' in msg: msg = msg % team + # send priv msg after all joins, seem like we can lose the msg # otherwise... - queue.append((c, colorpfx+msg)) + queue.append((c, colorpfx + msg)) if spec: self.console.write('forceteam %s blue' % spec.cid) @@ -1172,34 +1420,10 @@ def _move(self, blue, red, scores=None): return moves - def cmd_pabalance(self, data=None, client=None, cmd=None): - """\ - Move as few players as needed to create teams balanced by numbers AND skill. - Locked players are not moved. - """ - now = self.console.time() - sinceLast = now - self._lastbal - if client and client.maxLevel < 20 and self.ignoreCheck() and sinceLast < 60*self._minbalinterval: - client.message('Teams changed recently, please wait a while') - return None - self._balancing = True - # always allow at least 2 moves, but don't move more than 30% of the - # players - olddiff, bestdiff, bestblue, bestred, scores = self._randTeams(100, 0.1, 0.3) - if bestdiff is not None: - self.console.write('bigtext "Balancing teams!"') - self._move(bestblue, bestred, scores) - self.console.say('^4Team skill difference was ^1%.2f^4, is now ^1%.2f' % ( - olddiff, bestdiff)) - else: - # we couldn't beat the previous diff by moving only a few players, do a full skuffle - self.cmd_paskuffle(data, client, cmd) - self._forgetTeamContrib() - self._balancing = False - self._lastbal = now - def _randTeams(self, times, slack, maxmovesperc=None): - # randomize teams a few times and pick the most balanced + """ + Randomize teams a few times and pick the most balanced + """ clients = self.console.clients.getList() scores = self._getScores(clients) oldblue = [c for c in clients if c.team == b3.TEAM_BLUE] @@ -1208,24 +1432,28 @@ def _randTeams(self, times, slack, maxmovesperc=None): olddiff = self._getTeamScoreDiff(oldblue, oldred, scores) self.debug('rand: n=%s' % n) self.debug('rand: olddiff=%.2f' % olddiff) - bestdiff = None # best balance diff so far when diff > slack - sbestdiff = None # best balance diff so far when diff < slack - bestnumdiff = None # best difference in number of snipers so far - bestblue = bestred = None # best teams so far when diff > slack - sbestblue = sbestred = None # new teams so far when diff < slack + bestdiff = None # best balance diff so far when diff > slack + sbestdiff = None # best balance diff so far when diff < slack + bestnumdiff = None # best difference in number of snipers so far + bestblue = bestred = None # best teams so far when diff > slack + sbestblue = sbestred = None # new teams so far when diff < slack epsilon = 0.0001 + if not maxmovesperc and abs(len(oldblue) - len(oldred)) > 1: # Teams are unbalanced by count, force both teams two have equal number # of players self.debug('rand: force new teams') bestblue, bestred = self._getRandomTeams(clients, checkforced=True) bestdiff = self._getTeamScoreDiff(bestblue, bestred, scores) + for _ in xrange(times): blue, red = self._getRandomTeams(clients, checkforced=True) m = self._countMoves(oldblue, blue) + self._countMoves(oldred, red) if maxmovesperc and m > max(2, int(round(maxmovesperc * n))): continue + diff = self._getTeamScoreDiff(blue, red, scores) + if abs(diff) <= slack: # balance below slack threshold, try to distribute the snipers instead numdiff = abs(self._countSnipers(blue) - self._countSnipers(red)) @@ -1235,29 +1463,35 @@ def _randTeams(self, times, slack, maxmovesperc=None): self.debug('rand: first numdiff %d (sdiff=%.2f)' % (numdiff, diff)) else: self.debug('rand: found better numdiff %d < %d (sdiff=%.2f)' % (numdiff, bestnumdiff, diff)) + sbestblue, sbestred = blue, red sbestdiff, bestnumdiff = diff, numdiff + elif numdiff == bestnumdiff and abs(diff) < abs(sbestdiff) - epsilon: # same number of snipers but better balance diff self.debug('rand: found better sdiff %.2f < %.2f (numdiff=%d bestnumdiff=%d)' % ( abs(diff), abs(sbestdiff), numdiff, bestnumdiff)) sbestblue, sbestred = blue, red sbestdiff = diff + elif bestdiff is None or abs(diff) < abs(bestdiff) - epsilon: # balance above slack threshold if bestdiff is None: self.debug('rand: first diff %.2f' % abs(diff)) else: self.debug('rand: found better diff %.2f < %.2f' % (abs(diff), abs(bestdiff))) + bestblue, bestred = blue, red bestdiff = diff + if bestdiff is not None: self.debug('rand: bestdiff=%.2f' % bestdiff) + if sbestdiff is not None: self.debug('rand: sbestdiff=%.2f bestnumdiff=%d' % (sbestdiff, bestnumdiff)) - self.debug('rand: snipers: blue=%d red=%d' %\ - (self._countSnipers(sbestblue), self._countSnipers(sbestred))) + self.debug('rand: snipers: blue=%d red=%d' % (self._countSnipers(sbestblue), self._countSnipers(sbestred))) return olddiff, sbestdiff, sbestblue, sbestred, scores + return olddiff, bestdiff, bestblue, bestred, scores def _countMoves(self, old, new): @@ -1266,6 +1500,7 @@ def _countMoves(self, old, new): for c in old: if c.name not in newnames: i += 1 + return i def skillcheck(self): @@ -1274,12 +1509,11 @@ def skillcheck(self): gametype = self._getGameType() - # run skillbalancer only if current gametype is in autobalance_gametypes list - try: - self._autobalance_gametypes_array.index(gametype) - except: + # run skillbalancer only if current + # gametype is in autobalance_gametypes list + if not gametype in self._autobalance_gametypes_array: self.debug('Current gametype (%s) is not specified in autobalance_gametypes - skillbalancer disabled', - self.console.game.gameType) + self.console.game.gameType) return None if self._skill_balance_mode == 0: @@ -1320,70 +1554,181 @@ def _advise(self, avgdiff, mode): # mode 0: no advice # mode 1: give advice # mode 2: give advice if teams are unfair - absdiff = 5*abs(avgdiff) - unfair = absdiff > 2.31 # constant carefully reviewed by an eminent team of trained Swedish scientistians :) + absdiff = 5 * abs(avgdiff) + unfair = absdiff > 2.31 # constant carefully reviewed by an eminent team of trained Swedish scientistians :) word = None same = 'remains ' stronger = 'has become ' if 1 <= absdiff < 2: word = 'stronger' - if 2 <= absdiff < 4: + elif 2 <= absdiff < 4: word = 'dominating' - if 4 <= absdiff < 6: + elif 4 <= absdiff < 6: word = 'overpowering' - if 6 <= absdiff < 8: + elif 6 <= absdiff < 8: word = 'supreme' - if 8 <= absdiff < 10: + elif 8 <= absdiff < 10: word = 'Godlike!' - if 10 <= absdiff: + elif 10 <= absdiff: word = 'probably cheating :P' same = 'is ' stronger = 'is ' + if word: oldteam, oldword, oldabsdiff = self._oldadv - self.debug('advise: oldteam=%s oldword=%s oldabsdiff=%s' %\ - (oldteam, oldword, oldabsdiff)) + self.debug('advise: oldteam=%s oldword=%s oldabsdiff=%s' % (oldteam, oldword, oldabsdiff)) team = avgdiff < 0 and 'Red' or 'Blue' if team == oldteam: if word == oldword: msg = '%s team %s%s' % (team, same, word) elif absdiff > oldabsdiff: - # Stronger team is becoming even stronger + # stronger team is becoming even stronger msg = '%s team %s%s' % (team, stronger, word) elif absdiff < oldabsdiff: - # Stronger team is becoming weaker + # stronger team is becoming weaker msg = '%s team is just %s' % (team, word) if absdiff < 4: - # Difference not too big, teams may soon be fair + # difference not too big, teams may soon be fair unfair = False + else: + # FIXME (Fenix): here 'msg' is not initialized thus it produces a warning + return else: msg = '%s team is now %s' % (team, word) + if unfair and (mode == 1 or mode == 2): msg += ', use !bal to balance the teams' + if not unfair and mode == 1: msg += ', but no action necessary yet' - self.debug('advise: team=%s word=%s absdiff=%s' %\ - (team, word, absdiff)) + + self.debug('advise: team=%s word=%s absdiff=%s' % (team, word, absdiff)) self._oldadv = (team, word, absdiff) + else: msg = 'Teams seem fair' self._oldadv = (None, None, None) + self.console.say(msg) +#--Commands implementation ------------------------------------------------------------------------ +# /rcon commands: +# slap +# nuke +# forceteam +# veto (vote cancellen) +# mute +# pause +# swapteams +# shuffleteams + + def cmd_paadvise(self, data, client, cmd=None): + """ + Report team skill balance, and give advice if teams are unfair + """ + avgdiff, diff = self._getTeamScoreDiffForAdvise() + self.console.say('Avg kill ratio diff is %.2f, skill diff is %.2f' % (avgdiff, diff)) + self._advise(avgdiff, 1) + + def cmd_paunskuffle(self, data, client, cmd=None): + """ + Create unbalanced teams. Used to test !paskuffle and !pabalance. + """ + self._balancing = True + clients = self.console.clients.getList() + scores = self._getScores(clients) + decorated = [(scores.get(c.id, 0), c) for c in clients + if c.team in (b3.TEAM_BLUE, b3.TEAM_RED)] + + decorated.sort() + players = [c for score, c in decorated] + n = len(players) / 2 + blue = players[:n] + red = players[n:] + self.console.write('bigtext "Unskuffling! Noobs beware!"') + self._move(blue, red) + self._forgetTeamContrib() + self._balancing = False + + def cmd_paskuffle(self, data=None, client=None, cmd=None): + """ + Skill shuffle. Shuffle players to balanced teams by numbers and skill. + Locked players are also moved. + """ + now = self.console.time() + sincelast = now - self._lastbal + if client and client.maxLevel < 20 and self.ignoreCheck() and sincelast < 60 * self._minbalinterval: + client.message('Teams changed recently, please wait a while') + return + + self._balancing = True + olddiff, bestdiff, blue, red, scores = self._randTeams(100, 0.1) + if client: + if (client.team == b3.TEAM_BLUE and client.cid not in [c.cid for c in blue]) or \ + (client.team == b3.TEAM_RED and client.cid not in [c.cid for c in red]): + # don't move player who initiated skuffle + blue, red = red, blue + + moves = 0 + if bestdiff is not None: + self.console.write('bigtext "Skill Shuffle in Progress!"') + moves = self._move(blue, red, scores) + + if moves: + self.console.say('^4Team skill difference was ^1%.2f^4, is now ^1%.2f' % (olddiff, bestdiff)) + else: + self.console.say('^1Cannot improve team balance!') + + self._forgetTeamContrib() + self._balancing = False + self._lastbal = now + + def cmd_pabalance(self, data=None, client=None, cmd=None): + """ + Move as few players as needed to create teams balanced by numbers AND skill. + Locked players are not moved. + """ + now = self.console.time() + sincelast = now - self._lastbal + if client and client.maxLevel < 20 and self.ignoreCheck() and sincelast < 60 * self._minbalinterval: + client.message('Teams changed recently, please wait a while') + return + + self._balancing = True + # always allow at least 2 moves, but don't move more than 30% of the players + olddiff, bestdiff, bestblue, bestred, scores = self._randTeams(100, 0.1, 0.3) + if bestdiff is not None: + self.console.write('bigtext "Balancing teams!"') + self._move(bestblue, bestred, scores) + self.console.say('^4Team skill difference was ^1%.2f^4, is now ^1%.2f' % (olddiff, bestdiff)) + else: + # we couldn't beat the previous diff by moving only a few players, do a full skuffle + self.cmd_paskuffle(data, client, cmd) + + self._forgetTeamContrib() + self._balancing = False + self._lastbal = now + def cmd_paautoskuffle(self, data, client, cmd=None): + """ + [] - Set the skill balancer mode. + """ modes = ["0-none", "1-advise", "2-autobalance", "3-autoskuffle"] if not data: mode = modes[self._skill_balance_mode] self.console.say("Skill balancer mode is '%s'" % mode) self.console.say("Options are %s" % ', '.join(modes)) return + mode = None + try: mode = int(data) except ValueError: for i, m in enumerate(modes): if data in m: mode = i + if mode is not None and 0 <= mode <= 3: self._skill_balance_mode = mode self.console.say("Skill balancer mode is now '%s'" % modes[mode]) @@ -1392,100 +1737,102 @@ def cmd_paautoskuffle(self, data, client, cmd=None): self.console.say("Valid options are %s" % ', '.join(modes)) def cmd_paswap(self, data, client, cmd=None): - """\ + """ [player2] - Swap two teams for 2 clients. If player2 is not specified, the admin using the command is swapped with player1. Doesn't work with spectators (exception for calling admin). """ - #Check the input - input = self._adminPlugin.parseUserCmd(data) - #Check for input. If none, exist with a message. - if input: - #Check if the first player exists. If none, exit. - client1 = self._adminPlugin.findClientPrompt(input[0], client) + # check the input + args = self._adminPlugin.parseUserCmd(data) + # check for input. If none, exist with a message. + if args: + # check if the first player exists. If none, exit. + client1 = self._adminPlugin.findClientPrompt(args[0], client) if not client1: - return False + return else: - client.message("Invalid parameters, try !swap client1 [client2]") - return False - #Check if there's a second, valid input. If no input, mark the admin to be changed. - #If the specified player doesn't exist, exit. - if input[1] is not None: - client2 = self._adminPlugin.findClientPrompt(input[1], client) + client.message("Invalid parameters, try !help paswap") + return + + # if the specified player doesn't exist, exit. + if args[1] is not None: + client2 = self._adminPlugin.findClientPrompt(args[1], client) if not client2: - return False + return else: client2 = client + if client1.team == b3.TEAM_SPEC: - client.message("%s is a spectator! - Can't be swapped" % (client1.name)) - return False + client.message("%s is a spectator! - Can't be swapped" % client1.name) + return + if client2.team == b3.TEAM_SPEC: - client.message("%s is a spectator! - Can't be swapped" % (client2.name)) - return False + client.message("%s is a spectator! - Can't be swapped" % client2.name) + return + if client1.team == client2.team: - client.message("%s and %s are on the same team! - Swapped them anyway :p" % ((client1.name), client2.name)) - return False + client.message("%s and %s are on the same team! - Swapped them anyway :p" % (client1.name, client2.name)) + return + if client1.team == b3.TEAM_RED: self._move([client1], [client2]) else: self._move([client2], [client1]) - # No need to send the message twice to the switching admin :-) - if (client1 != client): - client1.message("^4You were swapped with %s by the admin." % (client2.name)) - if (client2 != client): - client2.message("^4You were swapped with %s by the admin." % (client1.name)) - client.message("^3Successfully swapped %s and %s." % (client1.name, client2.name)) - return True + # No need to send the message twice to the switching admin :-) + + if client1 != client: + client1.message("^4You were swapped with %s by the admin" % client2.name) + + if client2 != client: + client2.message("^4You were swapped with %s by the admin" % client1.name) + + client.message("^3Successfully swapped %s and %s" % (client1.name, client2.name)) -#--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.') + client.message('^7Teams are already balanced') else: - client.message('^7Teams are now balanced.') + client.message('^7Teams are now balanced') self._teamsbalanced = True else: - client.message('^7Teambalancing failed, please try a again in a few moments.') - return None + client.message('^7Teambalancing failed, please try a again in a few moments') 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 + + return + else: + if data in ('on', 'off', 'reset'): if client: - client.message('^7Voting: ^1%s' % (data)) + client.message('^7Voting: ^1%s' % data) else: - self.debug('Voting: %s' % (data)) + 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 + + return if data == 'off': curvalue = self.console.getCvar('g_allowvote').getInt() @@ -1496,219 +1843,200 @@ def cmd_pavote(self, data, client=None, cmd=None): 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. + """ + 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! ) + (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) + client.message('^7Missing data, try !help paexec') + return - return True + 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) 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) """ time.sleep(1) 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! ) + (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 - return True + # are we still here? Let's write it to console + args = data.split(' ', 1) + cvar = args[0] + value = args[1] if len(args) == 2 else "" + self.console.setCvar(cvar, value) 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 - return True + # 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) 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 - return True + # are we still here? Let's write it to console + self.console.write('bigtext "%s"' % data) 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) + args = self._adminPlugin.parseUserCmd(data) + if args: + # args[0] is the player id + sclient = self._adminPlugin.findClientPrompt(args[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 + return else: client.message('^7Invalid data, try !help pamute') - return False + return if sclient.maxLevel > client.maxLevel: client.message("^7You don't have enough privileges to mute this player") - return False - if input[1] is not None and re.match('^([0-9]+)\s*$', input[1]): - duration = int(input[1]) + return + + if args[1] is not None and re.match('^([0-9]+)\s*$', args[1]): + duration = int(args[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) + args = self._adminPlugin.parseUserCmd(data) + if args: + # args[0] is the player id + sclient = self._adminPlugin.findClientPrompt(args[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 + return else: client.message('^7Invalid data, try !help paslap') - return False + return if sclient.maxLevel >= self._slapSafeLevel and client.maxLevel < 90: client.message("^7You don't have enough privileges to slap an Admin") - return False + return + + if args[1]: - if input[1]: try: - x = int(input[1]) - except: + x = int(args[1]) + except ValueError: client.message('^7Invalid data, try !help paslap') - return False + return + 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 + self.console.write('slap %s' % sclient.cid) 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) + args = self._adminPlugin.parseUserCmd(data) + if args: + # args[0] is the player id + sclient = self._adminPlugin.findClientPrompt(args[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 + return else: client.message('^7Invalid data, try !help panuke') - return False + return + + if args[1]: - if input[1]: try: - x = int(input[1]) - except: + x = int(args[1]) + except ValueError: client.message('^7Invalid data, try !help panuke') - return False + return + 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 + self.console.write('nuke %s' % sclient.cid) def multipunish(self, x, sclient, client, cmd): self.debug('Entering multipunish...') @@ -1720,52 +2048,48 @@ def multipunish(self, x, sclient, client, cmd): 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. + using 'all free' will 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: + args = self._adminPlugin.parseUserCmd(data) + if args: # check if all Locks should be released - if input[0] == "all" and input[1] == "free": + if args[0] == "all" and args[1] == "free": self.resetTeamLocks() self.console.say('All TeamLocks were released') - return None + return - # input[0] is the player id - sclient = self._adminPlugin.findClientPrompt(input[0], client) + # args[0] is the player id + sclient = self._adminPlugin.findClientPrompt(args[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 + return else: client.message('^7Invalid data, try !help paforce') - return False + return - if not len(input[1]): + if not len(args[1]): client.message('^7Missing data, try !help paforce') - return False + return - tdata = input[1].split(' ') + tdata = args[1].split(' ') team = tdata[0] - try: - if tdata[1] == 'lock': - lock = True - except: - lock = False + lock = False + if len(tdata) > 1 and tdata[1] == 'lock': + lock = True if team == 'spec' or team == 'spectator': team = 's' @@ -1781,43 +2105,43 @@ def cmd_paforce(self, data, client, cmd=None): 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.message('^3Your have been released by the admin') + client.message('^7%s ^3has been released' % sclient.name) sclient.delvar(self, 'paforced') - return False + return else: - client.message('^3There was no lock on ^7%s' % (sclient.name)) + 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)) + 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)) + sclient.message('^3Your are forced to: ^7%s' % teamname) + else: - client.message('^7Invalid or missing data, try !help paforce') - return False + client.message('^7Invalid data, try !help paforce') + return if lock: - sclient.setvar(self, 'paforced', team) # s, red or blue + 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)) client.message('^3%s ^7forced to ^3%s' % (sclient.name, teamname)) - return True def cmd_paswapteams(self, data, client, cmd=None): - """\ - Swap teams. + """ + Swap current teams. (You can safely use the command without the 'pa' at the beginning) """ - # Ignore automatic checking before giving the command + # 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) """ @@ -1825,107 +2149,104 @@ def cmd_pashuffleteams(self, data, client, cmd=None): 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 + return + + 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: ^1OFF') def cmd_papublic(self, data, client, cmd=None): - """\ - Set server public mode on/off + """ + 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') - self.console.queueEvent(b3.events.Event(b3.events.EVT_CLIENT_PUBLIC, '', client)) - 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 !!"') - self.console.queueEvent(b3.events.Event(b3.events.EVT_CLIENT_PUBLIC, newpassword, client)) - return True + return + + if data == 'on': + self.console.setCvar('g_password', '') + self.console.say('^7public mode: ^2ON') + self.console.queueEvent(self.console.getEvent('EVT_CLIENT_PUBLIC', '', client)) + + 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('^1ERROR: ^7could not set public mode off because \ + there is no password specified in the config file') + return + + self.console.setCvar('g_password', '%s' % newpassword) + self.console.say('^7public mode: ^1OFF') + client.message('^7password is \'^4%s^7\'' % newpassword) + client.message('^7type ^5!mapreload^7 to apply change') + self.console.write('bigtext "^7Server going ^3PRIVATE ^7soon!!"') + self.console.queueEvent(self.console.getEvent('EVT_CLIENT_PUBLIC', newpassword, client)) 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') - - 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) - client.message('^7type ^5!mapreload^7 to apply change') - self.set_configmode(None) - return True + return + 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: ^1OFF') + + 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) + client.message('^7type ^5!mapreload^7 to apply change') + + self.set_configmode(None) def cmd_pagear(self, data, client=None, cmd=None): - """\ - - Set allowed weapons. + """ + [] - 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 @@ -1936,16 +2257,19 @@ def cmd_pagear(self, data, client=None, cmd=None): 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 + return + 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 + self.debug('invalid data sent to cmd_pagear') + + return if data[:5] == 'all': self.console.setCvar('g_gear', '0') @@ -1954,6 +2278,7 @@ def cmd_pagear(self, data, client=None, cmd=None): 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': @@ -1967,142 +2292,135 @@ def cmd_pagear(self, data, client=None, cmd=None): elif data[1:5] == 'nege': bit = 32 else: - return False + return 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') + self.console.setCvar('g_gametype', '0') if client: client.message('^7game type changed to ^4Free For All') + self.set_configmode('ffa') - return True 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') + self.console.setCvar('g_gametype', '3') if client: client.message('^7game type changed to ^4Team Death Match') + self.set_configmode('tdm') - return True 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') + self.console.setCvar('g_gametype', '4') if client: client.message('^7game type changed to ^4Team Survivor') + self.set_configmode('ts') - return True 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') + self.console.setCvar('g_gametype', '5') if client: client.message('^7game type changed to ^4Follow The Leader') + self.set_configmode('ftl') - return True 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') + self.console.setCvar('g_gametype', '6') if client: client.message('^7game type changed to ^4Capture And Hold') + self.set_configmode('cah') - return True 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') + self.console.setCvar('g_gametype', '7') if client: client.message('^7game type changed to ^4Capture The Flag') + self.set_configmode('ctf') - return True 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') + self.console.setCvar('g_gametype', '8') if client: client.message('^7game type changed to ^4Bomb') - self.set_configmode('bomb') - return True + self.set_configmode('bomb') 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: - cmd.sayLoudOrPM(client, 'Your id is ^2@%s' % (client.id)) - return True - 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 + args = self._adminPlugin.parseUserCmd(data) + if not args: + cmd.sayLoudOrPM(client, 'Your id is ^2@%s' % client.id) + return + + # args[0] is the player id + sclient = self._adminPlugin.findClientPrompt(args[0], client) + if not sclient: + # a player matching 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 if client.maxLevel < self._full_ident_level: - cmd.sayLoudOrPM(client, - '%s ^4@%s ^2%s' % (self.console.formatTime(self.console.time()), sclient.id, sclient.exactName)) + cmd.sayLoudOrPM(client, '%s ^4@%s ^2%s' % (self.console.formatTime(self.console.time()), + sclient.id, sclient.exactName)) else: - cmd.sayLoudOrPM(client, '%s ^4@%s ^2%s ^2%s ^2%s' % ( - self.console.formatTime(self.console.time()), sclient.id, sclient.exactName, sclient.ip, - self.console.formatTime(sclient.timeAdd))) - return True + cmd.sayLoudOrPM(client, '%s ^4@%s ^2%s ^2%s ^2%s' % (self.console.formatTime(self.console.time()), + sclient.id, sclient.exactName, sclient.ip, + self.console.formatTime(sclient.timeAdd))) #---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.) - """ +# +# /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) + self.verbose('client variable teamtime set to: %s' % client.var(self, 'teamtime').value) # remember current stats so we can tell how the player # is performing on the new team self._saveTeamvars(client) 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)) + 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)) + 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 @@ -2115,9 +2433,9 @@ def onTeamChange(self, team, client): return None if self.isEnabled() and not self._balancing: - # set balancing flag + # set balancing flag self._balancing = True - self.verbose('Teamchanged cid: %s, name: %s, team: %s' % (client.cid, client.name, team)) + 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: @@ -2127,12 +2445,12 @@ def onTeamChange(self, team, client): # did player join spectators? if team == b3.TEAM_SPEC: - self.verbose('Player joined specs') + self.verbose('player joined specs') # done balancing self._balancing = False return None elif team == b3.TEAM_UNKNOWN: - self.verbose('Team is unknown') + self.verbose('team is unknown') # done balancing self._balancing = False return None @@ -2140,38 +2458,47 @@ def onTeamChange(self, team, client): # check if player was allowed to join this team if not self.countteams(): self._balancing = False - self.error('Aborting teambalance. Counting teams failed!') + 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)) + 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)) + 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)) + + + self.console.queueEvent(self.console.getEvent('EVT_CLIENT_SUICIDE', + (100, 'penalty', 'body', 'Team_Switch_Penalty'), + client, + client)) + + self.console.queueEvent(self.console.getEvent('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!') + 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.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.verbose('forcing %s to the Red team' % client.name) self.console.write('forceteam %s red' % client.cid) # done balancing @@ -2187,38 +2514,37 @@ def countteams(self): self._teamred = len(self.console.getCvar('g_redteamlist').getString()) self._teamblue = len(self.console.getCvar('g_blueteamlist').getString()) return True - except: + except Exception: return False def _getGameType(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 + # 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: + if self.console.game.gameType is 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) + self.debug('current gametype found - changed to (%s)', self.console.game.gameType) + except Exception: + self.debug('unable to determine current gametype - remains at (%s)', self.console.game.gameType) return self.console.game.gameType def teamcheck(self): gametype = self._getGameType() - # run teambalance only if current gametype is in autobalance_gametypes list - try: - self._autobalance_gametypes_array.index(gametype) - except: - self.debug('Current gametype (%s) is not specified in autobalance_gametypes - teambalancer disabled', - self.console.game.gameType) + # run teambalance only if current gametype + # is in autobalance_gametypes list + if not gametype in self._autobalance_gametypes_array: + self.debug('current gametype (%s) is not specified in autobalance_gametypes - teambalancer disabled', + gametype) return None if self._skill_balance_mode != 0: - self.debug('Skill balancer is active, not performing classic teamcheck'); + self.debug('skill balancer is active, not performing classic teamcheck') if self.console.time() > self._ignoreTill: self.teambalance() @@ -2227,28 +2553,28 @@ def teamcheck(self): def teambalance(self): if self.isEnabled() and not self._balancing and not self._matchmode: - #set balancing flag + # set balancing flag self._balancing = True - self.verbose('Checking for balancing') + self.verbose('checking for balancing') if not self.countteams(): self._balancing = False - self.warning('Aborting teambalance. Counting teams failed!') + self.warning('aborting teambalance: counting teams failed!') return False if abs(self._teamred - self._teamblue) <= self._teamdiff: - #teams are balanced + # 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.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)) + 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: @@ -2260,13 +2586,14 @@ def teambalance(self): else: newteam = 'red' oldteam = b3.TEAM_BLUE - self.verbose('Smaller team is: %s' % newteam) - #endless loop protection + 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) + self.verbose('uptime bot: %s' % stime) forceclient = None clients = self.console.clients.getList() for c in clients: @@ -2275,42 +2602,45 @@ def teambalance(self): # 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) + 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'): + 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.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') + 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? + # recount the teams... do we need to balance once more? if not self.countteams(): self._balancing = False - self.error('Aborting teambalance. Counting teams failed!') + 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)) + 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.verbose('smaller team is: %s' % newteam) + + # done balancing self._balancing = False + return True def resetTeamLocks(self): @@ -2323,15 +2653,16 @@ def resetTeamLocks(self): return None #---Dupes/Forbidden Names Mechanism---------------------------------------------------------------- + def namecheck(self): if self._matchmode: return None - self.debug('Checking Names') + 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): + if not player.name in d.keys(): d[player.name] = [player.cid] else: #l = d[player.name] @@ -2341,7 +2672,11 @@ def namecheck(self): 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)]))) + 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') @@ -2349,13 +2684,15 @@ def namecheck(self): 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.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.info("warning player %s <%s> @%s for using forbidden name 'all'" % + (client.exactName, client.cid, client.id)) self._adminPlugin.warnClient(client, 'badname') return None @@ -2395,78 +2732,77 @@ def resetNameChanges(self): 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: + if self.isEnabled() and self._g_maxGameClients == 0 and not self._matchmode: + self.debug('checking for idle spectators') clients = self.console.clients.getList() if len(clients) < self._smaxplayers: - self.verbose('Clients online (%s) < maxplayers (%s), ignoring' % (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') + self.debug('client %s has no variable teamtime' % c) # 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) + 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) + 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) + 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) + 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') + self.debug('checking for bot support') if self.isEnabled() and not self._matchmode: + try: test = self.console.game.mapName - except: + except AttributeError: 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.debug('enabling bots for this map: %s' % self.console.game.mapName) self.botsenable() return None def botsdisable(self): - self.debug('Disabling the bots') + 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 - + self.debug('enabling the bots') + self.console.write('set bot_minplayers %s' % self._botminplayers) #---Headshot Counter------------------------------------------------------------------------------- + def setupVars(self, client): if not client.isvar(self, 'totalhits'): client.setvar(self, 'totalhits', 0.00) @@ -2480,6 +2816,7 @@ def setupVars(self, client): 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) @@ -2494,16 +2831,17 @@ def resetVars(self): 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: + 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]) + weapon = data[1] + hitloc = data[2] # set totals t = attacker.var(self, 'totalhits').value + 1 @@ -2512,54 +2850,59 @@ def headshotcounter(self, attacker, victim, data): victim.setvar(self, 'totalhitted', t) # headshots... no helmet! - if hitloc == self._hitloc_head: + if hitloc == self._hitlocations['HL_HEAD']: 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 == self._hitloc_helmet: + elif hitloc == self._hitlocations['HL_HELMET']: t = attacker.var(self, 'helmethits').value + 1 attacker.setvar(self, 'helmethits', t) # torso... no kevlar! - elif hitloc == self._hitloc_torso: + elif hitloc == self._hitlocations['HL_TORSO']: t = victim.var(self, 'torsohitted').value + 1 victim.setvar(self, 'torsohitted', t) # announce headshots - if self._hsall == True and hitloc in (self._hitloc_head, self._hitloc_helmet): + if self._hsall and hitloc in (self._hitlocations['HL_HEAD'], self._hitlocations['HL_HELMET']): 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: + if self._hspercent 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: + if self._hsbroadcast: 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: + if self._hswarnhelmet 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) + if self._hswarnkevlar 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: @@ -2582,12 +2925,16 @@ def adjustrotation(self, delta): self.setrotation(3) else: - self.error('Error: Invalid delta passed to adjustrotation') + self.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: + 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: @@ -2618,7 +2965,7 @@ def recountplayers(self): for p in self.console.clients.getList(): self._playercount += 1 - self.debug('Initial PlayerCount: %s' % (self._playercount)) + self.debug('Initial PlayerCount: %s' % self._playercount) if self._oldplayercount == -1: self.adjustrotation(0) @@ -2626,8 +2973,6 @@ def recountplayers(self): self.adjustrotation(+1) elif self._playercount < self._oldplayercount: self.adjustrotation(-1) - else: - pass #--Support Functions------------------------------------------------------------------------------ @@ -2656,184 +3001,176 @@ def ignoreCheck(self): else: return False - #--Rcon commands------by:FSK405|Fear-------------------------------------------------------------- -# setnextmap -# respawngod -# respawndelay -# caplimit -# fraglimit -# waverespawns -# bluewave -# redwave -# timelimit -# hotpotato +# 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 + client.message('^7Invalid or missing data, try !help pawaverespawns') + return + + 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: ^1OFF') 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') + client.message('^7Invalid or missing data, try !help pasetnextmap') + return + + match = self.console.getMapsSoundingLike(data) + if isinstance(match, basestring): + mapname = match + self.console.setCvar('g_nextmap', mapname) + if client: + client.message('^7nextmap set to %s' % mapname) + elif isinstance(match, list): + client.message('do you mean : %s ?' % string.join(match[:5], ', ')) else: - match = self.console.getMapsSoundingLike(data) - if isinstance(match, basestring): - mapname = match - self.console.write('g_nextmap %s' % mapname) - if client: - client.message('^7nextmap set to %s' % mapname) - elif isinstance(match, list): - client.message('do you mean : %s ?' % string.join(match,', ')) - else: - client.message('^7cannot find any map like [^4%s^7].' % data) + client.message('^7cannot find any map like [^4%s^7]' % data) 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 + client.message('^7Missing data, try !help parespawngod') + return + + self.console.setCvar('g_respawnProtection', data) 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 + client.message('^7Missing data, try !help parespawndelay') + return + + self.console.setCvar('g_respawnDelay', data) 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 + client.message('^7Missing data, try !help pacaplimit') + return + + self.console.setCvar('capturelimit', data) 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 + client.message('^7Missing data, try !help patimelimit') + return + + self.console.setCvar('timelimit', data) 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 + client.message('^7Missing data, try !help pafraglimit') + return + + self.console.setCvar('fraglimit', data) 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') + client.message('^7Missing data, try !help pabluewave') return False - else: - self.console.write('g_bluewave "%s"' % data) - return True + + self.console.setCvar('g_bluewave', data) 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') + client.message('^7Missing data, try !help paredwave') return False - else: - self.console.write('g_redwave "%s"' % data) - return True + + self.console.setCvar('g_redwave', data) 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') + client.message('^7Missing data, try !help pahotpotato') return False - else: - self.console.write('g_hotpotato "%s"' % data) - return True + self.console.setCvar('g_hotpotato', data) + +#------------- SGT -------------------------------------------- - #------------- SGT -------------------------------------------- def cmd_pasetwave(self, data, client, cmd=None): - """\ + """ - Set the wave respawn time for both teams. """ if not data: - client.message('^7Invalid or missing data, try !help setwave') - return False - else: - self.console.write('g_bluewave "%s"' % data) - self.console.write('g_redwave "%s"' % data) - return True + client.message('^7Missing data, try !help pasetwave') + return + + self.console.setCvar('g_bluewave', data) + self.console.setCvar('g_redwave', data) def cmd_pasetgravity(self, data, client, cmd=None): - """\ + """ - Set the gravity value. default = 800 (less means less gravity) """ if not data: - client.message('^7Invalid or missing data, try !help pasetgravity') - return False + client.message('^7Missing data, try !help pasetgravity') + return + if data == 'def': data = 800 + self.console.setCvar('g_gravity', data) client.message('^7Gravity: %s' % data) - return True def set_configmode(self, mode=None): if mode: - if self.gameconfig.has_key('mode_%s' % mode): - cfgfile = self.gameconfig.get('mode_%s' % mode) + modestring = 'mode_%s' % mode + if modestring in self._gameconfig: + cfgfile = self._gameconfig.get(modestring) filename = os.path.join(self.console.game.fs_homepath, self.console.game.fs_game, cfgfile) if os.path.isfile(filename): - self.debug('Executing configfile = [%s]', cfgfile) + self.debug('executing config file: %s', cfgfile) self.console.write('exec %s' % cfgfile) - cfgfile = None + if self._matchmode: - cfgfile = self.gameconfig.get('matchon', None) + cfgfile = self._gameconfig.get('matchon', None) else: - cfgfile = self.gameconfig.get('matchoff', None) + cfgfile = self._gameconfig.get('matchoff', None) + if cfgfile: filename = os.path.join(self.console.game.fs_homepath, self.console.game.fs_game, cfgfile) if os.path.isfile(filename): - self.debug('Executing configfile = [%s]', cfgfile) - self.console.write('exec %s' % cfgfile) - + self.debug('executing configfile: %s', cfgfile) + self.console.write('exec %s' % cfgfile) \ No newline at end of file diff --git a/poweradminurt/iourt42.py b/poweradminurt/iourt42.py new file mode 100644 index 0000000..e749aeb --- /dev/null +++ b/poweradminurt/iourt42.py @@ -0,0 +1,569 @@ +# +# PowerAdmin Plugin for BigBrotherBot(B3) (www.bigbrotherbot.net) +# Copyright (C) 2012 Thomas LEVEIL self.getTime(): + self.debug("ignoring radio event") + return + + points = 0 + data = repr(event.data) + last_message_data = client.var(self, 'last_radio_data').value + + now = self.getTime() + last_radio_time = client.var(self, 'last_radio_time', None).value + gap = None + if last_radio_time is not None: + gap = now - last_radio_time + if gap < 20: + points += 1 + if gap < 2: + points += 1 + if data == last_message_data: + points += 3 + if gap < 1: + points += 3 + + spamins = client.var(self, 'radio_spamins', 0).value + points + + # apply natural points decrease due to time + if gap is not None: + spamins -= int(gap / self._rsp_falloffRate) + + if spamins < 1: + spamins = 0 + + # set new values + self.verbose("radio_spamins for %s : %s" % (client.name, spamins)) + client.setvar(self, 'radio_spamins', spamins) + client.setvar(self, 'last_radio_time', now) + client.setvar(self, 'last_radio_data', data) + + # should we warn ? + if spamins >= self._rsp_maxSpamins: + self.console.writelines(["mute %s %s" % (client.cid, self._rsp_mute_duration)]) + client.setvar(self, 'radio_spamins', int(self._rsp_maxSpamins / 2.0)) + client.setvar(self, 'radio_ignore_till', int(self.getTime() + self._rsp_mute_duration - 1)) + + ############################################################################################### + # + # commands + # + ############################################################################################### + + def cmd_pakill(self, data, client, cmd=None): + """ + - kill a player. + (You can safely use the command without the 'pa' at the beginning) + """ + if not data: + client.message('^7Invalid data, try !help pakill') + return + + sclient = self._adminPlugin.findClientPrompt(data, 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 + + self.console.write('smite %s' % sclient.cid) + + def cmd_palms(self, data, client, cmd=None): + """ + Change game type to Last Man Standing + (You can safely use the command without the 'pa' at the beginning) + """ + self.console.setCvar('g_gametype', '1') + if client: + client.message('^7game type changed to ^4Last Man Standing') + + self.set_configmode('lms') + + def cmd_pajump(self, data, client, cmd=None): + """ + Change game type to Jump + (You can safely use the command without the 'pa' at the beginning) + """ + self.console.setCvar('g_gametype', '9') + if client: + client.message('^7game type changed to ^4Jump') + + self.set_configmode('jump') + + def cmd_pafreeze(self, data, client, cmd=None): + """ + Change game type to Freeze Tag + (You can safely use the command without the 'pa' at the beginning) + """ + self.console.setCvar('g_gametype', '10') + if client: + client.message('^7game type changed to ^4Freeze Tag') + + self.set_configmode('freeze') + + def cmd_paskins(self, data, client, cmd=None): + """ + Set the use of client skins + (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 paskins') + return + + if data == 'on': + self.console.setCvar('g_skins', '1') + self.console.say('^7Client skins: ^2ON') + elif data == 'off': + self.console.setCvar('g_skins', '0') + self.console.say('^7Client skins: ^1OFF') + + def cmd_pafunstuff(self, data, client, cmd=None): + """ + Set the use of funstuff + (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 pafunstuff') + return + + if data == 'on': + self.console.setCvar('g_funstuff', 1) + self.console.say('^7Funstuff: ^2ON') + elif data == 'off': + self.console.setCvar('g_funstuff', 0) + self.console.say('^7Funstuff: ^1OFF') + + def cmd_pagoto(self, data, client, cmd=None): + """ + Set the goto + (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 pagoto') + return + + if data == 'on': + self.console.setCvar('g_allowgoto', 1) + self.console.say('^7Goto: ^2ON') + elif data == 'off': + self.console.setCvar('g_allowgoto', 0) + self.console.say('^7Goto: ^1OFF') + + def cmd_pastamina(self, data, client, cmd=None): + """ + Set the stamina behavior + (You can safely use the command without the 'pa' at the beginning) + """ + if not data or data not in ('default', 'regain', 'infinite'): + client.message('^7Invalid or missing data, try !help pastamina') + return + + if data == 'default': + self.console.setCvar('g_stamina', 0) + self.console.say('^7Stamina mode: ^3DEFAULT') + elif data == 'regain': + self.console.setCvar('g_stamina', 1) + self.console.say('^7Stamina mode: ^3REGAIN') + elif data == 'infinite': + self.console.setCvar('g_stamina', 2) + self.console.say('^7Stamina mode: ^3INFINITE') + + def cmd_paident(self, data, client=None, cmd=None): + """ + - show the ip and guid and authname of a player + (You can safely use the command without the 'pa' at the beginning) + """ + args = self._adminPlugin.parseUserCmd(data) + if not args: + cmd.sayLoudOrPM(client, 'Your id is ^2@%s' % client.id) + return + + # args[0] is the player id + sclient = self._adminPlugin.findClientPrompt(args[0], client) + if not sclient: + # a player matching 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 + + if client.maxLevel < self._full_ident_level: + cmd.sayLoudOrPM(client, '%s ^4@%s ^2%s' % (self.console.formatTime(self.console.time()), + sclient.id, sclient.exactName)) + else: + cmd.sayLoudOrPM(client, '%s ^4@%s ^2%s ^2%s ^7[^2%s^7] since ^2%s' % ( + self.console.formatTime(self.console.time()), sclient.id, sclient.exactName, sclient.ip, sclient.pbid, + self.console.formatTime(sclient.timeAdd))) + + def cmd_pagear(self, data, client=None, cmd=None): + """ + [] - set the allowed gear on the server + (You can safely use the command without the 'pa' at the beginning) + """ + if not data: + self.printgear(client=client, cmd=cmd) + # display help text + client.message('^7Usage: ^3!^7pagear [+/-][%s]' % '|'.join(self._weapons.keys())) + client.message('^7Load weapon groups: ^3!^7pagear [+/-][%s]' % '|'.join(self._weapon_groups.keys())) + client.message('^7Load defaults: ^3!^7pagear [%s]' % '|'.join(self._gears.keys())) + return + + def update_gear(gear_set, param_data): + """ + update gear_set given the param_data + + @param gear_set: set of letters representing the g_gear cvar value + @param param_data: !pagear command parameter representing a weapon/item name/preset/group + """ + cleaned_data = re.sub(r'\s', "", param_data) + + # set a predefined gear + if cleaned_data in self._gears.keys(): + gear_set.clear() + gear_set.add(self._gears[cleaned_data]) + return + + # add a specific weapon to the current gear string + if cleaned_data[:1] in ('+', '-'): + opt = cleaned_data[:1] + weapon_codes = self.get_weapon_code(cleaned_data[1:]) + + if not weapon_codes: + client.message("could not recognize weapon/item %r" % cleaned_data[1:]) + return + + for weapon_code in weapon_codes: + if opt == '+': + gear_set.discard(weapon_code) + if opt == '-': + gear_set.add(weapon_code) + + current_gear_set = set(self.console.getCvar('g_gear').getString()) + new_gear_set = set(current_gear_set) + for m in re.finditer(r"(all|none|reset|[+-]\s*[\w.]+)", data.strip().lower()): + update_gear(new_gear_set, m.group()) + + if current_gear_set == new_gear_set: + client.message('^7gear ^1not ^7changed') + return + + new_gear_cvar = "".join(sorted(new_gear_set)) + self.console.setCvar('g_gear', new_gear_cvar) + self.printgear(client=client, cmd=cmd, gearstr=new_gear_cvar) + + #### + ## override iourt41 command since Urban Terror 4.2 now provides a /rcon swap command + def cmd_paswap(self, data, client, cmd=None): + """ + [player2] - Swap two teams for 2 clients. If player2 is not specified, the admin + using the command is swapped with player1. Doesn't work with spectators (exception for calling admin). + """ + # check the input + args = self._adminPlugin.parseUserCmd(data) + # check for input. If none, exist with a message. + if args: + # check if the first player exists. If none, exit. + client1 = self._adminPlugin.findClientPrompt(args[0], client) + if not client1: + return + else: + client.message("Invalid parameters, try !help paswap") + return + + # if the specified player doesn't exist, exit. + if args[1] is not None: + client2 = self._adminPlugin.findClientPrompt(args[1], client) + if not client2: + return + else: + client2 = client + + if client1.team == b3.TEAM_SPEC: + client.message("%s is a spectator! - Can't be swapped" % client1.name) + return + + if client2.team == b3.TEAM_SPEC: + client.message("%s is a spectator! - Can't be swapped" % client2.name) + return + + if client1.team == client2.team: + client.message("%s and %s are on the same team! - Swapped them anyway :p" % (client1.name, client2.name)) + return + + # /rcon swap + self.console.write('swap %s %s' % (client1.cid, client2.cid)) + + # No need to send the message twice to the switching admin :-) + + if client1 != client: + client1.message("^4You were swapped with %s by the admin" % client2.name) + + if client2 != client: + client2.message("^4You were swapped with %s by the admin" % client1.name) + + client.message("^3Successfully swapped %s and %s" % (client1.name, client2.name)) + + def cmd_pacaptain(self, data, client, cmd=None): + """ + [] - Set the given client as the captain for its team + (You can safely use the command without the 'pa' at the beginning) + """ + if not self._matchmode: + client.message("!pacaptain command is available only in match mode") + return + + if not data: + sclient = client + else: + sclient = self._adminPlugin.findClientPrompt(data, client) + if not sclient: + return + + if sclient.team == b3.TEAM_SPEC: + client.message("%s is a spectator! - Can't set captain status" % sclient.name) + return + + self.console.write("forcecaptain %s" % sclient.cid) + + # only give notice if the client is not the admin who issued the command: + # urban terror already display a server message when the captain flag is changed + if sclient != client: + team = "^1RED" if sclient.team == b3.TEAM_RED else "^4BLUE" + sclient.message("^7You were set as captain for the %s ^7team by the Admin" % team) + + def cmd_pasub(self, data, client, cmd=None): + """ + [] - set the given client as a substitute for its team + (You can safely use the command without the 'pa' at the beginning) + """ + if not self._matchmode: + client.message("!pasub command is available only in match mode") + return + + if not data: + sclient = client + else: + sclient = self._adminPlugin.findClientPrompt(data, client) + if not sclient: + return + + if sclient.team == b3.TEAM_SPEC: + client.message("%s is a spectator! - Can't set substitute status" % sclient.name) + return + + self.console.write("forcesub %s" % sclient.cid) + + # only give notice if the client is not the admin who issued the command: + # urban terror already display a server message when the substitute flag is changed + if sclient != client: + team = "^1RED" if sclient.team == b3.TEAM_RED else "^4BLUE" + sclient.message("^7You were set as substitute for the %s ^7team by the Admin" % team) + + ############################################################################################### + # + # Other methods + # + ############################################################################################### + + def printgear(self, client, cmd, gearstr=None): + """ + Print the current gear in the game chat + """ + if not gearstr: + # if not explicitly passed get it form the server + gearstr = self.console.getCvar('g_gear').getString() + + lines = [] + for key in self._weapons.keys(): + lines.append('%s:%s' % (key, '^2ON' if self._weapons[key] not in gearstr else '^1OFF')) + + cmd.sayLoudOrPM(client, '^3current gear: ^7%s' % '^7, '.join(lines)) + + def getTime(self): + """ just to ease automated tests """ + return self.console.time() + + def get_weapon_code(self, name): + """ + try its best to guess the weapon code given a name. + If name is a group name, then return multiple weapon codes as a string + """ + if name in self._weapon_groups.keys(): + return self._weapon_groups[name] + name_tries = [name[:length] for length in (5, 4, 3, 2)] + for _name in name_tries: + if _name in self._weapons.keys(): + return self._weapons[_name] + for _name in name_tries: + if _name in self._weapon_aliases.keys(): + key = self._weapon_aliases[_name] + return self._weapons[key] diff --git a/poweradminurt/tests/__init__.py b/poweradminurt/tests/__init__.py new file mode 100644 index 0000000..cceb160 --- /dev/null +++ b/poweradminurt/tests/__init__.py @@ -0,0 +1,26 @@ +import b3.output # do not remove, needed because the module alters some defaults of the logging module +import logging + + +class logging_disabled(object): + """ + context manager that temporarily disable logging. + + USAGE: + with logging_disabled(): + # do stuff + """ + DISABLED = False + + def __init__(self): + self.nested = logging_disabled.DISABLED + + def __enter__(self): + if not self.nested: + logging.getLogger('output').propagate = False + logging_disabled.DISABLED = True + + def __exit__(self, exc_type, exc_val, exc_tb): + if not self.nested: + logging.getLogger('output').propagate = True + logging_disabled.DISABLED = False diff --git a/tests/common/README b/poweradminurt/tests/common/README similarity index 100% rename from tests/common/README rename to poweradminurt/tests/common/README diff --git a/tests/__init__.py b/poweradminurt/tests/common/__init__.py similarity index 100% rename from tests/__init__.py rename to poweradminurt/tests/common/__init__.py diff --git a/tests/common/test_cmd_nuke.py b/poweradminurt/tests/common/test_cmd_nuke.py similarity index 81% rename from tests/common/test_cmd_nuke.py rename to poweradminurt/tests/common/test_cmd_nuke.py index d69cc73..383aa27 100644 --- a/tests/common/test_cmd_nuke.py +++ b/poweradminurt/tests/common/test_cmd_nuke.py @@ -6,8 +6,8 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt41 import Iourt41TestCase -from tests.iourt42 import Iourt42TestCase +from ..iourt41 import Iourt41TestCase +from ..iourt42 import Iourt42TestCase class mixin_cmd_nuke(object): def setUp(self): @@ -18,12 +18,7 @@ def setUp(self): panuke-nuke: 20 """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() diff --git a/poweradminurt/tests/common/test_cmd_paset.py b/poweradminurt/tests/common/test_cmd_paset.py new file mode 100644 index 0000000..6965ebb --- /dev/null +++ b/poweradminurt/tests/common/test_cmd_paset.py @@ -0,0 +1,72 @@ +# -*- encoding: utf-8 -*- +from mock import patch, Mock, call +import time +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt41 import Iourt41TestCase +from ..iourt42 import Iourt42TestCase +from poweradminurt import __version__ as plugin_version, __author__ as plugin_author + + +class mixin_cmd_paset(object): + def setUp(self): + super(mixin_cmd_paset, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +paset: 20 + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + + self.sleep_patcher = patch.object(time, 'sleep') + self.sleep_patcher.start() + self.setCvar_patcher = patch.object(self.console, 'setCvar') + self.setCvar_mock = self.setCvar_patcher.start() + + self.moderator.connects("2") + + def assert_setCvar_calls(self, expected_calls): + self.assertListEqual(expected_calls, self.setCvar_mock.mock_calls) + + def tearDown(self): + super(mixin_cmd_paset, self).tearDown() + self.sleep_patcher.stop() + self.setCvar_patcher.stop() + + + def test_nominal(self): + # WHEN + self.moderator.says('!paset sv_foo bar') + # THEN + self.assert_setCvar_calls([call('sv_foo', 'bar')]) + self.assertListEqual([], self.moderator.message_history) + + def test_no_parameter(self): + # WHEN + self.moderator.says('!paset') + # THEN + self.assert_setCvar_calls([]) + self.assertListEqual(['Invalid or missing data, try !help paset'], self.moderator.message_history) + + def test_no_value(self): + # WHEN + self.moderator.says('!paset sv_foo') + # THEN + self.assert_setCvar_calls([call('sv_foo', '')]) + self.assertListEqual([], self.moderator.message_history) + + +class Test_cmd_nuke_41(mixin_cmd_paset, Iourt41TestCase): + """ + call the mixin test using the Iourt41TestCase parent class + """ + +class Test_cmd_nuke_42(mixin_cmd_paset, Iourt42TestCase): + """ + call the mixin test using the Iourt42TestCase parent class + """ diff --git a/tests/common/test_cmd_pasetnextmap.py b/poweradminurt/tests/common/test_cmd_pasetnextmap.py similarity index 75% rename from tests/common/test_cmd_pasetnextmap.py rename to poweradminurt/tests/common/test_cmd_pasetnextmap.py index 184f9e3..0efe6cf 100644 --- a/tests/common/test_cmd_pasetnextmap.py +++ b/poweradminurt/tests/common/test_cmd_pasetnextmap.py @@ -5,8 +5,8 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt41 import Iourt41TestCase -from tests.iourt42 import Iourt42TestCase +from ..iourt41 import Iourt41TestCase +from ..iourt42 import Iourt42TestCase class mixin_cmd_pasetnextmap(object): @@ -18,12 +18,7 @@ def setUp(self): pasetnextmap-snmap: 20 """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() @@ -45,7 +40,7 @@ def tearDown(self): def test_missing_parameter(self): self.moderator.clearMessageHistory() self.moderator.says("!snmap") - self.assertEqual(['Invalid or missing data, try !help setnextmap'], self.moderator.message_history) + self.assertEqual(['Invalid or missing data, try !help pasetnextmap'], self.moderator.message_history) def test_existing_map(self): diff --git a/tests/common/test_cmd_paversion.py b/poweradminurt/tests/common/test_cmd_paversion.py similarity index 72% rename from tests/common/test_cmd_paversion.py rename to poweradminurt/tests/common/test_cmd_paversion.py index 31cbd0c..7cb2aa4 100644 --- a/tests/common/test_cmd_paversion.py +++ b/poweradminurt/tests/common/test_cmd_paversion.py @@ -5,8 +5,8 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt41 import Iourt41TestCase -from tests.iourt42 import Iourt42TestCase +from ..iourt41 import Iourt41TestCase +from ..iourt42 import Iourt42TestCase from poweradminurt import __version__ as plugin_version, __author__ as plugin_author @@ -19,12 +19,7 @@ def setUp(self): paversion-version: 20 """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() diff --git a/tests/common/test_config.py b/poweradminurt/tests/common/test_config.py similarity index 54% rename from tests/common/test_config.py rename to poweradminurt/tests/common/test_config.py index 4bfc1f2..5795978 100644 --- a/tests/common/test_config.py +++ b/poweradminurt/tests/common/test_config.py @@ -1,13 +1,10 @@ # -*- encoding: utf-8 -*- import logging -from mockito import when -from b3.cvar import Cvar -from poweradminurt import PoweradminurtPlugin, __file__ as poweradminurt_file +from poweradminurt import PoweradminurtPlugin from b3.config import CfgConfigParser - -from tests.iourt41 import Iourt41TestCase -from tests.iourt42 import Iourt42TestCase +from ..iourt41 import Iourt41TestCase +from ..iourt42 import Iourt42TestCase class mixin_conf(object): @@ -15,19 +12,10 @@ def setUp(self): super(mixin_conf, self).setUp() self.conf = CfgConfigParser() self.p = PoweradminurtPlugin(self.console, self.conf) - - # when starting the PoweradminurtPlugin expects the game server to provide a few cvar values - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) - + self.init_default_cvar() logger = logging.getLogger('output') logger.setLevel(logging.INFO) - - def test_empty_config(self): self.conf.loadFromString(""" [foo] @@ -35,7 +23,6 @@ def test_empty_config(self): self.p.onLoadConfig() # should not raise any error - ####################################### matchmode ####################################### def test_matchmode__plugins_disable(self): @@ -44,28 +31,24 @@ def test_matchmode__plugins_disable(self): [matchmode] plugins_disable: """) - self.p.LoadMatchMode() - self.assertEqual([], self.p.match_plugin_disable) + self.p.loadMatchMode() + self.assertEqual([], self.p._match_plugin_disable) # one element self.conf.loadFromString(""" [matchmode] plugins_disable: foo """) - self.p.LoadMatchMode() - self.assertEqual(['foo'], self.p.match_plugin_disable) + self.p.loadMatchMode() + self.assertEqual(['foo'], self.p._match_plugin_disable) # many self.conf.loadFromString(""" [matchmode] plugins_disable: foo, bar """) - self.p.LoadMatchMode() - self.assertEqual(['foo', 'bar'], self.p.match_plugin_disable) - - - - + self.p.loadMatchMode() + self.assertEqual(['foo', 'bar'], self.p._match_plugin_disable) ############################################################################## diff --git a/tests/common/test_name_checker.py b/poweradminurt/tests/common/test_name_checker.py similarity index 84% rename from tests/common/test_name_checker.py rename to poweradminurt/tests/common/test_name_checker.py index 588f0f0..9d594b5 100644 --- a/tests/common/test_name_checker.py +++ b/poweradminurt/tests/common/test_name_checker.py @@ -6,8 +6,8 @@ from b3.cvar import Cvar from b3.fake import FakeClient from poweradminurt import PoweradminurtPlugin -from tests.iourt41 import Iourt41TestCase -from tests.iourt42 import Iourt42TestCase +from ..iourt41 import Iourt41TestCase +from ..iourt42 import Iourt42TestCase class mixin_name_checker(object): def setUp(self): @@ -20,12 +20,7 @@ def setUp(self): checkbadnames: True """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() diff --git a/poweradminurt/tests/iourt41/__init__.py b/poweradminurt/tests/iourt41/__init__.py new file mode 100644 index 0000000..e1f0650 --- /dev/null +++ b/poweradminurt/tests/iourt41/__init__.py @@ -0,0 +1,69 @@ +import unittest2 as unittest + +from b3.cvar import Cvar +from mockito import when +from b3 import TEAM_UNKNOWN +from b3.config import XmlConfigParser +from b3.parsers.iourt41 import Iourt41Parser +from b3.plugins.admin import AdminPlugin +from b3.update import B3version +from b3 import __version__ as b3_version +from tests import logging_disabled + + +class Iourt41TestCase(unittest.TestCase): + """ + Test case that is suitable for testing Iourt41 parser specific features + """ + + @classmethod + def setUpClass(cls): + with logging_disabled(): + from b3.parsers.q3a.abstractParser import AbstractParser + from b3.fake import FakeConsole + AbstractParser.__bases__ = (FakeConsole,) + # Now parser inheritance hierarchy is : + # Iourt41Parser -> abstractParser -> FakeConsole -> Parser + + def setUp(self): + with logging_disabled(): + # create a Iourt41 parser + self.parser_conf = XmlConfigParser() + self.parser_conf.loadFromString("""""") + self.console = Iourt41Parser(self.parser_conf) + self.console.startup() + + # load the admin plugin + if B3version(b3_version) >= B3version("1.10dev"): + admin_plugin_conf_file = '@b3/conf/plugin_admin.ini' + else: + admin_plugin_conf_file = '@b3/conf/plugin_admin.xml' + with logging_disabled(): + self.adminPlugin = AdminPlugin(self.console, admin_plugin_conf_file) + self.adminPlugin.onLoadConfig() + self.adminPlugin.onStartup() + + # make sure the admin plugin obtained by other plugins is our admin plugin + when(self.console).getPlugin('admin').thenReturn(self.adminPlugin) + + # prepare a few players + from b3.fake import FakeClient + self.joe = FakeClient(self.console, name="Joe", exactName="Joe", guid="zaerezarezar", groupBits=1, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.simon = FakeClient(self.console, name="Simon", exactName="Simon", guid="qsdfdsqfdsqf", groupBits=0, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.reg = FakeClient(self.console, name="Reg", exactName="Reg", guid="qsdfdsqfdsqf33", groupBits=4, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.moderator = FakeClient(self.console, name="Moderator", exactName="Moderator", guid="sdf455ezr", groupBits=8, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.admin = FakeClient(self.console, name="Level-40-Admin", exactName="Level-40-Admin", guid="875sasda", groupBits=16, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.superadmin = FakeClient(self.console, name="God", exactName="God", guid="f4qfer654r", groupBits=128, team=TEAM_UNKNOWN, teamId=0, squad=0) + + def tearDown(self): + self.console.working = False +# sys.stdout.write("\tactive threads count : %s " % threading.activeCount()) +# sys.stderr.write("%s\n" % threading.enumerate()) + + def init_default_cvar(self): + when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) + when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) + when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) + when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) + when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + when(self.console).getCvar('g_gear').thenReturn(Cvar('g_gear', value='')) \ No newline at end of file diff --git a/poweradminurt/tests/iourt41/test_cmd_ident.py b/poweradminurt/tests/iourt41/test_cmd_ident.py new file mode 100644 index 0000000..d71205b --- /dev/null +++ b/poweradminurt/tests/iourt41/test_cmd_ident.py @@ -0,0 +1,64 @@ +# -*- encoding: utf-8 -*- +from mock import Mock, patch +from b3.config import CfgConfigParser +from poweradminurt import PoweradminurtPlugin +from ..iourt41 import Iourt41TestCase + + +class Test_cmd_ident(Iourt41TestCase): + def setUp(self): + super(Test_cmd_ident, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +paident-id: 20 + +[special] +paident_full_level: 40 + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.parser_conf._settings.update({'b3': {"time_zone": "GMT", "time_format": "%I:%M%p %Z %m/%d/%y"}}) + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + self.moderator.message_history = [] + + def test_no_parameter(self): + # WHEN + self.moderator.says("!id") + # THEN + self.assertListEqual(["Your id is @2"], self.moderator.message_history) + + def test_junk(self): + # WHEN + self.moderator.says("!id qsdfsqdq sqfd qf") + # THEN + self.assertListEqual(["No players found matching qsdfsqdq"], self.moderator.message_history) + + def test_nominal_under_full_level(self): + # GIVEN + self.joe.pbid = "joe_pbid" + self.joe.connects('3') + # WHEN + with patch('time.time', return_value=0.0) as time_mock: + self.moderator.says("!id joe") + # THEN + self.assertListEqual(['12:00AM GMT 01/01/70 @3 Joe'], self.moderator.message_history) + + def test_nominal_above_full_level(self): + # GIVEN + self.joe.pbid = "joe_pbid" + self.joe.connects('3') + self.joe.timeAdd = 90*60.0 + self.superadmin.connects('1') + # WHEN + with patch('time.time', return_value=180*60.0): + self.superadmin.says("!id joe") + # THEN + self.assertListEqual(['03:00AM GMT 01/01/70 @3 Joe 01:30AM GMT 01/01/70'], self.superadmin.message_history) + diff --git a/poweradminurt/tests/iourt41/test_cmd_pagear.py b/poweradminurt/tests/iourt41/test_cmd_pagear.py new file mode 100644 index 0000000..b553f3a --- /dev/null +++ b/poweradminurt/tests/iourt41/test_cmd_pagear.py @@ -0,0 +1,175 @@ +# -*- encoding: utf-8 -*- +from mock import patch, call +import time +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt41 import Iourt41TestCase + +G_NADES = 0b000001 +G_SNIPERS = 0b000010 +G_SPAS = 0b000100 +G_PISTOLS = 0b001000 +G_RIFLES = 0b010000 +G_NEGEV = 0b100000 +G_ALL = 0b111111 +G_NONE = 0b000000 + +class Test_cmd_pagear(Iourt41TestCase): + + def setUp(self): + super(Test_cmd_pagear, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pagear-gear: 20 + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + + when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) + when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) + when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) + when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) + when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + when(self.console).getCvar('g_modversion').thenReturn(Cvar('g_modversion', value="4.1")) + self.given_forbidden_weapon_are(G_NONE) + self.p.onLoadConfig() + self.p.onStartup() + + self.sleep_patcher = patch.object(time, 'sleep') + self.sleep_patcher.start() + self.setCvar_patcher = patch.object(self.console, 'setCvar') + self.setCvar_mock = self.setCvar_patcher.start() + + self.superadmin.connects("2") + + def tearDown(self): + super(Test_cmd_pagear, self).tearDown() + self.sleep_patcher.stop() + self.setCvar_patcher.stop() + + def given_forbidden_weapon_are(self, g_gear_value): + when(self.console).getCvar('g_gear').thenReturn(Cvar('g_gear', value=str(g_gear_value))) + + def assert_set_gear(self, expected_gear_value): + self.assertListEqual([call('g_gear', str(expected_gear_value))], self.setCvar_mock.mock_calls) + + def test_reset(self): + # GIVEN + self.given_forbidden_weapon_are("1234") + self.p.onStartup() + # WHEN + self.superadmin.says("!gear reset") + # THEN + self.assert_set_gear('1234') + + def test_all(self): + # WHEN + self.superadmin.says("!gear all") + # THEN + self.assert_set_gear(G_NONE) + + def test_none(self): + # WHEN + self.superadmin.says("!gear none") + # THEN + self.assert_set_gear(G_ALL) + + def test_forbid_nades(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -nade") + # THEN + self.assert_set_gear(G_NADES) + + def test_allow_nades(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +nade") + # THEN + self.assert_set_gear(G_ALL - G_NADES) + + def test_forbid_snipers(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -snip") + # THEN + self.assert_set_gear(G_SNIPERS) + + def test_allow_snipers(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +snip") + # THEN + self.assert_set_gear(G_ALL - G_SNIPERS) + + def test_forbid_spas(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -spas") + # THEN + self.assert_set_gear(G_SPAS) + + def test_allow_spas(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +spas") + # THEN + self.assert_set_gear(G_ALL - G_SPAS) + + def test_forbid_pistols(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -pist") + # THEN + self.assert_set_gear(G_PISTOLS) + + def test_allow_pistols(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +pist") + # THEN + self.assert_set_gear(G_ALL - G_PISTOLS) + + + def test_forbid_auto(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -auto") + # THEN + self.assert_set_gear(G_RIFLES) + + def test_allow_auto(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +auto") + # THEN + self.assert_set_gear(G_ALL - G_RIFLES) + + def test_forbid_negev(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -negev") + # THEN + self.assert_set_gear(G_NEGEV) + + def test_allow_negev(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +negev") + # THEN + self.assert_set_gear(G_ALL - G_NEGEV) + diff --git a/tests/iourt41/test_headshotcounter.py b/poweradminurt/tests/iourt41/test_headshotcounter.py similarity index 76% rename from tests/iourt41/test_headshotcounter.py rename to poweradminurt/tests/iourt41/test_headshotcounter.py index e801710..f3ab6c5 100644 --- a/tests/iourt41/test_headshotcounter.py +++ b/poweradminurt/tests/iourt41/test_headshotcounter.py @@ -4,7 +4,7 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt41 import Iourt41TestCase +from ..iourt41 import Iourt41TestCase class Test_headshotcounter(Iourt41TestCase): def setUp(self): @@ -34,12 +34,7 @@ def setUp(self): warn_kevlar_nr: 50 """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() @@ -65,22 +60,22 @@ def assertCounts(head, helmet, torso): self.simon.connects("7") # WHEN - joe_hits_simon(4) + joe_hits_simon('4') # THEN assertCounts(head=0.0, helmet=0.0, torso=0.0) # WHEN - joe_hits_simon(0) # 1 is head on 4.1 + joe_hits_simon('0') # THEN assertCounts(head=1.0, helmet=0.0, torso=0.0) # WHEN - joe_hits_simon(1) # 4 is helmet on 4.1 + joe_hits_simon('1') # THEN assertCounts(head=1.0, helmet=1.0, torso=0.0) # WHEN - joe_hits_simon(2) # 5 is torso on 4.1 + joe_hits_simon('2') # THEN assertCounts(head=1.0, helmet=1.0, torso=1.0) diff --git a/poweradminurt/tests/iourt42/__init__.py b/poweradminurt/tests/iourt42/__init__.py new file mode 100644 index 0000000..d7a23ee --- /dev/null +++ b/poweradminurt/tests/iourt42/__init__.py @@ -0,0 +1,79 @@ +import sys +from b3.cvar import Cvar + +if sys.version_info[:2] < (2, 7): + import unittest2 as unittest +else: + import unittest +from mockito import when +from tests import logging_disabled +from b3 import TEAM_UNKNOWN, __version__ as b3__version__ +from b3.config import XmlConfigParser +from b3.plugins.admin import AdminPlugin +from b3.update import B3version +from b3 import __version__ as b3_version +try: + from b3.parsers.iourt42 import Iourt42Parser +except ImportError: + HAS_IOURT42_PARSER = False +else: + HAS_IOURT42_PARSER = True + + +@unittest.skipUnless(HAS_IOURT42_PARSER, "B3 %s does not have the iourt42 parser" % b3__version__) +class Iourt42TestCase(unittest.TestCase): + """ + Test case that is suitable for testing Iourt41 parser specific features + """ + + @classmethod + def setUpClass(cls): + with logging_disabled(): + from b3.parsers.q3a.abstractParser import AbstractParser + from b3.fake import FakeConsole + AbstractParser.__bases__ = (FakeConsole,) + # Now parser inheritance hierarchy is : + # Iourt41Parser -> abstractParser -> FakeConsole -> Parser + + def setUp(self): + with logging_disabled(): + # create a Iourt41 parser + self.parser_conf = XmlConfigParser() + self.parser_conf.loadFromString("""""") + self.console = Iourt42Parser(self.parser_conf) + self.console.startup() + + # load the admin plugin + if B3version(b3_version) >= B3version("1.10dev"): + admin_plugin_conf_file = '@b3/conf/plugin_admin.ini' + else: + admin_plugin_conf_file = '@b3/conf/plugin_admin.xml' + with logging_disabled(): + self.adminPlugin = AdminPlugin(self.console, admin_plugin_conf_file) + self.adminPlugin.onLoadConfig() + self.adminPlugin.onStartup() + + # make sure the admin plugin obtained by other plugins is our admin plugin + when(self.console).getPlugin('admin').thenReturn(self.adminPlugin) + + # prepare a few players + from b3.fake import FakeClient + self.joe = FakeClient(self.console, name="Joe", exactName="Joe", guid="zaerezarezar", groupBits=1, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.simon = FakeClient(self.console, name="Simon", exactName="Simon", guid="qsdfdsqfdsqf", groupBits=0, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.reg = FakeClient(self.console, name="Reg", exactName="Reg", guid="qsdfdsqfdsqf33", groupBits=4, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.moderator = FakeClient(self.console, name="Moderator", exactName="Moderator", guid="sdf455ezr", groupBits=8, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.admin = FakeClient(self.console, name="Level-40-Admin", exactName="Level-40-Admin", guid="875sasda", groupBits=16, team=TEAM_UNKNOWN, teamId=0, squad=0) + self.superadmin = FakeClient(self.console, name="God", exactName="God", guid="f4qfer654r", groupBits=128, team=TEAM_UNKNOWN, teamId=0, squad=0) + + def tearDown(self): + self.console.working = False +# sys.stdout.write("\tactive threads count : %s " % threading.activeCount()) +# sys.stderr.write("%s\n" % threading.enumerate()) + + def init_default_cvar(self): + when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) + when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) + when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) + when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) + when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + when(self.console).getCvar('g_gear').thenReturn(Cvar('g_gear', value='')) \ No newline at end of file diff --git a/poweradminurt/tests/iourt42/test_cmd_captain.py b/poweradminurt/tests/iourt42/test_cmd_captain.py new file mode 100644 index 0000000..8a11221 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_captain.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +from b3 import TEAM_SPEC +from b3 import TEAM_RED +from b3 import TEAM_BLUE +from mock import call, Mock +from b3.config import CfgConfigParser +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_captain(Iourt42TestCase): + def setUp(self): + super(Test_cmd_captain, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pacaptain-captain: 40 ; set the the given client as the captain for its team + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + self.console.say = Mock() + self.console.write = Mock() + self.admin.connects("2") + self.moderator.connects("3") + + def test_match_mode_deactivated(self): + self.p._matchmode = False + self.admin.message_history = [] + self.admin.says("!captain") + self.assertListEqual(["!pacaptain command is available only in match mode"], self.admin.message_history) + + def test_client_spectator(self): + self.p._matchmode = True + self.admin.message_history = [] + self.admin.team = TEAM_SPEC + self.admin.says("!captain") + self.assertListEqual(["Level-40-Admin is a spectator! - Can't set captain status"], self.admin.message_history) + + def test_client_with_no_parameters(self): + self.p._matchmode = True + self.admin.message_history = [] + self.admin.team = TEAM_RED + self.admin.says("!captain") + self.console.write.assert_has_calls([call('forcecaptain %s' % self.admin.cid)]) + + def test_client_with_parameters(self): + self.p._matchmode = True + self.admin.message_history = [] + self.admin.team = TEAM_RED + self.moderator.message_history = [] + self.moderator.team = TEAM_BLUE + self.admin.says("!captain 3") + self.console.write.assert_has_calls([call('forcecaptain %s' % self.moderator.cid)]) + self.assertListEqual(["You were set as captain for the BLUE team by the Admin"], self.moderator.message_history) diff --git a/poweradminurt/tests/iourt42/test_cmd_funstuff.py b/poweradminurt/tests/iourt42/test_cmd_funstuff.py new file mode 100644 index 0000000..81d3375 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_funstuff.py @@ -0,0 +1,46 @@ +# -*- encoding: utf-8 -*- +from mock import call, Mock +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_funstuff(Iourt42TestCase): + def setUp(self): + super(Test_cmd_funstuff, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pafunstuff-funstuff: 20 ; set the use of funstuff + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + + def test_missing_parameter(self): + self.moderator.message_history = [] + self.moderator.says("!funstuff") + self.assertListEqual(["Invalid or missing data, try !help pafunstuff"], self.moderator.message_history) + + def test_junk(self): + self.moderator.message_history = [] + self.moderator.says("!funstuff qsdf") + self.assertListEqual(["Invalid or missing data, try !help pafunstuff"], self.moderator.message_history) + + def test_on(self): + self.moderator.says("!funstuff on") + self.console.write.assert_has_calls([call('set g_funstuff "1"')]) + + def test_off(self): + self.moderator.says("!funstuff off") + self.console.write.assert_has_calls([call('set g_funstuff "0"')]) + + diff --git a/poweradminurt/tests/iourt42/test_cmd_goto.py b/poweradminurt/tests/iourt42/test_cmd_goto.py new file mode 100644 index 0000000..5d2c746 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_goto.py @@ -0,0 +1,46 @@ +# -*- encoding: utf-8 -*- +from mock import call, Mock +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_goto(Iourt42TestCase): + def setUp(self): + super(Test_cmd_goto, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +paskins-skins: 20 ; set the use of client skins + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + + def test_missing_parameter(self): + self.moderator.message_history = [] + self.moderator.says("!skins") + self.assertListEqual(["Invalid or missing data, try !help paskins"], self.moderator.message_history) + + def test_junk(self): + self.moderator.message_history = [] + self.moderator.says("!skins qsdf") + self.assertListEqual(["Invalid or missing data, try !help paskins"], self.moderator.message_history) + + def test_on(self): + self.moderator.says("!skins on") + self.console.write.assert_has_calls([call('set g_skins "1"')]) + + def test_off(self): + self.moderator.says("!skins off") + self.console.write.assert_has_calls([call('set g_skins "0"')]) + + diff --git a/poweradminurt/tests/iourt42/test_cmd_ident.py b/poweradminurt/tests/iourt42/test_cmd_ident.py new file mode 100644 index 0000000..55466dc --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_ident.py @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +from mock import call, Mock, patch +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_ident(Iourt42TestCase): + def setUp(self): + super(Test_cmd_ident, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +paident-id: 20 + +[special] +paident_full_level: 40 + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.parser_conf._settings.update({'b3': {"time_zone": "GMT", "time_format": "%I:%M%p %Z %m/%d/%y"}}) + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + self.moderator.message_history = [] + + def test_no_parameter(self): + # WHEN + self.moderator.says("!id") + # THEN + self.assertListEqual(["Your id is @2"], self.moderator.message_history) + + def test_junk(self): + # WHEN + self.moderator.says("!id qsdfsqdq sqfd qf") + # THEN + self.assertListEqual(["No players found matching qsdfsqdq"], self.moderator.message_history) + + def test_nominal_under_full_level(self): + # GIVEN + self.joe.pbid = "joe_pbid" + self.joe.connects('3') + # WHEN + with patch('time.time', return_value=0.0) as time_mock: + self.moderator.says("!id joe") + # THEN + self.assertListEqual(['12:00AM GMT 01/01/70 @3 Joe'], self.moderator.message_history) + + def test_nominal_above_full_level(self): + # GIVEN + self.joe.pbid = "joe_pbid" + self.joe.connects('3') + self.joe.timeAdd = 90*60.0 + self.superadmin.connects('1') + # WHEN + with patch('time.time', return_value=180*60.0): + self.superadmin.says("!id joe") + # THEN + self.assertListEqual(['03:00AM GMT 01/01/70 @3 Joe [joe_pbid] since 01:30AM GMT 01/01/70'], self.superadmin.message_history) + + diff --git a/poweradminurt/tests/iourt42/test_cmd_jump.py b/poweradminurt/tests/iourt42/test_cmd_jump.py new file mode 100644 index 0000000..51c791b --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_jump.py @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- +from mock import call, Mock +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + +class Test_cmd_jump(Iourt42TestCase): + def setUp(self): + super(Test_cmd_jump, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pajump-jump: 20 ; change game type to Jump + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + + + def test_nominal(self): + self.moderator.message_history = [] + self.moderator.says("!jump") + self.console.write.assert_has_calls([call('set g_gametype "9"')]) + self.assertEqual(['game type changed to Jump'], self.moderator.message_history) + + diff --git a/tests/iourt42/test_cmd_kill.py b/poweradminurt/tests/iourt42/test_cmd_kill.py similarity index 74% rename from tests/iourt42/test_cmd_kill.py rename to poweradminurt/tests/iourt42/test_cmd_kill.py index afec766..0c6c0d8 100644 --- a/tests/iourt42/test_cmd_kill.py +++ b/poweradminurt/tests/iourt42/test_cmd_kill.py @@ -4,7 +4,7 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt42 import Iourt42TestCase +from ..iourt42 import Iourt42TestCase class Test_cmd_kill(Iourt42TestCase): def setUp(self): @@ -15,12 +15,7 @@ def setUp(self): pakill-kill: 20 """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() diff --git a/tests/iourt42/test_cmd_lms.py b/poweradminurt/tests/iourt42/test_cmd_lms.py similarity index 60% rename from tests/iourt42/test_cmd_lms.py rename to poweradminurt/tests/iourt42/test_cmd_lms.py index f544450..74c4482 100644 --- a/tests/iourt42/test_cmd_lms.py +++ b/poweradminurt/tests/iourt42/test_cmd_lms.py @@ -4,7 +4,7 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt42 import Iourt42TestCase +from ..iourt42 import Iourt42TestCase class Test_cmd_lms(Iourt42TestCase): def setUp(self): @@ -15,12 +15,7 @@ def setUp(self): palms-lms: 20 ; change game type to Last Man Standing """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() @@ -33,7 +28,7 @@ def setUp(self): def test_nominal(self): self.moderator.message_history = [] self.moderator.says("!lms") - self.console.write.assert_has_calls([call('g_gametype 1')]) + self.console.write.assert_has_calls([call('set g_gametype "1"')]) self.assertEqual(['game type changed to Last Man Standing'], self.moderator.message_history) diff --git a/poweradminurt/tests/iourt42/test_cmd_pagear.py b/poweradminurt/tests/iourt42/test_cmd_pagear.py new file mode 100644 index 0000000..c0f5b34 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_pagear.py @@ -0,0 +1,378 @@ +# -*- encoding: utf-8 -*- +from mock import patch, call +import time +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +G_ALL = "FGHIJKLMNZacefghOQRSTUVWX" +G_NONE = "" +G_BERETTA_92FS = "F" +G_50_DESERT_EAGLE = "G" +G_SPAS_12 = "H" +G_MP5K = "I" +G_UMP45 = "J" +G_HK69 = "K" +G_LR300ML = "L" +G_G36 = "M" +G_PSG1 = "N" +G_SR8 = "Z" +G_AK103 = "a" +G_NEGEV_LMG = "c" +G_COLT_M4 = "e" +G_GLOCK = "f" +G_COLT1911 = "g" +G_MAC11 = "h" +G_HE_GRENADE = "O" +G_SMOKE_GRENADE = "Q" +G_KEVLAR_VEST = "R" +G_GOGGLES = "S" +G_MEDKIT = "T" +G_SILENCER = "U" +G_LASER_SIGHT = "V" +G_HELMET = "W" +G_EXTRA_AMMO = "X" + +weapon_codes = ( + ('beretta', G_BERETTA_92FS), + ('beret', G_BERETTA_92FS), + ('ber', G_BERETTA_92FS), + + ('desert eagle', G_50_DESERT_EAGLE), + ('desert', G_50_DESERT_EAGLE), + ('des', G_50_DESERT_EAGLE), + ('.50', G_50_DESERT_EAGLE), + ('deagle', G_50_DESERT_EAGLE), + ('eagle', G_50_DESERT_EAGLE), + + ('spas12', G_SPAS_12), + ('spas', G_SPAS_12), + + ('mp5k', G_MP5K), + ('mp5', G_MP5K), + ('mp', G_MP5K), + + ('ump45', G_UMP45), + ('ump', G_UMP45), + + ('hk69', G_HK69), + ('hk', G_HK69), + + ('lr300ml', G_LR300ML), + ('lr300', G_LR300ML), + ('lr', G_LR300ML), + + ('g36', G_G36), + + ('psg1', G_PSG1), + ('psg', G_PSG1), + + ('sr8', G_SR8), + ('sr', G_SR8), + + ('ak103', G_AK103), + ('ak', G_AK103), + + ('negev', G_NEGEV_LMG), + ('neg', G_NEGEV_LMG), + + ('m4', G_COLT_M4), + + ('glock', G_GLOCK), + ('gloc', G_GLOCK), + ('glok', G_GLOCK), + ('glo', G_GLOCK), + + ('colt1911', G_COLT1911), + ('1911', G_COLT1911), + + ('mac11', G_MAC11), + ('mac', G_MAC11), + + ('he grenade', G_HE_GRENADE), + ('he', G_HE_GRENADE), + + ('smoke grenade', G_SMOKE_GRENADE), + ('smoke', G_SMOKE_GRENADE), + ('smo', G_SMOKE_GRENADE), + + ('kevlar vest', G_KEVLAR_VEST), + ('kevlar', G_KEVLAR_VEST), + ('kev', G_KEVLAR_VEST), + ('vest', G_KEVLAR_VEST), + + ('goggles', G_GOGGLES), + ('gog', G_GOGGLES), + ('nvg', G_GOGGLES), + + ('medkit', G_MEDKIT), + ('med', G_MEDKIT), + + ('silencer', G_SILENCER), + ('sil', G_SILENCER), + + ('laser sight', G_LASER_SIGHT), + ('laser', G_LASER_SIGHT), + ('las', G_LASER_SIGHT), + + ('helmet', G_HELMET), + ('hel', G_HELMET), + + ('extra ammo', G_EXTRA_AMMO), + ('extra', G_EXTRA_AMMO), + ('ext', G_EXTRA_AMMO), + ('ammo', G_EXTRA_AMMO), + ('amm', G_EXTRA_AMMO), +) + + +def all_gear_but(*args): + return "".join(sorted(G_ALL.translate(None, "".join(args)))) + + +def only_gear(*args): + return "".join(sorted(G_ALL.translate(None, all_gear_but(*args)))) + + +class Test_cmd_pagear(Iourt42TestCase): + + def setUp(self): + super(Test_cmd_pagear, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pagear-gear: 20 + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + + when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) + when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) + when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) + when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) + when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + when(self.console).getCvar('g_modversion').thenReturn(Cvar('g_modversion', value="4.2.018")) + self.given_forbidden_weapon_are(G_NONE) + self.p.onLoadConfig() + self.p.onStartup() + + self.sleep_patcher = patch.object(time, 'sleep') + self.sleep_patcher.start() + self.setCvar_patcher = patch.object(self.console, 'setCvar') + self.setCvar_mock = self.setCvar_patcher.start() + + self.superadmin.connects("2") + + def tearDown(self): + super(Test_cmd_pagear, self).tearDown() + self.sleep_patcher.stop() + self.setCvar_patcher.stop() + + def given_forbidden_weapon_are(self, g_gear_value): + when(self.console).getCvar('g_gear').thenReturn(Cvar('g_gear', value=str(g_gear_value))) + + def assert_set_gear(self, expected_gear_value, fail_message=None): + self.assertListEqual([call('g_gear', str(expected_gear_value))], self.setCvar_mock.mock_calls, fail_message) + + def test_no_parameter(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + self.p.onStartup() + # WHEN + self.superadmin.message_history = [] + self.superadmin.says("!gear") + # THEN + self.assertListEqual(["current gear: med:ON, vest:ON, ak:ON, de:ON, psg:ON, nvg:ON, hk:ON, mac:ON, mp5:ON, las:ON, ber:ON, ump:ON, g36:ON, neg:ON, glo:ON, smo:ON, m4:ON, lr:ON, hel:ON, spas:ON, sr8:ON, he:ON, colt:ON, ammo:ON, sil:ON", + "Usage: !pagear [+/-][med|vest|ak|de|psg|nvg|hk|mac|mp5|las|ber|ump|g36|neg|glo|smo|m4|lr|hel|spas|sr8|he|colt|ammo|sil]", + "Load weapon groups: !pagear [+/-][all_nades|all_auto|all_pistols|all_snipers]", + "Load defaults: !pagear [reset|all|none]"], + self.superadmin.message_history) + + def test_reset(self): + # GIVEN + self.given_forbidden_weapon_are("1234") + self.p.onStartup() + # WHEN + self.superadmin.says("!gear reset") + # THEN + self.assert_set_gear('1234') + + def test_all(self): + # WHEN + self.superadmin.says("!gear all") + # THEN + self.assert_set_gear(G_NONE) + + def test_none(self): + # WHEN + self.superadmin.says("!gear none") + # THEN + self.assert_set_gear(G_ALL) + + def test_unknown_weapon(self): + # WHEN + self.superadmin.says("!gear +f00") + # THEN + self.assertListEqual([], self.setCvar_mock.mock_calls) + self.assertListEqual([ + "could not recognize weapon/item 'f00'", + "gear not changed" + ], self.superadmin.message_history) + + def test_disallow_negev(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -neg") + # THEN + self.assert_set_gear(G_NEGEV_LMG) + + def test_allow_negev_short(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +neg") + # THEN + self.assert_set_gear(all_gear_but(G_NEGEV_LMG)) + + def test_allow_negev_long(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +negev") + # THEN + self.assert_set_gear(all_gear_but(G_NEGEV_LMG)) + + def test_allow_negev_long_spaced(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear + negev") + # THEN + self.assert_set_gear(all_gear_but(G_NEGEV_LMG)) + + def test_disallow(self): + for weapon_name, weapon_code in weapon_codes: + # GIVEN + self.setCvar_mock.reset_mock() + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -%s" % weapon_name) + # THEN + self.assert_set_gear(weapon_code, weapon_name) + + def test_allow(self): + for weapon_name, weapon_code in weapon_codes: + # GIVEN + self.setCvar_mock.reset_mock() + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +%s" % weapon_name) + # THEN + self.assert_set_gear(all_gear_but(weapon_code), weapon_name) + + def test_allow_group_nades(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +all_nades") + # THEN + self.assert_set_gear(all_gear_but(G_SMOKE_GRENADE, G_HE_GRENADE)) + + def test_disallow_group_nades(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -all_nades") + # THEN + self.assert_set_gear(only_gear(G_SMOKE_GRENADE, G_HE_GRENADE)) + + def test_disallow_group_nades_spaced(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear - all_nades") + # THEN + self.assert_set_gear(only_gear(G_SMOKE_GRENADE, G_HE_GRENADE)) + + def test_allow_all_snipers(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +all_snipers") + # THEN + self.assert_set_gear(all_gear_but(G_SR8, G_PSG1)) + + def test_disallow_all_snipers(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -all_snipers") + # THEN + self.assert_set_gear(only_gear(G_SR8, G_PSG1)) + + def test_allow_all_pistols(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +all_pistols") + # THEN + self.assert_set_gear(all_gear_but(G_BERETTA_92FS, G_50_DESERT_EAGLE, G_GLOCK, G_COLT1911)) + + def test_disallow_all_pistols(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -all_pistols") + # THEN + self.assert_set_gear(only_gear(G_BERETTA_92FS, G_50_DESERT_EAGLE, G_GLOCK, G_COLT1911)) + + def test_allow_all_auto(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear +all_auto") + # THEN + self.assert_set_gear(all_gear_but(G_MP5K, G_LR300ML, G_COLT_M4, G_MAC11, G_UMP45, G_G36, G_AK103, G_NEGEV_LMG)) + + def test_disallow_all_auto(self): + # GIVEN + self.given_forbidden_weapon_are(G_NONE) + # WHEN + self.superadmin.says("!gear -all_auto") + # THEN + self.assert_set_gear(only_gear(G_MP5K, G_LR300ML, G_COLT_M4, G_MAC11, G_UMP45, G_G36, G_AK103, G_NEGEV_LMG)) + + def test_disallow_all_auto_and_no_smoke(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear all -all_auto -smoke") + # THEN + self.assert_set_gear(only_gear(G_MP5K, G_LR300ML, G_COLT_M4, G_MAC11, G_UMP45, G_G36, G_AK103, G_NEGEV_LMG, G_SMOKE_GRENADE)) + + def test_disallow_all_auto_and_no_smoke_spaced(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear all - all_auto - smoke") + # THEN + self.assert_set_gear(only_gear(G_MP5K, G_LR300ML, G_COLT_M4, G_MAC11, G_UMP45, G_G36, G_AK103, G_NEGEV_LMG, G_SMOKE_GRENADE)) + + def test_disallow_all_auto_but_lr300(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear all -all_auto +lr") + # THEN + self.assert_set_gear(only_gear(G_MP5K, G_COLT_M4, G_MAC11, G_UMP45, G_G36, G_AK103, G_NEGEV_LMG)) + + def test_disallow_all_auto_but_lr300_spaced(self): + # GIVEN + self.given_forbidden_weapon_are(G_ALL) + # WHEN + self.superadmin.says("!gear all - all_auto + lr") + # THEN + self.assert_set_gear(only_gear(G_MP5K, G_COLT_M4, G_MAC11, G_UMP45, G_G36, G_AK103, G_NEGEV_LMG)) \ No newline at end of file diff --git a/poweradminurt/tests/iourt42/test_cmd_skins.py b/poweradminurt/tests/iourt42/test_cmd_skins.py new file mode 100644 index 0000000..9e1b807 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_skins.py @@ -0,0 +1,46 @@ +# -*- encoding: utf-8 -*- +from mock import call, Mock +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_skins(Iourt42TestCase): + def setUp(self): + super(Test_cmd_skins, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pagoto-goto: 20 ; set the goto + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + + def test_missing_parameter(self): + self.moderator.message_history = [] + self.moderator.says("!goto") + self.assertListEqual(["Invalid or missing data, try !help pagoto"], self.moderator.message_history) + + def test_junk(self): + self.moderator.message_history = [] + self.moderator.says("!goto qsdf") + self.assertListEqual(["Invalid or missing data, try !help pagoto"], self.moderator.message_history) + + def test_on(self): + self.moderator.says("!goto on") + self.console.write.assert_has_calls([call('set g_allowgoto "1"')]) + + def test_off(self): + self.moderator.says("!goto off") + self.console.write.assert_has_calls([call('set g_allowgoto "0"')]) + + diff --git a/poweradminurt/tests/iourt42/test_cmd_stamina.py b/poweradminurt/tests/iourt42/test_cmd_stamina.py new file mode 100644 index 0000000..f4958d7 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_stamina.py @@ -0,0 +1,50 @@ +# -*- encoding: utf-8 -*- +from mock import call, Mock +from mockito import when +from b3.config import CfgConfigParser +from b3.cvar import Cvar +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_funstuff(Iourt42TestCase): + def setUp(self): + super(Test_cmd_funstuff, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pastamina-stamina: 20 ; set the stamina behavior + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + + self.console.say = Mock() + self.console.write = Mock() + + self.moderator.connects("2") + + def test_missing_parameter(self): + self.moderator.message_history = [] + self.moderator.says("!stamina") + self.assertListEqual(["Invalid or missing data, try !help pastamina"], self.moderator.message_history) + + def test_junk(self): + self.moderator.message_history = [] + self.moderator.says("!stamina qsdf") + self.assertListEqual(["Invalid or missing data, try !help pastamina"], self.moderator.message_history) + + def test_default(self): + self.moderator.says("!stamina default") + self.console.write.assert_has_calls([call('set g_stamina "0"')]) + + def test_regain(self): + self.moderator.says("!stamina regain") + self.console.write.assert_has_calls([call('set g_stamina "1"')]) + + def test_infinite(self): + self.moderator.says("!stamina infinite") + self.console.write.assert_has_calls([call('set g_stamina "2"')]) + + diff --git a/poweradminurt/tests/iourt42/test_cmd_sub.py b/poweradminurt/tests/iourt42/test_cmd_sub.py new file mode 100644 index 0000000..bd4e552 --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_sub.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +from b3 import TEAM_SPEC +from b3 import TEAM_RED +from b3 import TEAM_BLUE +from mock import call, Mock +from b3.config import CfgConfigParser +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_sub(Iourt42TestCase): + def setUp(self): + super(Test_cmd_sub, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +pasub-sub:40 ; set the given client as a substitute for its team + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + self.console.say = Mock() + self.console.write = Mock() + self.admin.connects("2") + self.moderator.connects("3") + + def test_match_mode_deactivated(self): + self.p._matchmode = False + self.admin.message_history = [] + self.admin.says("!sub") + self.assertListEqual(["!pasub command is available only in match mode"], self.admin.message_history) + + def test_client_spectator(self): + self.p._matchmode = True + self.admin.message_history = [] + self.admin.team = TEAM_SPEC + self.admin.says("!sub") + self.assertListEqual(["Level-40-Admin is a spectator! - Can't set substitute status"], self.admin.message_history) + + def test_client_with_no_parameters(self): + self.p._matchmode = True + self.admin.message_history = [] + self.admin.team = TEAM_RED + self.admin.says("!sub") + self.console.write.assert_has_calls([call('forcesub %s' % self.admin.cid)]) + + def test_client_with_parameters(self): + self.p._matchmode = True + self.admin.message_history = [] + self.admin.team = TEAM_RED + self.moderator.message_history = [] + self.moderator.team = TEAM_BLUE + self.admin.says("!sub 3") + self.console.write.assert_has_calls([call('forcesub %s' % self.moderator.cid)]) + self.assertListEqual(["You were set as substitute for the BLUE team by the Admin"], self.moderator.message_history) diff --git a/poweradminurt/tests/iourt42/test_cmd_swap.py b/poweradminurt/tests/iourt42/test_cmd_swap.py new file mode 100644 index 0000000..d063efa --- /dev/null +++ b/poweradminurt/tests/iourt42/test_cmd_swap.py @@ -0,0 +1,31 @@ +# -*- encoding: utf-8 -*- +from b3 import TEAM_RED +from b3 import TEAM_BLUE +from mock import call, Mock +from b3.config import CfgConfigParser +from poweradminurt import PoweradminurtPlugin +from ..iourt42 import Iourt42TestCase + + +class Test_cmd_swap(Iourt42TestCase): + def setUp(self): + super(Test_cmd_swap, self).setUp() + self.conf = CfgConfigParser() + self.conf.loadFromString(""" +[commands] +paswap-swap: 20 + """) + self.p = PoweradminurtPlugin(self.console, self.conf) + self.init_default_cvar() + self.p.onLoadConfig() + self.p.onStartup() + self.console.say = Mock() + self.console.write = Mock() + self.admin.connects("2") + self.moderator.connects("3") + + def test_plugin_using_overridden_command_method(self): + self.admin.team = TEAM_RED + self.moderator.team = TEAM_BLUE + self.admin.says("!swap 2 3") + self.console.write.assert_has_calls([call('swap %s %s' % (self.admin.cid, self.moderator.cid))]) diff --git a/tests/iourt42/test_headshotcounter.py b/poweradminurt/tests/iourt42/test_headshotcounter.py similarity index 76% rename from tests/iourt42/test_headshotcounter.py rename to poweradminurt/tests/iourt42/test_headshotcounter.py index 94a4dac..9ccab17 100644 --- a/tests/iourt42/test_headshotcounter.py +++ b/poweradminurt/tests/iourt42/test_headshotcounter.py @@ -4,7 +4,7 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt42 import Iourt42TestCase +from ..iourt42 import Iourt42TestCase class Test_headshotcounter(Iourt42TestCase): def setUp(self): @@ -34,19 +34,13 @@ def setUp(self): warn_kevlar_nr: 50 """) self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() self.p.onLoadConfig() self.p.onStartup() self.console.say = Mock() self.console.write = Mock() - def test_hitlocation(self): def joe_hits_simon(hitloc): @@ -59,28 +53,27 @@ def assertCounts(head, helmet, torso): self.assertEqual(helmet, self.joe.var(self.p, 'helmethits', default=0.0).value) self.assertEqual(torso, self.simon.var(self.p, 'torsohitted', default=0.0).value) - # GIVEN self.joe.connects("6") self.simon.connects("7") # WHEN - joe_hits_simon(0) + joe_hits_simon('0') # THEN assertCounts(head=0.0, helmet=0.0, torso=0.0) # WHEN - joe_hits_simon(1) # 1 is head on 4.2 + joe_hits_simon('1') # THEN assertCounts(head=1.0, helmet=0.0, torso=0.0) # WHEN - joe_hits_simon(4) # 4 is helmet on 4.2 + joe_hits_simon('2') # THEN assertCounts(head=1.0, helmet=1.0, torso=0.0) # WHEN - joe_hits_simon(5) # 5 is torso on 4.2 + joe_hits_simon('3') # THEN assertCounts(head=1.0, helmet=1.0, torso=1.0) diff --git a/tests/iourt42/test_radio_spam_protection.py b/poweradminurt/tests/iourt42/test_radio_spam_protection.py similarity index 84% rename from tests/iourt42/test_radio_spam_protection.py rename to poweradminurt/tests/iourt42/test_radio_spam_protection.py index 1318b16..e13541f 100644 --- a/tests/iourt42/test_radio_spam_protection.py +++ b/poweradminurt/tests/iourt42/test_radio_spam_protection.py @@ -5,19 +5,14 @@ from b3.config import CfgConfigParser from b3.cvar import Cvar from poweradminurt import PoweradminurtPlugin -from tests.iourt42 import Iourt42TestCase +from ..iourt42 import Iourt42TestCase class Test_radio_spam_protection(Iourt42TestCase): def setUp(self): super(Test_radio_spam_protection, self).setUp() self.conf = CfgConfigParser() self.p = PoweradminurtPlugin(self.console, self.conf) - - when(self.console).getCvar('timelimit').thenReturn(Cvar('timelimit', value=20)) - when(self.console).getCvar('g_maxGameClients').thenReturn(Cvar('g_maxGameClients', value=16)) - when(self.console).getCvar('sv_maxclients').thenReturn(Cvar('sv_maxclients', value=16)) - when(self.console).getCvar('sv_privateClients').thenReturn(Cvar('sv_privateClients', value=0)) - when(self.console).getCvar('g_allowvote').thenReturn(Cvar('g_allowvote', value=0)) + self.init_default_cvar() def init(self, config_content=None): diff --git a/tests/requirements.txt b/poweradminurt/tests/requirements.txt similarity index 100% rename from tests/requirements.txt rename to poweradminurt/tests/requirements.txt diff --git a/readme-poweradminurt.txt b/readme-poweradminurt.txt deleted file mode 100644 index 2f74788..0000000 --- a/readme-poweradminurt.txt +++ /dev/null @@ -1,133 +0,0 @@ -################################################################################### -# -# PowerAdminUrt -# Plugin for B3 (www.bigbrotherbot.com) -# (c) 2006 www.xlr8or.com (mailto:xlr8or@xlr8or.com) -# -# This program is free software and licensed under the terms of -# the GNU General Public License (GPL), version 2. -# -# http://www.gnu.org/copyleft/gpl.html -################################################################################### - -PowerAdminUrt for B3 -################################################################################### - -This plugin works for Urban Terror 4.1 and 4.2. This plugin adds powerfull commands -and functions to your b3: - -Added Commands: -!pamute (!mute) [] - mute a player -!panuke (!nuke) [] - nuke a player (a number of times) -!paslap (!slap) [] - slap a player (a number of times) -!paveto (!veto) - veto the current running vote -!pabigtext (!bigtext) - display a bigtext message center screen -!pashuffleteams (!shuffleteams) - shuffle the teams -!paswapteams (!swapteams) - swap the teams -!paforce (!force) - force a player to a team - -!pamaprestart (!maprestart) - restart current map -!pamapreload (!mapreload) - reload current map -!pacyclemap (!cyclemap) - start next map in rotation -!papause - pause the current game -!paset - set a server cvar -!paget - display a server cvar -!paexec - execute a server configfile -!pateams (!teams) - activate teambalancer (read below) -!pavote - Switch voting. It remembers your voting settings! - -!pamoon (!moon) - Activate Moon Mode... low gravity -!papublic (!public) - Make the server public, or private! -!pamatch (!match) - Switch to MatchMode -!pagear (!gear) - - Set allowed weapons. - -!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: BombMode - -!paident (!id) - prints a players B3-id, Guid and IP to screen for demo purposes -!paversion (!paver) - spits out the version of PowerAdminUrt - - -Commands specific to Urban Terror 4.2 -------------------------------------- - -!pakill (!kill) - kill a player -!palms (!lms) - change game type to Last Man Standing - - - -Each command (except !paversion) can be leveled in the config file. - -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 can check 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. - -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. - -Requirements: -################################################################################### - -- ioUrT -- B3 version 1.8.2 or higher - - -Installation of the B3 plugin: -################################################################################### - -To install the b3-plugin part: - -1. Unzip the contents of this package. Go to the unzipped folder extplugins and -copy the 'poweradminurt' folder that contains the .py file in the bots folder b3/extplugins. -Then copy the config file .ini in the b3/extplugins/conf folder. - -2. Open the .ini file with your favorite editor and modify the -commands levels if you want them different. Do not edit the command-names -for they will not function under a different name. - -3. Open your b3.xml file (in b3/conf) and add the next line in the - section of the file: - - - - -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 - - -################################################################################### -xlr8or - 29 july 2008 - www.bigbrotherbot.com // www.xlr8or.com diff --git a/tests/common/__init__.py b/tests/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/iourt41/__init__.py b/tests/iourt41/__init__.py deleted file mode 100644 index be64790..0000000 --- a/tests/iourt41/__init__.py +++ /dev/null @@ -1,64 +0,0 @@ -import sys - -if sys.version_info[:2] < (2, 7): - import unittest2 as unittest -else: - import unittest -import logging -from b3 import TEAM_UNKNOWN -from b3.config import XmlConfigParser -from b3.fake import FakeClient -from b3.parsers.iourt41 import Iourt41Parser -from b3.plugins.admin import AdminPlugin - - -class Iourt41TestCase(unittest.TestCase): - """ - Test case that is suitable for testing Iourt41 parser specific features - """ - - @classmethod - def setUpClass(cls): - # less logging - logging.getLogger('output').setLevel(logging.ERROR) - - from b3.parsers.q3a.abstractParser import AbstractParser - from b3.fake import FakeConsole - AbstractParser.__bases__ = (FakeConsole,) - # Now parser inheritance hierarchy is : - # Iourt41Parser -> abstractParser -> FakeConsole -> Parser - - - def setUp(self): - # create a Iourt41 parser - self.parser_conf = XmlConfigParser() - self.parser_conf.loadFromString("""""") - self.console = Iourt41Parser(self.parser_conf) - self.console.startup() - - # load the admin plugin - self.adminPlugin = AdminPlugin(self.console, '@b3/conf/plugin_admin.xml') - self.adminPlugin.onStartup() - - # make sure the admin plugin obtained by other plugins is our admin plugin - def getPlugin(name): - if name == 'admin': - return self.adminPlugin - else: - return self.console.getPlugin(name) - self.console.getPlugin = getPlugin - - # prepare a few players - self.joe = FakeClient(self.console, name="Joe", exactName="Joe", guid="zaerezarezar", groupBits=1, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.simon = FakeClient(self.console, name="Simon", exactName="Simon", guid="qsdfdsqfdsqf", groupBits=0, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.reg = FakeClient(self.console, name="Reg", exactName="Reg", guid="qsdfdsqfdsqf33", groupBits=4, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.moderator = FakeClient(self.console, name="Moderator", exactName="Moderator", guid="sdf455ezr", groupBits=8, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.admin = FakeClient(self.console, name="Level-40-Admin", exactName="Level-40-Admin", guid="875sasda", groupBits=16, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.superadmin = FakeClient(self.console, name="God", exactName="God", guid="f4qfer654r", groupBits=128, team=TEAM_UNKNOWN, teamId=0, squad=0) - - logging.getLogger('output').setLevel(logging.DEBUG) - - def tearDown(self): - self.console.working = False -# sys.stdout.write("\tactive threads count : %s " % threading.activeCount()) -# sys.stderr.write("%s\n" % threading.enumerate()) diff --git a/tests/iourt42/__init__.py b/tests/iourt42/__init__.py deleted file mode 100644 index c1d706e..0000000 --- a/tests/iourt42/__init__.py +++ /dev/null @@ -1,64 +0,0 @@ -import sys - -if sys.version_info[:2] < (2, 7): - import unittest2 as unittest -else: - import unittest -import logging -from b3 import TEAM_UNKNOWN -from b3.config import XmlConfigParser -from b3.fake import FakeClient -from b3.parsers.iourt42 import Iourt42Parser -from b3.plugins.admin import AdminPlugin - - -class Iourt42TestCase(unittest.TestCase): - """ - Test case that is suitable for testing Iourt41 parser specific features - """ - - @classmethod - def setUpClass(cls): - # less logging - logging.getLogger('output').setLevel(logging.ERROR) - - from b3.parsers.q3a.abstractParser import AbstractParser - from b3.fake import FakeConsole - AbstractParser.__bases__ = (FakeConsole,) - # Now parser inheritance hierarchy is : - # Iourt41Parser -> abstractParser -> FakeConsole -> Parser - - - def setUp(self): - # create a Iourt41 parser - self.parser_conf = XmlConfigParser() - self.parser_conf.loadFromString("""""") - self.console = Iourt42Parser(self.parser_conf) - self.console.startup() - - # load the admin plugin - self.adminPlugin = AdminPlugin(self.console, '@b3/conf/plugin_admin.xml') - self.adminPlugin.onStartup() - - # make sure the admin plugin obtained by other plugins is our admin plugin - def getPlugin(name): - if name == 'admin': - return self.adminPlugin - else: - return self.console.getPlugin(name) - self.console.getPlugin = getPlugin - - # prepare a few players - self.joe = FakeClient(self.console, name="Joe", exactName="Joe", guid="zaerezarezar", groupBits=1, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.simon = FakeClient(self.console, name="Simon", exactName="Simon", guid="qsdfdsqfdsqf", groupBits=0, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.reg = FakeClient(self.console, name="Reg", exactName="Reg", guid="qsdfdsqfdsqf33", groupBits=4, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.moderator = FakeClient(self.console, name="Moderator", exactName="Moderator", guid="sdf455ezr", groupBits=8, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.admin = FakeClient(self.console, name="Level-40-Admin", exactName="Level-40-Admin", guid="875sasda", groupBits=16, team=TEAM_UNKNOWN, teamId=0, squad=0) - self.superadmin = FakeClient(self.console, name="God", exactName="God", guid="f4qfer654r", groupBits=128, team=TEAM_UNKNOWN, teamId=0, squad=0) - - logging.getLogger('output').setLevel(logging.DEBUG) - - def tearDown(self): - self.console.working = False -# sys.stdout.write("\tactive threads count : %s " % threading.activeCount()) -# sys.stderr.write("%s\n" % threading.enumerate())