Created
November 17, 2008 05:12
-
-
Save Lixivial/25665 to your computer and use it in GitHub Desktop.
Preliminary Infobot qstat wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
qstat: Usage for '## <params>': | |
qstat: ----- __Aliasing__ | |
qstat: -a - Add an alias | |
qstat: -c - Check an alias | |
qstat: -e - Edit an alias | |
qstat: -r - Remove an alias | |
qstat: ----- __Querying__ | |
qstat: -h - List of hosts to report/query from. | |
qstat: -p - Player name to search for in output of -c or -h | |
qstat: -g - Game type to use to query. | |
qstat: -l - Limit to n results. | |
qstat: ----- __Miscellaneous__ | |
qstat: -m - Message a user. | |
qstat: -s - Sort the results by a particular field. | |
qstat: -d - Sends various debugging messages to the infobot console. | |
qstat: See 'help ## <param>' for more info on a particular param. | |
qstat: End of help. | |
qstat -a: D: Alias a set of hosts to a single parameter to be used with -c. | |
qstat -a: D: The gameType can be optionally be mapped by appending |gameType to the alias. | |
qstat -a: U: ## aliasName=host:port,host:port,...,hostn:port|gameType | |
qstat -a: E: ## bcs=optimalclan.com:26001,optimalclan.com:26002,optimalclan.com:26003,optimalclan.com:26004 | |
qstat -a: E: ## master=dpmaster.deathmask.net:27950|nexuizm | |
qstat -c: D: Check an aliased list of servers. Combine with -a/-e to check an aliasName's existence. | |
qstat -c: U: ## aliasName | |
qstat -c: E: ## master -p [prae] | |
qstat -c: E: ## aliasName -a | |
qstat -e: D: Edit an existing alias. | |
qstat -e: D: The gameType can be optionally be mapped by appending |gameType to the alias. | |
qstat -e: U: ## aliasName=host:port,host:port,...,hostn:port|gameType | |
qstat -e: E: ## bcs=optimalclan.com:26001,optimalclan.com:26002 | |
qstat -e: E: ## galts=mshade.org:26000,mshade.org:26002|nexuizs | |
qstat -r: D: Remove an existing alias. | |
qstat -r: U: ## aliasName | |
qstat -r: E: ## bcs | |
qstat -h: D: Host(s) to query | |
qstat -h: U: ## host:port,host:port,...,hostn:port | |
qstat -h: E: qstat -g nexuizm -h dpmaster.deathmask.net:27950 -p [prae] | |
qstat -h: E: qstat -c bcs -h mshade.org:26000 -p [prae] | |
qstat -h: E: ## optimalclan.com:26001 | |
qstat -p: D: Player name to search for within output. (Case insensitive) | |
qstat -p: U: ## playerSearch | |
qstat -p: E: qstat -g nexuizm -c masters -p [prae] | |
qstat -g: D: Game type for the host being queried. If omitted, defaults to 'nexuizs' | |
qstat -g: U: ## gameType | |
qstat -g: E: ## warsowm -c masters -p [BOT] | |
qstat -g: E: ## a2s -h 64.27.13.34:27015 -p [woot] | |
qstat -l: D: Limit output by n lines. | |
qstat -l: U: ## # | |
qstat -l: E: qstat -p [prae] -c bcs -l 10 | |
qstat -m: D: Redirect message to user. If -m isn't specified, PM's requestor, and if -m is blank, tells the channel. | |
qstat -m: U: ## userName (userName needn't be specified) | |
qstat -m: E: qstat -p [prae] -c bcs -m Lixivial -l 10 | |
qstat -m: E: qstat -h mshade.org:26001 -m | |
qstat -s: D: Sort output by a given field. (Values are: frags, ping, name, team) | |
qstat -s: U: ## sortField | |
qstat -s: E: qstat -c bcs -s frags | |
qstat -d: D: Print debugging information to the infobot console. | |
qstat -d: U: ## |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# qstat.pl: qstat wrapper | |
# Author: Lixivial (Jesse M. Pearson) | |
# Contact: jesall@gmail.com | |
# Version: v0.5 (20090501). | |
# Created: 20081116 | |
# NOTE: There are none, other than this is quite possibly the worst thing you'll ever read. | |
# Possible TODO: Allow for multiple -m's (to message an entire distribution list) | |
# Related: Aliasing for messaging? | |
# TODOs: | |
# + Port this to mozbot. | |
# + Port this to rbot. | |
# + Create a notification daemon to perform the following functions: | |
# * Watch a server, or servers (with all the limiting in hand) for a short period of time | |
# * NOTE: This should be limited to sane limits, say, 30 minutes, and a minimum of something like | |
# a minute between notifications. | |
# * Notify users of subscribed "watched usernames" across n servers and gametypes. | |
# | |
# | |
# Darkplaces-related colour tables/logic copyright (c) 2008 Rudolf "divVerent" Polzer | |
# Permission is hereby granted, free of charge, to any person | |
# obtaining a copy of this software and associated documentation | |
# files (the "Software"), to deal in the Software without | |
# restriction, including without limitation the rights to use, | |
# copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the | |
# Software is furnished to do so, subject to the following | |
# conditions: | |
# | |
# The above copyright notice and this permission notice shall be | |
# included in all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
# OTHER DEALINGS IN THE SOFTWARE. | |
package qstat; | |
use Switch; | |
my %paramsMap; | |
my $aliasFile = $::bot_data_dir."infobot.qstat"; | |
my @text_qfont_table = ( # ripped from DP console.c qfont_table | |
"\0", '#', '#', '#', '#', '.', '#', '#', | |
'#', 9, 10, '#', ' ', 13, '.', '.', | |
'[', ']', '0', '1', '2', '3', '4', '5', | |
'6', '7', '8', '9', '.', '<', '=', '>', | |
' ', '!', '"', '#', '$', '%', '&', '\'', | |
'(', ')', '*', '+', ',', '-', '.', '/', | |
'0', '1', '2', '3', '4', '5', '6', '7', | |
'8', '9', ':', ';', '<', '=', '>', '?', | |
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', | |
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | |
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | |
'X', 'Y', 'Z', '[', '\\', ']', '^', '_', | |
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | |
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | |
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | |
'x', 'y', 'z', '{', '|', '}', '~', '<', | |
'<', '=', '>', '#', '#', '.', '#', '#', | |
'#', '#', ' ', '#', ' ', '>', '.', '.', | |
'[', ']', '0', '1', '2', '3', '4', '5', | |
'6', '7', '8', '9', '.', '<', '=', '>', | |
' ', '!', '"', '#', '$', '%', '&', '\'', | |
'(', ')', '*', '+', ',', '-', '.', '/', | |
'0', '1', '2', '3', '4', '5', '6', '7', | |
'8', '9', ':', ';', '<', '=', '>', '?', | |
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', | |
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | |
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | |
'X', 'Y', 'Z', '[', '\\', ']', '^', '_', | |
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | |
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | |
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | |
'x', 'y', 'z', '{', '|', '}', '~', '<' | |
); | |
my @color_irc2dp_table = (7, 0, 4, 2, 1, 1, 6, 1, 3, 2, 5, 5, 4, 6, 7, 7); | |
my @color_dp2irc_table = (-1, 4, 9, 8, 12, 11, 13, -1, -1, -1); # not accurate, but legible | |
my @color_dp2ansi_table = ("m", "1;31m", "1;32m", "1;33m", "1;34m", "1;36m", "1;35m", "m", "1m", "1m"); # not accurate, but legible | |
my %color_team2dp_table = (5 => 1, 14 => 4, 13 => 3, 10 => 6); | |
my %color_team2irc_table = (5 => 4, 14 => 12, 13 => 8, 10 => 13); | |
# Main method. | |
sub qstat::qstat { | |
my ( $result, $string, $server, $fileName, $rowCount, $columnCount, $sanitisedName, $sanitisedSearch, @messageArray); | |
# Setup the parameter map. | |
&tokenizeParams(shift); | |
# Enforce field requirements and their relationships. | |
&populateRequiredFields(); | |
# Call the alias-related checking, which handles the relevant parameter checking. | |
# Will return a value if we need to halt execution. | |
if (exists $paramsMap{'checkAlias'} || exists $paramsMap{'addAlias'} || exists $paramsMap{'editAlias'} || exists $paramsMap{'removeAlias'}) { | |
if (&checkAliases() ne "") { | |
&debug("qstat-> Aliasing in effect, halting execution."); | |
return ""; | |
} | |
} | |
# Populate the output templates. | |
$fileName = &generateTemplates(); | |
# Debug some crucial items. | |
&debug("qstat-> gameType: ${paramsMap{'gameType'}}"); | |
&debug("qstat-> host: ${paramsMap{'host'}}"); | |
&debug("qstat-> fileName: ${fileName}"); | |
&debug("qstat-> blockPlayers: $paramsMap{'blockPlayers'}"); | |
# Support for comma delimited server lists | |
for ($i = 0; $i< @ { ${paramsMap}->{'host'} }; $i++) { | |
&debug("qstat-> splitHost ${i}: ".${paramsMap}->{'host'}[$i]); | |
# Execute the parameterised qstat. | |
# TODO: Parameterise the binary path such that all known instances are taken into account qstat, quakestat, qstat.exe (CAREFUL: DO NOT OPEN A SECURITY HOLE HERE) | |
$server = `qstat -Ts ${fileName}Ts.txt -Tp ${fileName}Tp.txt -carets -retry 1 -maxsim 25 $paramsMap{'blockPlayers'} ${paramsMap{'sortType'}} -cn -${paramsMap{'gameType'}} @{ ${paramsMap}->{'host'} }[${i}]`; | |
# Parse the output, and build the result. | |
COLUMN: while ($server =~ /(.*)\n/gx) { | |
$string = ""; | |
$rowCount++; | |
$columnCount = 0; | |
# Conduct player searching. | |
if ($paramsMap{'searchPlayer'} ne "") { | |
# Split the output fields | |
for (split('___', $1)) { | |
# Only perform the following garbage on the name column. | |
# Or, if they're searching with -b, then search server names. | |
if (($columnCount == 1 && $paramsMap{'blockPlayers'} eq "-P") || ($columnCount == 4 && $paramsMap{'blockPlayers'} eq "")) { | |
# TODO: Allow for multiple name searching. | |
# Sanitise variables to make comparison fair and get rid of bloody special chars (@, [, ], \, *) | |
$sanitisedName = lc(&sanitizeInput(color_dp2none($_))); | |
$sanitisedSearch = lc(&sanitizeInput(color_dp2none($paramsMap{'searchPlayer'}))); | |
&debug("qstat-> We're past the 1st row and should be parsing playername as $sanitisedName"); | |
# Perform a generalised query using standardised query syntax (prae* or prae?) | |
# TODO: Open this up to more perl regexes. | |
if (rindex($string, '?') > -1 || rindex($string, '*') > -1) { | |
if ($_ =~ $paramsMap{'searchPlayer'}) { | |
$string .= "\t ".&checkColours($_); | |
} else { | |
$string = ""; | |
next COLUMN; | |
} | |
} else { | |
# If we don't have any query params in the -p parameter, then just do a standard | |
# rindex against the sanitised inputs. | |
if (rindex($sanitisedName, $sanitisedSearch) > -1) { | |
&debug("qstat-> We've rindex'd $_ to $paramsMap{'searchPlayer'}!"); | |
$string .= "\t ".&checkColours($_); | |
} else { | |
$string = ""; | |
next COLUMN; | |
} | |
} | |
} else { | |
# Everything else is standard fare. | |
# Strip back to standard colourisation. | |
$string .= "\t\017$_"; | |
} | |
$columnCount++; | |
} | |
$result .= $string; | |
push(@messageArray, $string); | |
} else { | |
# Split the output fields | |
for (split('___', $1)) { | |
# Only colour the name column. | |
if ($columnCount == 1) { | |
$string .= "\t ".&checkColours($_); | |
} else { | |
# Strip back to standard colourisation. | |
$string .= "\t\017 $_"; | |
} | |
if ($columnCount >= 2) { | |
$columnCount = 0; | |
} else { | |
$columnCount++; | |
} | |
} | |
$string .= "\n"; | |
push(@messageArray, $string); | |
$result .= $string; | |
} | |
} | |
} | |
# Inform the user that nothing was found. | |
if ($result eq "") { | |
&debug("qstat-> result: $result"); | |
push(@messageArray, "No results."); | |
} else { | |
# If the output limit is greater than the actual result set size, just use the result set size. | |
if ($paramsMap{'outputLimit'} > @messageArray || $paramsMap{'outputLimit'} == 0) { | |
$paramsMap{'outputLimit'} = @messageArray; | |
&debug("qstat-> outputLimit: $paramsMap{'outputLimit'}"); | |
} | |
unshift(@messageArray, "Here are ".$::who."'s ".($paramsMap{'outputLimit'} ne "" ? $paramsMap{'outputLimit'}." results of ".(@messageArray) : (@messageArray-1)." results").($paramsMap{'searchPlayer'} ne "" ? " for ".&checkColours($paramsMap{'searchPlayer'}."\017") : "").": "); | |
} | |
# Reverse the array so that output comes out in the right order due to using pop(). | |
@messageArray = reverse(@messageArray); | |
$messageCount = $paramsMap{'outputLimit'} ne "" ? $paramsMap{'outputLimit'} : @messageArray; | |
&debug("qstat-> Our message array is: @messageArray"); | |
&debug("qstat-> Our message count is: $messageCount"); | |
# Tell the channel that they won't be spammed, if they're trying to spam. | |
if (($messageCount > 10 && $paramsMap{'publicMessage'} ne '' && $paramsMap{'publicMessage'} eq "true") && $paramsMap{'override'} eq "false") { | |
&performMessage("Output is greater than 10 entries, $::who, PMing you the results so we don't bother these fine folks."); | |
$paramsMap{'publicMessage'} = $::who; | |
} | |
# Loop and perform the messages. This allows us to limit the output, redirect the output, if too long, etc. | |
for ($i = 0; $i<=$messageCount; $i++) { | |
if ($messageCount >= 10 && $i > 5) { | |
&performMessage(pop(@messageArray)); | |
sleep(2); | |
} else { | |
&performMessage(pop(@messageArray)); | |
} | |
} | |
# Clean up. | |
unlink("${fileName}Tp.txt"); | |
unlink("${fileName}Ts.txt"); | |
return $result; | |
} | |
# Tokenize parameters to accommodate indifference to order | |
sub qstat::tokenizeParams { | |
# Split the parameters on their hyphen. | |
my @params = split(/(^-)|(\s+-)/, $_[0]); | |
my $paramCount = @params; | |
# Loop over the passed in parameters and place them in the map. | |
# NOTE: $i starts at 1 due to extemporaneous output coming back from the hyphen split. | |
for ($i = 1; $i<$paramCount; $i++) | |
{ | |
switch (substr($params[$i], 0, 1)) { | |
# If hostname is specified, put in the scalar, which could simply be a single entity. | |
case "h" { &debug("qstat-> proposed host: ".substr($params[$i], 2, length $params[$i])); ${paramsMap}->{'host'} = [ split(/,/,&trim(substr($params[$i], 2, length $params[$i]))) ]; } | |
case "g" { &debug("qstat-> proposed gameType: ".substr($params[$i], 2, length $params[$i])); $paramsMap{'gameType'} = &trim(substr($params[$i], 2, length $params[$i])); } | |
case "p" { &debug("qstat-> proposed searchPlayer: ".color_dp2none(substr($params[$i], 2, length $params[$i]))); $paramsMap{'searchPlayer'} = &trim(substr($params[$i], 2, length $params[$i])); } | |
case "l" { &debug("qstat-> proposed outputLimit: ".substr($params[$i], 2, length $params[$i])); $paramsMap{'outputLimit'} = &trim(substr($params[$i], 2, length $params[$i])); } | |
case "d" { &debug("qstat-> proposed debug: true "); $paramsMap{'debug'} = "true"; } | |
case "o" { &debug("qstat-> proposed override: true "); $paramsMap{'override'} = "true"; } | |
case "b" { &debug("qstat-> proposed blockPlayers: true "); $paramsMap{'blockPlayers'} = "true"; } | |
# If messaging, place in the name, otherwise place in true so that we know to message the channel (blank -m param; not the same as -m missing) | |
case "m" { | |
if (substr($params[$i], 2, length $params[$i]) eq "") { | |
$paramsMap{'publicMessage'} = "true"; | |
} else { | |
$paramsMap{'publicMessage'} = &trim(substr($params[$i], 2, length $params[$i])); | |
} | |
&debug("qstat-> proposed publicMessage: $paramsMap{'publicMessage'}"); | |
} | |
case "a" { | |
if (substr($params[$i], 2, length $params[$i]) eq "") { | |
$paramsMap{'addAlias'} = "true"; | |
} else { | |
$paramsMap{'addAlias'} = &trim(substr($params[$i], 2, length $params[$i])); | |
} | |
&debug("qstat-> proposed addAlias: $paramsMap{'addAlias'}"); | |
} | |
case "e" { | |
if (substr($params[$i], 2, length $params[$i]) eq "") { | |
$paramsMap{'editAlias'} = "true"; | |
} else { | |
$paramsMap{'editAlias'} = &trim(substr($params[$i], 2, length $params[$i])); | |
} | |
&debug("qstat-> proposed addAlias: $paramsMap{'addAlias'}"); | |
} | |
case "r" { | |
if (substr($params[$i], 2, length $params[$i]) eq "") { | |
$paramsMap{'removeAlias'} = "true"; | |
} else { | |
$paramsMap{'removeAlias'} = &trim(substr($params[$i], 2, length $params[$i])); | |
} | |
&debug("qstat-> proposed addAlias: $paramsMap{'addAlias'}"); | |
} | |
case "s" { &debug("qstat-> proposed sortType: ".substr($params[$i], 2, length $params[$i])); $paramsMap{'sortType'} = &trim(substr($params[$i], 2, length $params[$i])); } | |
case "c" { &debug("qstat-> proposed checkAlias: ".substr($params[$i], 2, length $params[$i])); $paramsMap{'checkAlias'} = &trim(substr($params[$i], 2, length $params[$i])); } | |
else {} | |
} | |
} | |
} | |
# Trims trailing spaces | |
sub qstat::trim { | |
my ($returnVar) = @_; | |
$returnVar =~ s/\s+$//; | |
return $returnVar; | |
} | |
# Returns an escaped version of the input. | |
sub qstat::sanitizeInput { | |
my ($returnVar) = @_; | |
if ($returnVar =~ /([\[\$\#\@\\\]])/gx) | |
{ | |
$returnVar =~ s/([\[\$\#\@\\\]])/\\$1/g ; | |
} | |
&debug("qstat-> escaped variable: $returnVar"); | |
return $returnVar; | |
} | |
# Enforces required fields (gameType, etc) to have defaults | |
# Also checks field dependencies (if searching, don't use host, etc) | |
# TODO: Player searching, master server translation, etc, etc, etc, etc. | |
# TODO: Use the master list at http://d3.jpn.org/wiki/GyaASE:Master_Servers to grab a default list for mservers. | |
# Code stub. | |
sub qstat::populateRequiredFields { | |
# Set a default game type. | |
if ($paramsMap{'gameType'} eq "") { | |
$paramsMap{'gameType'} = "nexuizs"; | |
} elsif (rindex($paramsMap{'gameType'}, 'nexuiz') == -1) { | |
# Append ,10 as per suggestions @ http://d3.jpn.org/wiki/GyaASE:Master_Servers | |
$paramsMap{'gameType'} .= ",10"; | |
} | |
# If override has not been specified, give it a default value. | |
if ($paramsMap{'override'} eq "") { | |
$paramsMap{'override'} = "false"; | |
} | |
# Default the output limit to 10. Accommodate all and convert it to a integer. | |
if ($paramsMap{'outputLimit'} eq "") { | |
$paramsMap{'outputLimit'} = 10; | |
} elsif ($paramsMap{'outputLimit'} eq "all") { | |
$paramsMap{'outputLimit'} = 0; | |
} | |
# If they left off -b, then use -P. | |
if ($paramsMap{'blockPlayers'} ne "true") { | |
$paramsMap{'blockPlayers'} = "-P"; | |
} else { | |
$paramsMap{'blockPlayers'} = ""; | |
} | |
# Allow usage of -h and -g | |
$result = rindex($paramsMap{'editAlias'}, '='); | |
if ($result == -1 && $paramsMap{'editAlias'} ne "") { | |
&debug("qstat-> editAlias is only a single param"); | |
if (${paramsMap}->{'host'} ne "") { | |
$result = rindex(${paramsMap}->{'host'}[0], '|'); | |
&debug(${paramsMap}->{'host'}[0]); | |
$paramsMap{'editAlias'} .= "=".${paramsMap}->{'host'}[0]; | |
if ($result == -1) { | |
&debug("qstat-> host did not contain '|'"); | |
$paramsMap{'editAlias'} .= "|".$paramsMap{'gameType'}; | |
} | |
} | |
} elsif (exists $paramsMap{'editAlias'} && rindex($paramsMap{'editAlias'}, '|') == -1){ | |
$paramsMap{'editAlias'} .= "|".$paramsMap{'gameType'}; | |
} | |
# Allow usage of -h and -g | |
$result = rindex($paramsMap{'addAlias'}, '='); | |
# Only perform this if they're not checking an alias and if they've actually defined an alias name without an "=" | |
if ($result == -1 && ($paramsMap{'checkAlias'} ne "true" && $paramsMap{'addAlias'} ne "true" && exists $paramsMap{'addAlias'})) { | |
&debug("qstat-> addAlias is only a single param"); | |
if (exists ${paramsMap}->{'host'}) { | |
$result = rindex(${paramsMap}->{'host'}[0], '|'); | |
$paramsMap{'addAlias'} .= "=".${paramsMap}->{'host'}[0]; | |
if ($result == -1) { | |
&debug("qstat-> host did not contain '|'"); | |
$paramsMap{'addAlias'} .= "|".$paramsMap{'gameType'}; | |
} | |
} | |
} elsif (exists $paramsMap{'addAlias'} && rindex($paramsMap{'addAlias'}, '|') == -1){ | |
$paramsMap{'addAlias'} .= "|".$paramsMap{'gameType'}; | |
} | |
&debug("qstat-> editAlias rindex: ".(rindex($paramsMap{'editAlias'}, '='))); | |
&debug("qstat-> addAlias rindex: ".(rindex($paramsMap{'addAlias'}, '='))); | |
# Map the sortType to a valid qstat parameter. | |
$sortType = lc(&trim($paramsMap{'sortType'})); | |
if ($sortType eq "ping") { | |
$paramsMap{'sortType'} = "-sort P"; | |
} elsif ($sortType eq "frags") { | |
$paramsMap{'sortType'} = "-sort F"; | |
} elsif ($sortType eq "team") { | |
$paramsMap{'sortType'} = "-sort T"; | |
} elsif ($sortType eq "name") { | |
$paramsMap{'sortType'} = "-sort N"; | |
} elsif ($sortType eq "serverping") { | |
$paramsMap{'sortType'} = "-sort p"; | |
} elsif ($sortType eq "servergame") { | |
$paramsMap{'sortType'} = "-sort g"; | |
} elsif ($sortType eq "serverip") { | |
$paramsMap{'sortType'} = "-sort i"; | |
} elsif ($sortType eq "serverhost") { | |
$paramsMap{'sortType'} = "-sort h"; | |
} elsif ($sortType eq "serverplayers") { | |
$paramsMap{'sortType'} = "-sort n"; | |
} else { | |
$paramsMap{'sortType'} = ""; | |
} | |
#if ($paramsMap{'addAlias'} ne "" && $paramsMap{'addAlias'} ne "true") { | |
#} | |
} | |
# Wrap messaging so that we can call one standard method. | |
sub qstat::performMessage { | |
# Do we msg the channel, or do we private message the peep? | |
if ($paramsMap{'publicMessage'} ne "") { | |
if ($paramsMap{'publicMessage'} eq "true") { | |
&debug("qstat-> Channel: $::talkchannel, Message: @_"); | |
&::msg($::talkchannel, @_); | |
} else { | |
&debug("qstat-> -m'd person: $paramsMap{'publicMessage'}, Message: @_"); | |
&::msg($paramsMap{'publicMessage'}, @_); | |
} | |
} else { | |
&debug("qstat-> Requestor: $::who, Message: @_"); | |
&::msg($::who, @_); | |
} | |
} | |
# Dynamically manipulates qstat's template | |
# Hopefully this method can be removed such that we can reliably use -raw '___' to get output. | |
sub qstat::generateTemplates { | |
# Perform a poor man's hash so that multiple operations don't obliterate the generated file. | |
my $hashFileName = rand(); | |
my $serverTemplate = "$::param{'tempDir'}/${hashFileName}Ts.txt"; | |
my $playerTemplate = "$::param{'tempDir'}/${hashFileName}Tp.txt"; | |
&debug("qstat-> hashFileName: ${hashFileName}"); | |
&debug("qstat-> serverTemplate: ${serverTemplate}"); | |
&debug("qstat-> playerTemplate: ${playerTemplate}"); | |
# Open the files. | |
if ( !open(OUTS, ">$serverTemplate")) { | |
&::ERROR("qstat-> cannot open ${serverTemplate}"); | |
} | |
if ( !open(OUTP, ">$playerTemplate")) { | |
&::ERROR("qstat-> cannot open ${playerTemplate}"); | |
} | |
# Perform some special formatting per the type of stuff we're doing. | |
# Currently only searchPlayer is a special case (so that hostname and user are on same line). | |
#if ($paramsMap{'searchPlayer'} ne "" && $paramsMap{'gameType'} eq 'nexuizs') { | |
# print OUTS "\$PLAYERTEMPLATE"; | |
# print OUTP "\$HOSTNAME:\$PORT___\$PLAYERNAME___\$PLAYERPING___\$FRAGS\n"; | |
#} elsif ($paramsMap{'searchPlayer'} ne "") { | |
if ($paramsMap{'searchPlayer'} ne "" && $paramsMap{'blockPlayers'} eq "-P") { | |
print OUTS "\$PLAYERTEMPLATE"; | |
print OUTP "\$HOSTNAME___\$PLAYERNAME___\$PLAYERPING___\$FRAGS\n"; | |
} else { | |
print OUTS "\$PING___\$PLAYERS/\$MAXPLAYERS___\$MAP___\$HOSTNAME___\$SERVERNAME___\n\$PLAYERTEMPLATE"; | |
print OUTP "\$PLAYERPING___\$PLAYERNAME___\$FRAGS\n"; | |
} | |
# Close the files. | |
close OUTS; | |
close OUTP; | |
return "$::param{'tempDir'}/$hashFileName"; | |
} | |
# Shall we debug? Woot. | |
sub qstat::debug { | |
if ($paramsMap{'debug'} eq "true") { | |
&::status(@_); | |
} | |
} | |
# Handles reading aliases from the persistent alias file. | |
sub qstat::readAliases { | |
my ($count, $recordCount) = 0; | |
my $found = "false"; | |
# Try opening the file for reading. | |
if ( !open ROFILE, $aliasFile ) { | |
# Try creating the file. | |
if ( !open(ROFILE, ">$aliasFile")) { | |
&::ERROR("qstat-> cannot open ${aliasFile}"); | |
return ""; | |
} | |
} | |
# Read in, line-by-line. | |
READ: while (<ROFILE>) { | |
$recordCount++; | |
$count = 0; | |
# Retain the line item we're parsing. | |
$lineItem = $_; | |
# Skip if it's commented. | |
next if /^$/; | |
next if /^#/; | |
# Don't do it if we've found the record, because we shouldn't be allowed to have duplicate keys. | |
if ($found eq "false") { | |
for (split('=', $lineItem)) { | |
if($_ ne $paramsMap{'checkAlias'} && $count == 0 && $found eq "false") { | |
next READ; | |
} elsif ($found eq "true") { | |
# TODO: Add a check in here to split the value string on | to allow aliasing gameType. | |
&debug("qstat-> Found the alias $paramsMap{'checkAlias'}. "); | |
# Split the alias on | in case they've attached a gameType. | |
@tempArray = split(/\|/,&trim($_)); | |
# Place the array into the paramsMap for host. | |
${paramsMap}->{'host'} = [ split(/,/,&trim($tempArray[0])) ]; | |
# This is to accommodate special characters in the |gameType addition. | |
if (split('=', $lineItem) > 1) { | |
@tempArray = split(/\|/,$lineItem); | |
&debug("qstat-> tempArray[0]: $tempArray[0]"); | |
} | |
# If they have attached a gameType set them up. | |
if ($tempArray[1] ne '' && $paramsMap{'editAlias'} eq "") { | |
&debug("qstat-> tempArray[1]: $tempArray[1]"); | |
$paramsMap{'gameType'} = &trim($tempArray[1]); | |
} | |
# Skip to the last record, as we're done reading the file. | |
last READ; | |
} elsif ($_ eq $paramsMap{'checkAlias'} && $count == 0) { | |
# Set the flag so the next pass will parse the alias value. | |
$found = "true"; | |
} | |
$count++; | |
} | |
} | |
} | |
close ROFILE; | |
return $found; | |
} | |
# Writes aliases to the storage file. | |
sub qstat::writeAliases { | |
my $count = 1; | |
# Open the file for reading if we're editing. Note that we should pass a param | |
# into &readAliases() to handle this in the future. For now, this hack'll suffice. | |
if (($paramsMap{'editAlias'} ne "" || $paramsMap{'removeAlias'} ne "true") && (exists $paramsMap{'editAlias'} || exists $paramsMap{'removeAlias'})) { | |
# Setup a new temp file. | |
$aliasFileNew = $aliasFile.".tmp.".rand(); | |
if (!open(ROFILE, "$aliasFile")) { | |
&::ERROR("qstat-> cannot open ${aliasFile}"); | |
return ""; | |
} | |
# Try creating to the file. | |
if ( !open(RWFILE, "> $aliasFileNew")) { | |
&::ERROR("qstat-> cannot open ${aliasFileNew}"); | |
return ""; | |
} | |
while(<ROFILE>) { | |
if (m/^$paramsMap{'checkAlias'}/) { | |
$serverCount = split(/,/,&trim(@{ [ split('=',$paramsMap{'editAlias'}) ] }[1])); | |
if (!exists $paramsMap{'removeAlias'}) { | |
$newServerList = @{ [ split(/\|/,&trim(@{ [ split('=',$paramsMap{'editAlias'}) ] }[1])) ] }[0]; | |
$currentServerList = @{ [ split(/\|/,&trim(@{ [ split('=',$_) ] }[1])) ] }[0].','.$newServerList; | |
$paramsMap{'editAlias'} = $paramsMap{'checkAlias'}."=".$currentServerList."|".$paramsMap{'gameType'}."\n"; | |
&performMessage("Appended ".$serverCount." server".($serverCount == 1 ? "" : "s").($newServerList ne "" ? " and changed game type to ".$paramsMap{'gameType'} : "")." to $paramsMap{'checkAlias'}, $::who."); | |
} elsif ($paramsMap{'removeAlias'} eq "true") { | |
$paramsMap{'editAlias'} .= "\n"; | |
&performMessage("Replaced ".$paramsMap{'checkAlias'}." with ".$serverCount." server".($serverCount == 1 ? "" : "s")." and game type ".$paramsMap{'gameType'}.", $::who."); | |
} elsif (exists $paramsMap{'removeAlias'} && $paramsMap{'removeAlias'} ne "true") { | |
$paramsMap{'editAlias'} = ""; | |
&performMessage("The alias $paramsMap{'checkAlias'} was removed, $::who."); | |
} | |
print RWFILE $paramsMap{'editAlias'}; | |
} else { | |
print RWFILE $_; | |
} | |
} | |
close ROFILE; | |
close RWFILE; | |
unlink($aliasFile); | |
rename($aliasFileNew, $aliasFile); | |
return ""; | |
} else { | |
# Try appending to the file. | |
if ( !open(RWFILE, ">>$aliasFile")) { | |
# Try creating to the file. | |
if ( !open(RWFILE, ">$aliasFile")) { | |
&::ERROR("qstat-> cannot open ${aliasFile}"); | |
return ""; | |
} | |
} | |
} | |
if ($paramsMap{'editAlias'} eq "") { | |
# Write the suggested alias. | |
print RWFILE $paramsMap{'addAlias'}."\n"; | |
&debug("qstat-> Added $paramsMap{'addAlias'} to $aliasFile by $::who"); | |
} | |
# Close the file. | |
close RWFILE; | |
# Break apart the serverlist that they added. | |
@serverList = split(/,/,&trim(@{ [ split('=',$paramsMap{'addAlias'}) ] }[1])); | |
for (split('=', $paramsMap{'addAlias'})) { | |
if($count == 1) { | |
# First time through, tell them they've aliased # of servers. | |
&debug(@{ [ split(/\|/,&trim(@{ [ split('=',$paramsMap{'addAlias'}) ] }[1])) ] }[1]); | |
&performMessage("Aliased ".@serverList." server".(@serverList == 1 ? "" : "s").(@{ [ split(/\|/,&trim(@{ [ split('=',$paramsMap{'addAlias'}) ] }[1])) ] }[1] ne "" ? " of game type ".@{ [ split(/\|/,&trim(@{ [ split('=',$paramsMap{'addAlias'}) ] }[1])) ] }[1] : "")." to $_, $::who."); | |
# Deletion. This is just annoying. | |
#} else { | |
# # Tell them the servers they aliased. | |
# for ($i = 0; $i<@serverList; $i++) { | |
# &performMessage($serverList[$i]); | |
# } | |
} | |
$count++; | |
} | |
} | |
# Performs relevant alias related routines. | |
# Will return a value if something is supposed to break main execution. | |
sub qstat::checkAliases { | |
# If they're trying to use an alias to query, check for its existence. | |
if ($paramsMap{'checkAlias'} ne "" && $paramsMap{'addAlias'} eq "" && !exists $paramsMap{'removeAlias'} && !exists $paramsMap{'editAlias'}) { | |
# Let them know if it wasn't found and break execution. | |
if (&readAliases() eq "false") { | |
&debug("qstat-> The alias $paramsMap{'checkAlias'} was not found."); | |
&performMessage("The alias $paramsMap{'checkAlias'} was not found, $::who"); | |
return "break"; | |
} | |
# If they're trying to check for an alias's existence, tell them the relevant status. | |
} elsif ($paramsMap{'checkAlias'} ne "" && $paramsMap{'addAlias'} ne "" && !exists $paramsMap{'removeAlias'} && !exists $paramsMap{'editAlias'} && exists $paramsMap{'addAlias'} && exists $paramsMap{'checkAlias'}) { | |
if (&readAliases() eq "true") { | |
&performMessage("The alias $paramsMap{'checkAlias'} has already been defined, $::who. You may edit it with -e $paramsMap{'checkAlias'}=host1:port,host2:port,hostn:port."); | |
} else { | |
&performMessage("The alias $paramsMap{'checkAlias'} is available, $::who. Please see help qstat -e for more information on editing/removing it."); | |
} | |
return "break"; | |
# If they're trying to add an alias, check the relevant alias first, then do the corresponding action. | |
} elsif ($paramsMap{'addAlias'} ne "" && $paramsMap{'addAlias'} ne "true" && !exists $paramsMap{'removeAlias'} && !exists $paramsMap{'editAlias'}) { | |
$paramsMap{'checkAlias'} = @{ [ split('=',$paramsMap{'addAlias'}) ] }[0]; | |
&debug("qstat-> checkAlias: $paramsMap{'checkAlias'}"); | |
&debug("qstat-> readAliases(): ".&readAliases()); | |
if (&readAliases() eq "true") { | |
&performMessage("The alias $paramsMap{'checkAlias'} has already been defined, $::who. Please see help qstat -e for more information on editing/removing it."); | |
} else { | |
&writeAliases(); | |
} | |
return "break"; | |
# If they specified nothing for adding. | |
} elsif (exists $paramsMap{'removeAlias'} && $paramsMap{'removeAlias'} ne "true") { | |
$paramsMap{'checkAlias'} = $paramsMap{'removeAlias'}; | |
$paramsMap{'editAlias'} = "\n"; | |
if (&readAliases() eq "true") { | |
&writeAliases(); | |
} else { | |
&performMessage("The alias $paramsMap{'checkAlias'} was not found, and so it could not be replaced, $::who."); | |
} | |
return "break"; | |
# If they want to replace the alias. | |
} elsif ($paramsMap{'editAlias'} ne "" && $paramsMap{'removeAlias'} eq "true") { | |
$paramsMap{'checkAlias'} = @{ [ split('=',$paramsMap{'editAlias'}) ] }[0]; | |
if (&readAliases() eq "true") { | |
&writeAliases(); | |
} else { | |
$paramsMap{'addAlias'} = $paramsMap{'editAlias'}; | |
&writeAliases(); | |
} | |
return "break"; | |
# If they want to append something (gameType or a couple of servers) to the alias name. | |
} elsif (exists $paramsMap{'editAlias'} && $paramsMap{'editAlias'} ne "") { | |
$paramsMap{'checkAlias'} = @{ [ split('=',$paramsMap{'editAlias'}) ] }[0]; | |
if (&readAliases() eq "true") { | |
&writeAliases(); | |
} else { | |
# Set the addAlias to be editAlias and clear the edit alias so that | |
# standard writing can move forward. | |
$paramsMap{'addAlias'} = $paramsMap{'editAlias'}; | |
$paramsMap{'editAlias'} = ""; | |
&performMessage("The alias $paramsMap{'checkAlias'} was not found, $::who. Creating a new one for you."); | |
&writeAliases(); | |
} | |
return "break"; | |
} | |
} | |
# For darkplaces, based on div0's colour parsing table. | |
# Generic wrapper. This should be expanded to accommodate other common game types. | |
sub qstat::checkColours { | |
# Todo: Map out common gametypes, like a2s, etc, to handle those colours. | |
my ($message) = @_; | |
if ($paramsMap{'gameType'} eq 'nexuizs' || $paramsMap{'gameType'} eq 'nexuizm') { | |
return color_dp2irc($message); | |
} else { | |
return $message; | |
} | |
} | |
# Ripped straight from divVerent's excellent rcon2irc script. | |
sub color_dp_transform(&$) | |
{ | |
my ($block, $message) = @_; | |
$message =~ s{(?:(\^\^)|\^x([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])|\^([0-9])|(.))(?=([0-9,]?))}{ | |
defined $1 ? $block->(char => '^', $7) : | |
defined $2 ? $block->(rgb => [hex $2, hex $3, hex $4], $7) : | |
defined $5 ? $block->(color => $5, $7) : | |
defined $6 ? $block->(char => $6, $7) : | |
die "Invalid match"; | |
}esg; | |
return $message; | |
} | |
sub color_dp2irc($) | |
{ | |
my ($message) = @_; | |
my $color = -1; | |
return color_dp_transform | |
{ | |
my ($type, $data, $next) = @_; | |
if($type eq 'rgb') | |
{ | |
$type = 'color'; | |
$data = color_rgb2basic($data); | |
} | |
$type eq 'char' ? $text_qfont_table[ord $data] : | |
$type eq 'color' ? do { | |
my $oldcolor = $color; | |
$color = $color_dp2irc_table[$data]; | |
$color == $oldcolor ? '' : | |
$color < 0 ? "\017" : | |
$next eq ',' ? "\003$color\002\002" : | |
sprintf "\003%02d", $color; | |
} : | |
die "Invalid type"; | |
} | |
$message; | |
} | |
sub color_dp2none($) | |
{ | |
my ($message) = @_; | |
return color_dp_transform | |
{ | |
my ($type, $data, $next) = @_; | |
$type eq 'char' | |
? $text_qfont_table[ord $data] | |
: ""; | |
} | |
$message; | |
} | |
sub color_rgb2basic($) | |
{ | |
my ($data) = @_; | |
my ($R, $G, $B) = @$data; | |
my $min = [sort { $a <=> $b } ($R, $G, $B)]->[0]; | |
my $max = [sort { $a <=> $b } ($R, $G, $B)]->[-1]; | |
my $v = $max / 15; | |
my $s = ($max == $min) ? 0 : 1 - $min/$max; | |
if($s < 0.2) | |
{ | |
return 0 if $v < 0.5; | |
return 7; | |
} | |
my $h; | |
if($max == $min) | |
{ | |
$h = 0; | |
} | |
elsif($max == $R) | |
{ | |
$h = (60 * ($G - $B) / ($max - $min)) % 360; | |
} | |
elsif($max == $G) | |
{ | |
$h = (60 * ($B - $R) / ($max - $min)) + 120; | |
} | |
elsif($max == $B) | |
{ | |
$h = (60 * ($R - $G) / ($max - $min)) + 240; | |
} | |
return 1 if $h < 36; | |
return 3 if $h < 80; | |
return 2 if $h < 150; | |
return 5 if $h < 200; | |
return 4 if $h < 270; | |
return 6 if $h < 330; | |
return 1; | |
} | |
sub color_dp_rgb2basic($) | |
{ | |
my ($message) = @_; | |
return color_dp_transform | |
{ | |
my ($type, $data, $next) = @_; | |
$type eq 'char' ? ($data eq '^' ? '^^' : $data) : | |
$type eq 'color' ? "^$data" : | |
$type eq 'rgb' ? "^" . color_rgb2basic $data : | |
die "Invalid type"; | |
} | |
$message; | |
} | |
1; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment