├── chatfun ├── ouch.txt ├── rail.txt ├── fuckyou.txt ├── hit.txt ├── gay.txt ├── kill.txt ├── cookies.txt ├── README.md ├── beer.txt ├── fuck.txt └── shit.txt ├── Map_Names ├── README.md └── Map_Names.txt ├── scripts ├── wipeout.factories └── mappool_wipeout.txt ├── linux_tools ├── ServerStatus.py ├── autodownload.sh ├── download_new.sh ├── redownload.sh ├── README.md └── server_reboot.py ├── teamsize.py ├── restartonlybots.py ├── echo.py ├── highping.py ├── commands.py ├── mapmonitor.py ├── autobalance.py ├── getmap.py ├── specall.py ├── mapLimiter.py ├── voicechat.py ├── doVote.py ├── listmaps.py ├── myFun ├── Working_Sounds.txt └── README.md ├── restartserver.py ├── funwarmup.py ├── handicap.py ├── commlink.py └── inviteonly.py /chatfun/ouch.txt: -------------------------------------------------------------------------------- 1 | ^7Do you need a bandaid {}^7? Don't cry. 2 | ^7Do you need a bandaid {}^7? Don't cry. 3 | ^7Do you need a bandaid {}^7? Don't cry. 4 | -------------------------------------------------------------------------------- /chatfun/rail.txt: -------------------------------------------------------------------------------- 1 | ^7Get some skill {}^7. The rail is just a weapon. 2 | ^7Get some skill {}^7. The rail is just a weapon. 3 | ^7Get some skill {}^7. The rail is just a weapon. 4 | -------------------------------------------------------------------------------- /chatfun/fuckyou.txt: -------------------------------------------------------------------------------- 1 | ^7Why so mean {}^7? Masturbation relieves stress. 2 | ^7Why so mean {}^7? Masturbation relieves stress. 3 | ^7Why so mean {}^7? Masturbation relieves stress. 4 | -------------------------------------------------------------------------------- /chatfun/hit.txt: -------------------------------------------------------------------------------- 1 | ^7{}^7 slaps {}^7 in the face with a rocket launcher. 2 | ^7If {}^7 slaps {}^7 the bitch scream might make everyone deaf. 3 | {}^7 pulls out his LG and slaps {}^7 with the butt end. 4 | {}^7's hand hurts from slapping {}^7 repeatedly. 5 | -------------------------------------------------------------------------------- /Map_Names/README.md: -------------------------------------------------------------------------------- 1 | # USE THIS Map_Names.txt file with the listmaps.py on the main plugins page. 2 | 3 | Put the Map_Names.txt into your Quake Live server install directory, typically /qlds 4 | 5 | Keep the format of the file if you edit it to add maps or correct errors. 6 | 7 | Feel free to upload additions/corrections. 8 | -------------------------------------------------------------------------------- /chatfun/gay.txt: -------------------------------------------------------------------------------- 1 | ^7Are you saying you are gay {}^7? 2 | ^7Hug a gay person {}^7, it might make you feel better. 3 | ^7Gays are people too {}^7. 4 | ^7Why do you have to bash on being gay {}^7? Hiding something? 5 | ^7Oh, you're straight? Well, so is spaghetti until it gets hot and wet. 6 | ^7Why did God create gay men? So fat girls could dance. 7 | ^7Are you saying you are gay {}^7? 8 | ^7Are you saying you are gay {}^7? 9 | -------------------------------------------------------------------------------- /scripts/wipeout.factories: -------------------------------------------------------------------------------- 1 | [{ 2 | "cvars": { 3 | "roundtimelimit": "18000", 4 | "g_startingweapons": "8447", 5 | "g_startingAmmo_rg": "50", 6 | "g_startingAmmo_rl": "50", 7 | "g_startingAmmo_gl": "15", 8 | "g_startingAmmo_lg": "200", 9 | "g_startingAmmo_pg": "200", 10 | "g_startingAmmo_sg": "50", 11 | "g_startingAmmo_hmg": "200", 12 | "g_startingAmmo_mg": "200", 13 | "dmflags": "28", 14 | "g_startingArmor": "100", 15 | "g_startingHealthBonus": "0", 16 | "g_startingHealth": "200", 17 | "g_allowKill": "0", 18 | "g_overtime": "0" 19 | }, 20 | "author": "BarelyMiSSeD", 21 | "description": "Wipeout gametype", 22 | "basegt": "ca", 23 | "id": "wipeout", 24 | "title": "Wipeout" 25 | }] -------------------------------------------------------------------------------- /chatfun/kill.txt: -------------------------------------------------------------------------------- 1 | {}^7 shoots {}^7 in the ass with a rocket. 2 | {}^7 had fun watching {}^7 elplode from all the LG fire. 3 | {}^7 stuck a Rail Gun up {}^7's ass and watched the rail come out of his head. 4 | {}^7 likes to watch {}^7 explode from rocket fire. 5 | ^7The gauntlet is the perfect weapon for {}^7 to kill {}^7 REPEATEDLY. 6 | ^7Who wants to see {}^7 shove his machine gun up {}^7's nose and shoot? 7 | {}^7 doesn't like having to clean {}^7's brains off of his gauntlet. 8 | {}^7 is a big fan of Face Rockets, especially in {}^7's face. 9 | {}^7 grabs his machine gun and shoots {}^7 in the back of the head. 10 | ^7The cops can't prove {}^7 killed {}^7. The rocket blew him away. 11 | {}^7 doesn't want to kill {}^7, but the rocket doesn't have that problem. 12 | ^7All the bullets from {}^7's HMG are making {}^7 leak all over the floor. 13 | {}^7 made a big mess in here with {}^7's brains. 14 | {}^7 is ligning up a rail shot on {}^7 right now. 15 | ^7Do you think {}^7 will go to jail if {}^7 shows up dead? 16 | -------------------------------------------------------------------------------- /linux_tools/ServerStatus.py: -------------------------------------------------------------------------------- 1 | # ServerStatus.py is a plugin for minqlx to: 2 | # -Store the current player count into the redis database to be read by server_reboot.py 3 | # created by BarelyMiSSeD on 1-4-2020 4 | # 5 | 6 | import minqlx 7 | 8 | COUNT_KEY = "minqlx:connected" # Must match CHECK_KEY in server_reboot.py (should not need to edit) 9 | 10 | VERSION = "1.1" 11 | 12 | 13 | class ServerStatus(minqlx.Plugin): 14 | def __init__(self): 15 | self.add_hook("player_loaded", self.handle_player_loaded) 16 | self.add_hook("player_disconnect", self.handle_player_disconnect) 17 | 18 | self.save_count() 19 | 20 | def handle_player_loaded(self, player): 21 | self.save_count() 22 | return 23 | 24 | def handle_player_disconnect(self, player, reason): 25 | self.save_count() 26 | return 27 | 28 | @minqlx.delay(2) 29 | def save_count(self): 30 | connected_count = len(self.players()) 31 | if connected_count < 0: 32 | connected_count = 0 33 | self.db.set(COUNT_KEY, connected_count) 34 | -------------------------------------------------------------------------------- /scripts/mappool_wipeout.txt: -------------------------------------------------------------------------------- 1 | # specify 1 map per line, mapname|factoryid 2 | # ex: aerowalk|ffa 3 | # see factories.txt for valid factory id values 4 | 6plusplus|wipeout 5 | almostlost|wipeout 6 | asylum|wipeout 7 | brimstoneabbey|wipeout 8 | campgrounds|wipeout 9 | ca_tamb|wipeout 10 | chemicalreaction|wipeout 11 | corrosion|wipeout 12 | cowsmap|wipeout 13 | deepinside|wipeout 14 | devilish|wipeout 15 | dreadfulplace|wipeout 16 | eviscerated|wipeout 17 | foolishlegacy|wipeout 18 | henhouse|wipeout 19 | hiddenfortress|wipeout 20 | intervention|wipeout 21 | leftbehind|wipeout 22 | leviathan|wipeout 23 | limbus|wipeout 24 | lockdown|wipeout 25 | monastery|wipeout 26 | overkill|wipeout 27 | prodcmap7|wipeout 28 | proverek2|wipeout 29 | psidm7|wipeout 30 | purgatory|wipeout 31 | quarantine|wipeout 32 | realmofsteelrats|wipeout 33 | repent|wipeout 34 | retribution|wipeout 35 | terminatria|wipeout 36 | theedge|wipeout 37 | theoldendomain|wipeout 38 | tornado|wipeout 39 | trinity|wipeout 40 | ts_ca1|wipeout 41 | warehouse|wipeout 42 | windsongkeep|wipeout 43 | -------------------------------------------------------------------------------- /chatfun/cookies.txt: -------------------------------------------------------------------------------- 1 | ^1Q^7: What cookie makes you rich? ^1A^7: A fortune cookie! 2 | ^1Q^7: Why did the cookie cry? ^1A^7: Because his mother was a wafer so long! 3 | ^7For me? Thank you, {}! 4 | ^7Why do we cook bacon and bake cookies? 5 | ^1Q^7: When should you take a cookie to the doctor? ^1A^7: When it feels crummy. 6 | ^1Q^7: What do the cookie and the computer have in common? ^1A^7: They both have chips. 7 | ^7For me? Thank you, {}! 8 | ^1Q^7: How can you tell a blonde's baking chocolate chip cookies? ^1A^7: M&M shells are all over the floor 9 | ^1Q^7: What is green and brown and crawls through the grass? ^1A^7: A Girl Scout who has lost her cookie 10 | ^1Q^7: What kind of keys to kids like to carry? ^1A^7: Cookies! 11 | ^1Q^7: Why do basketball players love cookies? ^1A^7: Because they can dunk them! 12 | ^7For me? Thank you, {}! 13 | ^1Q^7: What did the Gingerbread Man put on his bed? ^1A^7: A cookie sheet! 14 | ^1Q^7: What is a monster's favorite food? ^1A^7: Ghoul scout cookies. 15 | ^7For me? Thank you, {}! 16 | ^7I LOVE cookies {}^7!! Do you have some cookies for me? 17 | ^7I LOVE cookies {}^7!! Do you have some cookies for me? 18 | -------------------------------------------------------------------------------- /linux_tools/autodownload.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | timestamp() { 3 | date +"%T" 4 | } 5 | 6 | me=`basename "$0"` 7 | echo "========== $me has started. ==========" 8 | echo "========= $(date) =========" 9 | 10 | workshopFile='workshop.txt' 11 | steamCMD='/home/steam/steamcmd' 12 | installLocation='/home/steam/steamcmd/steamapps/common/qlds' 13 | workshopIDs=`cat $installLocation/baseq3/$workshopFile | grep -v '#' | sed '/^[ \t]*$/d'` 14 | numOfIDs=`echo "$workshopIDs" | wc -l` 15 | counter=0 16 | 17 | while [ $counter -lt $numOfIDs ]; do 18 | currentID=`echo $workshopIDs | awk '{ print $1 }'` 19 | workshopIDs=`echo $workshopIDs | cut -d ' ' -f2-` 20 | echo -e "$(timestamp) Downloading item $(expr $counter + 1) of $numOfIDs from Steam with id $currentID" 21 | $steamCMD/steamcmd.sh +login anonymous +force_install_dir /home/steam/steamcmd/steamapps/common/qlds/ +workshop_download_item 282440 $currentID +quit > /dev/null 22 | if [ $? -ne 0 ]; then 23 | echo -e "$(timestamp) Download of id $currentID failed with steamcmd error code $?" 24 | else 25 | echo -e "$(timestamp) Download of id $currentID completed" 26 | fi 27 | ((counter++)) 28 | done 29 | echo "=== Done === $(date) ===" 30 | exit 0 31 | -------------------------------------------------------------------------------- /teamsize.py: -------------------------------------------------------------------------------- 1 | # teamsize.py is a plugin for minqlx to: 2 | # -limit the teamsize admins are able to set when using !teamsize or !ts 3 | # created by BarelyMiSSeD on 7-12-2019 4 | # 5 | """ 6 | //Set these cvar(s) in your server.cfg (or wherever you set your minqlx variables).: 7 | set qlx_teamsizemin "2" 8 | set qlx_teamsizemax "12" 9 | """ 10 | 11 | import minqlx 12 | 13 | VERSION = "v1.00" 14 | 15 | 16 | class teamsize(minqlx.Plugin): 17 | def __init__(self): 18 | self.add_command(("teamsize", "ts"), self.cmd_teamsize, priority=minqlx.PRI_HIGH) 19 | 20 | # Cvar(s). 21 | self.set_cvar_once("qlx_teamsizemin", "2") 22 | self.set_cvar_once("qlx_teamsizemax", "12") 23 | 24 | def cmd_teamsize(self, player, msg, channel): 25 | if len(msg) < 2: 26 | return 27 | try: 28 | n = int(msg[1]) 29 | except ValueError: 30 | return 31 | if n > self.get_cvar("qlx_teamsizemax", int): 32 | player.tell("^6That teamsize is too large") 33 | return minqlx.RET_STOP_ALL 34 | elif n < self.get_cvar("qlx_teamsizemin", int): 35 | player.tell("^6That teamsize is too small") 36 | return minqlx.RET_STOP_ALL 37 | -------------------------------------------------------------------------------- /linux_tools/download_new.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | timestamp() { 3 | date +"%T" 4 | } 5 | 6 | me=`basename "$0"` 7 | echo "========== $me has started. ==========" 8 | echo "========= $(date) =========" 9 | 10 | workshopFile='workshop.txt' 11 | steamCMD='/home/steam/steamcmd' 12 | installLocation='/home/steam/steamcmd/steamapps/common/qlds' 13 | workshopIDs=`cat $installLocation/baseq3/$workshopFile | grep -v '#' | sed '/^[ \t]*$/d'` 14 | numOfIDs=`echo "$workshopIDs" | wc -l` 15 | counter=0 16 | 17 | while [ $counter -lt $numOfIDs ]; do 18 | currentID=`echo $workshopIDs | awk '{ print $1 }'` 19 | workshopIDs=`echo $workshopIDs | cut -d ' ' -f2-` 20 | if [ ! -d "$installLocation/steamapps/workshop/content/282440/$currentID" ]; then 21 | echo -e "$(timestamp) Downloading item $(expr $counter + 1) of $numOfIDs from Steam with id $currentID" 22 | $steamCMD/steamcmd.sh +login anonymous +force_install_dir $installLocation/ +workshop_download_item 282440 $currentID +quit 23 | if [ $? -ne 0 ]; then 24 | echo -e "$(timestamp) Download of id $currentID failed with steamcmd error code $?" 25 | else 26 | echo -e "$(timestamp) Download of id $currentID completed" 27 | fi 28 | else 29 | echo -e "Steam directory for workshop item $currentID already exists, skipping." 30 | fi 31 | ((counter++)) 32 | done 33 | echo "=== Done === $(date) ===" 34 | exit 0 35 | -------------------------------------------------------------------------------- /chatfun/README.md: -------------------------------------------------------------------------------- 1 | # Chatfun.py 2 | 3 | I created this plugin to create a little bit of fun on the server that doesn't ahve to do with playing. 4 | 5 | The server will respond to things said in the server in chat and with some !commands. 6 | 7 | Put the chatfun.py and all the text files in the minqlx plugins directory. 8 | 9 | Use !fun to see what commands will get a repsonse. 10 | 11 |

12 | Command(s) available with chatfun.py listed with the set permission level. 13 | 14 | • Permission level 4 15 | 16 | !chatfun 17 | 18 | Turns the automatic response to certain words said in normal chat on or off. 19 | This will override the setting in the config until the server is restarted. 20 | 21 | Usage: !chatfun on|off 22 | 23 |

24 | CVARs to be set. The settings are shown with the default settings. Set these in the same config file you set the other minqlx bot cvars. 25 | 26 | 27 | set qlx_chatfunAdmin "4" - Sets the minqlx permission level needed to turn the chatfun auto responses on/off in game with
28 | !chatfun . This will override the qlx_chatfunReply setting until the server is restarted.
29 | set qlx_chatfunPauseTime "5" - Sets the amount of seconds between each response from the server.
30 | set qlx_chatfunReply "1" - Turns on/off the auto responses from the server to trigger text said in normal chat.
31 | -------------------------------------------------------------------------------- /restartonlybots.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2018 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should review a copy of the GNU General Public License 9 | # along with minqlx. See . 10 | 11 | # This is a plugin for the minqlx admin bot. 12 | # It kicks bots out of a server if they are the only ones remaining 13 | 14 | """ 15 | // Enable to restart the server when only bots remain. Disabling it will kick all bots when only bots remain. 16 | // (0=disable, 1=enable) 17 | set qlx_rboRestartServer "0" 18 | """ 19 | 20 | import minqlx 21 | 22 | VERSION = "1.1" 23 | 24 | 25 | class restartonlybots(minqlx.Plugin): 26 | def __init__(self): 27 | self.set_cvar_once("qlx_rboRestartServer", "0") 28 | 29 | self.add_hook("player_disconnect", self.handle_player_disconnect) 30 | 31 | def handle_player_disconnect(self, player, reason): 32 | self.check_players() 33 | 34 | @minqlx.delay(5) 35 | def check_players(self): 36 | bots_count = 0 37 | players = self.players() 38 | for player in players: 39 | if str(player.steam_id)[0] == "9": 40 | bots_count += 1 41 | 42 | if bots_count > 0 and bots_count == len(players): 43 | if self.get_cvar("qlx_rboRestartServer", bool): 44 | minqlx.console_print("^1Restarting server because no human players are connected.") 45 | minqlx.console_command("quit") 46 | else: 47 | minqlx.console_print("^1Kicking the bots because no human players are connected.") 48 | for player in players: 49 | player.kick() 50 | -------------------------------------------------------------------------------- /linux_tools/redownload.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | timestamp() { 3 | date +"%T" 4 | } 5 | 6 | me=`basename "$0"` 7 | echo "========== $me has started. ==========" 8 | echo "========= $(date) =========" 9 | 10 | workshopFile='workshop.txt' 11 | steamCMD='/home/steam/steamcmd' 12 | installLocation='/home/steam/steamcmd/steamapps/common/qlds' 13 | workshopIDs=`cat $installLocation/baseq3/$workshopFile | grep -v '#' | sed '/^[ \t]*$/d'` 14 | numOfIDs=`echo "$workshopIDs" | wc -l` 15 | counter=0 16 | 17 | if [ -f "$installLocation/steamapps/workshop/appworkshop_282440.acf" ]; then 18 | echo -e "Renaming workshop item file $installLocation/steamapps/workshop/appworkshop_282440.acf to appworkshop_282440.acf.old" 19 | mv -f $installLocation/steamapps/workshop/appworkshop_282440.acf $installLocation/steamapps/workshop/appworkshop_282440.acf.old 20 | fi 21 | if [ -d "$installLocation/steamapps/workshop/content/282440_old" ]; then 22 | rm -rf $installLocation/steamapps/workshop/content/282440_old 23 | fi 24 | if [ -d "$installLocation/steamapps/workshop/content/282440" ]; then 25 | echo -e "Renaming workshop directory $installLocation/steamapps/workshop/content/282440 to $installLocation/steamapps/workshop/content/282440_old" 26 | mv $installLocation/steamapps/workshop/content/282440 $installLocation/steamapps/workshop/content/282440_old 27 | fi 28 | 29 | while [ $counter -lt $numOfIDs ]; do 30 | currentID=`echo $workshopIDs | awk '{ print $1 }'` 31 | workshopIDs=`echo $workshopIDs | cut -d ' ' -f2-` 32 | echo -e "$(timestamp) Downloading item $(expr $counter + 1) of $numOfIDs from Steam with id $currentID" 33 | $steamCMD/steamcmd.sh +login anonymous +force_install_dir $installLocation/ +workshop_download_item 282440 $currentID +quit > /dev/null 34 | if [ $? -ne 0 ]; then 35 | echo -e "$(timestamp) Download of id $currentID failed with steamcmd error code $?" 36 | else 37 | echo -e "$(timestamp) Download of id $currentID completed" 38 | fi 39 | ((counter++)) 40 | done 41 | echo "=== Done === $(date) ===" 42 | exit 0 43 | -------------------------------------------------------------------------------- /chatfun/beer.txt: -------------------------------------------------------------------------------- 1 | {}^7 hands {}^7 an ice cold ^4Miller Lite^7. 2 | {}^7 gives {}^7 a rocky mountain cold ^4Coors Light^7. 3 | {}^7 hands {}^7 an ice cold ^4Miller Lite^7. 4 | {}^7 gives {}^7 a ^4Heineken^7 to enjoy. 5 | {}^7 slides a ^4Samuel Adams Boston Lager^7 to {}^7. 6 | {}^7 offers {}^7 a cold ^4Dos Equis^7. 7 | {}^7 tosses {}^7 a can of ^4Budweiser^7. 8 | {}^7 likes {}^7 and slides over a cold ^4Blue Moon^7 beer. 9 | {}^7 is trying to get {}^7 drunk on ^4Newcastle Brown Ale^7. 10 | {}^7 will give {}^7 a ^4Corona^7 for a blow job. 11 | {}^7 pops open a ^4Amstel Lager^7 and hands it to {}^7. 12 | {}^7 gives {}^7 an ice cold ^4Yuengling^7. 13 | {}^7 reaches in the coller and pulls out an ice cold ^4Michelob^7 for {}^7. 14 | {}^7 offers {}^7 a refreshing ^4Killian's Irish Red^7. 15 | {}^7 gets a ^4Sierra Nevada^7 and hands it to {}^7. 16 | {}^7 thinks this ^4Bass Ale^7 is just what {}^7 needs. 17 | {}^7 opens a cooler full of ^4Dogfish Head^7 and gets the coldest one for {}^7. 18 | {}^7 gets a ^4Sapparo Premium Lager^7 for {}^7. 19 | {}^7 thinks {}^7 deserves an ice cold ^4Molson Canadian^7. 20 | {}^7 hopes {}^7 will enjoy this ^4Cerveceria Modelo^7. 21 | ^7A ^4Labatt Blue^7 sounds like the perfect beer for {}^7 to give to {}^7. 22 | {}^7 tosses {}^7 a ^4St. Pauli Girl^7 Lager. 23 | {}^7 gives {}^7 a ^4Beck's^7 with all the ice cold condesation still dripping from it. 24 | {}^7 hands {}^7 a nice ^4Bitburger^7 Premium Beer. 25 | {}^7 gets a ^4Shiner Bock^7 for {}^7. 26 | {}^7 gets a ^4Magic Hat^7 beer for {}^7. 27 | {}^7 hands a bottle of ^4Foster's Lager^7 to {}^7. 28 | {}^7 hands {}^7 a six pack of ^4Coors Light^7. 29 | {}^7 has a lot of ^4Coors Light^7 and offers a can to {}^7. 30 | {}^7 is a big fan of ^4Hofbrauhaus^7 German beer and hands a bottle to {}^7. 31 | {}^7 knows that {}^7 likes Pabst ^4Blue Ribbon^7 and got some just for {}^7. 32 | {}^7 konws that {}^7 is a real Quake Player and got ^4Miller Genuine Draft^7 to celebrate. 33 | {}^7 hands {}^7 a cold ^4Browns Cherry Rasberry Ale^7. 34 | {}^7 gives a ice cold ^4Hofbrau Munchen Dunkel^7 to {}^7. 35 | {}^7 slides a ^4Weihenstephaner Lager^7 across the bar to {}^7. 36 | {}^7 gives a ice cold ^4Black Sheep Monty Python^7 to {}^7. 37 | {}^7 gives {}^7 a cold ^4Hoegaarden Witbier^7 to {}^7. 38 | {}^7 makes sure the ^4Steigl Pilsner^7 is ice cold and gives it to {}^7. 39 | {}^7 unzips his zipper and offers a ^4Staropramen Premium Lager^7 to {}^7 for a blow job. 40 | {}^7 hopes {}^7 likes this ice cold ^4Yanjing Pale Lager^7 from China. 41 | {}^7 thinks {}^7 is inot exotic beers and passes over an ice cold ^4Defiant Medusa IPA^7. 42 | {}^7 saves his ^4Brooklyn Brown Ale^7 for real Quake players and slides a cold one to {}^7. 43 | -------------------------------------------------------------------------------- /chatfun/fuck.txt: -------------------------------------------------------------------------------- 1 | ^7That's a dirty mouth you have {}^7. 2 | ^7Are you asking how to fuck {}^7? 3 | ^7Put on a good show {}^7, no one likes a bad Fucker. 4 | ^1Fuck^7: ^31)^7 to have sexual intercourse with. ^32)^7 [Slang] To treat unfairly or harshly. 5 | ^7I am probably going to HELL for exessive use of the word FUCK. How about you {}^7? 6 | ^7The short answer is 'NO', the long answer is '^1FUCK NO^7'. 7 | ^7I hate it when people tell me swearing isn't necessary. I am FUCKING vulgar, not stupid. 8 | ^7You'r right, lets do this the dumbest fucking way possible since it's easier for you. 9 | ^7When i say 'Have a nice day.', remember that the ^1Fucker^7 is silent. 10 | ^7I konw I swear a lot. ^41)^7 I'm sorry. ^42)^7 I'll be good. ^43)^7 1&2 are lies. ^44)^1 Fuck Off!!! 11 | ^7I am currently experiencing life at a rate of Several ^4WTF's^7 per hour. 12 | ^7FaceBook asks 'What's on your mind?', it shoudl ask ^'3What's your fucking problem now?' 13 | ^7If you go home with somebody, and they don't have books, don't fuck 'em! 14 | ^7Style is knowing who you are, what you want to say, and not giving a FUCK. 15 | ^7If you can't say 'Fuck' you can't say, 'Fuck the government'. 16 | ^7What kind of fuckery is this? 17 | ^7If i'm going to fuck up my life, does it really matter which way i do it? 18 | ^7Who’d cum first, you or your clone {}^7? To find out, go fuck yourself! 19 | ^7You going to ^1fuck it^7 or kill it {}^7? 20 | ^7I like the word 'fuck'. The word means what it means, it also means whatever you need it to mean. 21 | ^7One middle finger isn't always enough to let someone know how you feel. Use both hands. 22 | ^7Keep your head up high and your middle finger higher {}^7. 23 | ^7Haters are like crickets, they chirp all fucking day and when you walk by them they shut the fuck up! 24 | ^7Im having one of those days where my middle finger is answering all my questions. 25 | ^7If you are asking how to fuck {}^7 try visiting PornHub.com 26 | ^7My attitude today is brought to you by, fuck you. 27 | ^7When I put on headphones, it seems like suddenly everybody wants to fucking talk. 28 | ^7I love the sound you make when you shut the fuck up. 29 | ^7If you have a problem, write it on a piece of paper, fold it up, and shove it up your fucking ass. 30 | ^7Please, just save us both some time, and Shut the fuck up. 31 | ^7Some people just need a high five, in the face with the CHAIR. 32 | ^7Sometimes I wish I was a bird. So I could fly over certain people and shit on their heads! 33 | ^7And by 'K...' I mean 'Fuck you.' 34 | ^7I'm working with two speeds today, slow and fuck you. Which one you want? 35 | ^7Didn't give a fuck yesterday, Don't give a fuck today, And I probably won't give a fuck tomorrow. 36 | ^7OFF is the general direction which you should fuck. 37 | ^7Sorry... I couldn't hear you over all the fuck I don't give. 38 | -------------------------------------------------------------------------------- /chatfun/shit.txt: -------------------------------------------------------------------------------- 1 | ^7Don't shit in here {}^7. We don't want it to smell like your house. 2 | ^7Quiet {}^7. No one wants to hear you complain. 3 | ^7You have such a vast vocabulary {}^7. 4 | ^7Yours stinks like everyone else's {}^7, so shit in your own house. 5 | ^7Why are you advertising shitting {}^7? Think yours is pretty? 6 | ^7Stop describing the contents of your underwear {}^7. 7 | ^7You know it was a good shit when you come back and your screensaver is on. 8 | ^7Is there a Number 2 stain remover? {}^7's shorts are Fucked!! 9 | ^1Constipation^7: Same SHIT different day. 10 | ^7There's nothing worse than walking in on your mom having a shit... on dad!!! 11 | ^7How many of us look at the toilet paper after a wipe? How do the blind cope? 12 | ^7What is 6 inches long and starts with a ^1P^7? A shit. 13 | ^7Constipation sufferers just don't give a shit. 14 | ^7I just shit my pants. Serves me right for eating them in the first place. 15 | ^7I jsut looked up myself on the internet. Now my webcam smells like shit. 16 | {}^7 feels like shit in the morning, but his wife makes him stick to cornflakes. 17 | ^7Anyone else notice that it is impossible to scratch your ass without smelling your fingers after? 18 | ^7I just took my son's shitty diaper off. Not sure why I tried it on in the first place. 19 | ^7What all fortune cookies should say: You are about to take a massive shit in 10 minutes. 20 | ^7Guess who is still together after all that shit?... My ass cheeks. 21 | ^7My Chinese girlfriend said 'You shit in bed'.... So I did. 22 | ^7Confucius say: Crowded elevator always smell different to midget. 23 | ^7Confucius say: Man who fart in Church sit in own pew. 24 | ^7Definition of a fart: A turd honking for the right of way! 25 | ^7Definition of bravery: A man with diarrhea chancing a fart! 26 | ^7Definition of surprise: A fart with a lump in it! 27 | ^7Why do farts stink? So that deaf people can enjoy them too! 28 | ^7How can you tell if a woman is wearing pantyhose? If she farts, her ankles swell. 29 | ^7What do you get if you eat beans and onions? Tear Gas. 30 | ^7What's invisible and smells like carrots? Easter Bunny farts. 31 | ^1'Shit Ton'^7 is my favorite unit of measurement. 32 | ^7When hiring a prostitute in Amsterdam, never ask her to "sit on my face" in a 'shilly dutch akshent' 33 | ^7I felt shit earlier. Now my fingers stink!! 34 | ^7I went on the bus this morning. Probably should have gone before leaving the house though. 35 | ^7I don't konw why my wife complains. I shit a log today bigger than my cock and it didn't hurt much. 36 | ^7There are two reasons I never drink toilet water: Number 1 and Number 2. 37 | ^7My wife knocked on the door: 'Are you shitting love?' Me: ' No I am shitting shit.' 38 | ^7Fill a whoopee cushion with brown sauce to make it look like the person who sat on it has followed through. 39 | ^7Replace the fake turds with real ones to play a trick on the tricksters. 40 | ^7I am not saying it's cold outside, but I just tripped over some dog shit. 41 | ^7I was on the toilet once for so long, I finally said to myself, 'I'm getting too old for this shit'. 42 | -------------------------------------------------------------------------------- /echo.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2018 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should review a copy of the GNU General Public License 9 | # along with minqlx. See . 10 | 11 | # This is a plugin prints the command issuer and the command with arguments to the console 12 | # so they can be seen and logged. 13 | # It gets all the commands loaded on the server automatically. 14 | # Edit the DONT_ECHO list below to exclude the commands you don't want echoed. 15 | # The command !echo will show all the commands being echoed. 16 | 17 | import minqlx 18 | import time 19 | 20 | DONT_ECHO = ["elo", "elos", "bdm", "bdms", "bdsm", "teams", "teens", "a", "listsounds", "q", "s", "time", "rockets", 21 | "pummel", "airpummel", "grenades", "plasma", "airrail", "telefrag", "teamtelefrag", "speed"] 22 | 23 | VERSION = "1.7" 24 | 25 | 26 | class echo(minqlx.Plugin): 27 | def __init__(self): 28 | self.add_hook("command", self.handle_command, priority=minqlx.PRI_HIGHEST) 29 | self.add_command("echo", self.cmd_echo) 30 | 31 | self._server_commands = [] 32 | self._command = [None, 0] 33 | self.populate_server_commands() 34 | 35 | def handle_command(self, caller, command, args): 36 | self.process_command(caller, command, args) 37 | return 38 | 39 | def process_command(self, caller, command, args): 40 | _name = command.name 41 | _time = time.time() 42 | if not str(caller).startswith("RconDummyPlayer") and _name in self._server_commands: 43 | if _name == self._command[0] and _time - self._command[1] < 0.1: 44 | return 45 | self._command = [_name, _time] 46 | minqlx.console_print("^1{} ^3issued command^7: {}".format(caller, args)) 47 | return 48 | 49 | @minqlx.delay(10) 50 | def populate_server_commands(self): 51 | loaded_scripts = self.plugins 52 | for script, handler in loaded_scripts.items(): 53 | try: 54 | for cmd in handler.commands: 55 | self._server_commands.append(cmd.name) 56 | except: 57 | continue 58 | for cmd in DONT_ECHO: 59 | for entry in self._server_commands: 60 | if cmd in entry: 61 | self._server_commands.remove(entry) 62 | 63 | def cmd_echo(self, player, msg, channel): 64 | self.list_echo(player) 65 | return 66 | 67 | @minqlx.thread 68 | def list_echo(self, player): 69 | count = 0 70 | echo_commands = [] 71 | for entry in self._server_commands: 72 | count += len(entry) 73 | echo_commands += entry 74 | player.tell("^2Echoing {} commands^7: ^1{}".format(count, "^7, ^1".join(echo_commands))) 75 | -------------------------------------------------------------------------------- /highping.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2020 BarelyMiSSeD (github) 3 | # https://github.com/BarelyMiSSeD/minqlx-plugins 4 | 5 | # You can redistribute it and/or modify it under the terms of the 6 | # GNU General Public License as published by the Free Software Foundation, 7 | # either version 3 of the License, or (at your option) any later version. 8 | 9 | # You should have received a copy of the GNU General Public License 10 | # along with minqlx. If not, see . 11 | 12 | # highping.py is a plugin for minqlx to: 13 | # - check players when they join a team for a ping that is higher than the setting. 14 | # - Put the players back to spec who do not meet the ping requirements 15 | # - allow admins to execute the high ping check on all players using the !999 command 16 | 17 | # created by BarelyMiSSeD on 3-16-2020 18 | 19 | """ 20 | // sets the ping limit looked for when getting a players average ping 21 | set qlx_highpingMax "250" 22 | // sets the number of ping samples taken for each player checked 23 | // Adjust this lower/higher if the ping test results are taking too long or are inaccurate 24 | set qlx_highpingSamples "10" 25 | """ 26 | 27 | import minqlx 28 | import time 29 | 30 | VERSION = "1.2" 31 | 32 | 33 | class highping(minqlx.Plugin): 34 | def __init__(self): 35 | super().__init__() 36 | self.set_cvar_once("qlx_highpingMax", "250") 37 | self.set_cvar_once("qlx_highpingSamples", "10") 38 | self.add_hook("team_switch", self.handle_team_switch) 39 | self.add_command("999", self.cmd_check_pings, 5) 40 | 41 | def handle_team_switch(self, player, old_team, new_team): 42 | self.check_ping(player, new_team) 43 | 44 | @minqlx.thread 45 | def check_ping(self, player, new_team): 46 | if new_team != "spectator": 47 | ping = 0 48 | samples = self.get_cvar("qlx_highpingSamples", int) 49 | for x in range(samples): 50 | ping += player.ping 51 | time.sleep(0.2) 52 | ping = ping / samples 53 | if ping >= self.get_cvar("qlx_highpingMax", int): 54 | player.put("spectator") 55 | player.tell("^1Your ping is too high to play on this server, as a result you were put to spectate.") 56 | 57 | def cmd_check_pings(self, player=None, msg=None, channel=None): 58 | self.check_pings() 59 | 60 | @minqlx.thread 61 | def check_pings(self): 62 | players = self.teams() 63 | teams = players['red'] + players['blue'] + players['free'] 64 | pings = {} 65 | max_ping = self.get_cvar("qlx_highpingMax", int) 66 | samples = self.get_cvar("qlx_highpingSamples", int) 67 | for player in teams: 68 | pings[player.steam_id] = 0 69 | for x in range(samples): 70 | pings[player.steam_id] += player.ping 71 | time.sleep(0.2) 72 | pings[player.steam_id] = pings[player.steam_id] / samples 73 | 74 | for player, ping in pings: 75 | if ping >= max_ping: 76 | player.put("spectator") 77 | player.tell("^1Your ping is too high to play on this server, as a result you were put to spectate.") 78 | -------------------------------------------------------------------------------- /commands.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2018 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should have received a copy of the GNU General Public License 9 | # along with minqlx. If not, see . 10 | 11 | # This is a plugin and command listing script for the minqlx admin bot. 12 | # This plugin will list all the in game commands loaded on the server. 13 | """ 14 | //Server Config cvars 15 | //Set the permission level needed to list the commands 16 | set qlx_commandsAdmin "0" 17 | //Enable to show only the commands the calling player can use, disable to show all commands (0=disable, 1=enable) 18 | set qlx_commandsOnlyEligible "1" 19 | """ 20 | 21 | import minqlx 22 | from re import sub 23 | 24 | VERSION = "1.0" 25 | 26 | 27 | class commands(minqlx.Plugin): 28 | def __init__(self): 29 | # queue cvars 30 | self.set_cvar_once("qlx_commandsAdmin", "0") 31 | self.set_cvar_once("qlx_commandsOnlyEligible", "1") 32 | 33 | # Minqlx bot commands 34 | self.add_command("plugins", self.list_plugins, self.get_cvar("qlx_commandsAdmin", int)) 35 | self.add_command(("lc", "listcmds", "listcommands"), self.cmd_list, self.get_cvar("qlx_commandsAdmin", int), 36 | usage="") 37 | 38 | def list_plugins(self, player, msg, channel): 39 | p = self.plugins 40 | s = set(p) 41 | message = [] 42 | count = 0 43 | for i in s: 44 | count += 1 45 | if count % 7 or count == 0: 46 | message.append(i + "^7, ^6") 47 | else: 48 | message.append(i + "^7, ^6\n") 49 | if count: 50 | message[count - 1] = sub(r"\^[0-9][, \\n]", "", message[count - 1]) 51 | player.tell("^1{} ^3Plugins found:".format(count)) 52 | player.tell("^6{}".format("".join(message))) 53 | 54 | def cmd_list(self, player, msg, channel): 55 | p = self.plugins 56 | s = set(p) 57 | e = self.get_cvar("qlx_commandsOnlyEligible", bool) 58 | a = self.db.get_permission(player) 59 | player.tell("^1Plugin^7: ^2Number of Commands") 60 | count = 0 61 | search = msg[1].lower() if len(msg) > 1 else None 62 | for i in s: 63 | if search and search not in i.lower(): 64 | continue 65 | message = [] 66 | try: 67 | c = p[i].commands 68 | if len(c): 69 | for cmd in c: 70 | name = cmd.name 71 | b = cmd.permission 72 | c_list = [] 73 | if e and a < b: 74 | continue 75 | for item in name: 76 | c_list.append(item) 77 | message.append("^7(^2{}^7) ^6{}".format(b, "^7|^6".join(c_list))) 78 | m = len(message) 79 | if m: 80 | player.tell("^1{}^7: {} ^3Command{}".format(i, m, "s" if m > 1 else "")) 81 | player.tell("{}".format("^7, ".join(message))) 82 | count += 1 83 | except: 84 | continue 85 | if not count: 86 | player.tell("^3No Plugin matches ^4{}".format(search)) 87 | -------------------------------------------------------------------------------- /mapmonitor.py: -------------------------------------------------------------------------------- 1 | # mapmonitor.py is a plugin for minqlx to: 2 | # -check on a map change for a change to a bad map 3 | # -If all players are disconnected on a map change it changes to the default map 4 | # -If enabled (default is enabled) the script will also change to the default map when all players disconnect 5 | # created by BarelyMiSSeD on 11-17-2018 6 | # 7 | """ 8 | Set these cvars in your server.cfg (or wherever you set your minqlx variables).: 9 | set qlx_mmDefaultMap "almostlost ca" //set the default map and factory type 10 | set qlx_mmCheckTime "60" //The amount of time the script will check after a map change for a bad map 11 | set qlx_mmChangeWhenEmpty "1" //Enable to change to default map when all players disconnect (1=enabled, 0=disabled) 12 | """ 13 | 14 | import minqlx 15 | import time 16 | 17 | Version = 1.5 18 | 19 | 20 | class mapmonitor(minqlx.Plugin): 21 | def __init__(self): 22 | # cvars 23 | self.set_cvar_once("qlx_mmDefaultMap", "almostlost ca") 24 | self.set_cvar_once("qlx_mmCheckTime", "60") 25 | self.set_cvar_once("qlx_mmChangeWhenEmpty", "1") 26 | 27 | # Minqlx bot Hooks 28 | self.add_hook("map", self.handle_map) 29 | self.add_hook("player_disconnect", self.handle_player_disconnect) 30 | self.add_hook("console_print", self.handle_console_print) 31 | self.add_hook("game_end", self.handle_game_end) 32 | 33 | # Minqlx bot commands 34 | self.add_command("map", self.map_change, 2, usage=" [factory]") 35 | 36 | # Script Variables 37 | self._map_change_time = 0.0 38 | self.map_changed = True 39 | self.player_count = 0 40 | 41 | def handle_map(self, mapname, factory): 42 | self._map_change_time = time.time() 43 | 44 | @minqlx.delay(5) 45 | def check(): 46 | self.check_player_count() 47 | 48 | check() 49 | 50 | def handle_player_disconnect(self, player, reason): 51 | if len(self.players()) - 1 <= 0 and self.get_cvar("qlx_mmChangeWhenEmpty", bool): 52 | self.def_change_map() 53 | self.player_count = 0 54 | 55 | def handle_console_print(self, text): 56 | if text.startswith("zmq RCON command"): 57 | args = text.split(":") 58 | if args[1].startswith(" map "): 59 | self.player_count = len(self.players()) 60 | self.map_changed = True 61 | 62 | def handle_game_end(self, data): 63 | self.player_count = len(self.players()) 64 | 65 | @minqlx.thread 66 | def check_player_count(self): 67 | if self.player_count != 0 or not self.map_changed and self._map_change_time != 0.0: 68 | loop_time = self.get_cvar("qlx_mmCheckTime", int) 69 | while time.time() - self._map_change_time < loop_time: 70 | time.sleep(1) 71 | if len(self.players()) == 0: 72 | self.player_count = 0 73 | self.def_change_map() 74 | return 75 | self.map_changed = False 76 | self.player_count = len(self.players()) 77 | self._map_change_time = 0.0 78 | 79 | @minqlx.next_frame 80 | def def_change_map(self): 81 | current_map = "{} {}".format(self.get_cvar("mapname"), self.get_cvar("g_factory")) 82 | default_map = self.get_cvar("qlx_mmDefaultMap").strip() 83 | if current_map != default_map: 84 | minqlx.console_print("^1Changing map to {}".format(default_map)) 85 | self.map_changed = True 86 | minqlx.console_command("map {}".format(self.get_cvar("qlx_mmDefaultMap"))) 87 | 88 | def map_change(self, player, msg, channel): 89 | if "essentials" not in self._loaded_plugins: 90 | """Changes the map.""" 91 | if len(msg) < 2: 92 | return minqlx.RET_USAGE 93 | self.change_map(msg[1], msg[2] if len(msg) > 2 else None) 94 | self.map_changed = True 95 | self.player_count = len(self.players()) 96 | -------------------------------------------------------------------------------- /autobalance.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2023 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should have received a copy of the GNU General Public License 9 | # along with minqlx. If not, see . 10 | 11 | # This plugin is meant to be used as an extension to the balance.py included in the minqlx-plugins. 12 | # It will automatically execute a balance at the start of a match. 13 | # It will deny a shuffle vote, if enabled, since it will balance at the start of the match. 14 | # It will give a message that a balance will execute at the start of the game whenever someone calls a shuffle, 15 | # even when denying shuffle voting is enabled. 16 | # The specqueue plugin is used to ensure we have even teams at game start, so make sure it is loaded before this plugin. 17 | # This plugin will still work without specqueue, but if the total player on the teams add to an odd number, balancing 18 | # will not occur. 19 | 20 | # created by BarelyMiSSeD on 1-28-2023 21 | 22 | 23 | """ 24 | // Cvar(s) 25 | // Deny a shuffle vote called by a player (1=Deny Vote, 0=Allow Vote). 26 | set qlx_balanceDenyShuffleVote "1" 27 | 28 | 29 | Commands 30 | toggle : changes the setting of the shuffle vote deny. This does not change the cvar setting, 31 | so restarting the server will restore it to the setting in the cvar. 32 | """ 33 | 34 | import minqlx 35 | 36 | VERSION = "1.0" 37 | SUPPORTED_GAMETYPES = ("ad", "ca", "ctf", "dom", "ft", "tdm") 38 | 39 | 40 | class autobalance(minqlx.Plugin): 41 | def __init__(self): 42 | try: 43 | self.balance = self.plugins["balance"] 44 | except KeyError: 45 | raise KeyError("balance.py was not found as a loaded minqlx script.\n" 46 | "Check the load order so doVote loads after balance.\n" 47 | "Exiting script load.") 48 | 49 | self.set_cvar_once("qlx_balanceDenyShuffleVote", "1") 50 | 51 | self.add_hook("vote_called", self.handle_vote_called, priority=minqlx.PRI_HIGH) 52 | self.add_hook("game_countdown", self.handle_game_countdown) 53 | 54 | self.add_command("toggle", self.toggle_shuffle, 3) 55 | 56 | self.deny_shuffle = bool(int(minqlx.get_cvar("qlx_balanceDenyShuffleVote"))) 57 | try: 58 | self.specqueue = self.plugins["specqueue"] 59 | except KeyError: 60 | self.specqueue = None 61 | 62 | def handle_vote_called(self, caller, vote, args): 63 | if vote.lower() == "shuffle": 64 | if self.game.state in ["in_progress", "countdown"]: 65 | self.msg("^3Game is active. Shuffle vote denied.") 66 | return minqlx.RET_STOP_ALL 67 | elif self.deny_shuffle and self.game.type_short in SUPPORTED_GAMETYPES: 68 | self.msg("^3Shuffle vote ^1denied^3. Teams ^4will be balanced ^3at start of game.") 69 | return minqlx.RET_STOP_ALL 70 | 71 | @minqlx.delay(1) 72 | def handle_game_countdown(self): 73 | teams = self.teams() 74 | diff = len(teams["red"]) - len(teams["blue"]) 75 | if diff != 0 and self.specqueue: 76 | self.specqueue.even_the_teams() 77 | self.center_print("*Balancing Teams*") 78 | self.msg("^3Balancing by ^2ELO ^3Skill ratings from ^7{}".format(minqlx.get_cvar("qlx_balanceUrl"))) 79 | players = dict([(p.steam_id, self.game.type_short) for p in teams["red"] + teams["blue"]]) 80 | self.balance.add_request(players, self.balance.callback_balance, minqlx.CHAT_CHANNEL) 81 | 82 | def toggle_shuffle(self, player, msg, channel): 83 | if self.deny_shuffle: 84 | self.deny_shuffle = False 85 | player.tell("^3Shuffle vote denying has been ^4Disabled^7. ^3Players may now call shuffle votes.") 86 | else: 87 | self.deny_shuffle = True 88 | player.tell("^3Shuffle vote denying has been ^2Enabled^7. ^3Player's shuffle votes will be denied.") 89 | -------------------------------------------------------------------------------- /linux_tools/README.md: -------------------------------------------------------------------------------- 1 | # Download Files 2 | I put the download files together to allow server admins to update their maps on their server by just running these bash shell scripts.
3 | *** Make sure to set execute permissions on these .sh files ***
4 | 5 | Here is how I use them:
6 | 1) Shut down all the servers running on the same Quake Live server install
7 | 2) Run the redownload.sh using one of your workshop.txt files. (If you only have one workshop file, skip to step 4)
8 | 3) Run the download_new.sh for each of the workshop files you have, including the one you ran the redownload on, so you can catch any that were missed or see why they didn't download. (you can rename the files to something like download_new_server2.sh, but make sure to have a .sh file for each workshop.txt file)
9 | 4) start your servers.
10 | 11 | The autodownload.sh is there if you like the way it downloads and overwrites the current files, but I don't use this file.
12 | See the descriptions for each file below.
13 | 14 | 15 | # autodownload.sh
16 | *** This works best if ran when the server is not running .. because of updates to the appworkshop_282440.acf file ***
17 | This file is for downloading maps to your quake live server without messing with what has already downloaded.
18 | Unlike when the server starts up, this gives a lot more time for the workshop item to download.
19 | Line 10: Edit the workshopFile name to match the name of your server's workshop file.
20 | Line 11: Edit the steamCMD to match the containing directory structure of the steamcmd.sh file.
21 | Line 12: Edit the installLocation to match the directory where your quake live server is installed.
22 | 23 | The remaining variables should not need to be edited.
24 | 25 | # redownload.sh 26 | *** This needs to be ran when the server is not running .. because it moves the workshop items to a different location so the server will not have access to them until the downloads are complete. ***
27 | This file is for re-downloading maps to your quake live server.
28 | Unlike when the server starts up, this gives a lot more time for the workshop item to download.
29 | It moves the workshop items to a directory named 282440_old then downloads the items to the default directory.
30 | It renames the file used to record what has been downloaded to appworkshop_282440.acf.old so steam thinks nothing has been downloaded.
31 | Line 10: Edit the workshopFile name to match the name of your server's workshop file.
32 | Line 11: Edit the steamCMD to match the containing directory structure of the steamcmd.sh file.
33 | Line 12: Edit the installLocation to match the directory where your quake live server is installed.
34 | 35 | The remaining variables should not need to be edited.
36 | 37 | # download_new.sh 38 | *** This should be ran when the server is not running .. because the server will not know of the existence of the workshop items until it is started again and editing the appworkshop_282440.acf when the server is running is problematic. ***
39 | This file is for downloading new workshop items added to your workshop items file,
40 | or items that did not load with redownload.sh.
41 | It will skip items that have already been downloaded.
42 | The download messages from steamcmd will be displayed using this file so you can see if a workshop item does not exist.
43 | Line 10: Edit the workshopFile name to match the name of your server's workshop file.
44 | Line 11: Edit the steamCMD to match the containing directory structure of the steamcmd.sh file.
45 | Line 12: Edit the installLocation to match the directory where your quake live server is installed.
46 | 47 | The remaining variables should not need to be edited.
48 | 49 | 50 | If the status messages are not appearing correctly you may have a workshop.txt that is not correctly formatted for Linux.
51 | Try running dos2unix on the file. EX: dos2unix workshop.txt
52 | This will convert the line feeds from windows format to the standard linux format. 53 | 54 | # Server Restarting 55 | server_reboot.py and ServerStatus.py
56 | The files must be used together. They allow rebooting the linux server if the Quake Live servers are empty.
57 | The server_reboot.py is responsible for checking and rebooting the linux server.
58 | The ServerStatus.py is a minqlx plugin that saves the current player count to the redis database so it can be read by server_reboot.py.
59 | The setup instructions for the files are in the files at the top. The ServerStatus.py should only need to be loaded as a plugin on each Quake Live server, it has no variables that need to be set.
60 | The server_reboot.py has variables at the top of the file, the ones in all CAPS, that need to be set. The redis database password is the only one that HAS to be set if you use a password for your database, otherwise set it to "".
61 | -------------------------------------------------------------------------------- /getmap.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2020 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should review a copy of the GNU General Public License 9 | # along with minqlx. See . 10 | 11 | # This is a plugin for the minqlx admin bot. 12 | # It allows the admin to download a map to the server and add new IDs to the server's workshop text file. 13 | # It will them restart the server, if it is empty, so that the map can be played. 14 | # It will also allow removal of maps from the server. This wills imply comment the ID in the workshop 15 | # file then restart the server. 16 | # This has worked in my tests every time. I am not guaranteeing it will work in every case. 17 | 18 | # Usage Instructions: !getmap 19 | # !delmap 20 | # It will work from RCON with: qlx !getmap 21 | # qlx !delmap 22 | # 23 | 24 | """ 25 | //script cvars to be put in server configuration file (default: server.cfg). Default values shown. 26 | // set the permission level for admins to allow setting and unsetting of the fun warm up mode 27 | set qlx_getmapAdmin "5" 28 | //set the executable path for steamcmd (the executable may be steamcmd.sh or steamcmd) 29 | set qlx_getmapSteamCmd "/home/steam/steamcmd/steamcmd.sh" 30 | """ 31 | 32 | import minqlx 33 | import subprocess 34 | import shlex 35 | 36 | VERSION = "1.2" 37 | 38 | 39 | class getmap(minqlx.Plugin): 40 | def __init__(self): 41 | self.set_cvar_once("qlx_getmapAdmin", "5") 42 | self.set_cvar_once("qlx_getmapSteamCmd", "/home/steam/steamcmd/steamcmd.sh") 43 | 44 | self.add_command(("getmap", "get"), self.cmd_getmap, self.get_cvar("qlx_getmapAdmin", int)) 45 | self.add_command(("delmap", "remmap"), self.cmd_delmap, self.get_cvar("qlx_getmapAdmin", int)) 46 | 47 | def cmd_getmap(self, player, msg, channel): 48 | if len(msg) < 2 or not msg[1].isnumeric(): 49 | player.tell("^1You must include a map steam ID number") 50 | self.download_map(msg[1], player) 51 | 52 | def cmd_delmap(self, player, msg, channel): 53 | if len(msg) < 2 or not msg[1].isnumeric(): 54 | player.tell("^1You must include a map steam ID number") 55 | self.remove_map(msg[1], player) 56 | 57 | @minqlx.thread 58 | def download_map(self, map_id, player=None): 59 | steam_cmd = self.get_cvar("qlx_getmapSteamCmd") 60 | base_path = self.get_cvar("fs_basepath") 61 | 62 | def run(cmd): 63 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) 64 | stdout, stderr = proc.communicate() 65 | return proc.returncode, stdout, stderr 66 | 67 | args = shlex.split("{} +login anonymous +force_install_dir {}/ +workshop_download_item 282440 {}" 68 | " +quit".format(steam_cmd, base_path, map_id)) 69 | code, out, err = run(args) 70 | lines = out.split() 71 | if b'Success.' in lines: 72 | player.tell("^1Map {} Download Success".format(map_id)) 73 | workshop_file = self.get_cvar("sv_workshopfile") 74 | f = open("{}/baseq3/{}".format(base_path, workshop_file), "r") 75 | items = f.readlines() 76 | f.close() 77 | found = False 78 | for item in items: 79 | if item.startswith("#"): 80 | continue 81 | if str(map_id) == item.strip(): 82 | found = True 83 | break 84 | if not found: 85 | f = open("{}/baseq3/{}".format(base_path, workshop_file), "a") 86 | if items[-1] not in ['\n', '\r\n']: 87 | f.write("\n") 88 | f.write("{}\n".format(map_id)) 89 | f.close() 90 | if len(self.players()) == 0: 91 | minqlx.console_command("quit") 92 | else: 93 | player.tell("The server is not empty. Restart aborted.") 94 | else: 95 | player.tell("^1Map {} download failed.".format(map_id)) 96 | 97 | @minqlx.thread 98 | def remove_map(self, map_id, player=None): 99 | base_path = self.get_cvar("fs_basepath") 100 | workshop_file = self.get_cvar("sv_workshopfile") 101 | f = open("{}/baseq3/{}".format(base_path, workshop_file), "r") 102 | items = f.readlines() 103 | f.close() 104 | found = False 105 | for index, item in enumerate(items): 106 | if item.startswith(str(map_id)): 107 | items[index] = "#" + item 108 | found = True 109 | if found: 110 | f = open("{}/baseq3/{}".format(base_path, workshop_file), "w") 111 | for item in items: 112 | f.write(item) 113 | f.close() 114 | player.tell("^1Map {} commented out in {}".format(map_id, workshop_file)) 115 | if len(self.players()) == 0: 116 | minqlx.console_command("quit") 117 | else: 118 | player.tell("The server is not empty. Restart aborted.") 119 | -------------------------------------------------------------------------------- /specall.py: -------------------------------------------------------------------------------- 1 | # specall.py is a plugin for minqlx to: 2 | # -designed to put all players on the server into spectator 3 | # -a desired teamsize can be included after the command to set a teamsize while putting all to spectator. 4 | # created by BarelyMiSSeD on 2-29-16 5 | # 6 | """ 7 | Set these cvar(s) in your server.cfg (or wherever you set your minqlx variables): 8 | qlx_specallAdminLevel "5" - Sets the minqlx server permission level needed to put everyone into spectate. 9 | 10 | 11 | Commands: 12 | !specall (!allspec) : Puts everyone into spectate if the game has not started. 13 | If a number is inculded after the command the teamsize will be set to that value. 14 | !specallforce (!forceallspec, !forcespecall): Puts everyone to spectate at any time. 15 | If a number is inculded after the command the teamsize will be set to that value. 16 | !specallversion (!specall_version) : Checks the version number of the plugin and 17 | compares it to the version available for download. 18 | """ 19 | 20 | import minqlx 21 | import requests 22 | 23 | VERSION = "v1.4" 24 | 25 | 26 | class specall(minqlx.Plugin): 27 | def __init__(self): 28 | self.add_hook("player_loaded", self.player_loaded) 29 | 30 | # Cvar(s). 31 | self.set_cvar_once("qlx_specallAdminLevel", "5") 32 | 33 | # Commands: permission level is set using some of the Cvars. See the Cvars descrition at the top of the file. 34 | self.add_command(("specall", "allspec"), self.cmd_specAll, int(self.get_cvar("qlx_specallAdminLevel"))) 35 | self.add_command(("specallforce", "forceallspec", "forcespecall"), self.cmd_specAllForce, int(self.get_cvar("qlx_specallAdminLevel"))) 36 | self.add_command(("specallversion", "specall_version"), self.specAll_version, int(self.get_cvar("qlx_specallAdminLevel"))) 37 | 38 | # protect.py version checker. Thanks to iouonegirl for most of this section's code. 39 | @minqlx.thread 40 | def check_version(self, player=None, channel=None): 41 | url = "https://raw.githubusercontent.com/barelymissed/minqlx-plugins/master/{}.py".format(self.__class__.__name__) 42 | res = requests.get(url) 43 | if res.status_code != requests.codes.ok: 44 | return 45 | for line in res.iter_lines(): 46 | if line.startswith(b'VERSION'): 47 | line = line.replace(b'VERSION = ', b'') 48 | line = line.replace(b'"', b'') 49 | # If called manually and outdated 50 | if channel and VERSION.encode() != line: 51 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's ^6{}^7 plugin ^1outdated^7" 52 | " version ^6{}^7. The latest version is ^6{}" 53 | .format(self.__class__.__name__, VERSION, line.decode())) 54 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 55 | # If called manually and alright 56 | elif channel and VERSION.encode() == line: 57 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's latest ^6{}^7 plugin version ^6{}^7." 58 | .format(self.__class__.__name__, VERSION)) 59 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 60 | # If routine check and it's not alright. 61 | elif player and VERSION.encode() != line: 62 | try: 63 | player.tell("^4Server: ^3Plugin update alert^7:^6 {}^7's latest version is ^6{}^7 and" 64 | " you're using ^6{}^7!".format(self.__class__.__name__, line.decode(), VERSION)) 65 | player.tell("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 66 | except Exception as e: minqlx.console_command("echo {}".format(e)) 67 | return 68 | 69 | def specAll_version(self, player, msg, channel): 70 | self.check_version(channel=channel) 71 | 72 | # Player Join actions. Version checker. 73 | @minqlx.delay(4) 74 | def player_loaded(self, player): 75 | try: 76 | if player.steam_id == minqlx.owner() or \ 77 | self.db.has_permission(player, self.get_cvar("qlx_specallAdminLevel", int)): 78 | self.check_version(player=player) 79 | except: 80 | return 81 | 82 | # Forces everyone on the server to spectate. 83 | # If a teamsize is included after the command it will also set the teamsize. 84 | def cmd_specAll(self, player, msg, channel): 85 | 86 | if self.game.state == "in_progress": 87 | player.tell("^4Server: ^7There is a game in progress. To force everyone to spectator use the" 88 | " ^1!specallforce ^7command") 89 | return minqlx.RET_STOP_EVENT 90 | 91 | try: 92 | wanted_teamsize = int(msg[1]) 93 | except: 94 | if len(msg) > 1: 95 | player.tell("^4Server: ^7If a temasize was intended to be set please include an intelligible" 96 | " teamsize in the command.") 97 | wanted_teamsize = int(0) 98 | 99 | teams = self.teams() 100 | 101 | for client in teams["red"]: 102 | client.put("spectator") 103 | for client in teams["blue"]: 104 | client.put("spectator") 105 | 106 | if wanted_teamsize: 107 | self.game.teamsize = wanted_teamsize 108 | self.msg("^4Server: ^7The teamsize was set to ^1{}^7, players were put to spectator to allow the change." 109 | .format(wanted_teamsize)) 110 | return minqlx.RET_STOP_EVENT 111 | 112 | self.msg("^4Server: ^7All players were put to spectator by a server admin.") 113 | return minqlx.RET_STOP_EVENT 114 | 115 | def cmd_specAllForce(self, player, msg, channel): 116 | try: 117 | wanted_teamsize = int(msg[1]) 118 | except: 119 | if len(msg) > 1: 120 | player.tell("^4Server: ^7If a temasize was intended to be set please include an intelligible" 121 | " teamsize in the command.") 122 | wanted_teamsize = int(0) 123 | 124 | teams = self.teams() 125 | 126 | for client in teams["red"]: 127 | client.put("spectator") 128 | for client in teams["blue"]: 129 | client.put("spectator") 130 | 131 | if wanted_teamsize: 132 | self.game.teamsize = wanted_teamsize 133 | self.msg("^4Server: ^7The teamsize was set to ^1{}^7, players were put to spectator to allow the change." 134 | .format(wanted_teamsize)) 135 | return minqlx.RET_STOP_EVENT 136 | self.msg("^4Server: ^7All players were put to spectator by a server admin.") 137 | return minqlx.RET_STOP_EVENT 138 | -------------------------------------------------------------------------------- /mapLimiter.py: -------------------------------------------------------------------------------- 1 | # mapLimiter is a plugin for the minqlx bot to limit the maps and 2 | # game types that can be voted for on a server. 3 | # 4 | # Created by BarelyMiSSeD 12/10/2017 5 | 6 | """ 7 | By default the maps in the baseq3/mappool.txt will be used. 8 | Set the qlx_mapLimiterFile cvar to point to a different file. 9 | EXAMPLE: 10 | set qlx_mapLimiterFile "baseq3/myMapPool.txt" 11 | 12 | The script will look in the qlds folder, so if you want to use the same map pool 13 | file for this script as you are using for your server for map voting at the end of the 14 | match, you need to point it to the baseq3 folder as the EXAMPLE shows. 15 | 16 | The file used must be formatted the same way as the map pool files are for the Quake Live server. 17 | That is one map and factory per line. map|factory 18 | 19 | EXAMPLE: 20 | # specify 1 map per line, mapname|factoryid 21 | # ex: aerowalk|ffa 22 | # see factories.txt for valid factory id values 23 | almostlost|ffa 24 | almostlost|ca 25 | arcanecitadel|tdm 26 | arkinholm|tdm 27 | asylum|ffa 28 | asylum|ca 29 | basesiege|ctf 30 | beyondreality|ctf 31 | bitterembrace|ffa 32 | blackcathedral|ca 33 | blackcathedral|dom 34 | brimstoneabbey|ffa 35 | brimstoneabbey|ca 36 | brimstoneabbey|dom 37 | 38 | As the example shows, if you want more than one game type to be allowed per map then list the map on 39 | multiple lines with a different game type on each line. 40 | """ 41 | 42 | import minqlx 43 | 44 | VERSION = 1.0 45 | 46 | class mapLimiter(minqlx.Plugin): 47 | def __init__(self): 48 | # Cvars. 49 | self.set_cvar_once("qlx_mapLimiterFile", "baseq3/mappool.txt") 50 | 51 | # Minqlx Hooks 52 | self.add_hook("vote_called", self.handle_vote_called, priority=minqlx.PRI_HIGH) 53 | 54 | # Minqlx server commands 55 | self.add_command(("votablemaps", "votemaps", "allowedmaps", "mappool", "maps", "maplist"), self.voteable_maps) 56 | 57 | # Allowed maps dictionary with the allowed factories for each map 58 | self.allowed_maps = {} 59 | 60 | self.get_allowed_maps() 61 | self.unload_overlapping_commands() 62 | 63 | @minqlx.delay(1) 64 | def unload_overlapping_commands(self): 65 | try: 66 | essentials = minqlx.Plugin._loaded_plugins['essentials'] 67 | remove_commands = set(['maps']) 68 | for cmd in essentials.commands.copy(): 69 | if remove_commands.intersection(cmd.name): 70 | essentials.remove_command(cmd.name, cmd.handler) 71 | except Exception as e: 72 | pass 73 | 74 | def handle_vote_called(self, caller, vote, args): 75 | vote = vote.lower() 76 | if vote == "map": 77 | #caller.tell("^3Map voting is not allowed during an active match") 78 | #minqlx.console_print(str(args)) 79 | values = args.split(" ") 80 | if values[0] not in self.allowed_maps: 81 | caller.tell("^3That map is not votable on this server. Type ^1!maps ^3to see the allowed list.") 82 | return minqlx.RET_STOP_ALL 83 | if values[1] and values[1] not in self.allowed_maps[values[0]]: 84 | caller.tell("^1{0} ^3is not allowed on {1}. Type ^1!maps {1} ^3to see the allowed list." 85 | .format(values[1], values[0])) 86 | return minqlx.RET_STOP_ALL 87 | 88 | 89 | def get_allowed_maps(self): 90 | self.allowed_maps.clear() 91 | try: 92 | f = open(self.get_cvar("qlx_mapLimiterFile"), 'r') 93 | lines = f.readlines() 94 | f.close() 95 | except IOError: 96 | channel.reply("^4Server^7: Map List ^1Not Available^7. Contact a server admin.") 97 | return 98 | lines.sort() 99 | for line in lines: 100 | if line.startswith("#"): 101 | continue 102 | map = line.split("|") 103 | map_name = map[0] 104 | game_type = map[1].strip("\n") 105 | if map_name in self.allowed_maps: 106 | self.allowed_maps[map_name].append(game_type) 107 | else: 108 | self.allowed_maps[map_name] = [game_type] 109 | 110 | @minqlx.thread 111 | def voteable_maps(self, player, msg, channel): 112 | title = ["^1MAPS: These are the ^3map ^4factories^1, not always the map name. Use these in a callvote.^7\n"] 113 | map_list = [] 114 | type_list = [] 115 | items = 0 116 | 117 | for map_name in self.allowed_maps: 118 | if len(msg) > 1 and msg[1] not in map_name: 119 | continue 120 | type_list.clear() 121 | if len(self.allowed_maps[map_name]): 122 | for type in self.allowed_maps[map_name]: 123 | type_list.append(type) 124 | else: 125 | type_list.append("not restricted") 126 | map_list.append("^3{} ^4{}".format(map_name, ", ".join(type_list))) 127 | items += 1 128 | 129 | title.append("\n^2{} ^1MAPS: These are the map designations, not always the map name. Use these in a callvote." 130 | .format(items)) 131 | 132 | map_list.sort() 133 | 134 | list_lines = [] 135 | for item in map_list: 136 | line = len(list_lines) 137 | line -= 1 138 | try: 139 | display_line = list_lines[line] 140 | except IndexError: 141 | display_line = "" 142 | part1, part2 = self.line_up(display_line, item) 143 | try: 144 | list_lines[line] = part1 145 | except IndexError: 146 | list_lines.append(part1) 147 | if part2: 148 | list_lines.append(part2) 149 | 150 | if "console" == channel: 151 | minqlx.console_print(title[0].strip("\n")) 152 | for line in list_lines: 153 | minqlx.console_print(line) 154 | minqlx.console_print(title[1].strip("\n")) 155 | return 156 | 157 | player.tell("{}{}{}".format(title[0], "\n".join(list_lines), title[1])) 158 | return 159 | 160 | def line_up(self, mapLine, addMap): 161 | length = len(mapLine) 162 | newLine = None 163 | if length == 0: 164 | line = addMap 165 | elif length < 15: 166 | line = mapLine + " " * (15 - length) + addMap 167 | elif length < 30: 168 | line = mapLine + " " * (30 - length) + addMap 169 | elif length < 45: 170 | line = mapLine + " " * (45 - length) + addMap 171 | elif length < 60: 172 | line = mapLine + " " * (60 - length) + addMap 173 | elif length < 75: 174 | line = mapLine + " " * (75 - length) + addMap 175 | else: 176 | line = mapLine 177 | newLine = addMap 178 | return line, newLine 179 | 180 | -------------------------------------------------------------------------------- /voicechat.py: -------------------------------------------------------------------------------- 1 | # voicechat.py is a plugin for minqlx to: 2 | # -this script is to allow players to be able to vote for Global or Team voice chat setting. 3 | # created by BarelyMiSSeD on 3-26-16 4 | # 5 | """ 6 | Set these cvars in your server.cfg (or wherever you set your minqlx variables).: 7 | qlx_voicechatAdminLevel "5" - Sets the minqlx server permisson level needed to use the admin level commands in this script. 8 | qlx_voicechatVoiceChatVoting "1" - Set to "1" to allow players to vote for changing the voice chat option to team/global. 9 | qlx_voicechatJoinMessage "1" - Set to "1" to display the script join message to connecting players. 10 | """ 11 | 12 | 13 | 14 | import minqlx 15 | import requests 16 | 17 | VERSION = "v1.00" 18 | 19 | class voicechat(minqlx.Plugin): 20 | def __init__(self): 21 | self.add_hook("vote_called", self.handle_vote_called) 22 | self.add_hook("player_loaded", self.player_loaded) 23 | 24 | # Cvars. 25 | self.set_cvar_once("qlx_voicechatAdminLevel", "5") 26 | self.set_cvar_once("qlx_voicechatVoiceChatVoting", "1") 27 | self.set_cvar_once("qlx_voicechatJoinMessage", "1") 28 | 29 | voicechatAdmin = int(self.get_cvar("qlx_voicechatAdminLevel")) 30 | 31 | # Commands: voicechat Admin permission level is set using the qlx_optionsAdminLevel Cvar. See the Cvars descrition at the top of the file. 32 | self.add_command(("globalvoice", "gvoice"), self.cmd_globalVoice, voicechatAdmin) 33 | self.add_command(("teamvoice", "tvoice"), self.cmd_teamVoice, voicechatAdmin) 34 | self.add_command(("voicechatversion", "voicechat_version"), self.voicechat_version, voicechatAdmin) 35 | self.add_command(("voicechat_status", "status", "settings"), self.voicechat_status, voicechatAdmin) 36 | self.add_command("voicechat", self.voicechat_list) 37 | 38 | # voicechat.py version checker. Thanks to iouonegirl for most of this section's code. 39 | @minqlx.thread 40 | def check_version(self, player=None, channel=None): 41 | url = "https://raw.githubusercontent.com/barelymissed/minqlx-plugins/master/{}.py".format(self.__class__.__name__) 42 | res = requests.get(url) 43 | if res.status_code != requests.codes.ok: 44 | return 45 | for line in res.iter_lines(): 46 | if line.startswith(b'VERSION'): 47 | line = line.replace(b'VERSION = ', b'') 48 | line = line.replace(b'"', b'') 49 | # If called manually and outdated 50 | if channel and VERSION.encode() != line: 51 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's ^6{}^7 plugin ^1outdated^7 version ^6{}^7. The latest version is ^6{}".format(self.__class__.__name__, VERSION, line.decode())) 52 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 53 | # If called manually and alright 54 | elif channel and VERSION.encode() == line: 55 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's latest ^6{}^7 plugin version ^6{}^7.".format(self.__class__.__name__, VERSION)) 56 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 57 | # If routine check and it's not alright. 58 | elif player and VERSION.encode() != line: 59 | try: 60 | player.tell("^4Server: ^3Plugin update alert^7:^6 {}^7's latest version is ^6{}^7 and you're using ^6{}^7!".format(self.__class__.__name__, line.decode(), VERSION)) 61 | player.tell("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 62 | except Exception as e: minqlx.console_command("echo {}".format(e)) 63 | return 64 | 65 | def voicechat_version(self, player, msg, channel): 66 | self.check_version(channel=channel) 67 | 68 | # Player Join actions. Version checker and join message. 69 | @minqlx.delay(6) 70 | def player_loaded(self, player): 71 | if player.steam_id == minqlx.owner(): 72 | self.check_version(player=player) 73 | if self.get_cvar("qlx_voicechatJoinMessage", bool): 74 | player.tell("^3Callvote ^4globalvoice ^3or ^4teamvoice^3. Try ^2/cv ^3 to vote for changing the server setting." 75 | "Say ^1{}voicechat ^3to see the voicechat status.".format(self.get_cvar("qlx_commandPrefix"))) 76 | 77 | # Handles votes called: Kick protection, Map voting rejection during active matches, AFK voting, and Mute/UnMute voting. 78 | def handle_vote_called(self, caller, vote, args): 79 | # Global Voice Chat Vote 80 | if vote.lower() == "globalvoice" or vote.lower() == "globalchat" or vote.lower() == "gvoice" or vote.lower() == "gchat": 81 | if not self.get_cvar("qlx_voicechatVoiceChatVoting", bool): 82 | caller.tell("^3Voting for global voice chatting is not enabled on this server.") 83 | return minqlx.RET_STOP_ALL 84 | self.callvote("set g_alltalk 1", "Set voice chatting to GLOBAL?") 85 | minqlx.client_command(caller.id, "vote yes") 86 | self.msg("{}^7 called a vote.".format(caller.name)) 87 | return minqlx.RET_STOP_ALL 88 | # Team Voice Chat Vote 89 | if vote.lower() == "teamvoice" or vote.lower() == "teamchat" or vote.lower() == "tvoice" or vote.lower() == "tchat": 90 | if not self.get_cvar("qlx_voicechatVoiceChatVoting", bool): 91 | caller.tell("^3Voting for team voice chatting is not enabled on this server.") 92 | return minqlx.RET_STOP_ALL 93 | self.callvote("set g_alltalk 0", "Set voice chatting to TEAM ONLY?") 94 | minqlx.client_command(caller.id, "vote yes") 95 | self.msg("{}^7 called a vote.".format(caller.name)) 96 | return minqlx.RET_STOP_ALL 97 | 98 | #Set voice chat to GLOBAL 99 | def cmd_globalVoice(self, player, msg, channel): 100 | minqlx.console_command("set g_alltalk 1") 101 | channel.reply("^3Voice chatting has been set to ^4GLOBAL^3.") 102 | 103 | #Set voice chat to TEAM 104 | def cmd_teamVoice(self, player, msg, channel): 105 | minqlx.console_command("set g_alltalk 0") 106 | channel.reply("^3Voice chatting has been set to ^4TEAM^3.") 107 | 108 | #Shows the status of voice chat 109 | def voicechat_status(self, player, msg, channel): 110 | chat = self.get_cvar("g_alltalk") 111 | if int(chat) == 1: 112 | player.tell("^3Voice chatting on the server is set to ^4GLOBAL^3.") 113 | else: 114 | player.tell("^3Voice chatting on the server is set to ^4TEAM^3.") 115 | return minqlx.RET_STOP_ALL 116 | 117 | #Shows the allowed setting votes to the player 118 | def voicechat_list(self, player, msg, channel): 119 | player.tell("^3Callvote ^4globalvoice ^3or ^4teamvoice^3. Try ^2/cv ^3 to vote for changing the server setting.") 120 | self.voicechat_status(player, msg, channel) -------------------------------------------------------------------------------- /doVote.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2018 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should have received a copy of the GNU General Public License 9 | # along with minqlx. If not, see . 10 | 11 | # doVote.py is a plugin for minqlx to: 12 | # - allow players to callvote forcing the suggested player switch gotten from !teams 13 | # - Players would "/callvote do" to utilize the vote 14 | 15 | # *** IMPORTANT *** This plugin is meant as an add-on to the balance plugin that comes with minqlx 16 | # Make sure the balance plugin is loaded prior to loading doVote. 17 | 18 | # created by BarelyMiSSeD on 8-1-2019 19 | 20 | """ 21 | // sets the percentage of total players on the teams needing to vote yes for it to pass. 22 | // (set to 0 to just pass if yes votes are greater than no votes) 23 | set qlx_balanceDoVotePerc "26" 24 | // enabling this will end the vote at the end of the round (if at least 5 seconds of vote time has passed) and 25 | // pass or fail the vote based on the current votes. (recommend setting above to 0 if this is enabled) 26 | // (0=disabled, 1=enabled) 27 | set qlx_balanceDoVoteEnd "0" 28 | """ 29 | 30 | import minqlx 31 | import random 32 | import time 33 | 34 | VERSION = "2.0" 35 | SUPPORTED_GAMETYPES = ("ad", "ca", "ctf", "dom", "ft", "tdm") 36 | 37 | 38 | class doVote(minqlx.Plugin): 39 | def __init__(self): 40 | try: 41 | self.balance = self.plugins["balance"] 42 | except KeyError: 43 | raise KeyError("balance.py was not found as a loaded minqlx script.\n" 44 | "Check the load order so doVote loads after balance.\n" 45 | "Exiting script load.") 46 | self.set_cvar_once("qlx_balanceDoVotePerc", "26") 47 | self.set_cvar_once("qlx_balanceDoVoteEnd", "0") 48 | self.add_hook("vote_called", self.handle_vote_called, priority=minqlx.PRI_HIGH) 49 | self.add_hook("vote", self.handle_vote_count) 50 | self.add_hook("round_end", self.handle_round_end) 51 | self.add_hook("game_start", self.handle_game_start) 52 | self.add_command("force_agree", self.cmd_force_agree, 3) 53 | self.vote_count = [0, 0, 0] 54 | self.vote_active = False 55 | self.vote_start_time = None 56 | 57 | def handle_vote_called(self, caller, vote, args): 58 | if vote.lower() == "do": 59 | if not self.balance.suggested_pair: 60 | caller.tell("^3There are no suggested players to switch.") 61 | return minqlx.RET_STOP_ALL 62 | if not all(self.balance.suggested_agree): 63 | self.vote_count = [0, 0, 0] 64 | self.vote_start_time = time.time() 65 | self.force_switch_vote(caller, vote) 66 | return minqlx.RET_STOP_ALL 67 | 68 | def handle_vote_count(self, player, yes): 69 | if self.vote_count[2]: 70 | try: 71 | if yes: 72 | self.vote_count[0] += 1 73 | else: 74 | self.vote_count[1] += 1 75 | except Exception as e: 76 | minqlx.console_print("^1doVote handle_vote_count Exception: {}".format(e)) 77 | 78 | def handle_round_end(self, data): 79 | self.process_round_end() 80 | 81 | @minqlx.thread 82 | def process_round_end(self): 83 | try: 84 | if self.vote_active and self.get_cvar("qlx_balanceDoVoteEnd", bool) and\ 85 | time.time() - self.vote_start_time >= 5: 86 | voter_perc = self.get_cvar("qlx_balanceDoVotePerc", int) 87 | if voter_perc > 0: 88 | teams = self.teams() 89 | voters = len(teams["red"]) + len(teams["blue"]) 90 | if self.vote_count[0] / voters * 100 >= voter_perc and self.vote_count[0] > self.vote_count[1]: 91 | time.sleep(1) 92 | self.force_vote(True) 93 | else: 94 | self.force_vote(False) 95 | else: 96 | if self.vote_count[0] > self.vote_count[1]: 97 | time.sleep(1) 98 | self.force_vote(True) 99 | else: 100 | self.force_vote(False) 101 | self.vote_active = False 102 | except Exception as e: 103 | minqlx.console_print("^doVote handle_round_end Exception: {}".format(e)) 104 | 105 | def handle_game_start(self, data): 106 | if self.get_cvar("qlx_balanceAuto", bool): 107 | gt = self.game.type_short 108 | if gt not in SUPPORTED_GAMETYPES: 109 | return 110 | 111 | @minqlx.delay(1.5) 112 | def f(): 113 | players = self.teams() 114 | players = dict([(p.steam_id, gt) for p in players["red"] + players["blue"]]) 115 | self.balance.add_request(players, self.balance.callback_balance, minqlx.CHAT_CHANNEL) 116 | 117 | f() 118 | 119 | @minqlx.thread 120 | def force_switch_vote(self, caller, vote): 121 | try: 122 | self.callvote("qlx {}force_agree".format(self.get_cvar("qlx_commandPrefix")), 123 | "Force the suggested player switch?") 124 | minqlx.client_command(caller.id, "vote yes") 125 | self.msg("{}^7 called vote /cv {}".format(caller.name, vote)) 126 | voter_perc = self.get_cvar("qlx_balanceDoVotePerc", int) 127 | self.vote_active = True 128 | self.vote_count[0] = 1 129 | self.vote_count[1] = 0 130 | self.vote_count[2] = thread_number = random.randrange(0, 10000000) 131 | teams = self.teams() 132 | if voter_perc > 0: 133 | voters = len(teams["red"]) + len(teams["blue"]) 134 | time.sleep(28.7) 135 | if self.vote_active and self.vote_count[2] == thread_number and\ 136 | self.vote_count[0] / voters * 100 >= voter_perc and self.vote_count[0] > self.vote_count[1]: 137 | self.force_vote(True) 138 | self.vote_active = False 139 | else: 140 | time.sleep(28.7) 141 | if self.vote_active and self.vote_count[2] == thread_number and self.vote_count[0] > self.vote_count[1]: 142 | self.force_vote(True) 143 | self.vote_active = False 144 | return 145 | except Exception as e: 146 | minqlx.console_print("^1doVote check_force_switch_vote Exception: {}".format(e)) 147 | 148 | def cmd_force_agree(self, player=None, msg=None, channel=None): 149 | if self.balance.suggested_pair: 150 | self.balance.suggested_agree[0] = True 151 | self.balance.suggested_agree[1] = True 152 | if self.game.state in ["in_progress", "countdown"] and not self.balance.in_countdown: 153 | self.msg("The switch will be executed at the start of next round.") 154 | return 155 | # Otherwise, switch right away. 156 | self.balance.execute_suggestion() 157 | -------------------------------------------------------------------------------- /linux_tools/server_reboot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.5 2 | 3 | """ 4 | This program checks the database entries from the Quake Live servers and reboots the server if the servers are empty. 5 | It requires that the Quake servers run minqlx and the ServerStatus.py plugin for minqlx. 6 | Set the environment variables for this by editing the values below after the equals signs, using the descriptions. 7 | 8 | This needs to be run as root. 9 | To make it execute on a schedule I entered it as a linux cron job. Follow these steps: 10 | 1) log into your server as root or run commands with sudo 11 | 2) on command line run: crontab -e 12 | If this is the first time crontab was run choose the editor type you want to use (i pick the default) 13 | 3) add this line at the end of the file: 1 4 * * * /home/steam/server_reboot.py 14 | the last part is the path and executable file name 15 | the 5 items before are described in the crontab file, the 1 4 and * * * will make it execute at 4am every day 16 | the 4am is based on your server's system time. 17 | 4) exit the crontab editor and the cron job execution should give a message like 'crontab: installing new crontab' 18 | this shows the job was scheduled with the linux operating system 19 | 20 | Load the ServerStatus onto your Quake Live server just like every other plugin, it has no variables to set on the server 21 | """ 22 | 23 | import os 24 | import time 25 | import redis 26 | 27 | # The REDIS_DATABASE values are all of the unique settings used by the Quake Live server's minqlx qlx_redisDatabase 28 | # values. This example meas there are 3 servers. The servers each have a different qlx_redisDatabase setting. 29 | # One has a '0', another a '1', and the last a '2'. 30 | REDIS_DATABASES = [0, 1, 2] # This tells which databases are used by the QL servers *** NEEDS TO BE SET CORRECTLY *** 31 | REDIS_PASS = "Redis_Server_Password_Here" # Redis password ( "" if none) 32 | CHECK_KEY = "minqlx:connected" # Must match COUNT_KEY in ServerStatus.py (should not need to edit) 33 | REDIS_ADDRESS = "127.0.0.1" # address of the redis server (127.0.0.1 is the same as localhost, should not need to edit) 34 | LOG_MESSAGES = True # Enable status logging to linux system log 35 | USE_SYSLOG = False # Set the logging (if enabled) to use the OS's system log 36 | RECHECK_SERVERS = True # Check the servers again if they are not empty (enable=True, disable=False) 37 | WAIT_TIME = 20 # The minutes the process will wait before checking again if the servers are not empty 38 | MAX_CHECKS = 10 # The maximum amount of times the process will check for an empty server (0 to disable) 39 | FORCE_RESTART = False # If MAX_CHECKS has been met, should the server be restarted anyway (True or False) 40 | 41 | if LOG_MESSAGES: 42 | if USE_SYSLOG: 43 | import syslog 44 | else: 45 | import sys 46 | # edit the file name to include the path, otherwise it will be in the root home directory) 47 | sys.stdout = open("server_reboot.log", "a") 48 | 49 | 50 | # ==================================================================== 51 | # Redis 52 | # ==================================================================== 53 | class Redis: 54 | _conn = None 55 | _pool = None 56 | _pass = REDIS_PASS 57 | _counter = 0 58 | 59 | def __init__(self): 60 | self.__class__._counter += 1 61 | 62 | def __del__(self): 63 | self.__class__._counter -= 1 64 | self.close() 65 | 66 | def __contains__(self, key): 67 | return self.r.exists(key) 68 | 69 | def __getitem__(self, key): 70 | res = self.r.get(key) 71 | if res is None: 72 | raise KeyError("The key '{}' is not present in the database.".format(key)) 73 | else: 74 | return res 75 | 76 | def __setitem__(self, key, item): 77 | res = self.r.set(key, item) 78 | if res is False: 79 | raise RuntimeError("The database assignment failed.") 80 | 81 | def __delitem__(self, key): 82 | res = self.r.delete(key) 83 | if res == 0: 84 | raise KeyError("The key '{}' is not present in the database.".format(key)) 85 | 86 | def __getattr__(self, attr): 87 | return getattr(self.r, attr) 88 | 89 | @property 90 | def r(self): 91 | return self.connect() 92 | 93 | def connect(self, host="127.0.0.1", database=0, unix_socket=False, password=None): 94 | if not host and not self._conn: 95 | if not Redis._conn: 96 | _host = REDIS_ADDRESS 97 | _db = database 98 | _unixsocket = unix_socket 99 | if _unixsocket: 100 | Redis._conn = redis.StrictRedis(unix_socket_path=_host, db=_db, password=Redis._pass, 101 | decode_responses=True) 102 | else: 103 | split_host = _host.split(":") 104 | if len(split_host) > 1: 105 | port = int(split_host[1]) 106 | else: 107 | port = 6379 # Default port. 108 | Redis._pool = redis.ConnectionPool(host=split_host[0], port=port, db=_db, password=Redis._pass, 109 | decode_responses=True) 110 | Redis._conn = redis.StrictRedis(connection_pool=Redis._pool, decode_responses=True) 111 | self._conn = None 112 | return Redis._conn 113 | elif not self._conn: 114 | split_host = host.split(":") 115 | if len(split_host) > 1: 116 | port = int(split_host[1]) 117 | else: 118 | port = 6379 # Default port. 119 | 120 | if unix_socket: 121 | self._conn = redis.StrictRedis(unix_socket_path=host, db=database, password=password, 122 | decode_responses=True) 123 | else: 124 | self._pool = redis.ConnectionPool(host=split_host[0], port=port, db=database, password=password, 125 | decode_responses=True) 126 | self._conn = redis.StrictRedis(connection_pool=self._pool, decode_responses=True) 127 | return self._conn 128 | 129 | def close(self): 130 | if self._conn: 131 | self._conn = None 132 | if self._pool: 133 | self._pool.disconnect() 134 | self._pool = None 135 | 136 | if Redis._counter <= 1 and Redis._conn: 137 | Redis._conn = None 138 | if Redis._pool: 139 | Redis._pool.disconnect() 140 | Redis._pool = None 141 | 142 | 143 | # ==================================================================== 144 | # Get Total Player Count 145 | # ==================================================================== 146 | class PlayerCount: 147 | def __init__(self): 148 | self.usage = 0 149 | self.r = Redis() 150 | 151 | def get_occupied_count(self): 152 | count = 0 153 | for x in REDIS_DATABASES: 154 | self.r.connect(REDIS_ADDRESS, x, False, REDIS_PASS) 155 | if self.r[CHECK_KEY.format(x)]: 156 | count += int(self.r[CHECK_KEY.format(x)]) 157 | self.r.close() 158 | if LOG_MESSAGES: 159 | if USE_SYSLOG: 160 | syslog.syslog("Player Count: {}\n".format(count)) 161 | else: 162 | print("{} Player Count: {}".format(time.strftime("(%d %b %Y %H:%M:%S)"), count)) 163 | return count 164 | 165 | 166 | # ==================================================================== 167 | # Reboot 168 | # ==================================================================== 169 | if __name__ == "__main__": 170 | check_count = 0 171 | if LOG_MESSAGES: 172 | if USE_SYSLOG: 173 | syslog.syslog("Checking server(s) player count") 174 | else: 175 | print("{} Checking server(s) player count".format(time.strftime("(%d %b %Y %H:%M:%S)"))) 176 | player_count = PlayerCount() 177 | total_players = player_count.get_occupied_count() 178 | while total_players != 0 and check_count < MAX_CHECKS and RECHECK_SERVERS: 179 | check_count += 1 180 | if LOG_MESSAGES: 181 | if USE_SYSLOG: 182 | syslog.syslog("Servers are not empty. Waiting {} minutes".format(WAIT_TIME)) 183 | else: 184 | print("{} Servers are not empty. Waiting {} minutes" 185 | .format(time.strftime("(%d %b %Y %H:%M:%S)"), WAIT_TIME)) 186 | time.sleep(WAIT_TIME * 60) 187 | total_players = player_count.get_occupied_count() 188 | 189 | if total_players == 0: 190 | print("Player Count: {}".format(total_players)) 191 | if LOG_MESSAGES: 192 | if USE_SYSLOG: 193 | syslog.syslog("Servers are empty: Rebooting") 194 | else: 195 | print("{} Servers are empty: Rebooting".format(time.strftime("(%d %b %Y %H:%M:%S)"))) 196 | os.system('/sbin/shutdown -r now') 197 | elif check_count >= MAX_CHECKS and RECHECK_SERVERS and FORCE_RESTART: 198 | if LOG_MESSAGES: 199 | if USE_SYSLOG: 200 | syslog.syslog("MAX_CHECKS has been met: forcing Reboot") 201 | else: 202 | print("{} MAX_CHECKS has been met: forcing Reboot".format(time.strftime("(%d %b %Y %H:%M:%S)"))) 203 | os.system('/sbin/shutdown -r now') 204 | -------------------------------------------------------------------------------- /Map_Names/Map_Names.txt: -------------------------------------------------------------------------------- 1 | ra3map6 - Sanity Slipped 2 | ra3map6a - Slightly Schizo 3 | ra3map6b - Brain Broke 4 | ra3map6c - Moon Man 5 | ra3map22 - Quiet Times 6 | ra3map22a - Garden O Gibs 7 | ra3map22b - Simple Speed 8 | ra3map22c - Angry **** Tube 9 | ra3map22d - Abx: Docking Unload 10 | sanctorium 11 | ra3map21 - City of the Dead 12 | ra3map21a - The Pit of Souls 13 | ra3map21b - Spectral Tower 14 | ra3map21c - Oblivions Call 15 | ra3map8 - Gaudy 16 | ra3map8a - Gleam 17 | ra3map8b - Grit 18 | ra3map8c - Grunge Gib 19 | ra3map8d - Gutteral 20 | ra3map20 - Fragaholic 21 | ra3map20a - Fragma 22 | ra3map20b - Remix 23 | ra3map20c - Space Gib 24 | ra3map9 - Cannery 25 | ra3map9a - 3 Story 26 | ra3map9b - Q3Terrain 27 | ra3map9c - Megan's Place 28 | Solitude 29 | ra3map10 - Jupiter Rising 30 | ra3map10a - Arrival 31 | ra3map10b - Doomed 32 | ra3map10c - N.N.Y 33 | nkstrqldm1 - family fun factory 34 | ra3map13 - The Assassins Hide-Out 35 | ra3map13a - The Railing box 36 | ra3map13b - The Chromatic Death 37 | ra3map13c - The Stadium 38 | ra3map13d - The Try-Out 39 | ra3map15 - Stormatorium 40 | ra3map15a - Vertical Mayhem 41 | ra3map15b - Storm Keep 42 | ra3map15c - Dungeon of Doom 43 | ra3map15d - Chaos Within 44 | ra3map18 - Temple of Yog-Sothoth 45 | ra3map18a - Archway 46 | ra3map18b - Santicity 47 | ra3map18c - Downward Spiral 48 | ra3map14 - Mid Night 49 | ra3map14a - Caffeine 50 | ra3map14b - Nicotine 51 | ra3map14c - Tetrahydrocannabinol 52 | ra3map19 - The Voices 53 | ra3map19a - Roftop Rumble 54 | ra3map19b - Nog's Place 55 | ra3map19c - The Drain 56 | ospca1 - american can co. 57 | rustgrad 58 | q1dm6 - the dark zone 59 | padcastle 60 | padcenter 61 | padcrash 62 | padcrash_ctf 63 | padcrash_dm17 64 | padgallery 65 | padgallery_dl 66 | padgarden 67 | padhome 68 | padkitchen 69 | padpool 70 | padshop 71 | padspace 72 | ra3map1 - Theatre of Pain 73 | ra3map1a - Evolution 74 | ra3map1b - Thunderstruck 75 | ra3map1c - Canned Heat 76 | ra3map2 - Shaken Not Stirred 77 | ra3map2a - Electric Head 78 | ra3map2b - Somewhat Damaged 79 | ra3map2c - Window Pain 80 | pillcity 81 | quatrix 82 | pacman 83 | jpac 84 | hockey 85 | Pinball 86 | playground 87 | zastavka 88 | q1dm6t - Dark Zone 2020 89 | szq2dm1ish - The Edge Remix 90 | prodm7- Club Doom 91 | oxodm102_b1 - Campgrounds Redux [Q4DM9] 92 | nalq1dm6 - The Sinister Zone (q1dm6) 93 | oxodm2a - Aero Run (Second Edition) 94 | Rockshellwill 95 | Z-LANparty 96 | q3dm6remix - Q3DM6 In a hat 97 | tokaystowers 98 | thefragpipe 99 | ra3map5 - Abbey of Temptation 100 | ra3map5a - The Traps of Fate 101 | ra3map5b - Faith in Death 102 | ra3map5c - The Last Gift 103 | ra3map3 - AC's Temple 104 | ra3map3a - Electrodome 105 | ra3map3b - The Divide 106 | ra3map3c - The Generator 107 | ra3map3d - Industrial Buttress 108 | ra3map7 - House Of The Desolate 109 | ra3map7a - House Of Decay 110 | ra3map7b - House Of Blood 111 | ra3map7c - House Of Infliction 112 | storm3dm2 113 | storm3tourney1 114 | storm3tourney2 115 | storm3tourney4 116 | storm3tourney5 117 | storm3tourney6 118 | storm3tourney9 119 | polo3dm2 120 | polo3dm3 121 | polo3dm4 122 | polo3dm5 123 | polo3ctf1 124 | polo3t1 125 | Mars 126 | Dracula 127 | hogwarts 128 | casablanca 129 | kitnamek 130 | kitroom 131 | kitcapsule 132 | dapalace 133 | danorthern 134 | dacity 135 | bdra3 136 | raztrainql_beta3 137 | jof3ca 138 | aggressor 139 | chiropteraDM (deathmatch version) 140 | chiropteraTA (team arena version) 141 | distonic_small 142 | klcurves_small 143 | klhights 144 | platypus 145 | plutonians 146 | sparth 147 | qlclaustro - q1dm2 148 | qlabandoned - q1dm3 149 | qlbadplace - q1dm4 150 | nalq1dm6 - q1dm6 151 | qldamned - e1m2 152 | qlq2dm3 q2dm3 The Frag Pipe 153 | ztn2dm3 aka The Rage 154 | ospdm8 - q2rdm2 155 | cpm15 - Patibuh's Lair 156 | cpm28 - Unbalanced 157 | EADM1 - Monochrome 158 | EADM2 - Have a Banana 159 | EADM3 - Shady Towers 160 | EADM4 - Shady Towers 2 161 | EADM5 - Zion's Quarry 162 | EADM6 - Asylum 163 | EADM7 - Reduced Fat Wheat Thins 164 | EADM8 - Stomping Arena 165 | EADM9 - Aerodrome 166 | EADM10 - Circle Jerk 167 | EADM11 - Impossible Dinosaur Battlefield 168 | EADM12 - Carbon 169 | EACTF1 - Monochrome CTF 170 | ql_dust - pxcdust 171 | ql_dust2 - oxodm1 172 | Match1 173 | q2dm1 174 | q2dm2 175 | q2dm3 176 | q2dm4 177 | q2dm5 178 | q2dm6 179 | q2dm7 180 | q2dm8 181 | strike 182 | p1rate_overek - Cut em' Hard 183 | jof3dm1 - JustOneFix 184 | jof3dm2 185 | jof3ctf1 186 | rdogdm1 - Zero Tolerance 187 | rdogdm2 - Shakedown 188 | rdogdm2-lr - Shakedown - Llama's Revenge 189 | rdogdm4 -Epoch 190 | qfraggel1 -The Kristall Keep 191 | qfraggel2a - Last Fortress 192 | qfraggel3ffa - Swiss Cheese Trickster 193 | qfraggel3tdm - Swiss Cheese Trickster 194 | fr3dm1 - Iron Yard 195 | beneath 196 | uranus 197 | summer 198 | winter 199 | ospca1: American Can-CO 200 | ospctf1: White Noise 201 | ospctf2: Crossed Paths 202 | ospdm1: The New Edge 203 | ospdm2: Batcula 204 | ospdm3: Scrap Metal II 205 | ospdm4: Bitter Embrace 206 | ospdm5: Deep Inside 207 | ospdm6: Suicide 208 | ospdm7: The Dancehall 209 | ospdm8: The Chastity Belt 210 | ospdm9: Anticipating Oblivion 211 | ospdm10: Apologie 212 | ospdm11: The Olden Domain 213 | ospdm12: Deschutes 214 | ospdm13: Bullet Ride 215 | ospdm14: Epilogue 216 | ospdm15: Deeper Blue 217 | proverek - Projectile Vomiting (OK/camps) 218 | okremix - *same as p1rate_overek 219 | 6plus - Gayass Campgrounds 220 | nemesis 221 | akumacpm1a 222 | astronomybeta 223 | astronomy_ix 224 | cht3 225 | cpm15 226 | cpm23 227 | eizdm4_b15 228 | fjo3tourney1 229 | megadm4_09 230 | necro6 231 | ne_duel 232 | ospdm8a 233 | plduel1 234 | pukka3dm1 235 | pukka3tourney3 236 | pukka3tourney4 237 | pukka3tourney5 238 | q3nem06_v2 239 | q3tourney2 240 | reqtourney1 241 | rota3dm1 242 | rota3dm2 243 | rota3tourney1 244 | cowsmap 245 | aerospace 246 | nazibunker 247 | Deck2k7 248 | Kora 249 | jex3dm1 - Eulogy 250 | cpm18r - Powahaus Redux 251 | cmp1-dm6 - DSI 252 | el3dm1 - Evil Space 253 | el3t3 - The Summoning 254 | el3t4 - Pillamyd 255 | charon3dm1 256 | charon3dm2 257 | charon3dm3 258 | charon3dm4 259 | charon3dm5 260 | charon3dm666 261 | charon3dm7 262 | charon3dm8 263 | charon3dm9 264 | charon3dm10 265 | charon3dm11v2 266 | charon3dm12 267 | charon3dm13 268 | sin - Sinister Purpose 269 | wdw3dm3 - Toxic Skirmish 270 | wdw3dm4 - No Remorse! 271 | wdw3team1 - No Class! 272 | qxdm3 - Station 52 273 | qxdm4_v2 - Grindill 2.0 274 | rjldm1 - Eagles Temple 275 | rjldm2 - Renaissance 276 | rjldm3 - State Prison 36 277 | rjldom1 - Waste Processing Plant 13 278 | pjw3dm6 - Guns 279 | cpm22 - Aerowalk [Hubster's Remix II] 280 | davinci 281 | city1 282 | vinyl - Vinyl Room 283 | lun3dm1 284 | lun3dm2 285 | lun3dm3 286 | lun3dm4 287 | psidm7 288 | cpm8 289 | tscabdm2 290 | mvdm09 291 | opium 292 | haunted 293 | reqkitchen - Nu Clear Lunch Time 294 | reqbath - Deathroom Bathroom 295 | 1plus 296 | 5plus 297 | 6plusplus 298 | 7plus 299 | 16plus 300 | 16plusplus 301 | 17plus 302 | Bal3dm4 - Scrap Metal ][ 303 | uzul3 - Uzuldaroum III 304 | nemisis 305 | q3scamp15mc - Feel the Way You Hate 306 | Bal3dm3 - disinformation 307 | Bal3dm1 - Ash Rain 308 | Bal3dm2 - Golconda 309 | Bal3dm5 - Junk Data 310 | splatdm2 - Portal Mayhem 311 | Cloudwalk 312 | ca_tamb 313 | ts_ca1 - Fly on Rocket 314 | MARIO 315 | 4sgca1 - Winter 316 | gridworks 317 | Geit3dm6 - Black Shining Leather 318 | dust_dm1 - Lost Village 319 | gd_tourney1a - Thrill Kill 320 | spirit3t3a - Terror Twilight 321 | pro_faceoff 322 | NeoEgypt 323 | Ra3map11pp - OverkillPP 324 | herocerated 325 | 17plusplus 326 | bah 327 | cowdm1 - Vertigo 328 | hope 329 | imr3dm1 - Industrial Revolution 330 | js_tourney1 331 | klhights - Kleskonian Hights 332 | nodm18 - Grim Dungeons (CPM Edition) 333 | pqarena 334 | prodcmap7 - Fortress 2089 335 | q3mexx1 - Devolved 336 | Q3mIKE5 - Intergalactic 337 | q3ngiarena2 338 | qfraggel2 - Last Fortress 339 | rdogdm4 - Epoch 340 | thunda3dm2 - Degree of Power 341 | uberkill - Knows No Shame (overkill) 342 | batcula 343 | Ra3map2pp - ShakenNotStirredPP 344 | Cubicle 345 | q3dm2remix Spillway remix 346 | 4sgca2 - Cold as Death 347 | ts_dm5 - maido mod 348 | ts_dm5tmp 349 | Karist 350 | probgmp6 - Soremill V2 351 | cpm8 - Clowning Around 352 | feeltherush - Feel The Rush Redux 353 | damn_q3dm1 - Arena Gate (damn edition) 354 | q3dm1+ - Arena Gate + 355 | q3dm13remix - Lost World remix 356 | q3dm3remix - Hearth remix 357 | q3dm4remix - Eviscerated remix 358 | lsjgcoop - MaXimus 359 | ct3dm4 - Paranoid 360 | swift 361 | hyperblast 362 | dubneoc - Neorganic Epiphany 363 | 2sweet 364 | arena66 365 | ci - Citadel Insatiable 366 | dAdeX 367 | e1m7ish3 -quake1 map House of Chthon 368 | heffdm17 - DM17 Egyptian Redux 369 | pillars 370 | lastarena 371 | lloydmdm2 - Ancient Archipelago 372 | pasdm1 - Dynasty 373 | goldleaf 374 | ospdm14rc1 - Epilogue 375 | alm3dm4 - Arena 13 376 | xccc_dm7 - Bloodfactory 377 | lffd - Looking For Free Doom 378 | thephhortress 379 | rota3dm1 - Last Trip 380 | 13agony - Agony 381 | sinister2012 382 | pizza50 - pizzaland 383 | Oilrig 384 | cpm2 385 | mapel4b 386 | Precept 387 | jaxdm8 388 | Unholy 389 | qlairrox 390 | q3_ultrav - Ultra Violence 391 | poptart 392 | 13yard_xt 393 | ole 394 | Breathe 395 | inversekill 396 | lun3dm5 397 | burning1 - Burning Reminders 398 | dum 399 | rota3dm2 - KARIN 400 | rota3dm3 - Marilyn 401 | rota3dm5 - Kora 402 | anodm4 - Manic Depression 403 | dk - Steam Arena 404 | MKSTEEL - Fistful of Steel 405 | q3dmp4 - Sublevel 27 406 | q3dmp11 - Tech Edge 407 | q3dmp23 - Undergod 408 | auh3dm1 - OverWhelming Hostility 409 | auh3dm2 - The BackStab 410 | katdm3 - Inner Sanctum 411 | bluemonk 412 | railorfail 413 | arcdm17 414 | battlerail 415 | garena3 416 | -------------------------------------------------------------------------------- /listmaps.py: -------------------------------------------------------------------------------- 1 | # listmaps.py is a plugin for minqlx to: 2 | # -Build a list of maps that are loaded on the server installation. 3 | # -This works for the server installation. The server instances that are running share the downloaded maps. 4 | # -For this reason, I did not make this script work differently for each running server. 5 | # -The generated maplist is saved in the server's install directory, typically ./qlds 6 | # created by BarelyMiSSeD on 5-13-16 7 | # 8 | """ 9 | // Set these cvars in your server.cfg (or wherever you set your minqlx variables).: 10 | set qlx_listmapsAdmin "4" // Sets the minqlx server permisson level needed to admin the listmaps script (to use !getmaps). 11 | """ 12 | 13 | import minqlx 14 | import requests 15 | import re 16 | 17 | VERSION = "v1.19" 18 | FILE_NAME = "server_{}_map_list.txt" 19 | MAP_NAME_FILE = 'Map_Names.txt' 20 | _map_buffer = [] 21 | _map_redirection = False 22 | 23 | 24 | class listmaps(minqlx.Plugin): 25 | def __init__(self): 26 | # Minqlx Hooks 27 | self.add_hook("console_print", self.handle_console_print) 28 | self.add_hook("player_loaded", self.player_loaded) 29 | 30 | # CVARS 31 | self.set_cvar_once("qlx_listmapsAdmin", "4") 32 | 33 | # Minqlx server commands 34 | self.add_command("getmaps", self.get_maps, self.get_cvar("qlx_listmapsAdmin", int)) 35 | self.add_command(("listmaps", "listmap"), self.cmd_list_maps, 0, usage="|search string|") 36 | self.add_command(("listmapsversion", "listmaps_version"), self.listmaps_version, 0) 37 | self.add_command("mapname", self.cmd_mapname, 0) 38 | 39 | listmaps.map_file = FILE_NAME.format(self.get_cvar("net_port")) 40 | self.getting_maps = False 41 | 42 | self.get_maps() 43 | 44 | # listmaps.py version checker. Thanks to iouonegirl for most of this function's code. 45 | @minqlx.thread 46 | def check_version(self, player=None, channel=None): 47 | url = "https://raw.githubusercontent.com/barelymissed/minqlx-plugins/master/{}.py"\ 48 | .format(self.__class__.__name__) 49 | res = requests.get(url) 50 | if res.status_code != requests.codes.ok: 51 | return 52 | for line in res.iter_lines(): 53 | if line.startswith(b'VERSION'): 54 | line = line.replace(b'VERSION = ', b'') 55 | line = line.replace(b'"', b'') 56 | # If called manually and outdated 57 | if channel and VERSION.encode() != line: 58 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's ^6{}^7 plugin ^1missmatched^7" 59 | " version ^6{}^7. The latest github version is ^6{}" 60 | .format(self.__class__.__name__, VERSION, line.decode())) 61 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 62 | # If called manually and alright 63 | elif channel and VERSION.encode() == line: 64 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's latest ^6{}^7 plugin version ^6{}^7." 65 | .format(self.__class__.__name__, VERSION)) 66 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 67 | # If routine check and it's not alright. 68 | elif player and VERSION.encode() != line: 69 | try: 70 | player.tell("^4Server: ^3Plugin update alert^7:^6 {}^7's latest version is ^6{}^7 and you're" 71 | " using ^6{}^7!".format(self.__class__.__name__, line.decode(), VERSION)) 72 | player.tell("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 73 | except Exception as e: 74 | minqlx.console_print("LISTMAPS Version Checking Error: {}".format(e)) 75 | return 76 | 77 | def listmaps_version(self, player, msg, channel): 78 | self.check_version(channel=channel) 79 | 80 | # Server Owner Join version checker. 81 | @minqlx.delay(4) 82 | def player_loaded(self, player): 83 | if player.steam_id == minqlx.owner(): 84 | self.check_version(player=player) 85 | 86 | def handle_console_print(self, text): 87 | """Called whenever the server prints something to the console.""" 88 | if self.getting_maps: 89 | try: 90 | if text and _map_redirection: 91 | global _map_buffer 92 | if '.bsp' in text: 93 | _map_buffer.append(re.sub("maps/|.bsp", "", text)) 94 | return 95 | 96 | except: 97 | minqlx.log_exception() 98 | return True 99 | return 100 | 101 | def get_maps(self, player=None, msg=None, channel=None): 102 | self.getting_maps = True 103 | with self.gather_maps(): 104 | minqlx.console_command("fdir *.bsp") 105 | 106 | if player: 107 | player.tell("^4Server^7: The server maps have been stored in the file ^3{}^7.".format(listmaps.map_file)) 108 | 109 | minqlx.console_print("The server maps have been stored in the file ^3{}".format(listmaps.map_file)) 110 | 111 | self.getting_maps = False 112 | return True 113 | 114 | def gather_maps(self): 115 | 116 | class Redirector(listmaps): 117 | def __init__(self): 118 | self.trigger = True 119 | 120 | def __enter__(self): 121 | global _map_redirection 122 | _map_redirection = self.trigger 123 | 124 | def __exit__(self, exc_type, exc_val, exc_tb): 125 | global _map_redirection, _map_buffer 126 | map_write = open(listmaps.map_file, "w") 127 | for item in _map_buffer: 128 | map_write.write(str(item)) 129 | map_write.close() 130 | _map_redirection = False 131 | _map_buffer.clear() 132 | 133 | return Redirector() 134 | 135 | def cmd_list_maps(self, player, msg, channel): 136 | self.list_map_names(player, msg, channel) 137 | 138 | @minqlx.thread 139 | def list_map_names(self, player, msg, channel): 140 | title = ["^1MAPS: These are the map designations, not always the map name. Use these in a callvote.^7\n"] 141 | maps = [] 142 | try: 143 | maps_file = open(listmaps.map_file, 'r') 144 | lines = maps_file.readlines() 145 | maps_file.close() 146 | except IOError: 147 | channel.reply("^4Server^7: Map List creation ^1failed^7. Contact a server admin.") 148 | return 149 | lines.sort() 150 | items = 0 151 | if len(msg) < 2: 152 | for line in lines: 153 | map_line = len(maps) 154 | map_line -= 1 155 | try: 156 | mapLine = maps[map_line] 157 | except IndexError: 158 | mapLine = "" 159 | lineA, lineB = self.line_up(mapLine, line.strip()) 160 | try: 161 | maps[map_line] = lineA 162 | except IndexError: 163 | maps.append(lineA) 164 | if lineB: 165 | maps.append(lineB) 166 | items += 1 167 | else: 168 | search = " ".join(msg[1:]) 169 | for line in lines: 170 | if search in line: 171 | map_line = len(maps) 172 | map_line -= 1 173 | try: 174 | mapLine = maps[map_line] 175 | except IndexError: 176 | mapLine = "" 177 | lineA, lineB = self.line_up(mapLine, line.strip()) 178 | try: 179 | maps[map_line] = lineA 180 | except IndexError: 181 | maps.append(lineA) 182 | if lineB: 183 | maps.append(lineB) 184 | items += 1 185 | if items == 0: 186 | player.tell("^4Server^7: No maps contain the search string ^1{}^7.".format(search)) 187 | return 188 | 189 | title.append("\n^2{} ^1MAPS: These are the map designations, not always the map name. Use these in a callvote." 190 | .format(items)) 191 | 192 | if "console" == channel: 193 | minqlx.console_print(title[0].strip("\n")) 194 | for line in maps: 195 | minqlx.console_print("^4" + line) 196 | minqlx.console_print(title[1].strip("\n")) 197 | return 198 | 199 | player.tell("{}{}{}".format(title[0], "\n".join(maps), title[1])) 200 | return 201 | 202 | def line_up(self, mapLine, addMap): 203 | length = len(mapLine) 204 | newLine = None 205 | if length == 0: 206 | line = addMap 207 | elif length < 14: 208 | line = mapLine + " " * (14 - length) + addMap 209 | elif length < 29: 210 | line = mapLine + " " * (29 - length) + addMap 211 | elif length < 44: 212 | line = mapLine + " " * (44 - length) + addMap 213 | elif length < 59: 214 | line = mapLine + " " * (59 - length) + addMap 215 | elif length < 74: 216 | line = mapLine + " " * (74 - length) + addMap 217 | else: 218 | line = mapLine 219 | newLine = addMap 220 | return line, newLine 221 | 222 | def cmd_mapname(self, player, msg, channel): 223 | if len(msg) < 2: 224 | channel.reply("^3Usage^7: ^4(found with the ^3!listmaps^4 command)") 225 | return 226 | try: 227 | map_file = open(MAP_NAME_FILE, 'r') 228 | lines = map_file.readlines() 229 | map_file.close() 230 | except IOError: 231 | player.tell("^4Server^7: There is no Map Name file to reference. Talk to a server admin.") 232 | return 233 | 234 | map_search = msg[1] 235 | matching = [s for s in lines if map_search in s] 236 | if len(matching) == 1: 237 | item = matching[0].split(" - ") 238 | if len(item) > 1: 239 | item = item[1].strip("\n") 240 | item = item.rstrip(" ") 241 | channel.reply("^4Server^7: The name associated with {} is ^3{}^7.".format(map_search, item)) 242 | else: 243 | item = item[0].strip("\n") 244 | item = item.rstrip(" ") 245 | channel.reply("^4Server^7: The name associated with {} is ^3{}^7.".format(map_search, item)) 246 | return 247 | elif len(matching) > 1: 248 | for item in matching: 249 | item = item.split(" - ") 250 | if item[0] == map_search: 251 | if len(item) > 1: 252 | item = item[1].strip("\n") 253 | item = item.rstrip(" ") 254 | channel.reply("^4Server^7: The name associated with {} is ^3{}^7.".format(map_search, item)) 255 | else: 256 | item = item[0].strip("\n") 257 | item = item.rstrip(" ") 258 | channel.reply("^4Server^7: The name associated with {} is ^3{}^7.".format(map_search, item)) 259 | return 260 | matched = [] 261 | count = 0 262 | for item in matching: 263 | item = item.split(" - ") 264 | matched.append(item[0]) 265 | count += 1 266 | channel.reply("^4Server^7: {} matches to your search for {}. ({})".format(count, map_search, ", ".join(matched))) 267 | else: 268 | channel.reply("^4Server^7: There is no map called {} in the map name file.".format(map_search)) 269 | 270 | return 271 | -------------------------------------------------------------------------------- /myFun/Working_Sounds.txt: -------------------------------------------------------------------------------- 1 | Default Sounds: (41 Sounds) 2 | hahaha yeah 3 | haha yeah haha 4 | yeah hahaha 5 | duahahaha 6 | hahaha 7 | glhf 8 | f3 9 | holy shit 10 | welcome to quake live 11 | go 12 | beep boop 13 | you win 14 | you lose 15 | impressive 16 | excellent 17 | denied 18 | balls out 19 | one 20 | two 21 | three 22 | fight 23 | gauntlet 24 | humiliation 25 | perfect 26 | wah wah wah wah 27 | ah ah ah ah 28 | oink 29 | argh 30 | hah haha 31 | woohoo 32 | quake live 33 | chaching 34 | uh ah 35 | oohwee 36 | erah 37 | yeahhh 38 | scream 39 | salute 40 | squish 41 | oh god 42 | snarl 43 | 44 | Prestige Worldwide Soundhonks: (82 Sounds) 45 | assholes 46 | assshafter 47 | babydoll 48 | barelymissed 49 | belly 50 | bitch 51 | blud 52 | boats 53 | bobg 54 | bogdog 55 | boom 56 | boom2 57 | buk 58 | bullshit 59 | butthole 60 | buttsex 61 | cheeks 62 | cocksucker 63 | conquer 64 | countdown 65 | cum 66 | cumming 67 | cunt 68 | dirkfunk 69 | disappointment 70 | doomsday 71 | drumset 72 | eat 73 | eat me 74 | fag 75 | fingerass 76 | flash 77 | fuckface 78 | fuckyou 79 | get emm 80 | gonads 81 | gtfo 82 | hug it out 83 | idiot 84 | idiot2 85 | it?'s time 86 | jeopardy 87 | jerk off 88 | killo 89 | knocked 90 | ld3 91 | liquidswords 92 | massacre 93 | mixer 94 | mjman 95 | mmmm 96 | monty 97 | n8 98 | nikon 99 | nina 100 | nthreem 101 | olhip 102 | organic 103 | paintball 104 | pigfucker 105 | popeye 106 | rosie 107 | seaweed 108 | shit 109 | sit 110 | soulianis 111 | spam 112 | stalin 113 | stfu 114 | suck a dick 115 | suckit 116 | suck my dick 117 | teapot 118 | thank god 119 | traxion 120 | trixy 121 | twoon 122 | ty 123 | venny 124 | viewaskewer 125 | what's that 126 | who are you 127 | 128 | Funny Sounds Pack for Minqlx: (243 Sounds) 129 | 007 130 | A Scratch 131 | adams family 132 | All The Things 133 | allahuakbar 134 | allstar 135 | Amazing 136 | Ameno 137 | America 138 | Amerika 139 | And Nothing Else 140 | Animals 141 | asskicking 142 | ave 143 | baby baby 144 | baby evil 145 | baby laughing 146 | bad boys 147 | Banana Boat 148 | benny hill 149 | benzin 150 | blue wins 151 | bonkers 152 | boom headshot 153 | booo 154 | boring 155 | boze 156 | bright side of life 157 | buckdich 158 | bullshitter 159 | burns burns 160 | camel toe 161 | can't touch this 162 | cccp 163 | champions 164 | chicken 165 | chocolate rain 166 | coin 167 | come 168 | Come With Me Now 169 | Count down 170 | cowards 171 | crazy 172 | damnit 173 | Danger Zone 174 | dead soon 175 | defeated 176 | devil 177 | doesn't love you 178 | du bist 179 | du hast 180 | dumb ways 181 | Eat Pussy 182 | education 183 | einschrei 184 | Eins Zwei 185 | electro 186 | elementary 187 | engel 188 | erstwenn 189 | exit light 190 | faint 191 | fatality 192 | Feel Good 193 | flesh wound 194 | for you 195 | freestyler 196 | fuckfuck 197 | fucking burger 198 | fucking kids 199 | gangnam 200 | ganjaman 201 | gay 202 | get crowbar 203 | get out the way 204 | ghostbusters 205 | girl look 206 | girly 207 | gnr guitar 208 | goddamn right 209 | goodbye andrea 210 | goodbye sarah 211 | gotcha 212 | hakunamatata 213 | hammertime 214 | hello 215 | hellstestern 216 | holy 217 | hoppereiter 218 | how are you 219 | hush 220 | i bet 221 | i can't believe 222 | ichtuedieweh 223 | i do parkour 224 | i hate all 225 | ill be back 226 | imperial 227 | i'm sexy 228 | i'm your father 229 | incoming 230 | indiana jones 231 | in your head zombie 232 | i see assholes 233 | i see dead people 234 | it's my life 235 | it's not 236 | jackpot 237 | jesus 238 | Jesus Oh 239 | john cena 240 | jump motherfucker 241 | just do it 242 | kamehameha 243 | keep on fighting 244 | keep your shirt on 245 | Knocked Down 246 | kommtdiesonne 247 | kung fu 248 | lately 249 | Legitness 250 | let's get ready 251 | let's put a smile 252 | lights out 253 | lion king 254 | live to win 255 | losing my religion 256 | love me 257 | low 258 | luck 259 | lust 260 | mahnamahna 261 | mario 262 | Me 263 | meinland 264 | message 265 | mimimi 266 | mission 267 | moan 268 | mortal kombat 269 | move ass 270 | muppet opening 271 | my little pony 272 | my name 273 | never seen 274 | nightmare 275 | nobody likes you 276 | nonie 277 | nooo 278 | no time for loosers 279 | numanuma 280 | nyancat 281 | o fuck 282 | oh my god 283 | Oh My Gosh 284 | ohnedich 285 | oh no 286 | oh noe 287 | pacman 288 | pick me up 289 | pikachu 290 | pinkiepie 291 | Pink Panther 292 | pipe 293 | piss me off 294 | play a game 295 | pooping 296 | powerpuff 297 | radioactive 298 | rammsteinriff 299 | red wins 300 | renegade 301 | retard 302 | rocky 303 | rock you guitar 304 | sail 305 | Salil 306 | samba 307 | sandstorm 308 | saymyname 309 | scatman 310 | sell you all 311 | sense of humor 312 | shakesenora 313 | shut the fuck up 314 | shut your fucking mouth 315 | silence 316 | Skeet Skeet 317 | smooth criminal 318 | socobatevira 319 | socobatevira end 320 | socobatevira fast 321 | socobatevira slow 322 | sogivemereason 323 | so stupid 324 | Space Jam 325 | space unicorn 326 | spierdalaj 327 | stamp on 328 | star wars 329 | stayin alive 330 | stoning 331 | stop 332 | story 333 | surprise 334 | swedish chef 335 | sweet dreams 336 | take me down 337 | talk scotish 338 | teamwork 339 | technology 340 | this is sparta 341 | thunderstruck 342 | to church 343 | tsunami 344 | tuturu 345 | tututu 346 | unbelievable 347 | undderhaifisch 348 | up town girl 349 | valkyries 350 | wahwahwah 351 | want you 352 | wazzup 353 | wehmirohweh 354 | what is love 355 | when angels 356 | where are you 357 | whistle 358 | why mad 359 | Will Be Singing 360 | wimbaway 361 | windows 362 | would you like 363 | wtf 364 | yeee 365 | yes master 366 | yhehehe 367 | ymca 368 | you 369 | you are a cunt 370 | you fucked my wife 371 | You Realise 372 | 373 | Duke Nukem Voice Sound Pack for minqlx: (82 Sounds) 374 | my ride 375 | abort 376 | ahhh 377 | much better 378 | aisle4 379 | a mess 380 | annoying 381 | bitchin 382 | blow it out 383 | booby trap 384 | bookem 385 | born to be wild 386 | chew gum 387 | come on 388 | the con 389 | cool 390 | not crying 391 | daamn 392 | damit 393 | dance 394 | diesob 395 | doomed 396 | eyye 397 | duke nukem 398 | no way 399 | eat shit 400 | escape 401 | face ass 402 | a force 403 | get that crap 404 | get some 405 | game over 406 | gotta hurt 407 | groovy 408 | you guys suck 409 | hail king 410 | shit happens 411 | holy cow 412 | holy shit 413 | im good 414 | independence 415 | in hell 416 | going in 417 | dr jones 418 | kick your ass 419 | ktit 420 | let god 421 | let's rock 422 | lookin' good 423 | make my day 424 | midevil 425 | my meat 426 | no time 427 | i needed that 428 | nobody 429 | only one 430 | my kinda party 431 | gonna pay 432 | pisses me off 433 | pissin me off 434 | postal 435 | aint afraid 436 | r and r 437 | ready for action 438 | rip your head off 439 | rip em 440 | rockin 441 | shake it 442 | slacker 443 | smack dab 444 | so help me 445 | suck it down 446 | terminated 447 | this sucks 448 | vacation 449 | christmas 450 | wants some 451 | you and me 452 | where 453 | yippie kai yay 454 | bottle of jack 455 | long walk 456 | 457 | Warp Sounds for Quake Live: (171 ounds) 458 | thanks for the advice 459 | appreciate 460 | looking forward to it 461 | make me 462 | pessimist 463 | shoot me now 464 | shoot on sight 465 | won't happen again 466 | attractive 467 | awesome 468 | awkward 469 | bad feeling 470 | bad idea 471 | ballbag 472 | believe 473 | big leagues 474 | bj 475 | kill you with my brain 476 | bravery 477 | broke 478 | space bugs 479 | bunk 480 | burp 481 | cover your butt 482 | came out 483 | anybody care 484 | castrate 485 | hello2 486 | cock push ups 487 | code 488 | cold 489 | college student 490 | crush your enemies 491 | confident 492 | cooperation 493 | cow dick 494 | go crazy 495 | crowded 496 | dance off 497 | dead 498 | dead guy 499 | dick message 500 | dink bag 501 | dirty 502 | do as you're told 503 | what have we done 504 | done for the day 505 | done it 506 | do now 507 | don't like vaginas 508 | eat my grenade 509 | eat my 510 | electricity 511 | face 512 | face2 513 | not fair 514 | fall 515 | favor 516 | feel 517 | feels 518 | something wrong 519 | suspense 520 | fuku 521 | awaiting orders 522 | got your back 523 | keep moving 524 | nice work 525 | get away cat 526 | get off 527 | wasting my time 528 | just go crazy 529 | grows 530 | ha ha 531 | heroics 532 | hop 533 | horrible 534 | huge vagina 535 | hunting 536 | i am the law 537 | i don't trust you 538 | i have a plan 539 | i like you 540 | intensify 541 | in the ass 542 | i will eat 543 | jail 544 | kevin bacon 545 | kill 546 | kizuna 547 | need to kill 548 | ladybug 549 | legend 550 | human relationships 551 | incredible 552 | never happened 553 | like this thing 554 | wasn't listening 555 | listen up 556 | look fine 557 | lovely 558 | your luck 559 | maggot 560 | like an idiot 561 | killed with math 562 | me me me 563 | metaphor 564 | misdirection 565 | nobody move 566 | my friends 567 | my gun's bigger 568 | never look back 569 | nutsack 570 | on me 571 | on my mom 572 | ow what the 573 | pain in the ass 574 | pan out 575 | petty 576 | pile of shit 577 | plasma 578 | good point 579 | quarter 580 | rage 581 | real me 582 | no longer require 583 | ready for this 584 | rock this 585 | santa 586 | say my name 587 | you can scream 588 | smiley face 589 | oh snap 590 | sneezed 591 | sorry 592 | human speech 593 | sprechen sie dick 594 | start over 595 | stfu cunt 596 | stunned our ride 597 | sure 598 | take a break 599 | take down 600 | the creeps 601 | used to living 602 | talk to me 603 | tears 604 | that's right 605 | think 606 | tricked 607 | trusted 608 | trust me 609 | target 610 | ugly stick 611 | unfair 612 | unicorn 613 | v3 614 | valid 615 | volunteer 616 | waiting 617 | walk 618 | what i want 619 | at war 620 | wee lamb 621 | well 622 | we're grownups 623 | what happened 624 | what is this 625 | what now 626 | what the 627 | with my fist 628 | busy 629 | sometimes crazy 630 | i like 631 | orders 632 | right behind you 633 | what can i do 634 | your mom 635 | zooma 636 | 637 | West Coast Crew Sound: (161 Sounds) 638 | turple 639 | ay caramba 640 | ballin 641 | bigpippin 642 | big whoop 643 | did i do that 644 | DDDid i do that 645 | your'e doomed 646 | ez 647 | introtoo 648 | jsss 649 | opinion 650 | snort 651 | SNpete 652 | SNpete2 653 | bender 654 | 2ez 655 | reflexes 656 | rothko 657 | bite my shiny metal ass 658 | c3 659 | cthree 660 | cann 661 | clr 662 | hahahaha 663 | isabadmutha 664 | shaft 665 | bofumballs 666 | makaveli 667 | rothkoo 668 | oshikia 669 | qqaaq 670 | qaaq 671 | muthafucka 672 | fuckin bitch 673 | chocosaurus 674 | los 675 | get outta here 676 | nonsense 677 | counting on you 678 | damn im good 679 | really 680 | hehehe 681 | lol loser 682 | die motherfucker 683 | die mothafuckas 684 | die already 685 | give up and die 686 | im stephan 687 | no no no no no 688 | clever girl 689 | stfu2 690 | what did you do 691 | look what you did 692 | drunk 693 | gibbles 694 | rugged 695 | fox 696 | dr1nya 697 | vks 698 | obliv 699 | oblivion 700 | flush 701 | shenookie 702 | shenookies cookies 703 | campingtroll 704 | happy hour 705 | baiting 706 | solis 707 | tustamena 708 | atustamena 709 | hahaha2 710 | outro 711 | w3rd 712 | unstoppable 713 | monster kill 714 | dominating 715 | godlike 716 | dundun 717 | dundundun 718 | dundundundun 719 | filthy zealot 720 | lg 721 | mirai 722 | youlose 723 | scrub 724 | swish 725 | easy as 123 726 | kinraze 727 | mobil 728 | gimme a break 729 | pizza pizza 730 | inspector norse 731 | kinrazed 732 | martin 733 | bumblebee tuna 734 | ventt 735 | littlemeezers 736 | ty2 737 | gs 738 | great shot 739 | thetealduck 740 | giggs 741 | hehe 742 | clear 743 | rage quit 744 | im on fire 745 | so is your face 746 | jdub 747 | in the zone 748 | papabalyo 749 | boomshakalaka 750 | facial 751 | he's on fire 752 | he's heating up 753 | wuyoga 754 | feroz 755 | biff 756 | psygib 757 | in the face 758 | ability 759 | stitch 760 | killswitch 761 | shufflenufiguess 762 | elo 763 | vacuum 764 | wolf 765 | saved 766 | still feel like you're mad 767 | it's a disastah 768 | rekt 769 | troll toll 770 | gotta pay the troll 771 | let's get ready to rumble 772 | f33"] = [re.compile(r"f33\W? 773 | look what you've done 774 | doom2 775 | ooom 776 | and another one gone 777 | another one bites the dust 778 | easy come easy go 779 | yeah baby 780 | hehe yeah 781 | dead last 782 | skadoosh 783 | i'm a motherfuckin monster 784 | it's in the bag 785 | in the bag 786 | wow 787 | woww 788 | that's all folks 789 | spank you 790 | why you little 791 | you can do it 792 | did you miss me 793 | somebody stop me 794 | slime 795 | waow 796 | lovin' it 797 | yawn 798 | yawnn 799 | -------------------------------------------------------------------------------- /restartserver.py: -------------------------------------------------------------------------------- 1 | # restartserver.py is a plugin for minqlx to: 2 | # -Restart the server at a certain time, or as soon as the server empties, once the restart time has passed. 3 | # -The server restarting requires that a management program, such as supervisor, is installed and controlling 4 | # and monitoring the server to restart the process once the quit command is issued. 5 | # -The quit command, to restart the server, is issued anytime during the set minute. 6 | # Created by BarelyMiSSeD on 5-31-2019 7 | # 8 | """ 9 | Script COMMANDS (both of these commands need to be enabled with the cvars before they will function): 10 | restart - will restart the server, with an optional modifier included. If no modifier is included the server will issue an 11 | immediate restart command, even if the server is not empty. If a time is included the server will restart at the 12 | supplied time or as soon as the server has emptied after the supplied time has passed. If "clear" is included, the 13 | custom restart time, if previously set, will be cleared. if "time" is included the current restart time will be 14 | reported. 15 | time - will report the current server time. 16 | start - will report the time this script was loaded, typically that is the server start time 17 | 18 | // Copy below here for your server's config file 19 | // Set these cvars in your server.cfg (or wherever you set your minqlx variables): 20 | set qlx_restartTime "06:00" // Sets the time the server restarts. Format "HH:MM" to match to time on server (24 hour format) 21 | set qlx_restartCmdEnable "1" // Allow restart command usage (0=disable, 1=enable) 22 | set qlx_restartUseTime "1" // Allow time command usage (0=disable, 1=enable). Disables other script time commands. 23 | """ 24 | 25 | import minqlx 26 | import time 27 | from threading import Timer 28 | 29 | VERSION = "2.1" 30 | 31 | 32 | class restartserver(minqlx.Plugin): 33 | def __init__(self): 34 | # cvars 35 | self.set_cvar_once("qlx_restartTime", "06:00") # Format "HH:MM" to match to time on server 36 | self.set_cvar_once("qlx_restartCmdEnable", "1") # Allow restart command usage (0=disable, 1=enable) 37 | self.set_cvar_once("qlx_restartUseTime", "1") # Allow time command usage (0=disable, 1=enable) 38 | 39 | # hooks 40 | self.add_hook("player_disconnect", self.handle_player_disconnect) 41 | 42 | # player commands 43 | self.add_command("restart", self.restart_server, 4) 44 | self.add_command("time", self.get_server_time) 45 | self.add_command("start", self.server_start_time, 4) 46 | 47 | # Script Variables 48 | self.start_time = [time.strftime("%Y"), time.strftime("%j"), time.strftime("%H:%M:%S")] 49 | self.checking_restart = False 50 | self.restart_time = None 51 | self.check_timer = None 52 | 53 | # Initialize commands 54 | self.remove_conflicting_time_commands() 55 | 56 | def server_start_time(self, player, msg, channel): 57 | player.tell("^3The server was started ^2{} ^7Day: ^2{}" 58 | .format(time.strftime("%B %d %Y %H:%M:%S", 59 | time.strptime(" ".join(self.start_time), "%Y %j %H:%M:%S")), 60 | self.start_time[1])) 61 | 62 | def handle_player_disconnect(self, player, reason): 63 | try: 64 | def check_player_count(): 65 | if len(self.players()) == 0: 66 | self.check_restart_time() 67 | Timer(10, check_player_count).start() 68 | except Exception as e: 69 | minqlx.console_print("^1restartserver handle_player_disconnect Exceptions: {}".format(e)) 70 | 71 | @minqlx.thread 72 | def check_restart_time(self): 73 | try: 74 | if self.checking_restart: 75 | return 76 | self.checking_restart = True 77 | try: 78 | if self.check_timer.is_alive(): 79 | self.check_timer.cancel() 80 | except AttributeError: 81 | pass 82 | except Exception as e: 83 | minqlx.console_print("^1restartserver check_restart_time Timer Exception: {}".format(e)) 84 | 85 | restart_time = [time.strftime("%Y"), "0", (self.restart_time if self.restart_time else 86 | self.get_cvar("qlx_restartTime"))] 87 | if time.strptime(" ".join(self.start_time), "%Y %j %H:%M:%S") < \ 88 | time.strptime("{} {} {}".format(time.strftime("%Y"), 89 | time.strftime("%j"), restart_time[2]), "%Y %j %H:%M"): 90 | restart_time[1] = self.start_time[1] 91 | elif time.strptime(time.strftime("%H:%M"), "%H:%M") < time.strptime(restart_time[2], "%H:%M"): 92 | restart_time[1] = time.strftime("%j") 93 | else: 94 | restart_time[1] = str(int(time.strftime("%j")) + 1) 95 | 96 | restart_time[0] = int(restart_time[0]) 97 | restart_time[1] = int(restart_time[1]) 98 | year = int(self.start_time[0]) 99 | 100 | while self.checking_restart: 101 | if (restart_time[1] <= int(time.strftime("%j")) or restart_time[0] < year) and\ 102 | time.strptime(time.strftime("%H:%M"), "%H:%M") >= time.strptime(restart_time[2], "%H:%M"): 103 | minqlx.console_print("^1RestartServer^7: Restarting the empty server after the scheduled time of {}" 104 | .format(restart_time[2])) 105 | minqlx.console_command("quit") 106 | time.sleep(60) 107 | if len(self.players()) > 0: 108 | self.checking_restart = False 109 | except Exception as e: 110 | minqlx.console_print("^1restartserver check_time Exceptions: {}".format(e)) 111 | finally: 112 | self.checking_restart = False 113 | self.check_timer = Timer(3600, self.check_restart_time) 114 | self.check_timer.start() 115 | 116 | @minqlx.delay(5) 117 | def remove_conflicting_time_commands(self): 118 | if self.get_cvar("qlx_restartUseTime", bool): 119 | loaded_scripts = minqlx.Plugin._loaded_plugins 120 | scripts = set(loaded_scripts) 121 | command = {"time"} 122 | for script in scripts: 123 | if script == "restartserver": 124 | continue 125 | try: 126 | for cmd in loaded_scripts[script].commands.copy(): 127 | if command.intersection(cmd.name): 128 | loaded_scripts[script].remove_command(cmd.name, cmd.handler) 129 | except: 130 | continue 131 | self.check_restart_time() 132 | 133 | def get_server_time(self, player, msg, channel): 134 | if self.get_cvar("qlx_restartUseTime", bool): 135 | self.msg("^3The current server time is ^2{} ^7Day: ^2{}" 136 | .format(time.strftime("%b %d %Y %H:%M:%S"), time.strftime("%j"))) 137 | 138 | def restart_server(self, player, msg, channel): 139 | if self.get_cvar("qlx_restartCmdEnable", bool): 140 | if len(msg) > 1: 141 | if msg[1].lower().startswith("h"): 142 | player.tell("^3Schedule a server restart time greater than 1 minute from the command issue time.") 143 | player.tell("^3This gives you time to disconnect from the server to allow the command to execute.") 144 | player.tell("^3Valid time format is HH:MM in a 24 hour format. 9pm would be 21:00 and the" 145 | " current server time is {}".format(time.strftime("%H:%M"))) 146 | elif msg[1] == "clear": 147 | if self.restart_time: 148 | self.restart_time = None 149 | player.tell("^3The custom restart time has been cleared. The server will restart at the default" 150 | " time of {}".format(self.get_cvar("qlx_restartTime"))) 151 | if self.checking_restart: 152 | self.checking_restart = False 153 | 154 | @minqlx.delay(65) 155 | def server_check(): 156 | if len(self.players()) == 0: 157 | self.check_restart_time() 158 | 159 | server_check() 160 | else: 161 | player.tell("^3There was not a custom restart time set. The server will restart at the default" 162 | " time of {}".format(self.get_cvar("qlx_restartTime"))) 163 | elif msg[1] == "time": 164 | restart_time = [time.strftime("%Y"), "0", (self.restart_time if self.restart_time else 165 | self.get_cvar("qlx_restartTime"))] 166 | if time.strptime(" ".join(self.start_time), "%Y %j %H:%M:%S") < \ 167 | time.strptime("{} {} {}".format(time.strftime("%Y"), 168 | time.strftime("%j"), restart_time[2]), "%Y %j %H:%M"): 169 | restart_time[1] = self.start_time[1] 170 | elif time.strptime(time.strftime("%H:%M"), "%H:%M") < time.strptime(restart_time[2], "%H:%M"): 171 | restart_time[1] = time.strftime("%j") 172 | else: 173 | restart_time[1] = str(int(time.strftime("%j")) + 1) 174 | player.tell("^3The set restart time is ^2{}".format(" ".join(restart_time))) 175 | else: 176 | check_time_format = [int(s) for s in msg[1].split(":")] 177 | current_time = [int(time.strftime("%H")), int(time.strftime("%M"))] 178 | try: 179 | if len(check_time_format) == 2 and 0 <= check_time_format[0] <= 23 and\ 180 | 0 <= check_time_format[1] <= 59: 181 | if time.strftime("%H:%M") < msg[1]: 182 | if self.checking_restart: 183 | @minqlx.delay(65) 184 | def restart_init(): 185 | self.check_restart_time() 186 | 187 | if (check_time_format[0] - current_time[0] <= 0 and 188 | check_time_format[1] - current_time[1] < 3) or\ 189 | (check_time_format[0] - current_time[0] == 1 and 190 | 60 - (current_time[1] - check_time_format[1]) < 3): 191 | player.tell("^3The time you set is too close to the current time." 192 | " Chose a time further in the future but in the same day.") 193 | else: 194 | self.checking_restart = False 195 | self.restart_time = msg[1] 196 | player.tell("^1The server restart command has been sent to restart the" 197 | " server at {}".format(self.restart_time if self.restart_time else 198 | self.get_cvar("qlx_restartTime"))) 199 | restart_init() 200 | else: 201 | if (check_time_format[0] - current_time[0] <= 0 and 202 | check_time_format[1] - current_time[1] < 2) or\ 203 | (check_time_format[0] - current_time[0] == 1 and 204 | 60 - (current_time[1] - check_time_format[1]) < 2): 205 | player.tell("^6The time you set is too close to the current time." 206 | " Chose a time further in the future but in the same day.") 207 | else: 208 | self.restart_time = msg[1] 209 | player.tell("^1The server restart command has been sent to restart the" 210 | " server at {}".format(self.restart_time)) 211 | self.check_restart_time() 212 | else: 213 | player.tell("^3Scheduling a restart time only works if the time is in the future.") 214 | else: 215 | player.tell("^3Valid time format is HH:MM in a 24 hour format. 9pm would be 21:00") 216 | except ValueError: 217 | player.tell("^3Valid time format is HH:MM in a 24 hour format. 9pm would be 21:00") 218 | except Exception as e: 219 | minqlx.console_print("^1restartserver restart_server Exceptions: {}".format(e)) 220 | else: 221 | minqlx.console_print("^1Restart Server script is issuing a quit command because {}restart was issued" 222 | " by a server admin.".format(self.get_cvar("qlx_commandPrefix"))) 223 | minqlx.console_command("quit") 224 | else: 225 | player.tell("^3The {}restart command is disabled on this server".format(self.get_cvar("qlx_commandPrefix"))) 226 | -------------------------------------------------------------------------------- /myFun/README.md: -------------------------------------------------------------------------------- 1 | # *** This is my replacement for the minqlx fun.py so if you use this file make sure not to load fun.py ***
2 | 3 | This plugin plays sounds for players on the Quake Live server
4 | It plays the sounds included in fun.py and some from other workshop item sound packs.
5 | 6 | This can limit sound spamming to the server.
7 | It only allows one sound to be played at a time and each user is limited in the frequency they can play sounds.
8 | If you desire no restriction then set both of the 2 following cvars to "0".
9 | The default qlx_funSoundDelay setting will require 5 seconds from the start of any sound
10 | before another sound can be played by anyone.
11 | The default qlx_funPlayerSoundRepeat setting will require 30 seconds from the start of a player called sound before
12 | that player can call another sound.
13 | 14 | #To set the time required between any sound add this line to your server.cfg and edit the "5":
15 | set qlx_funSoundDelay "5"
16 | 17 | #To set the time a player has to wait after playing a sound add this like to your server.cfg and edit the "30":
18 | set qlx_funPlayerSoundRepeat "30"
19 | 20 | #Play Join Sound when players connect (set to "path/file" like below example to play sound)
21 | #*** Disable the MOTD sound to use this with set qlx_motdSound "0" ****
22 | set qlx_funJoinSound "sound/feedback/welcome_02.wav"
23 | 24 | #Play Join Sound even if players have sounds disabled
25 | set qlx_funJoinSoundForEveryone "0"
26 | 27 | #Play Join Sound on every map change (set to "1" to play join sound every map change)
28 | set qlx_funJoinSoundEveryMap "0"
29 | 30 | #Join sound path/file
31 | set qlx_funJoinSound "sound/feedback/welcome_02.wav"
32 | 33 | #Play Sound when last 2 players alive (should set to "3" to play sounds always)
34 | #0 = don't play sound for anyone when the last 2 (or 1 on either team of a team based game) remains
35 | #1 = play sound for all except those alive when the last 2 (or 1 on either team of a team based game) remains
36 | #2 = only play sounds for people who are dead/spectating when game is active
37 | #3 = play sound for everyone with sounds enabled
38 | set qlx_funLast2Sound "3"
39 | 40 | #Enable to use a dictionary to store sounds, faster responses to trigger text (0=disable, 1=enable) 41 | #Enabling will cause the server to use more memory, only enable if memory is available. 42 | #Must be enabled on server startup or it will not work. 43 | set qlx_funFastSoundLookup "0"
44 | 45 | 46 | #These extra workshop items need to be loaded on the server for it to work correctly if all sound packs are enabled:
47 | #(put the workshop item numbers in your workshop.txt file)
48 | #Prestige Worldwide Soundhonks
49 | 585892371
50 | #Funny Sounds Pack for Minqlx
51 | 620087103
52 | #Duke Nukem Voice Sound Pack for minqlx
53 | 572453229
54 | #Warp Sounds for Quake Live
55 | 1250689005
56 | #West Coast Crew Sound
57 | 1733859113
58 | 59 | #The minqlx 'workshop' plugin needs to be loaded and the required workshop
60 | #items added to the set qlx_workshopReferences line
61 | #(This example shows only these required workshop items):
62 | set qlx_workshopReferences "585892371, 620087103, 572453229, 1250689005, 908031086"
63 | #(Only include the sound pack workshop item numbers that you decide to enable on the server)
64 | #(The Default sounds use sounds already available as part of the Quake Live install)
65 | 66 | Soundpacks:
67 | 1) The Default soundpack uses sounds that are already included in the Quake Live install.
68 | 2) The Prestige Worldwide Soundhonks soundpack can be seen HERE.
69 | 3) The Funny Sounds Pack for Minqlx can be seen HERE.
70 | 4) The Duke Nukem Voice Sound Pack for minqlx soundpack can be seen HERE.
71 | 5) The Warp Sounds for Quake Live soundpack can be seen HERE.
72 | 6) The West Coast Crew Sound soundpack can be seen HERE.
73 | 74 | The soundpacks are all enabled by default. Which soundpacks are enabled can be set.
75 | set qlx_funEnableSoundPacks "63" : Enables all sound packs.
76 | ****** How to set which sound packs are enabled ******
77 | Add the values for each sound pack listed below and set that value to the qlx_funEnableSoundPacks in the same location as the rest of your minqlx cvar's.

78 | **** Sound Pack Values ****
79 |                                          Default: 1
80 | Prestige Worldwide Soundhonks: 2
81 |     Funny Sounds Pack for Minqlx: 4
82 |           Duke Nukem Voice Sound Pack for minqlx: 8
83 |        Warp Sounds for Quake Live: 16
84 |               West Coast Crew Sound: 32
85 | 86 | Duke Nukem Soundpack Disabled EXAMPLE: set qlx_funEnableSoundPacks "55"
87 | 88 | When a player issues the !listsounds command it wil list all of the sounds available on the server with 89 | the sounds displayed in a tabbed format to help enable the redability of the sounds without requiring the use of pages or exessie scrolling. Only the soundpacks that are enabled will be shown. Any disabled soundpacks will not be displayed and will not 90 | be searchable.
91 | 92 | If !listsounds is issued with a search term it will search for sounds that contain that term and diplay each enabled soundpack and the sounds found in them.
93 | 94 | Example: !listsounds yeah would list all the sound phrases containing 'yeah'
95 | and would print this to the player's console (assuming all sound packs are enabled):
96 | 97 | SOUNDS: Type these words/phrases in normal chat to play a sound on the server.
98 | Default
99 | haha yeah haha hahaha yeah yeah hahaha yeahhh
100 | Prestige Worldwide Soundhonks
101 | No Matches
102 | Funny Sounds Pack for Minqlx
103 | No Matches
104 | Duke Nukem Voice Sound Pack for minqlx
105 | No Matches
106 | Warp Sounds for Quake Live
107 | No Matches
108 | West Coast Crew Sound
109 | No Matches
110 | 4 SOUNDS: Type these words/phrases in normal chat to play a sound on the server.
111 | 112 | If !listsounds is issued with a sound pack limiting value it will only search that soundpack for sounds.
113 | !listsounds #Default would only list the sounds in the Default soundpack, if it is enabled.
114 | 115 | Example: '!listsounds #Default yeah' would list all the sounds containing 'yeah' but limit that search to
116 | just the Default sounds and produce the following output:
117 | 118 | SOUNDS: Type these words/phrases in normal chat to play a sound on the server.
119 | Default
120 | haha yeah haha hahaha yeah yeah hahaha yeahhh
121 | 4 SOUNDS: Type these words/phrases in normal chat to play a sound on the server.
122 | 123 | 124 | ######################################################################################## 125 | # Adding Additional Sound Packs 126 | ########################################################################################
127 | ***** To add new sound packs without editing this file "much" *****
128 | Sound Pack File Requirements:
129 | The sound file names will use a sound trigger based on the file name. The sound file name will replace
130 | underscores (_) with spaces or insert spaces after words starting with a capital letter.
131 | The path inside the sound_pack.pk3 is not used to generate the sound trigger, only the sound file name.
132 | I.E. The sound 'sound/warp/bend_me_over.ogg' will create the sound trigger 'bend me over'.
133 | The sound 'sound/warp/BendMeOver.ogg' will also create the sound trigger 'bend me over'.
134 | *** Note: These are the only 2 formats that will be correctly interpreted. I.E. 'sound/warp/bendMeOver.ogg' will not
135 | generate the full sound trigger correctly.
136 | *** NOTE: The name of the sound pack file stored in the baseq3 directory of the server does not have to match the
137 | name of the sound pack file in the steam workshop. As long as the contents of the file are exactly the same.
138 | You can rename the file stored in the baseq3 to whatever you want the sound pack called, ending it with
139 | the .pk3 file extension. Use underscores for spaces in the sound pack name. (i.e. My_Enjoyable_Sounds.pk3 will be
140 | shown as the sound pack 'My Enjoyable Sounds'.
141 |
142 | 1) put the file into the baseq3 directory of the server.
143 | The baseq3 sub-directory stored in the cvar fs_basepath.
144 | 2) add the file name to the ADDITIONAL_SOUNDPACKS below with the format
145 | ADDITIONAL_SOUNDPACKS = ['sound_pack.pk3', 'sound_pack_2.pk3', 'sound_pack_3.pk3']
146 | 3) add the steam workshop item number to the qlx_workshopReferences cvar in the server.cfg
147 | set qlx_workshopReferences "585892371, 620087103, 572453229, 1250689005, 1733859113, "
148 |
149 | The sound pack will be added starting at the end of the list, as sound pack 6 if nothing else was edited in the file.
150 | Any sound pack added here will automatically be enabled.
151 | 152 | 153 | # Commands 154 |

