Browse Source

Add Arma BattlEye Support

Ryan Davies 8 năm trước cách đây
mục cha
commit
cbd879f6e6
2 tập tin đã thay đổi với 246 bổ sung18 xóa
  1. 198 0
      ArmaBE/ArmaBE.pm
  2. 48 18
      ogp_agent.pl

+ 198 - 0
ArmaBE/ArmaBE.pm

@@ -0,0 +1,198 @@
+# ArmaBE - Perl extension BattlEye ARMA Rcon interface
+# Original Source for BattlEye source - https://github.com/Jaegerhaus/BE-RCon-Tools
+#
+# $Id:$
+#
+
+package ArmaBE;
+
+use strict;
+use warnings;
+use IO::Socket::INET;
+
+# release version
+our $VERSION = "0.01";
+
+# create class
+sub new {
+	my $class = shift;
+
+	# create object with defaults
+	my $self = {
+		hostname	=> undef,
+		port		=> 27015,
+		password	=> undef,
+		timeout		=> 5,
+		connected	=> 0,
+		authenticated	=> 0,
+		socket		=> undef,
+		sequence	=> 0,
+	};
+
+	# create object
+	bless($self, $class);
+
+	# initialize class instances
+	$self->init();
+
+	# parse constructor args
+	while (my ($key, $val) = splice(@_, 0, 2)) {
+		$key = lc($key);
+		if    ($key eq "hostname") { $self->hostname($val) }
+		elsif ($key eq "port")     { $self->port($val)     }
+		elsif ($key eq "password") { $self->password($val) }
+		elsif ($key eq "timeout")  { $self->timeout($val)  }
+		else { print STDERR "Unknown attribute: $key\n" }
+	}
+
+	return $self;
+}
+
+# initialize class instances
+sub init {
+	my $self = shift;
+	my $class = ref($self);
+
+	# manipulate symbol table.. gotta love perl
+	no strict "refs";
+	no warnings;
+	foreach my $instance (keys %$self) {
+		*{"${class}::${instance}"} = sub {
+			my $self = shift;
+			my $value = shift;
+			my $ref = \$self->{$instance};
+			if (defined $value) {
+				$$ref = $value;
+				return $self;
+			} else {
+				return $$ref;
+			}
+		};
+	}
+}
+
+# run a command and return its response
+sub run {
+	my $self = shift;
+	my $command = shift;
+
+	if (!$self->connected()) {
+		$self->connect();
+	}
+
+	if (!$self->authenticated()) {
+		$self->authenticate();
+	}
+
+	if ($self->authenticated()) {
+		my $socket = $self->socket();
+		print $socket $self->packet("\1\0".$command);
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+# create tcp socket
+sub connect {
+	my $self = shift;
+
+	my $socket = IO::Socket::INET->new(
+		PeerAddr        => $self->hostname(),
+		PeerPort        => $self->port(),
+		Timeout		=> $self->timeout(),
+		Proto           => "udp",
+	) || die "Failed to connect: $!\n";
+
+	$self->socket($socket);
+	$self->connected(1);
+}
+
+# authenticate rcon session
+sub authenticate {
+	my $self = shift;
+
+	# send authentication packet to server
+	my $socket = $self->socket();
+	print $socket $self->packet("\0".$self->password());
+
+	my $response = $self->response();
+	my $authenticated = int(substr($response, -1));
+
+	$self->authenticated($authenticated);
+}
+
+######################
+# PROTOCOL FUNCTIONS #
+######################
+
+# rcon command protocol:
+# https://www.battleye.com/downloads/BERConProtocol.txt
+
+sub crc32 {
+	my ($self,$input,$init_value,$polynomial) = @_;
+
+	$init_value = 0 unless (defined $init_value);
+	$polynomial = 0xedb88320 unless (defined $polynomial);
+
+	my @lookup_table;
+
+	for (my $i=0; $i<256; $i++) {
+		my $x = $i;
+		for (my $j=0; $j<8; $j++) {
+			if ($x & 1) {
+				$x = ($x >> 1) ^ $polynomial;
+			} else {
+				$x = $x >> 1;
+			}
+		}
+		push @lookup_table, $x;
+	}
+
+	my $crc = $init_value ^ 0xffffffff;
+
+	foreach my $x (unpack ('C*', $input)) {
+		$crc = (($crc >> 8) & 0xffffff) ^ $lookup_table[ ($crc ^ $x) & 0xff ];
+	}
+
+	$crc = $crc ^ 0xffffffff;
+
+	return $crc;
+}
+
+# create a packet of type (AUTH or CMD)
+sub packet {
+	my $self = shift;
+	my $payload = shift;
+
+	my $break = pack('C', 0xff);
+	my $packet = "BE"
+		. pack('V', $self->crc32($break . $payload))
+		. $break
+		. $payload;
+
+	return $packet;
+}
+
+# receive packet
+sub response {
+	my $self = shift;
+	my $payload = $self->read();
+
+	return $payload;
+}
+
+# read length of bytes from socket with timeout
+sub read {
+	my $self = shift;
+	my $received;
+	my $socket = $self->socket();
+	
+	$socket->recv($received, 9);
+
+	return unpack('H*', $received);
+}
+
+1;
+
+__END__

+ 48 - 18
ogp_agent.pl

@@ -1215,6 +1215,23 @@ sub stop_server_without_decrypt
 			my $rconCommand = "quit";
 			$rcon2->run($rconCommand);
 		}
+		elsif ($control_protocol eq "armabe")
+		{
+			use ArmaBE::ArmaBE;
+			my $armabe = new ArmaBE(
+								  hostname => $server_ip,
+								  port	 => $server_port, # Uses server port for now (Arma 2), Arma 3 BE uses a different, user definable port
+								  password => $control_password,
+								  timeout  => 2
+								 );
+
+			my $rconCommand = "#shutdown";
+			my $armabe_result = $armabe->run($rconCommand);
+			if ($armabe_result) {
+				logger "ArmaBE Shutdown command sent successfully - Waiting 5s";
+				sleep(5); # Arma servers take some time to shut down
+			}
+		}
 		
 		if (is_screen_running_without_decrypt(SCREEN_TYPE_HOME, $home_id) == 0)
 		{
@@ -1336,24 +1353,37 @@ sub send_rcon_command
 			my $encoded_content = encode_list(@modedlines);
 			return "1;" . $encoded_content;
 		}
-		else
-		{		
-			if ($control_protocol eq "rcon2")
-			{
-				use KKrcon::HL2;
-				my $rcon2 = new HL2(
-									  hostname => $server_ip,
-									  port	 => $server_port,
-									  password => $control_password,
-									  timeout  => 2
-									 );
-													
-				logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n  .";
-						
-				my(@modedlines) = $rcon2->run($rconCommand);
-				my $encoded_content = encode_list(@modedlines);
-				return "1;" . $encoded_content;
-			}
+		elsif ($control_protocol eq "rcon2")
+		{
+			use KKrcon::HL2;
+			my $rcon2 = new HL2(
+								  hostname => $server_ip,
+								  port	 => $server_port,
+								  password => $control_password,
+								  timeout  => 2
+								 );
+			
+			logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n  .";
+					
+			my(@modedlines) = $rcon2->run($rconCommand);
+			my $encoded_content = encode_list(@modedlines);
+			return "1;" . $encoded_content;
+		}
+		elsif ($control_protocol eq "armabe")
+		{
+			use ArmaBE::ArmaBE;
+			my $armabe = new ArmaBE(
+								  hostname => $server_ip,
+								  port	 => $server_port, # Uses server port for now (Arma 2), Arma 3 BE uses a different, user definable port
+								  password => $control_password,
+								  timeout  => 2
+								 );
+			
+			logger "Sending RCON command to $server_ip:$server_port: \n $rconCommand \n  .";
+					
+			my(@modedlines) = $armabe->run($rconCommand);
+			my $encoded_content = encode_list(@modedlines);
+			return "1;" . $encoded_content;
 		}
 	}
 	else