XCoreCommands.pm


package XSTAB::RTCW::XCoreCommands;
# RTCW core command module
# By default, RTCW doesn't log much of interest,
# So you'll probably want to install Shrub and
# use the XShrub module instead
#
# $Id: XCoreCommands.pm,v 1.14 2003/03/27 04:50:22 dranok Exp $
#
# This module is released under the GPL
# Author: DranoK
# Email: dranok@users.sourceforge.net
# Documentation: http://xstab.sourceforge.net
################################################


NAME

XSTAB::RTCW::XCoreCommands


DESCRIPTION

This module contains admin and player commands used by RTCW::XCore


INITIALIZATION

use IO::File;
use XSTAB::XData;
use strict;

my $VERSION = 0.9;

# Command list hash
# NOTE: VICTIM IS **ALWAYS** ARGUMENT 1!!!
my %Commands;
%Commands = ( 	"kick" => { "admin_flag" => 'k',
			"function" => \&_standard_say,
			"num_args" => 1,
			"id" => 'client',
			"translate" => 'clientkick',
			"protected" => 1,
			"help" => "kick <name|pbid> -- Kicks target player",
			"enabled" => 1},
		"grant" => { "admin_flag" => 'z',
			"function" => \&_grant_say,
			"num_args" => 2,
			"help" => "grant <name|pbid> <flag> -- Grants temporary admin access",
			"enabled" => 1},
		"delete" => { "admin_flag" => 'd',
			"function" => \&_delete_say,
			"num_args" => 1,
			"help" => "delete <name|pbid> -- Deletes target's record",
			"enabled" => 1},
		"ban" => { "admin_flag" => 'b',
			"function" => \&_standard_say,
			"num_args" => 1,
			"id" => 'pb',
			"translate" => 'pb_sv_kick',
			"options" => '60',
			"protected" => 1,
			"help" => "ban <name|pbid> -- Bans target player for 60 minutes",
			"enabled" => 1},
		"permaban" => { "admin_flag" => 'p',
			"function" => \&_standard_say,
			"num_args" => 1,
 			"id" => 'pb',
			"translate" => 'pb_kick',
			"protected" => 1,
			"help" => "permaban <name|pbid> -- Bans player...Forever",
			"enabled" => 1},		
		"nextmap" => { "admin_flag" => 'n',
			"function" => \&_standard_say,
			"num_Args" => 0,
			"id" => 'world',
			"translate" => 'vstr',
			"options" => 'nextmap',
			"help" => "nextmap -- Warps to next map",
			"enabled" => 1},
		"skipwarmup" => { "admin_flag" => 'n',
			"function" => \&_standard_say,
			"num_args" => 0,
			"id" => 'world',	
			"translate" => 'g_warmup',
			"options" => '5',
			"help" => "skipwarmup -- Sets warmup to 5 seconds",
			"enabled" => 1},
		"endlesswarmup" => { "admin_flag" => 'n',
			"function" => \&_standard_say,
			"num_args" => 0,
			"id" => 'world',
			"translate" => 'g_warmup',
			"options" => '1000',
			"help" => "endlesswarmup -- Sets warmup to 1000 seconds",
			"enabled" => 1},
		"reset" => { "admin_flag" => 'r',
			"function" => \&_standard_say,
			"num_args" => 0,
			"id" => 'world',
			"translate" => 'reset_match',
			"help" => "reset -- Resets match",
			"enabled" => 1}
		);


PUBLIC METHODS

create ()
This constructor will return the blessed object, after reading the admin list.
sub create
{
  my $class = shift;

  my $self = { };
  $self->{chatcmd} = 'say';
  $self->{admins} = { };
  
  # Need to load admin stuff
  my $tmpbuf;
  my $fileh = IO::File->new($Global{Admin_list}) or die "Cannot open $Global{Admin_list}: $!\n";
  $fileh->sysread($tmpbuf, 65507);
  $fileh->close();
  
  my @tmpar = split('\n', $tmpbuf);
  foreach my $elem (@tmpar) {
    #                 GUID       : flags
    if ($elem =~ /^([a-zA-Z0-9]+):([a-zA-Z0-9]+)/) {
      $self->{admins}{lc($1)} = $2;
      do_log("Adding admin: $elem", 2);
    }
  } 

  bless($self, $class);
  return $self;
}

add_admin_flags (int ClientID)
This method will add admin flags to players who connect.
sub add_admin_flags
{
  my $self = shift;
  my $client = shift;

  $Players{$client}{admin_flags} = $self->{admins}{$Players{$client}{guid}};
  $Players{$client}{admin_flags} .= 'w';
}

do_log (string Message, int Priority)
This is the overloaded do_log function. Purpose is to identify which module is logging the message
sub do_log
{
  my $string = shift;
  my $prio = shift;
  XSTAB::XData::do_log("RTCW::XCoreCommands: $string", $prio);
}

do_say (string Message)
This will push Message to the voice queue.
sub do_say
{   
  my $self = shift;
  my $text = shift;

  push(@Voice_queue, "75$Global{sp}$self->{chatcmd} [XStab] $text");
} 

do_cmd (string Command, int Priority)
This method will push Command into the voice queue with a priority of Priority
sub do_cmd
{
  my $self = shift;
  my $cmd = shift;
  my $prio = shift;

  push(@Voice_queue, "$prio$Global{sp}$cmd");
}

parse_command (int ClientID, string Command, array Args)
This method will parse a player's command, and (assuming they have permission) execute said command.
sub parse_command
{         
  my $self = shift;
  my $client = shift;
  my $cmdstring = shift;
          
  $cmdstring = lc($cmdstring);
  my @tokens = split(' ', $cmdstring);
  my $numargs = scalar(@tokens);
  $numargs--;

  if ($Commands{$tokens[0]}{enabled}) {
    if ($self->check_flags($Players{$client}{admin_flags}, $Commands{$tokens[0]}{admin_flag})) {
      if ($Players{$client}{r_commands} > 10) {
        $self->do_cmd("clientkick $client", 100);
        $self->do_say("$Players{$client}{rawname} was kicked for spamming commands.");
      } else {
        $Players{$client}{r_commands}++;
        my $victim = "";
        if ($Commands{$tokens[0]}{num_args}) {
          $victim = $self->find_player($self, $tokens[1]);
          if ($victim < 0) {
            $self->do_say("$Players{$client}{rawname}: Cannot find $tokens[1]");
            do_log("$Players{$client}{name} tried to issue $tokens[0] against $tokens[1], but $tokens[1] does not exist", 3);
            return;
          } 
        }
        if (($numargs == $Commands{$tokens[0]}{num_args}) || !$Commands{$tokens[0]}{num_args}) {
          my $time = time; 
          push(@Admin_queue, "$time$Global{sp}$Players{$client}{guid}$Global{sp}$Players{$victim}{guid}$Global{sp}$tokens[0]");
          &{$Commands{$tokens[0]}{function}}($self, $client, $victim, @tokens);
        } else {
          $self->do_say("$Players{$client}{rawname}: Incorrect number of arguments supplied for $tokens[0]");
          do_log("$Players{$client}{name} supplied incorrect number of arguments for $tokens[0]", 3);
        }
      }
    } else {
      do_log("$Players{$client}{name} does NOT have permission to call $tokens[0]", 3);
    }
  } else {
    do_log("$Players{$client}{name} tried to run $tokens[0], but it is not enabled", 4);
  }
}

check_flags (string Flags, char RequiredFlag)
This method check to see if Flags contains RequiredFlag. I must have been very stoned, or I would have just passed the ClientID instead of the player's fucking flags.
sub check_flags
{
  my $self = shift;
  my $client_flags = shift;
  my $required_flag = shift;
  
  if ($client_flags =~ /$required_flag/) {
    return 1;
  } elsif ($client_flags =~ /z/) {
    return 1;
  } else {
    return 0;
  }
}
  


PRIVATE METHODS

_standard_say (obj self, int AdminID, int VictimID, string Command, string unusedName, array Args)
This method will execute a generic command, based on the command hash info at the top of this file.
# NOTES: Standard say can only have one argument, the victim
sub _standard_say
{
  my $self = shift;
  my $admin = shift;
  my $victim = shift;
  my $command = shift;
  my $string = shift;
  
  my $tcmd = $Commands{$command}{translate};
  my $opts = $Commands{$command}{options};

  my $id = "";
  if ($Commands{$command}{id} eq 'client') {
    $id = $victim;
  } elsif ($Commands{$command}{id} eq 'pb') {
    $id = $Players{$victim}{pbid};
  }

  if ($Commands{$command}{protected}) {
    if ($self->check_flags($Players{$victim}{admin_flags}, 'P') && !$self->check_flags($Players{$admin}{admin_flags}, 'z')) {
      do_log("Cannot perform protected action on protected player $Players{$victim}{name}", 3);
    } else {
      $self->do_cmd("$tcmd $id $opts", 100);
    }
  } else {
    $self->do_cmd("$tcmd $id $opts", 100);
  }
}

find_player (int ClientID)
This method will find a player based on EITHER the PunkBuster ID or a player name (regex search) If more than one match is found, an error is returned (-1)
sub find_player
{
  my $self = shift;
  my $id = shift;

  my $ret;
  my $num;
  if ($id =~ /^\d+$/) {
    foreach my $elem (keys %Players) {
      if ($Players{$elem}{pbid} == $id) {
        $ret = $elem;
        $num++;
      }
    }
  } else {
    foreach my $elem (keys %Players) {
      if ($Players{$elem}{name} =~ /$id/i) {
        $ret = $elem;
        $num++;
      }
    }
  }
  ($num == 1) ? return $ret : return -1;
}

_grant_say (obj self, int AdminID, int VictimID, string Command, string unusedName, string Flags)
This method will TEMPORARILY grant Flags to VictimID. These flags will be reset when VictimID quits.
sub _grant_say
{
  my $self = shift;
  my $admin = shift;
  my $victim = shift;
  my $command = shift;
  my $string = shift;
  my $flag = shift;
  
  if (!$self->check_flags($Players{$victim}{admin_flags}, $flag)) {
    $Players{$victim}{admin_flags} .= "$flag";
    do_log("$Players{$victim}{name} now has flag $flag", 2);
    $self->do_say("$Players{$victim}{rawname} now has flag $flag");
  } 
}

_delete_say (obj self, int AdminID, int VictimID, string Command, string unusedName)
This method will delete VictimID's player record from the database.
sub _delete_say
{
  my $self = shift;
  my $admin = shift;
  my $victim = shift;
  my $command = shift;
  my $string = shift;

  do_log("Attempting to delete record $Players{$victim}{name}");
  $main::storage->delete_record($victim);
  $self->do_say("$Players{$victim}{rawname} has been deleted.");
  delete $Players{$victim};
} 

1;


AUTHOR

This module was coded by DranoK and is part of the core XStab modules. You may directly contact DranoK at dranok@users.sourceforge.net, or by posting to the forums at:

        http://www.oltl.net/forums/forumdisplay.php?s=&forumid=25