155 | Commands available with myFun.py listed with the set permission levels. 156 | 157 | • Permission level 0 158 | 159 | !listsounds (alternatively !ls) 160 | 161 | Retrieves the sounds available to lay on the server.
162 | The command can be issued with optional search parameters:
163 | !listsounds \<#Category\> \
164 | The user can include one or both of the optional search parameters to narrow the search results. 165 | 166 | Usage: !listsounds \<#Category\> \ 167 | 168 | !off (alternatively !soundoff) 169 | 170 | Disables the just played sound for the player issuing the command.
171 | The command can be issued with optional disable parameters:
172 | !off \
173 | !off #\
174 | NOTE: turning off all soundpack sounds will lag the server. It can only be done when all players connected to the server are in spectate. 175 | The user can include a sound trigger to disable the sound that is called with that sound trigger. 176 | Use !listsounds to find the sound trigger desired.
177 | 178 | Usage: !off \ 179 | 180 | !on (alternatively !soundon) 181 | 182 | Re-enables the just played sound for the player issuing the command.
183 | The command can be issued with optional disable parameters:
184 | !on \
185 | !on #\
186 | NOTE: turning on all soundpack sounds will lag the server. It can only be done when all players connected to the server are in spectate. 187 | The user can include a sound trigger to re-enable the sound that is called with that sound trigger. 188 | Use !listsounds to find the sound trigger desired.
189 | 190 | Usage: !on \ 191 | 192 | !offlist (alternatively !soundofflist) 193 | 194 | Shows the sounds to the player that that player has disabled.
195 | 196 | Usage: !offlist 197 | 198 | !cookies 199 | 200 | Randomly returns one of a few funny responses. 201 | 202 | Usage: !cookies 203 | 204 | • Permission level 3 205 | 206 | !playsound (alternatively !ps or !sound) 207 | 208 | Plays a sound on the server for all the players with sounds enabled.
209 | This does not use the sound anti-spam function. 210 | 211 | Usage: !playsound \ 212 | 213 | !listtriggers (alternatively !lt) 214 | 215 | Lists the custom sound trigers that have been added for the sound. 216 | 217 | Usage: !listtriggers \ 218 | 219 | • Permission level 5 220 | 221 | These 2 commands will reset the enabled sounds to the qlx_funEnableSoundPacks cvar and reload the sounds dictionaries. 222 | This erases the sound triggers in the Redis database. The Redis database is controlled by an ouside process, 223 | so the execution of the filldb command should not be immediately after the erasedb command (wait a couple of seconds is all). 224 | 225 | Usage: !erasedb
226 | Usage: !filldb 227 | 228 | !disablesound (alternatively !ds) 229 | 230 | Disables a specific sound on the server.
231 | !disablesound \
232 | The command must include a sound trigger to disable the sound that is called with that sound trigger. 233 | Use !listsounds to find the sound trigger desired.
234 | 235 | Usage: !disablesound \
236 | 237 | !enablesound (alternatively !es) 238 | 239 | Re-enables a specific sound on the server.
240 | !enablesound \
241 | The command must include a sound trigger to re-enable the sound that is called with that sound trigger. 242 | Use !listsounds to find the sound trigger desired.
243 | 244 | Usage: !enablesound \
245 | 246 | !listdisabled (alternatively !ld) 247 | 248 | Shows the sounds to the player that are disabled on the server.
249 | 250 | Usage: !listdisabled 251 | 252 | !addtrigger (alternatively !at) 253 | 254 | Adds the desired trigger to the custom sound triggers. The default trigger and cutom trigger must be seperated by an = sign.
255 | 256 | Usage: !addtrigger \ = \ 257 | 258 | !deltrigger (alternatively !dt) 259 | 260 | Removes the desired trigger from the custom sound triggers. The default trigger and cutom trigger must be seperated by an = sign. 261 | 262 | Usage: !deltrigger \ = \ 263 | 264 | !adminlevel 265 | 266 | Sets the admin level for playing un-restricted sounds. 267 | 268 | Usage: !adminlevel 269 | 270 | !restrictadmin 271 | 272 | Set the admin play sounds restriction level.
273 | 0-Restricted like all players; 1-Unrestricted for player time limits only; 2-Unrestricted from any time limit. 274 | 275 | Usage: !restrictadmin <0/1/2> 276 | 277 | 278 |

279 | 280 | # CVARs 281 | 282 | Read the top section of the myFun.py file 283 | -------------------------------------------------------------------------------- /funwarmup.py: -------------------------------------------------------------------------------- 1 | # This is an extension plugin for minqlx. 2 | # Copyright (C) 2018 BarelyMiSSeD (github) 3 | 4 | # You can redistribute it and/or modify it under the terms of the 5 | # GNU General Public License as published by the Free Software Foundation, 6 | # either version 3 of the License, or (at your option) any later version. 7 | 8 | # You should review a copy of the GNU General Public License 9 | # along with minqlx. See . 10 | 11 | # This is a plugin for the minqlx admin bot. 12 | # It modifies the game CVARs to make warmup time a little different and hopefully more fun. 13 | # Be careful modifying the setting too much. Too many projectiles needing to be kept track of 14 | # by the server may result in server crashes. Use/Modify this at your own risk. 15 | 16 | """ 17 | //script cvars to be put in server configuration file (default: server.cfg). Default values shown. 18 | // set the permission level for admins to allow setting and unsetting of the fun warm up mode 19 | set qlx_fwAdminLevel "3" 20 | // Enable or Disable the automatic enabling of the fun warm up mode during warmup 21 | set qlx_fwSetupWarmupFun "1" 22 | // Set the number of seconds each weapon is used during the fun warm up period 23 | set qlx_fwInterval "60" 24 | // Set the number of players allowed on the server to choose if the fun warm up uses WEAPONS or WEAPONS2 settings 25 | // It looks at teamsize or maxplayers to determine which to use. 26 | set qlx_fwPlayerSplit "8" 27 | // This will either load the default settings from the server settings (set to "1"). Use for other than ca game types. 28 | // Or it will use the default settings in the script, which are based on clan arena (set to "0") 29 | set qlx_fwLoadDefaultCvars "1" 30 | """ 31 | 32 | import minqlx 33 | import random 34 | from threading import Timer 35 | 36 | VERSION = "2.6" 37 | 38 | 39 | class funwarmup(minqlx.Plugin): 40 | def __init__(self): 41 | self.set_cvar_once("qlx_fwAdminLevel", "3") 42 | self.set_cvar_once("qlx_fwSetupWarmupFun", "1") 43 | self.set_cvar_once("qlx_fwInterval", "60") 44 | self.set_cvar_once("qlx_fwPlayerSplit", "8") 45 | self.set_cvar_once("qlx_fwLoadDefaultCvars", "1") 46 | 47 | self.add_hook("map", self.handle_map) 48 | self.add_hook("game_countdown", self.handle_game_countdown) 49 | self.add_hook("player_disconnect", self.handle_player_disconnect) 50 | self.add_hook("player_loaded", self.handle_player_loaded) 51 | self.add_hook("player_spawn", self.handle_player_spawn) 52 | self.add_hook("game_end", self.handle_game_end) 53 | self.add_hook("vote_ended", self.handle_vote_ended) 54 | self.add_hook("console_print", self.handle_console_print) 55 | self.add_hook("command", self.handle_command, priority=minqlx.PRI_HIGHEST) 56 | 57 | self.add_command("setfun", self.cmd_set_fun, self.get_cvar("qlx_fwAdminLevel", int)) 58 | self.add_command("unsetfun", self.cmd_unset_fun, self.get_cvar("qlx_fwAdminLevel", int)) 59 | self.add_command("weapon", self.cmd_weapon) 60 | self.add_command("dicts", self.dicts) 61 | 62 | self.fw_active = False 63 | self.fw_weapons = [] 64 | self.cycle = None 65 | self.fw_interval = self.get_cvar("qlx_fwInterval", int) 66 | self.fw_id = None 67 | self._game_type = self.game.type_short 68 | 69 | # Weapon Names 70 | self.WEAPON_NAMES = ["Rail Gun", "Shotgun", "Machine Gun", "Heavy Machine Gun", "Plasma Gun", "Rocket Launcher"] 71 | 72 | # settings variables 73 | self.PLAYER_CVARS = [] 74 | self.PLAYER_DFLT_CVARS = [] 75 | self.WEAPONS = {} 76 | self.WEAPONS2 = {} 77 | self.WEAPONS_DFLTS = {} 78 | self.DFLT_CVARS = [] 79 | 80 | # Get default settings 81 | self.get_defaults() 82 | 83 | def get_defaults(self): 84 | # Player Settings 85 | self.PLAYER_CVARS = ["g_infiniteAmmo 1", "g_startingWeapons 8447", "g_startingArmor 300", 86 | "g_startingHealth 500"] 87 | if self.get_cvar("qlx_fwLoadDefaultCvars", bool): 88 | self.PLAYER_DFLT_CVARS = [] 89 | get_cvars = ["g_infiniteAmmo", "g_startingWeapons", "g_startingArmor", "g_startingHealth"] 90 | for cvar in get_cvars: 91 | self.PLAYER_DFLT_CVARS.append("{} {}".format(cvar, self.get_cvar(cvar))) 92 | else: 93 | self.PLAYER_DFLT_CVARS = ["g_infiniteAmmo 0", "g_startingWeapons 8447", "g_startingArmor 100", 94 | "g_startingHealth 200"] 95 | # Weapon Fun CVARs 96 | self.WEAPONS = { 97 | 0: ["weapon_reload_rg 80", "g_knockback_rg 0.45", "g_railJump 1"], 98 | 1: ["weapon_reload_sg 150", "g_knockback_sg 0.45"], 99 | 2: ["weapon_reload_mg 20", "g_knockback_mg 0.25"], 100 | 3: ["weapon_reload_hmg 25", "g_knockback_hmg 0.25"], 101 | 4: ["weapon_reload_pg 40", "g_knockback_pg 0.25", "g_knockback_pg_self 2.60", "g_velocity_pg 20000"], 102 | 5: ["weapon_reload_rl 200", "g_knockback_rl 0.45", "g_knockback_rl_self 2.20", "g_velocity_rl 20000"] 103 | } 104 | self.WEAPONS2 = { 105 | 0: ["weapon_reload_rg 200", "g_knockback_rg 0.45", "g_railJump 1"], 106 | 1: ["weapon_reload_sg 300", "g_knockback_sg 0.45"], 107 | 2: ["weapon_reload_mg 40", "g_knockback_mg 0.25"], 108 | 3: ["weapon_reload_hmg 45", "g_knockback_hmg 0.25"], 109 | 4: ["weapon_reload_pg 60", "g_knockback_pg 0.25", "g_knockback_pg_self 2.60", "g_velocity_pg 20000"], 110 | 5: ["weapon_reload_rl 300", "g_knockback_rl 0.45", "g_knockback_rl_self 2.20", "g_velocity_rl 20000"] 111 | } 112 | # Weapon Default CVARs 113 | if self.get_cvar("qlx_fwLoadDefaultCvars", bool): 114 | self.WEAPONS_DFLTS = {} 115 | get_dflts = { 116 | 0: ["weapon_reload_rg", "g_knockback_rg", "g_railJump"], 117 | 1: ["weapon_reload_sg", "g_knockback_sg", "g_range_sg_falloff"], 118 | 2: ["weapon_reload_mg", "g_knockback_mg"], 119 | 3: ["weapon_reload_hmg", "g_knockback_hmg"], 120 | 4: ["weapon_reload_pg", "g_knockback_pg", "g_knockback_pg_self", "g_velocity_pg"], 121 | 5: ["weapon_reload_rl", "g_knockback_rl", "g_knockback_rl_self", "g_velocity_rl"] 122 | } 123 | for weapon in range(0, 6): 124 | self.WEAPONS_DFLTS[weapon] = [] 125 | for cvar in get_dflts[weapon]: 126 | self.WEAPONS_DFLTS[weapon].append("{} {}".format(cvar, self.get_cvar(cvar))) 127 | else: 128 | self.WEAPONS_DFLTS = { 129 | 0: ["weapon_reload_rg 1500", "g_knockback_rg 0.85", "g_railJump 0"], 130 | 1: ["weapon_reload_sg 1000", "g_knockback_sg 1.00", "g_range_sg_falloff 768"], 131 | 2: ["weapon_reload_mg 100", "g_knockback_mg 1.00"], 132 | 3: ["weapon_reload_hmg 75", "g_knockback_hmg 1.00"], 133 | 4: ["weapon_reload_pg 100", "g_knockback_pg 1.10", "g_knockback_pg_self 1.10", "g_velocity_pg 2000"], 134 | 5: ["weapon_reload_rl 800", "g_knockback_rl 0.90", "g_knockback_rl_self 1.10", "g_velocity_rl 1000"] 135 | } 136 | # Default CVARs 137 | if self.get_cvar("qlx_fwLoadDefaultCvars", bool): 138 | self.DFLT_CVARS = [] 139 | load_cvars = ["g_infiniteAmmo", "g_startingWeapons", "g_startingArmor", "g_startingHealth", 140 | "weapon_reload_rl", "weapon_reload_pg", "weapon_reload_rg", 141 | "weapon_reload_lg", "weapon_reload_sg", "weapon_reload_gl", 142 | "weapon_reload_mg", "weapon_reload_hmg", "g_velocity_rl", "g_velocity_pg", 143 | "g_velocity_gl", "g_knockback_rl", "g_knockback_pg", "g_knockback_rg", 144 | "g_knockback_lg", "g_knockback_sg", "g_knockback_gl", "g_knockback_mg", 145 | "g_knockback_hmg", "g_knockback_rl_self", "g_railJump", "g_velocity_pg", 146 | "g_velocity_rl"] 147 | for cvar in load_cvars: 148 | self.DFLT_CVARS.append("{} {}".format(cvar, self.get_cvar(cvar))) 149 | else: 150 | self.DFLT_CVARS = ["g_infiniteAmmo 0", "g_startingWeapons 8447", "g_startingArmor 100", 151 | "g_startingHealth 200", "weapon_reload_rl 800", "weapon_reload_pg 100", 152 | "weapon_reload_rg 1500", "weapon_reload_lg 50", "weapon_reload_sg 1000", 153 | "weapon_reload_gl 800", "weapon_reload_mg 100", "weapon_reload_hmg 75", 154 | "g_velocity_rl 1000", "g_velocity_pg 2000", "g_velocity_gl 700", "g_knockback_rl 0.90", 155 | "g_knockback_pg 1.10", "g_knockback_rg 0.85", "g_knockback_lg 1.75", 156 | "g_knockback_sg 1.00", "g_knockback_gl 1.10", "g_knockback_mg 1.00", 157 | "g_knockback_hmg 1.00", "g_knockback_rl_self 1.10", "g_railJump 0", "g_velocity_pg 2000", 158 | "g_velocity_rl 1000"] 159 | return True 160 | 161 | def handle_vote_ended(self, votes, vote, args, passed): 162 | if passed and vote.lower() == "map": 163 | self.set_normal_mode() 164 | 165 | def handle_console_print(self, text): 166 | if text.startswith("zmq RCON command"): 167 | args = text.split(":") 168 | if args[1].startswith(" map "): 169 | self.set_normal_mode() 170 | 171 | def handle_command(self, caller, command, args): 172 | args_list = args.split(" ") 173 | if args_list[0].endswith("map"): 174 | self.set_normal_mode() 175 | 176 | def handle_map(self, mapname, factory): 177 | got_defaults = False 178 | if self._game_type != self.game.type_short: 179 | got_defaults = self.get_defaults() 180 | if got_defaults: 181 | if self.fw_active: 182 | self.set_normal_mode() 183 | if self.get_cvar("qlx_fwSetupWarmupFun", bool): 184 | Timer(2, self.start_fun_warm_up).start() 185 | self._game_type = self.game.type_short 186 | 187 | def handle_game_countdown(self): 188 | self.set_normal_mode() 189 | 190 | def handle_player_disconnect(self, player, reason): 191 | self.disconnect_action() 192 | 193 | @minqlx.delay(1) 194 | def disconnect_action(self): 195 | teams = self.teams() 196 | if self.fw_active and (len(self.players()) == 0 or len(teams["red"] + teams["blue"] + teams["free"]) == 0): 197 | self.set_normal_mode() 198 | 199 | def handle_player_loaded(self, player): 200 | if self.fw_active: 201 | player.center_print("^6Fun Warmup mode is ^1ON") 202 | if len(self.fw_weapons) > 0: 203 | player.center_print("^2Fun Warmup weapon is ^1{} \n^2^1{} ^2seconds of fun per weapon." 204 | .format(self.WEAPON_NAMES[self.fw_weapons[-1]], self.fw_interval)) 205 | 206 | def handle_player_spawn(self, player): 207 | if self.fw_active: 208 | if player.team != "spectator": 209 | if len(self.fw_weapons) > 0: 210 | player.tell("^2Fun Warmup weapon is ^1{}".format(self.WEAPON_NAMES[self.fw_weapons[-1]])) 211 | else: 212 | player.tell("^6Fun Warmup mode is ^1ON") 213 | else: 214 | teams = self.teams() 215 | if len(teams["red"] + teams["blue"] + teams["free"]) == 0: 216 | self.set_normal_mode() 217 | elif self.get_cvar("qlx_fwSetupWarmupFun", bool) and self.game.state == "warmup": 218 | Timer(0.2, self.start_fun_warm_up).start() 219 | 220 | def handle_game_end(self, data): 221 | if data["ABORTED"]: 222 | if self.get_cvar("qlx_fwSetupWarmupFun", bool): 223 | Timer(2, self.start_fun_warm_up).start() 224 | 225 | def start_fun_warm_up(self): 226 | teams = self.teams() 227 | if not self.fw_active and len(teams["red"] + teams["blue"] + teams["free"]) > 0: 228 | self.set_fun_warm_up() 229 | 230 | def cmd_set_fun(self, player, msg, channel): 231 | if self.game.state == "warmup": 232 | if not self.fw_active: 233 | self.set_fun_warm_up() 234 | else: 235 | player.msg("^1The game is not in warmup. Changes not allowed.") 236 | 237 | def cmd_unset_fun(self, player, msg, channel): 238 | self.set_normal_mode() 239 | 240 | def cmd_weapon(self, player, msg, channel): 241 | if self.fw_active: 242 | if len(self.fw_weapons) > 0: 243 | self.msg("^2Fun Warmup weapon is ^1{}".format(self.WEAPON_NAMES[self.fw_weapons[-1]])) 244 | else: 245 | self.msg("^3No Fun Warmup weapon is set.") 246 | else: 247 | self.msg("^6Fun Warmup mode is ^3OFF") 248 | 249 | def set_fun_warm_up(self): 250 | self.fw_active = True 251 | self.fw_weapons = [] 252 | for setting in self.PLAYER_CVARS: 253 | minqlx.console_command("set {}".format(setting)) 254 | fw_id = self.fw_id = random.randint(0, 10000000) 255 | self.cycle_fun_weapons(fw_id) 256 | 257 | @minqlx.thread 258 | def cycle_fun_weapons(self, fw_id=None): 259 | if self.fw_active and self.game.state == "warmup" and fw_id == self.fw_id: 260 | for used in self.fw_weapons: 261 | cvar_list = self.WEAPONS_DFLTS[used] 262 | for cvar in cvar_list: 263 | minqlx.console_command("set {}".format(cvar)) 264 | max_players = self.get_max_players() 265 | if len(self.fw_weapons) >= len(self.WEAPON_NAMES): 266 | self.fw_weapons = [] 267 | numbers = [n for n in range(0, len(self.WEAPON_NAMES))] 268 | numbers = [n for n in numbers if n not in self.fw_weapons] 269 | choice = random.choice(numbers) 270 | self.fw_weapons.append(choice) 271 | if max_players <= self.get_cvar("qlx_fwPlayerSplit", int): 272 | minqlx.console_print("Using WEAPONS settings for {}".format(self.WEAPON_NAMES[choice])) 273 | for setting in self.WEAPONS[choice]: 274 | minqlx.console_command("set {}".format(setting)) 275 | else: 276 | minqlx.console_print("Using WEAPONS2 settings for {}".format(self.WEAPON_NAMES[choice])) 277 | for setting in self.WEAPONS2[choice]: 278 | minqlx.console_command("set {}".format(setting)) 279 | players = self.players() 280 | for player in players: 281 | player.center_print("^2Fun Warmup weapon is ^1{} \n^2for the next ^1{} ^2seconds." 282 | .format(self.WEAPON_NAMES[choice], self.fw_interval)) 283 | self.msg("^2Fun Warmup weapon is ^1{} ^2for the next ^1{} ^2seconds." 284 | .format(self.WEAPON_NAMES[self.fw_weapons[-1]], self.fw_interval)) 285 | Timer(self.fw_interval, self.cycle_fun_weapons, [fw_id]).start() 286 | elif not self.fw_active: 287 | self.set_normal_mode() 288 | 289 | def set_normal_mode(self): 290 | msg = False 291 | if self.fw_active: 292 | msg = True 293 | self.fw_active = False 294 | minqlx.console_print("Fun Warm Up: Setting normal mode.") 295 | if msg: 296 | for player in self.players(): 297 | player.center_print("^6Fun Warmup mode is ^3OFF") 298 | player.tell("^6Fun Warmup mode is ^3OFF") 299 | self.fw_weapons = [] 300 | for setting in self.DFLT_CVARS: 301 | minqlx.console_command("set {}".format(setting)) 302 | 303 | def get_max_players(self): 304 | max_players = self.get_cvar("teamsize", int) 305 | if max_players == 0: 306 | max_players = self.get_cvar("sv_maxClients", int) 307 | else: 308 | max_players = max_players * 2 309 | return max_players 310 | 311 | def dicts(self, player, msg, channel): 312 | minqlx.console_print(str(self.DFLT_CVARS)) 313 | -------------------------------------------------------------------------------- /handicap.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an extension plugin for minqlx, a Quake Live Server Admin Tool.. 3 | Copyright (C) 2016 BarelyMiSSeD (github) 4 | 5 | You can redistribute it and/or modify it under the terms of the 6 | GNU General Public License as published by the Free Software Foundation, 7 | either version 3 of the License, or (at your option) any later version. 8 | 9 | You should have received a copy of the GNU General Public License 10 | along with minqlx. If not, see . 11 | 12 | Handicap.py is a simple script made to automatically put a handicap on people who are over a certain ELO. 13 | It will calculate the percentage of a handicap to put on a player based on the lower and upper ELO settings. 14 | """ 15 | 16 | import minqlx 17 | import requests 18 | import time 19 | 20 | """ 21 | The handicap given to players above the LOWER_ELO setting. 22 | The severity of the handicap given can be adjusted by changing the UPPER_ELO setting. 23 | Increase it to reduce the severity of the handicap and lower it to increase the severity. 24 | It should not be lowered further than the highest ELO connected to the server. 25 | ****Adjust the LOWER_ELO/LOWER_BDM to the level you want the script to start giving handicaps*** 26 | ****Adjust the UPPER_ELO/UPPER_BDM to adjust the amount of handicap it gives. The higher the UPPER_ELO/UPPER_BDM*** 27 | ****the less severe the handicap.*** 28 | 29 | // Sets the minqlx permission level needed to use admin commands 30 | set qlx_handicapAdminLevel "3" 31 | // Show the player a message if they are handicapped by the server (1=on, 0=off) 32 | set qlx_handicapMsgPlayer "1" 33 | // Use BDM or ELO for handicaping players (1=BDM, 0=ELO) 34 | set qlx_handicapUseBDM "1" 35 | """ 36 | 37 | UPPER_ELO = 3500 38 | LOWER_ELO = 1750 39 | UPPER_BDM = 2500 40 | LOWER_BDM = 1550 41 | PING_ADJUSTMENT = 70 42 | MAX_ATTEMPTS = 3 43 | BDM_KEY = "minqlx:players:{}:bdm:{}:{}" 44 | ELO_KEY = "minqlx:players:{}:elo:{}:{}" 45 | VERSION = 2.02 46 | 47 | 48 | class handicap(minqlx.Plugin): 49 | def __init__(self): 50 | self.set_cvar_once("qlx_handicapAdminLevel", "3") 51 | self.set_cvar_once("qlx_handicapMsgPlayer", "1") 52 | self.set_cvar_once("qlx_handicapUseBDM", "1") 53 | 54 | self.add_hook("new_game", self.handle_new_game) 55 | self.add_hook("game_end", self.handle_game_end) 56 | self.add_hook("player_loaded", self.handle_player_loaded) 57 | self.add_hook("player_disconnect", self.handle_player_disconnect) 58 | self.add_hook("userinfo", self.handle_user_info) 59 | self.add_command(("handicap", "handi"), self.cmd_handicap) 60 | self.add_command("hversion", self.cmd_hversion) 61 | self.add_command(("handicapon", "handion"), self.cmd_handicap_on, 62 | self.get_cvar("qlx_handicapAdminLevel", int)) 63 | self.add_command(("handicapoff", "handioff"), self.cmd_handicap_off, 64 | self.get_cvar("qlx_handicapAdminLevel", int)) 65 | self.add_command(("listhandicaps", "listhandi"), self.cmd_list_handicaps, 66 | self.get_cvar("qlx_handicapAdminLevel", int)) 67 | 68 | self.handicapped_players = {} 69 | self.handicap_gametype = self.game.type_short 70 | self.handicap_on = True 71 | self.end_screen = False 72 | 73 | self.check_players() 74 | 75 | def modify_handicapped(self, pid, action, handi_value=0, ping_adjustment=None): 76 | if action == "add": 77 | if ping_adjustment: 78 | self.handicapped_players[str(pid)] = ping_adjustment 79 | else: 80 | self.handicapped_players[str(pid)] = handi_value 81 | elif action == "del" or handi_value == 0: 82 | del self.handicapped_players[str(pid)] 83 | 84 | @minqlx.thread 85 | def check_players(self): 86 | if self.handicap_on: 87 | players = self.players() 88 | loaded_scripts = self.plugins 89 | 90 | if self.get_cvar("qlx_handicapUseBDM", bool): 91 | if "serverBDM" not in loaded_scripts: 92 | minqlx.console_print("The serverBDM plugin needs to be loaded to use {} with BDMs." 93 | .format(self.__class__.__name__)) 94 | return minqlx.RET_STOP_ALL 95 | 96 | for player in players: 97 | pid = player.steam_id 98 | rating = loaded_scripts["serverBDM"].get_bdm_field(player, self.handicap_gametype, "rating") 99 | 100 | if rating > LOWER_BDM: 101 | percentage = 100 - abs(round(((LOWER_BDM - rating) / (UPPER_BDM - LOWER_BDM)) * 100)) 102 | self.modify_handicapped(pid, "add", percentage) 103 | 104 | else: 105 | pids = [] 106 | elo = self.get_cvar("qlx_balanceApi") 107 | response = False 108 | 109 | for player in players: 110 | pids.append(str(player.steam_id)) 111 | 112 | url = "http://{}/{}/{}".format(self.get_cvar("qlx_balanceUrl"), elo, "+".join(pids)) 113 | attempts = 0 114 | while attempts < MAX_ATTEMPTS: 115 | attempts += 1 116 | info = requests.get(url) 117 | if info.status_code != requests.codes.ok: 118 | continue 119 | info_js = info.json() 120 | if "players" in info_js: 121 | attempts = MAX_ATTEMPTS 122 | response = True 123 | if response: 124 | for info in info_js["players"]: 125 | rating = int(info[str(self.handicap_gametype)]["elo"]) 126 | self.db[ELO_KEY.format(int(info["steamid"]), elo, self.handicap_gametype)] =\ 127 | int(info[str(self.handicap_gametype)]["elo"]) 128 | if rating > LOWER_ELO: 129 | percentage = 100 - abs(round(((LOWER_ELO - rating) / (UPPER_ELO - LOWER_ELO)) * 100)) 130 | self.modify_handicapped(info["steamid"], "add", percentage) 131 | else: 132 | for player in players: 133 | rating = 0 134 | pid = player.steam_id 135 | try: 136 | rating = int(self.db.get(ELO_KEY.format(pid, elo, self.handicap_gametype))) 137 | except: 138 | pass 139 | if rating > LOWER_ELO: 140 | percentage = 100 - abs(round(((LOWER_ELO - rating) / (UPPER_ELO - LOWER_ELO)) * 100)) 141 | self.modify_handicapped(pid, "add", percentage) 142 | 143 | for player in players: 144 | pid = player.steam_id 145 | if str(pid) in self.handicapped_players: 146 | percentage = int(self.handicapped_players[str(pid)]) 147 | ping = player.ping 148 | if ping > PING_ADJUSTMENT: 149 | percentage = round(percentage + (ping * ping * ping * ping / 15000000)) 150 | if percentage > 100: 151 | percentage = 100 152 | self.modify_handicapped(pid, "add", percentage) 153 | player.handicap = percentage 154 | 155 | def cmd_handicap_on(self, player, msg, channel): 156 | self.handicap_on = True 157 | player.tell("^3Players will now not be able to change their handicaps") 158 | self.check_players() 159 | return minqlx.RET_STOP_ALL 160 | 161 | def cmd_handicap_off(self, player, msg, channel): 162 | self.handicap_on = False 163 | player.tell("^3Players will now be able to change their handicaps") 164 | return minqlx.RET_STOP_ALL 165 | 166 | def cmd_handicap(self, player, msg, channel): 167 | if len(msg) < 3: 168 | if len(msg) < 2: 169 | if str(player.steam_id) in self.handicapped_players: 170 | channel.reply("{} ^7has a handicap of ^1{}^7%" 171 | .format(player, self.handicapped_players[str(player.steam_id)])) 172 | else: 173 | channel.reply("{} ^7is not handicapped by the server.".format(player)) 174 | else: 175 | try: 176 | pid = int(msg[1]) 177 | except ValueError: 178 | player.tell("^3Enter a player ID from ^10 ^3to ^163^3.") 179 | return 180 | if 0 <= pid < 64: 181 | p = self.player(pid) 182 | if not p: 183 | player.tell("^3There is no player associated with that player ID.") 184 | return 185 | else: 186 | if str(p.steam_id) in self.handicapped_players: 187 | channel.reply("{} ^7has a handicap of ^1{}^7%" 188 | .format(p, self.handicapped_players[str(p.steam_id)])) 189 | else: 190 | channel.reply("{} ^7is not handicapped by the server.".format(p)) 191 | else: 192 | player.tell("^3Enter a player ID from ^10 ^3to ^163^3.") 193 | 194 | elif self.db.has_permission(player, self.get_cvar("qlx_handicapAdminLevel", int)): 195 | try: 196 | pid = int(msg[1]) 197 | handi = int(msg[2]) 198 | except ValueError: 199 | player.tell("^1Use a valid Player ID and a handicap between 1 and 100.") 200 | return minqlx.RET_STOP_ALL 201 | 202 | target_player = self.player(pid) 203 | 204 | if handi <= 0 or handi > 100: 205 | player.tell("^3The handicap must be between 1 and 100.") 206 | return minqlx.RET_STOP_ALL 207 | 208 | if target_player: 209 | self.modify_handicapped(target_player.steam_id, "add", handi) 210 | target_player.handicap = handi 211 | if int(self.get_cvar("qlx_handicapMsgPlayer")): 212 | self.admin_message_player(target_player, handi) 213 | return minqlx.RET_STOP_ALL 214 | 215 | def cmd_list_handicaps(self, player, msg, channel): 216 | if len(self.handicapped_players) > 0: 217 | handi_list = ["^1Handicapped ^7Players:\n"] 218 | for pl, handi in self.handicapped_players.items(): 219 | handi_list.append(" ^7{} ^7: ^2{}%\n".format(self.player(int(pl)), handi)) 220 | player.tell("".join(handi_list)) 221 | else: 222 | player.tell("^3There is no one being hadnicapped on the server by the {} script." 223 | .format(self.__class__.__name__)) 224 | return minqlx.RET_STOP_ALL 225 | 226 | def handle_player_loaded(self, player): 227 | if self.handicap_on: 228 | self.check_handi(player) 229 | 230 | @minqlx.thread 231 | def check_handi(self, player): 232 | time.sleep(6) 233 | if self.get_cvar("qlx_handicapUseBDM", bool): 234 | if "serverBDM" not in minqlx.Plugin._loaded_plugins: 235 | minqlx.console_print("The serverBDM plugin needs to be loaded to use {} with BDMs." 236 | .format(self.__class__.__name__)) 237 | return minqlx.RET_STOP_ALL 238 | rating = minqlx.Plugin._loaded_plugins["serverBDM"]\ 239 | .get_bdm_field(player, self.handicap_gametype, "rating") 240 | if rating > LOWER_BDM: 241 | ping_adjustment = None 242 | percentage = 100 - abs(round(((LOWER_BDM - rating) / (UPPER_BDM - LOWER_BDM)) * 100)) 243 | ping = player.ping 244 | if ping > PING_ADJUSTMENT: 245 | ping_adjustment = round(percentage + (ping * ping * ping * ping / 15000000)) 246 | if ping_adjustment: 247 | if ping_adjustment > 100: 248 | ping_adjustment = 100 249 | player.handicap = ping_adjustment 250 | else: 251 | player.handicap = percentage 252 | self.modify_handicapped(player.steam_id, "add", percentage, ping_adjustment) 253 | if int(self.get_cvar("qlx_handicapMsgPlayer")): 254 | self.message_player(player, percentage, ping_adjustment, "bdm") 255 | else: 256 | elo = self.get_cvar("qlx_balanceApi") 257 | response = False 258 | pid = player.steam_id 259 | ping = player.ping 260 | url = "http://{}/{}/{}".format(self.get_cvar("qlx_balanceUrl"), elo, pid) 261 | rating = 0 262 | attempts = 0 263 | while attempts < MAX_ATTEMPTS: 264 | attempts += 1 265 | info = requests.get(url) 266 | if info.status_code != requests.codes.ok: 267 | continue 268 | info_js = info.json() 269 | if "players" in info_js: 270 | attempts = MAX_ATTEMPTS 271 | response = True 272 | if response: 273 | for info in info_js["players"]: 274 | rating = int(info[str(self.handicap_gametype)]["elo"]) 275 | self.db[ELO_KEY.format(int(pid), elo, self.handicap_gametype)] =\ 276 | int(info[str(self.handicap_gametype)]["elo"]) 277 | else: 278 | try: 279 | rating = int(self.db.get(ELO_KEY.format(pid, elo, self.handicap_gametype))) 280 | except: 281 | return 282 | if rating > LOWER_ELO: 283 | ping_adjustment = None 284 | percentage = 100 - abs(round(((LOWER_ELO - rating) / (UPPER_ELO - LOWER_ELO)) * 100)) 285 | if ping > PING_ADJUSTMENT: 286 | ping_adjustment = round(percentage + (ping * ping * ping * ping / 15000000)) 287 | if ping_adjustment: 288 | if ping_adjustment > 100: 289 | ping_adjustment = 100 290 | player.handicap = ping_adjustment 291 | else: 292 | player.handicap = percentage 293 | self.modify_handicapped(pid, "add", percentage, ping_adjustment) 294 | if int(self.get_cvar("qlx_handicapMsgPlayer")): 295 | self.message_player(player, percentage, ping_adjustment, "elo") 296 | 297 | def message_player(self, player, percentage, ping_adjustment, type="bdm"): 298 | if type == "bdm": 299 | lower_limit = LOWER_BDM 300 | else: 301 | lower_limit = LOWER_ELO 302 | if ping_adjustment: 303 | player.tell("^7You, {}^7 would have had a handicap of ^1{}% ^7because your {} is over ^4{}^7, " 304 | "but because of your ping your handicap has been adjusted to ^1{}%^7." 305 | .format(player, percentage, lower_limit, type, ping_adjustment)) 306 | else: 307 | player.tell("^7You, {}^7 have been set to an auto handicap of ^1{}% ^7because your {} is over ^4{}^7." 308 | .format(player, percentage, type, lower_limit)) 309 | 310 | def admin_message_player(self, player, percentage): 311 | player.tell("^7You, {}^7 have been set to a handicap of ^1{}% ^7on this server." 312 | .format(player, percentage, LOWER_ELO)) 313 | 314 | def cmd_hversion(self, player, msg, channel): 315 | channel.reply('^7This server has installed ^2{} version {} by BarelyMiSSeD' 316 | .format(self.__class__.__name__, VERSION)) 317 | 318 | def handle_user_info(self, player, info): 319 | if not self.end_screen and 'handicap' in info: 320 | if str(player.steam_id) in self.handicapped_players and self.handicap_on: 321 | if int(info["handicap"]) > self.handicapped_players[str(player.steam_id)] or int(info["handicap"]) == 0: 322 | player.tell("^1Your handicap is being set by the server to ^1{}% ^7and can't be changed." 323 | .format(self.handicapped_players[str(player.steam_id)])) 324 | info["handicap"] = self.handicapped_players[str(player.steam_id)] 325 | return info 326 | return 327 | 328 | def handle_new_game(self): 329 | self.process_new_game() 330 | return 331 | 332 | @minqlx.delay(5) 333 | def process_new_game(self): 334 | self.end_screen = False 335 | gtype = self.game.type_short 336 | if gtype != self.handicap_gametype and self.handicap_on: 337 | self.handicap_gametype = gtype 338 | self.check_players() 339 | 340 | def handle_game_end(self, data): 341 | self.end_screen = True 342 | 343 | def handle_player_disconnect(self, player, reason): 344 | self.process_player_disconnect(player) 345 | return 346 | 347 | def process_player_disconnect(self, player): 348 | pid = player.steam_id 349 | if str(pid) in self.handicapped_players: 350 | self.modify_handicapped(pid, "del") 351 | -------------------------------------------------------------------------------- /commlink.py: -------------------------------------------------------------------------------- 1 | # minqlx - A Quake Live server administrator bot. 2 | # Copyright (C) 2015 Mino 3 | 4 | # minqlx is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | 9 | # minqlx is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | 14 | # You should have received a copy of the GNU General Public License 15 | # along with minqlx. If not, see . 16 | 17 | 18 | # Modified by Thomas Jones on 27/01/2016 - thomas@tomtecsolutions.com 19 | # commlink.py, a plugin for minqlx to enable inter-server communication functionality. 20 | # This plugin is released to everyone, for any purpose. 21 | # It comes with no warranty, no guarantee it works, it's released AS IS. 22 | # You can modify everything, except for lines 1-4 and the !tomtec_versions code. Please make it better :D 23 | 24 | # Modified by OrbitaL on 9/19/2019 changed irc server 25 | 26 | # Modified by BarelyMiSSeD on 10/6/2019: (only team games modifications) 27 | # added - !status (responds with the player status of the other servers) 28 | # added - !need (tells other server that num of players is needed on the requesting server) 29 | # 12/6/2022: added checking for self.game in places it is used before executing further code 30 | # 12/27/2022: added allow/disallow connect/disconnect messages based on the qlx_enableConnectDisconnectMessages 31 | 32 | """ 33 | 34 | Set the following cvars: 35 | qlx_commlinkIdentity - Set this cvar in your server.cfg, it needs to be the same for all your servers. If someone's using the same identity, there'll be crosstalk across the servers. 36 | No Default. This cvar MUST be set. 37 | qlx_commlinkServerName - Make this a 12 character or less identifier that will appear when messages from the server appear on other servers in the same identity group. 38 | Default: Server-XXXX (where X = random number) 39 | qlx_enableConnectDisconnectMessages - Enables the 'Player connected.' and 'Player disconnected.' messages in CommLink. 40 | Default: 1 41 | qlx_enableCommlinkMessages - Enables CommLink message reception for all players. If this is set to 0, players have to manually enable CommLink with the !commlink command. 42 | Default: 1 43 | 44 | """ 45 | 46 | import minqlx 47 | import threading 48 | import asyncio 49 | import random 50 | import time 51 | import re 52 | import urllib.request 53 | 54 | TEAM_BASED_GAMETYPES = ("ca", "ctf", "ft", "tdm", "ictf", "wipeout", "dom", "ad", "1f", "har") 55 | 56 | 57 | class commlink(minqlx.Plugin): 58 | def __init__(self): 59 | if not self.get_cvar("qlx_commlinkIdentity"): 60 | self.msg("^1Error: ^7Please set your ^4qlx_commlinkIdentity^7 cvar in your server.cfg.") 61 | minqlx.unload_plugin("commlink") 62 | return 63 | 64 | self.add_hook("unload", self.handle_unload) 65 | self.add_hook("player_connect", self.handle_player_connect, priority=minqlx.PRI_LOWEST) 66 | self.add_hook("player_disconnect", self.handle_player_disconnect, priority=minqlx.PRI_LOWEST) 67 | self.add_hook("game_countdown", self.game_countdown) 68 | 69 | self.set_cvar_once("qlx_commlinkServerName", "Server-{}".format(random.randint(1000, 9999))) 70 | self.set_cvar_once("qlx_enableConnectDisconnectMessages", "1") 71 | self.set_cvar_once("qlx_enableCommlinkMessages", "1") 72 | 73 | self.server = "irc.quakenet.org" 74 | self.identity = ("#" + self.get_cvar("qlx_commlinkIdentity")) 75 | self.clientName = self.get_cvar("qlx_commlinkServerName") 76 | 77 | self.add_command(("world", "say_world"), self.send_commlink_message, priority=minqlx.PRI_LOWEST, usage="") 78 | self.add_command("tomtec_versions", self.cmd_showversion) 79 | self.add_command("commlink", self.cmd_toggle_commlink) 80 | self.add_command("status", self.server_status) 81 | self.add_command("need", self.need_player, usage="") 82 | 83 | self.irc = SimpleAsyncIrc(self.server, self.clientName, self.handle_msg, self.handle_perform, self.handle_raw) 84 | self.irc.start() 85 | self.logger.info("Connecting to {}...".format(self.server)) 86 | self.msg("Connecting to ^3CommLink^7 server...") 87 | 88 | self.plugin_version = "1.5.pew" 89 | self.status_request = False 90 | self.server_ip = "" 91 | self.server_port = self.get_cvar("net_port") 92 | self.set_ip() 93 | 94 | @minqlx.delay(0.5) 95 | def set_ip(self): 96 | res = urllib.request.urlopen("http://checkip.amazonaws.com/").read() 97 | ip = "{}".format(res) 98 | ip = re.sub("[b']", "", ip) 99 | self.server_ip = ip[0:-2] 100 | 101 | def game_countdown(self): 102 | if self.game and self.game.type_short == "duel": 103 | self.msg("^3CommLink^7 message reception has been disabled during your Duel.") 104 | 105 | def cmd_toggle_commlink(self, player, msg, channel): 106 | flag = self.db.get_flag(player, "commlink:enabled", default=(self.get_cvar("qlx_enableCommlinkMessages", bool))) 107 | self.db.set_flag(player, "commlink:enabled", not flag) 108 | if flag: 109 | word = "disabled" 110 | else: 111 | word = "enabled" 112 | player.tell("^3CommLink^7 notices have been ^4{}^7.".format(word)) 113 | return minqlx.RET_STOP_ALL 114 | 115 | def handle_unload(self, plugin): 116 | if plugin == self.__class__.__name__ and self.irc and self.irc.is_alive(): 117 | self.irc.quit("CommLink plugin unloaded.") 118 | self.irc.stop() 119 | 120 | def handle_player_connect(self, player): 121 | if self.irc and self.get_cvar("qlx_enableConnectDisconnectMessages", bool): 122 | if str(player.steam_id)[0] == "9": 123 | return 124 | self.irc.msg(self.identity, self.translate_colors("{} connected.".format(player.name))) 125 | 126 | def handle_player_disconnect(self, player, reason): 127 | if self.irc and self.get_cvar("qlx_enableConnectDisconnectMessages", bool): 128 | if str(player.steam_id)[0] == "9": 129 | return 130 | if reason and reason[-1] not in ("?", "!", "."): 131 | reason = reason + "." 132 | self.irc.msg(self.identity, self.translate_colors("{} {}".format(player.name, reason))) 133 | 134 | def handle_msg(self, irc, user, channel, msg): 135 | def broadcast_commlink(pm): 136 | if pm[0].startswith("Duel-") or pm[0].startswith("Free-") and pm[1].startswith("Spec-"): 137 | if not self.status_request: 138 | return 139 | self.unset_server_status() 140 | pm[0] = "^3{}".format(pm[0]) 141 | pm[1] = "^6{}".format(pm[1]) 142 | pm[2] = "^5/connect {}".format(pm[2]) 143 | elif pm[0].startswith("Red-") and pm[1].startswith("Blue-") and pm[2].startswith("Spec-"): 144 | if not self.status_request: 145 | return 146 | self.unset_server_status() 147 | pm[0] = "^1{}".format(pm[0]) 148 | pm[1] = "^4{}".format(pm[1]) 149 | pm[2] = "^6{}".format(pm[2]) 150 | pm[3] = "^5/connect {}".format(pm[3]) 151 | minqlx.console_print("[CommLink] ^5{}^7:^3 {}".format(user[0], " ".join(pm))) 152 | duelers = self.teams()["free"] 153 | for p in self.players(): 154 | if self.game and self.game.type_short == "duel" and p in duelers and self.game.state != "warmup": 155 | continue 156 | if self.db.get_flag(p, "commlink:enabled", default=(self.get_cvar("qlx_enableCommlinkMessages", bool))): 157 | p.tell("[CommLink] ^4{}^7:^3 {}".format(user[0], " ".join(pm))) 158 | if not msg: 159 | return 160 | if msg[0] == 'request_status': 161 | status = self.get_status_msg() 162 | self.irc.msg(self.identity, "{} {}:{}".format(status, self.server_ip, self.server_port)) 163 | elif {'connected.', 'disconnected.'} & set(msg) and not self.get_cvar("qlx_enableConnectDisconnectMessages", bool): 164 | return 165 | else: 166 | broadcast_commlink(msg) 167 | 168 | def handle_perform(self, irc): 169 | self.logger.info("Connected to CommLink!".format(self.server)) 170 | self.msg("Connected to ^3CommLink^7.") 171 | irc.join(self.identity) 172 | 173 | def send_commlink_message(self, player, msg, channel): 174 | if len(msg) < 2: 175 | return minqlx.RET_USAGE 176 | 177 | text = "^7<{}> ^3{} ".format(player.name, " ".join(msg[1:])) 178 | self.irc.msg(self.identity, self.translate_colors(text)) 179 | player.tell("Message sent via ^3CommLink^7.") 180 | 181 | def get_status_msg(self): 182 | teams = self.teams() 183 | free = len(teams["free"]) 184 | red = len(teams["red"]) 185 | blue = len(teams["blue"]) 186 | spec = len(teams["spectator"]) 187 | if self.game: 188 | if self.game.type_short == "duel": 189 | status = "^3Duel-{}, ^6Spec-{}".format(free, spec) 190 | elif self.game.type_short in TEAM_BASED_GAMETYPES: 191 | status = "^1Red-{}, ^4Blue-{}, ^6Spec-{}".format(red, blue, spec) 192 | else: 193 | status = "^3Free-{}, ^6Spec-{}".format(free, spec) 194 | return status 195 | 196 | def server_status(self, player, msg, channel): 197 | self.query_status() 198 | 199 | @minqlx.thread 200 | def query_status(self): 201 | free = self.teams()["free"] 202 | status = self.get_status_msg() 203 | minqlx.console_print("[CommLink] ^5{}^7: {}".format(self.clientName, status)) 204 | if self.game: 205 | for p in self.players(): 206 | if self.game.type_short == "duel" and p in free and self.game.state != "warmup": 207 | continue 208 | if self.db.get_flag(p, "commlink:enabled", default=(self.get_cvar("qlx_enableCommlinkMessages", bool))): 209 | p.tell("[CommLink] ^4{}^7: {}".format(self.clientName, status)) 210 | self.status_request = True 211 | self.irc.msg(self.identity, "request_status") 212 | 213 | @minqlx.delay(1.5) 214 | def unset_server_status(self): 215 | self.status_request = False 216 | 217 | def need_player(self, player, msg, channel): 218 | if len(msg) > 1: 219 | try: 220 | needed = int(msg[1]) 221 | except: 222 | player.tell("^1You must include a number") 223 | return minqlx.RET_STOP_ALL 224 | else: 225 | needed = 1 226 | player.tell("^6Sent player request to other servers") 227 | status = self.get_status_msg() 228 | self.irc.msg(self.identity, "Need {} player{} here: {} /connect {}:{}" 229 | .format(needed, "s" if needed > 1 else "", status, self.server_ip, self.server_port)) 230 | 231 | def handle_raw(self, irc, msg): 232 | split_msg = msg.split() 233 | if len(split_msg) > 1 and split_msg[1] == "433": 234 | irc.nick(irc.nickname + "_") 235 | 236 | @classmethod 237 | def translate_colors(cls, text): 238 | return cls.clean_text(text) 239 | 240 | def cmd_showversion(self, player, msg, channel): 241 | channel.reply("^4commlink.py^7 - version {}, by Thomas Jones, ^1O^7rbitaL, & Barely^4MiSSeD.".format(self.plugin_version)) 242 | 243 | 244 | # ==================================================================== 245 | # COMMLINK CHANNEL 246 | # ==================================================================== 247 | 248 | class IrcChannel(minqlx.AbstractChannel): 249 | name = "irc" 250 | 251 | def __init__(self, irc, recipient): 252 | self.irc = irc 253 | self.recipient = recipient 254 | 255 | def __repr__(self): 256 | return "{} {}".format(str(self), self.recipient) 257 | 258 | def reply(self, msg): 259 | for line in msg.split("\n"): 260 | self.irc.msg(self.recipient, irc.translate_colors(line)) 261 | 262 | # ==================================================================== 263 | # SIMPLE ASYNC IRC 264 | # ==================================================================== 265 | 266 | 267 | re_msg = re.compile(r"^:([^ ]+) PRIVMSG ([^ ]+) :(.*)$") 268 | re_user = re.compile(r"^(.+)!(.+)@(.+)$") 269 | 270 | 271 | class SimpleAsyncIrc(threading.Thread): 272 | def __init__(self, address, nickname, msg_handler, perform_handler, raw_handler=None, stop_event=threading.Event()): 273 | split_addr = address.split(":") 274 | self.host = split_addr[0] 275 | self.port = int(split_addr[1]) if len(split_addr) > 1 else 6667 276 | self.nickname = nickname 277 | self.msg_handler = msg_handler 278 | self.perform_handler = perform_handler 279 | self.raw_handler = raw_handler 280 | self.stop_event = stop_event 281 | self.reader = None 282 | self.writer = None 283 | self.server_options = {} 284 | super().__init__() 285 | 286 | self._lock = threading.Lock() 287 | self._old_nickname = self.nickname 288 | 289 | def run(self): 290 | loop = asyncio.new_event_loop() 291 | logger = minqlx.get_logger("irc") 292 | asyncio.set_event_loop(loop) 293 | while not self.stop_event.is_set(): 294 | try: 295 | loop.run_until_complete(self.connect()) 296 | except Exception: 297 | minqlx.log_exception() 298 | 299 | # Disconnected. Try reconnecting in 30 seconds. 300 | logger.info("Disconnected from CommLink. Reconnecting in 30 seconds...") 301 | minqlx.CHAT_CHANNEL.reply("Disconnected from ^3CommLink^7. Reconnecting in 30 seconds...") 302 | time.sleep(30) 303 | loop.close() 304 | 305 | def stop(self): 306 | self.stop_event.set() 307 | 308 | def write(self, msg): 309 | if self.writer: 310 | with self._lock: 311 | self.writer.write(msg.encode(errors="ignore")) 312 | 313 | @asyncio.coroutine 314 | def connect(self): 315 | self.reader, self.writer = yield from asyncio.open_connection(self.host, self.port) 316 | self.write("NICK {0}\r\nUSER {0} 0 * :{0}\r\n".format(self.nickname)) 317 | 318 | while not self.stop_event.is_set(): 319 | line = yield from self.reader.readline() 320 | if not line: 321 | break 322 | line = line.decode("utf-8", errors="ignore").rstrip() 323 | if line: 324 | yield from self.parse_data(line) 325 | 326 | self.write("QUIT Quit by user.\r\n") 327 | self.writer.close() 328 | 329 | @asyncio.coroutine 330 | def parse_data(self, msg): 331 | split_msg = msg.split() 332 | if len(split_msg) > 1 and split_msg[0] == "PING": 333 | self.pong(split_msg[1].lstrip(":")) 334 | elif len(split_msg) > 3 and split_msg[1] == "PRIVMSG": 335 | r = re_msg.match(msg) 336 | user = re_user.match(r.group(1)).groups() 337 | channel = user[0] if self.nickname == r.group(2) else r.group(2) 338 | self.msg_handler(self, user, channel, r.group(3).split()) 339 | elif len(split_msg) > 2 and split_msg[1] == "NICK": 340 | user = re_user.match(split_msg[0][1:]) 341 | if user and user.group(1) == self.nickname: 342 | self.nickname = split_msg[2][1:] 343 | elif split_msg[1] == "005": 344 | for option in split_msg[3:-1]: 345 | opt_pair = option.split("=", 1) 346 | if len(opt_pair) == 1: 347 | self.server_options[opt_pair[0]] = "" 348 | else: 349 | self.server_options[opt_pair[0]] = opt_pair[1] 350 | elif len(split_msg) > 1 and split_msg[1] == "433": 351 | self.nickname = self._old_nickname 352 | # Stuff to do after we get the MOTD. 353 | elif re.match(r":[^ ]+ (376|422) .+", msg): 354 | self.perform_handler(self) 355 | 356 | # If we have a raw handler, let it do its stuff now. 357 | if self.raw_handler: 358 | self.raw_handler(self, msg) 359 | 360 | def msg(self, recipient, msg): 361 | self.write("PRIVMSG {} :{}\r\n".format(recipient, msg)) 362 | 363 | def nick(self, nick): 364 | with self._lock: 365 | self._old_nickname = self.nickname 366 | self.nickname = nick 367 | self.write("NICK {}\r\n".format(nick)) 368 | 369 | def join(self, channels): 370 | self.write("JOIN {}\r\n".format(channels)) 371 | 372 | def part(self, channels): 373 | self.write("PART {}\r\n".format(channels)) 374 | 375 | def mode(self, what, mode): 376 | self.write("MODE {} {}\r\n".format(what, mode)) 377 | 378 | def kick(self, channel, nick, reason): 379 | self.write("KICK {} {}:{}\r\n".format(channel, nick, reason)) 380 | 381 | def quit(self, reason): 382 | self.write("QUIT :{}\r\n".format(reason)) 383 | 384 | def pong(self, n): 385 | self.write("PONG :{}\r\n".format(n)) 386 | -------------------------------------------------------------------------------- /inviteonly.py: -------------------------------------------------------------------------------- 1 | # inviteonly.py is a plugin for minqlx to: 2 | # -only allow players added to the invoteonly list to play on the server, if enabled 3 | # created by BarelyMiSSeD on 2-24-16 4 | # 5 | """ 6 | Set these cvars in your server.cfg (or wherever you set your minqlx variables).: 7 | qlx_inviteonlyAdmin "5" - Sets the minqlx server permisson level needed to add and remove someone from the invite only list. 8 | qlx_invoteonlyAllowSpectator "0" - Set to "1" to allow spectators to remain on the server indefiniltely even when not on the invite only list. 9 | qlx_inviteonlySpectatorTime "3" - Sets the amount of time (in minutes) a player can be a spectaror before being kicked if not on the invite only list. 10 | Set qlx_invoteonlyAllowSpectator and qlx_inviteonlySpectatorTime to "0" to kick players immediately. 11 | change notes: Added !aio and !dio commands for adding and deleting from the inviteonly list. 12 | """ 13 | 14 | 15 | 16 | import minqlx 17 | import re 18 | import threading 19 | import requests 20 | import datetime 21 | import os 22 | 23 | VERSION = "v1.04" 24 | INVITEONLY_FILE = "inviteonly.txt" 25 | 26 | class inviteonly(minqlx.Plugin): 27 | def __init__(self): 28 | # Cvars. 29 | self.set_cvar_once("qlx_inviteonlyAdmin", "5") 30 | self.set_cvar_once("qlx_invoteonlyAllowSpectator", "0") 31 | self.set_cvar_once("qlx_inviteonlySpectatorTime", "3") 32 | 33 | # monitored server occurrences 34 | self.add_hook("player_loaded", self.player_loaded) 35 | self.add_hook("team_switch", self.handle_team_switch) 36 | self.add_hook("player_disconnect", self.handle_player_disconnect) 37 | 38 | # set script variable values 39 | self.allowSpec = int(self.get_cvar("qlx_invoteonlyAllowSpectator")) 40 | self.spectateTime = int(self.get_cvar("qlx_inviteonlySpectatorTime")) 41 | self.adminLevel = int(self.get_cvar("qlx_inviteonlyAdmin")) 42 | 43 | # commands 44 | self.add_command(("addinviteonly", "add_inviteonly", "aio"), self.cmd_inviteOnlyAdd, self.adminLevel) 45 | self.add_command(("delinviteonly", "del_inviteonly", "dio"), self.cmd_inviteOnlyDelete, self.adminLevel) 46 | self.add_command(("listinviteonly", "list_inviteonly", "iol"), self.cmd_inviteOnlyList, self.adminLevel) 47 | self.add_command(("reload_inviteonly", "load_inviteonly", "rlio"), self.cmd_loadInvites, self.adminLevel) 48 | self.add_command(("versioninviteonly", "version_inviteonly", "iov"), self.cmd_inviteOnlyVersion, self.adminLevel) 49 | self.add_command("iolist", self.cmd_IOList, 5) # command for testing purposes 50 | 51 | # Opens Invite Only list container 52 | self.inviteonly = [] 53 | self.notOnIOList = [] 54 | self.NotInvited = {} 55 | 56 | # Loads Invite Only list 57 | self.cmd_loadInvites() 58 | 59 | # inviteonly.py version checker. Thanks to iouonegirl for most of this section's code. 60 | @minqlx.thread 61 | def check_version(self, player=None, channel=None): 62 | url = "https://raw.githubusercontent.com/barelymissed/minqlx-plugins/master/{}.py".format(self.__class__.__name__) 63 | res = requests.get(url) 64 | if res.status_code != requests.codes.ok: 65 | return 66 | for line in res.iter_lines(): 67 | if line.startswith(b'VERSION'): 68 | line = line.replace(b'VERSION = ', b'') 69 | line = line.replace(b'"', b'') 70 | # If called manually and outdated 71 | if channel and VERSION.encode() != line: 72 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's ^6{}^7 plugin ^1outdated^7 version ^6{}^7. The latest version is ^6{}".format(self.__class__.__name__, VERSION, line.decode())) 73 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 74 | # If called manually and alright 75 | elif channel and VERSION.encode() == line: 76 | channel.reply("^4Server: ^7Currently using ^4BarelyMiSSeD^7's latest ^6{}^7 plugin version ^6{}^7.".format(self.__class__.__name__, VERSION)) 77 | channel.reply("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 78 | # If routine check and it's not alright. 79 | elif player and VERSION.encode() != line: 80 | try: 81 | player.tell("^4Server: ^3Plugin update alert^7:^6 {}^7's latest version is ^6{}^7 and you're using ^6{}^7!".format(self.__class__.__name__, line.decode(), VERSION)) 82 | player.tell("^4Server: ^7See ^3https://github.com/BarelyMiSSeD/minqlx-plugins") 83 | except Exception as e: minqlx.console_command("echo {}".format(e)) 84 | return 85 | 86 | def cmd_inviteOnlyVersion(self, player, msg, channel): 87 | self.check_version(channel=channel) 88 | 89 | # Command for testing: shows the contents of the self.inviteonly string used to allow only invited player to play. 90 | def cmd_IOList(self, player, msg, channel): 91 | player.tell("List: " + str(self.inviteonly)) 92 | player.tell("NotOnIOList: " + str(self.notOnIOList)) 93 | player.tell("Length: " + str(len(self.notOnIOList))) 94 | player.tell("{} {} {}".format(self.allowSpec, self.spectateTime, self.adminLevel)) 95 | if self.spectateTime: 96 | player.tell("SpectateTime") 97 | if self.allowSpec: 98 | player.tell("AllowSpec") 99 | return minqlx.RET_STOP_EVENT 100 | 101 | # Checks the invite only list when a player connects to see if they are allowed to play on the server. 102 | def player_loaded(self, player): 103 | if player.steam_id == minqlx.owner(): 104 | self.check_version(player=player) 105 | return 106 | id = int(player.steam_id) 107 | if id not in self.inviteonly: 108 | if self.allowSpec: 109 | player.tell("^2Server^7: ^3You are not on the Invited Player list for this server. " 110 | "Speak to a server admin to be added to the list or you will only be able to spectate.") 111 | elif self.spectateTime: 112 | player.tell("^2Server^7: ^3You are not on the Invited Player list for this server. " 113 | "Speak to a server admin to be added to the list or you will be kicked in ^1{}^3 minute(s).".format(self.spectateTime)) 114 | player.center_print("^2Server^7: ^3You are not on the Invited Player list for this server. " 115 | "Speak to a server admin to be added to the list or you will be kicked in ^1{}^3 minute(s).".format(self.spectateTime)) 116 | self.notOnIOList.append(id) 117 | name = player.clean_name 118 | timeJoined = str(id) + "time" 119 | now = datetime.datetime.now() 120 | self.NotInvited[str(id)] = name 121 | self.NotInvited[timeJoined] = now 122 | 123 | # Timer to kick uninvited player 124 | checkIOL = threading.Timer(10, self.check_nonInvite) 125 | checkIOL.start() 126 | else: 127 | player.tempban() 128 | self.msg("^2Server^7: ^4{} ^3is not on the Invite Only list and was kicked from the server.".format(player)) 129 | 130 | def check_nonInvite(self): 131 | if len(self.notOnIOList) > 0: 132 | teams = self.teams() 133 | for id in self.notOnIOList: 134 | for client in teams["spectator"]: 135 | if id == client.steam_id: 136 | timeJoined = str(id) + "time" 137 | now = datetime.datetime.now() 138 | if (now - self.NotInvited[timeJoined]).total_seconds() >= (self.spectateTime * 60): 139 | client.tempban() 140 | self.msg("^2Server^7: ^4{} ^3is not on the Invite Only list and was kicked from the server.".format(client)) 141 | self.notOnIOList.remove(id) 142 | 143 | if len(self.notOnIOList) > 0: 144 | checkIOL = threading.Timer(10, self.check_nonInvite) 145 | checkIOL.start() 146 | else: 147 | self.NotInvited.clear() 148 | 149 | def handle_player_disconnect(self, player, reason): 150 | if player.steam_id in self.notOnIOList: 151 | self.notOnIOList.remove(player.steam_id) 152 | 153 | def handle_team_switch(self, player, old, new): 154 | if player.steam_id not in self.inviteonly and new in ['red', 'blue']: 155 | player.put("spectator") 156 | player.center_print("^2Server^7: ^6You are not on the Invite Only list of allowed players for this server. Speak to a server admin.") 157 | player.tell("^2Server^7: ^6You are not on the Invite Only list of allowed players for this server. Speak to a server admin.") 158 | if self.allowSpec: 159 | player.tell("^2Server^7: ^6Feel free to continue spectating.") 160 | else: 161 | player.tell("^2Server^7: ^6You will be kicked soon if an admin does not add you to the Invite Only list.") 162 | 163 | # Checks for a inviteonly.txt file and loads the entries if the file exists. Creates one if it doesn't. 164 | def cmd_loadInvites(self, player=None, msg=None, channel=None): 165 | try: 166 | f = open(os.path.join(self.get_cvar("fs_homepath"), INVITEONLY_FILE), "r") 167 | lines = f.readlines() 168 | f.close() 169 | tempList = [] 170 | for id in lines: 171 | if id.startswith("#"): continue 172 | try: 173 | tempList.append(int(id.split(None, 1)[0].strip('\n'))) 174 | except: 175 | continue 176 | self.inviteonly = tempList 177 | except IOError as e: 178 | try: 179 | m = open(os.path.join(self.get_cvar("fs_homepath"), INVITEONLY_FILE), "w") 180 | m.write("# This is a commented line because it starts with a '#'\n") 181 | m.write("# Enter every invite only SteamID and name on a newline, format: SteamID Name\n") 182 | m.write("# The NAME is for a mental reference and may contain spaces but is required.\n") 183 | m.write("# NAME will be added automatically if the invite only entry is added with a client id when the player is connected to the server.\n") 184 | m.write("{} ServerOwner\n".format(minqlx.owner())) 185 | m.close() 186 | tempList = [] 187 | tempList.append(int(minqlx.owner())) 188 | self.inviteonly = tempList 189 | if player: 190 | player.tell("^3No ^1Invite Only^3 list was found so one was created and the server owner was added.") 191 | except: 192 | if player: 193 | player.tell("^1Error ^3reading and/or creating the Invite Only list: {}".format(e)) 194 | except Exception as e: 195 | if player: 196 | player.tell("^1Error ^3reading the Invite Only list: {}".format(e)) 197 | 198 | def cmd_inviteOnlyAdd(self, player, msg, channel): 199 | if len(msg) < 2: 200 | player.tell("^3usage^7=^7<^2player id^7|^2steam id^7> <^2name^7>") 201 | return minqlx.RET_STOP_EVENT 202 | target_player = False 203 | file = os.path.join(self.get_cvar("fs_homepath"), INVITEONLY_FILE) 204 | try: 205 | with open(file) as test: 206 | pass 207 | except Exception as e: 208 | player.tell("^1Error ^3reading the Invite Only list file: {}".format(e)) 209 | return minqlx.RET_STOP_EVENT 210 | 211 | # Checks to see if client_id or steam_id was used 212 | try: 213 | id = int(msg[1]) 214 | if 0 <= id <= 63: 215 | try: 216 | target_player = self.player(id) 217 | except minqlx.NonexistentPlayerError: 218 | player.tell("^3There is no one on the server using that Client ID.") 219 | return minqlx.RET_STOP_EVENT 220 | if not target_player: 221 | player.tell("^3There is no one on the server using that Client ID.") 222 | return minqlx.RET_STOP_EVENT 223 | id = int(target_player.steam_id) 224 | elif len(msg) < 3 or id < 0: 225 | player.tell("^3usage^7=^7<^2player id^7|^2steam id^7> <^2name^7>") 226 | return minqlx.RET_STOP_EVENT 227 | elif len(str(id)) != 17: 228 | player.tell("^3The STEAM ID given needs to be 17 digits in length.") 229 | return minqlx.RET_STOP_EVENT 230 | except ValueError: 231 | player.tell("^3Invalid ID. Use either a client ID or a SteamID64.") 232 | return minqlx.RET_STOP_EVENT 233 | 234 | if not target_player: 235 | target_player = " ".join(msg[2:]) 236 | 237 | # Checks to see if the player is already on the Invite Only list and adds if not. 238 | if id in self.inviteonly: 239 | player.tell("^2{}^3 is already in the Invite Only list.".format(target_player)) 240 | return minqlx.RET_STOP_EVENT 241 | 242 | h = open(file, "a") 243 | h.write(str(id) + " " + str(target_player) + "\n") 244 | h.close() 245 | self.inviteonly.append(id) 246 | player.tell("^2{}^3 has been added to the Invite Only list.".format(target_player)) 247 | 248 | if id in self.notOnIOList: 249 | self.notOnIOList.remove(id) 250 | player_list = self.players() 251 | for p in player_list: 252 | if id == p.steam_id: 253 | minqlx.console_command("tell {} ^3You have been added to the Invited Player list for this server. Enjoy your game!".format(p.id)) 254 | return minqlx.RET_STOP_EVENT 255 | 256 | return minqlx.RET_STOP_EVENT 257 | 258 | # Removes the desired player from the invite only list 259 | def cmd_inviteOnlyDelete(self, player, msg, channel): 260 | if len(msg) < 2: 261 | player.tell("^3usage^7=<^2player id^7|^2steam id^7>") 262 | return minqlx.RET_STOP_EVENT 263 | 264 | # Opens the invite only list file. 265 | file = os.path.join(self.get_cvar("fs_homepath"), INVITEONLY_FILE) 266 | try: 267 | f = open(file, "r") 268 | lines = f.readlines() 269 | f.close() 270 | except Exception as e: 271 | player.tell("^1Error ^3reading the Invite Only list file: {}".format(e)) 272 | return minqlx.RET_STOP_EVENT 273 | 274 | target_player = False 275 | 276 | # Checks to see if client_id or steam_id was used 277 | try: 278 | id = int(msg[1]) 279 | if 0 <= id <= 63: 280 | try: 281 | target_player = self.player(id) 282 | except minqlx.NonexistentPlayerError: 283 | player.tell("^3Invalid client ID. Use either a client ID or a SteamID64.") 284 | return minqlx.RET_STOP_EVENT 285 | if not target_player: 286 | player.tell("^3There is no one on the server using that Client ID.") 287 | return minqlx.RET_STOP_EVENT 288 | id = int(target_player.steam_id) 289 | elif id < 0: 290 | player.tell("^3usage^7=^7<^2player id^7|^2steam id^7> <^2name^7>") 291 | return minqlx.RET_STOP_EVENT 292 | elif len(str(id)) != 17: 293 | player.tell("^3The STEAM ID given needs to be 17 digits in length.") 294 | return minqlx.RET_STOP_EVENT 295 | except ValueError: 296 | player.tell("^3Invalid ID. Use either a client ID or a SteamID64.") 297 | return minqlx.RET_STOP_EVENT 298 | 299 | if target_player: 300 | messageName = target_player 301 | else: 302 | messageName = str(id) 303 | 304 | # Removes the identified player. 305 | for searchID in lines: 306 | if searchID.startswith("#"): continue 307 | if id == int(searchID.split(None, 1)[0]): 308 | h = open(file, "w") 309 | for line in lines: 310 | if line[0] == "#": 311 | h.write(line) 312 | elif id != int(line.split(None, 1)[0]): 313 | h.write(line) 314 | h.close() 315 | if id in self.inviteonly: 316 | self.inviteonly.remove(id) 317 | player.tell("^2{}^3 has been deleted from the Invite Only list.".format(messageName)) 318 | return minqlx.RET_STOP_EVENT 319 | 320 | player.tell("^2{}^3 is not on the Invite Only list.".format(messageName)) 321 | return minqlx.RET_STOP_EVENT 322 | 323 | # List players in the invite only file 324 | def cmd_inviteOnlyList(self, player, msg, channel): 325 | player_list = self.players() 326 | if not player_list: 327 | player.tell("There are no players connected at the moment.") 328 | 329 | file = os.path.join(self.get_cvar("fs_homepath"), INVITEONLY_FILE) 330 | try: 331 | f = open(file, "r") 332 | lines = f.readlines() 333 | f.close() 334 | except Exception as e: 335 | player.tell("^1Error ^3reading the Invite Only list file: {}".format(e)) 336 | return minqlx.RET_STOP_EVENT 337 | 338 | list = "^5Invite Only List:\n" 339 | 340 | for line in lines: 341 | if line.startswith("#"): continue 342 | foundName = False 343 | words = line.split(" ") 344 | id = words[0] 345 | for p in player_list: 346 | if int(id) == p.steam_id: 347 | foundName = p.name 348 | if not foundName: 349 | foundName = " ".join(words[1:]).strip("\n") 350 | 351 | if foundName: 352 | list += " ^7SteamID ^1{} ^7: ^3{}\n".format(id, foundName) 353 | else: 354 | list += " ^7SteamID ^1{} ^7: ^3No Name saved\n".format(id) 355 | 356 | player.tell(list[:-1]) 357 | return minqlx.RET_STOP_EVENT --------------------------------------------------------------------------------