2016-01-10 11:01:49 +01:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# LegacyWorlds Beta 5
|
|
|
|
# System control script
|
|
|
|
#
|
|
|
|
# Without arguments:
|
|
|
|
# should run as root; creates a FIFO from which it reads commands.
|
|
|
|
#
|
|
|
|
# With arguments:
|
|
|
|
# writes arguments to the pipe
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
## Configuration
|
|
|
|
#
|
|
|
|
|
|
|
|
$fifoPath = "/tmp/.lwFifo";
|
|
|
|
$ctrlPath = "/tmp/.lwControl";
|
|
|
|
$fsUser = 0;
|
|
|
|
$fsGroup = 33;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
## Main code below
|
|
|
|
#
|
|
|
|
|
|
|
|
use IO::File;
|
|
|
|
require POSIX;
|
|
|
|
|
|
|
|
# If we have arguments, write to the FIFO and exit
|
|
|
|
if (@ARGV > 0) {
|
|
|
|
if ($ARGV[0] eq '--start') {
|
|
|
|
&start();
|
|
|
|
} elsif ($ARGV[0] eq '--stop') {
|
|
|
|
&stop();
|
|
|
|
} else {
|
|
|
|
&sendMessage(@ARGV);
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Find the script's path
|
|
|
|
use FindBin qw($Bin);
|
|
|
|
if (! -f "$Bin/legacyworlds.xml") {
|
|
|
|
die "$0: could not find 'legacyworlds.xml' in '$Bin'\n";
|
|
|
|
}
|
|
|
|
$lwConf = "$Bin/legacyworlds.xml";
|
|
|
|
|
|
|
|
# Fork and detach
|
|
|
|
$pid = fork();
|
|
|
|
if ($pid < 0) {
|
|
|
|
die("$0: failed to fork\n");
|
|
|
|
} elsif ($pid != 0) {
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Detach
|
|
|
|
POSIX::setsid();
|
|
|
|
close STDIN; close STDOUT; close STDERR;
|
|
|
|
open(STDIN, "</dev/null"); open(STDOUT, ">/dev/null"); open(STDERR, ">/dev/null");
|
|
|
|
|
|
|
|
# First create the pipe if it doesn't exist
|
|
|
|
if (! -p $fifoPath) {
|
|
|
|
if (-e $fifoPath) {
|
|
|
|
die "$0: '$fifoPath' is not a pipe\n";
|
|
|
|
} else {
|
|
|
|
POSIX::mkfifo($fifoPath, 0400)
|
|
|
|
or die "$0: unable to create '$fifoPath'\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# Set the pipe's owner and group
|
|
|
|
if ($> == 0) {
|
|
|
|
chown $fsUser, $fsGroup, $fifoPath;
|
|
|
|
} else {
|
|
|
|
chown $>, $fsGroup, $fifoPath;
|
|
|
|
}
|
|
|
|
chmod 0620, $fifoPath;
|
|
|
|
|
|
|
|
# Create the control directory if needed
|
|
|
|
if (! -d $ctrlPath) {
|
|
|
|
if (-e $ctrlPath) {
|
|
|
|
die "$0: '$ctrlPath' is not a directory\n";
|
|
|
|
} else {
|
|
|
|
mkdir $ctrlPath, 0700
|
|
|
|
or die "$0: unable to create '$ctrlPath'\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# Set the owner and group
|
|
|
|
if ($> == 0) {
|
|
|
|
chown $fsUser, $fsGroup, $ctrlPath;
|
|
|
|
} else {
|
|
|
|
chown $>, $fsGroup, $ctrlPath;
|
|
|
|
}
|
|
|
|
chmod 0770, $ctrlPath;
|
|
|
|
|
|
|
|
# Define commands
|
|
|
|
%commands = (
|
|
|
|
DIE => \&endController,
|
|
|
|
MERGE => \&mergeConfiguration,
|
|
|
|
TMPID => \&tickManagerID,
|
|
|
|
TMINIT => \&tickManagerStart,
|
|
|
|
TMSTOP => \&tickManagerStop,
|
|
|
|
READY => \&gameReady,
|
|
|
|
START => \&gameStart,
|
|
|
|
SETEND => \&gameEnd,
|
|
|
|
NOEND => \&gameCancelEnd,
|
|
|
|
"END" => \&gameChangeEnd,
|
|
|
|
SETDEF => \&setDefaultGame,
|
|
|
|
);
|
|
|
|
|
|
|
|
# Reader loop
|
|
|
|
while (1) {
|
|
|
|
# Wait for someone to write to the pipe
|
|
|
|
close(FIFO);
|
|
|
|
open(FIFO, "< $fifoPath")
|
|
|
|
or die "$0: unable to open '$fifoPath'\n";
|
|
|
|
|
|
|
|
# Read it
|
|
|
|
$command = <FIFO>;
|
|
|
|
next unless defined $command;
|
|
|
|
chomp($command);
|
|
|
|
next if $command =~ /[^A-Za-z0-9\s]/;
|
|
|
|
|
|
|
|
# Extract the actual command
|
|
|
|
my @args = split /\s+/, $command;
|
|
|
|
$command = shift @args;
|
|
|
|
|
|
|
|
# Check if the command is allowed
|
|
|
|
next unless defined $commands{$command};
|
|
|
|
|
|
|
|
#print "Got command $command, arguments = (" . join(', ', @args) . ")\n";
|
|
|
|
&{$commands{$command}}(@args);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
## Helper functions
|
|
|
|
#
|
|
|
|
|
|
|
|
sub sendMessage {
|
|
|
|
my @args = @_;
|
|
|
|
die "$0: FIFO '$fifoPath' doesn't exist\n" unless -p $fifoPath;
|
|
|
|
open(FIFO, "> $fifoPath") or die "$0: unable to open FIFO '$fifoPath'\n";
|
|
|
|
print FIFO (join(' ', @args) . "\n");
|
|
|
|
close(FIFO);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub start {
|
|
|
|
$pid = fork();
|
|
|
|
if ($pid == -1) {
|
|
|
|
die "$0: could not fork\n";
|
|
|
|
} elsif ($pid) {
|
|
|
|
print "LegacyWorlds - Initialising game\n";
|
|
|
|
print " -> Controller\n";
|
|
|
|
sleep(1);
|
|
|
|
print " -> Ticks manager\n";
|
|
|
|
&sendMessage("TMINIT");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub stop {
|
|
|
|
print "LegacyWolrds - Shutting down\n";
|
|
|
|
print " -> Ticks manager\n";
|
|
|
|
&sendMessage("TMSTOP");
|
|
|
|
sleep(1);
|
|
|
|
print " -> Controller\n";
|
|
|
|
&sendMessage("DIE");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
## Command functions
|
|
|
|
#
|
|
|
|
|
|
|
|
#
|
|
|
|
# Function that terminates the controller
|
|
|
|
#
|
|
|
|
sub endController {
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Function that adds a new game
|
|
|
|
#
|
|
|
|
sub mergeConfiguration {
|
|
|
|
my $sourceFile = shift;
|
|
|
|
|
|
|
|
return unless -f "$ctrlPath/config.$sourceFile.xml";
|
|
|
|
|
|
|
|
# Read the new snippet
|
|
|
|
open(NEWCONF, "< $ctrlPath/config.$sourceFile.xml");
|
|
|
|
my @newConfiguration = <NEWCONF>;
|
|
|
|
close(NEWCONF);
|
|
|
|
|
|
|
|
# Read the old configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Merge it
|
|
|
|
my @merged = ();
|
|
|
|
foreach my $oldLine (@oldConfiguration) {
|
|
|
|
if ($oldLine =~ /^\s+<\/Games>\s*$/) {
|
|
|
|
@merged = (@merged, @newConfiguration, "\n");
|
|
|
|
}
|
|
|
|
@merged = (@merged, $oldLine);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write the new configuration file
|
|
|
|
open(OLDCONF, "> $lwConf");
|
|
|
|
print OLDCONF @merged;
|
|
|
|
close OLDCONF;
|
|
|
|
|
|
|
|
# Remove the source file
|
|
|
|
unlink "$ctrlPath/config.$sourceFile.xml";
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Tick manager PID update
|
|
|
|
#
|
|
|
|
sub tickManagerID {
|
|
|
|
my $pid = shift;
|
|
|
|
return unless $pid;
|
|
|
|
|
|
|
|
open(PIDFILE, "> $ctrlPath/tickManager.pid");
|
|
|
|
print PIDFILE "$pid " . time() . "\n";
|
|
|
|
close(PIDFILE);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Start tick manager
|
|
|
|
#
|
|
|
|
sub tickManagerStart {
|
|
|
|
return if &tickManagerStatus();
|
|
|
|
return unless -f "$Bin/ticks.php";
|
|
|
|
if ($> == 0) {
|
2024-12-31 12:13:52 +01:00
|
|
|
system("runuser -u lwticks -- bash -c 'cd $Bin; php ticks.php'");
|
2016-01-10 11:01:49 +01:00
|
|
|
} else {
|
|
|
|
system("cd $Bin; php ticks.php");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Stop tick manager
|
|
|
|
#
|
|
|
|
sub tickManagerStop {
|
|
|
|
my $pid;
|
|
|
|
return unless ($pid = &tickManagerStatus());
|
|
|
|
kill 15, $pid;
|
|
|
|
unlink("$ctrlPath/tickManager.pid");
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Check tick manager status
|
|
|
|
#
|
|
|
|
sub tickManagerStatus {
|
|
|
|
return 0 unless -f "$ctrlPath/tickManager.pid";
|
|
|
|
|
|
|
|
open(PIDFILE, "< $ctrlPath/tickManager.pid");
|
|
|
|
my $data = <PIDFILE>;
|
|
|
|
close(PIDFILE);
|
|
|
|
|
|
|
|
chomp($data);
|
|
|
|
my ($pid, $time) = split / /, $data;
|
|
|
|
return (time() - $time < 22 ? $pid : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Make a game ready
|
|
|
|
#
|
|
|
|
sub gameReady {
|
|
|
|
my $gName = shift;
|
|
|
|
|
|
|
|
# Read the current configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Generate new configuration
|
|
|
|
my @newConf = ();
|
|
|
|
foreach my $line (@oldConfiguration) {
|
|
|
|
if ($line =~ /<Game\s+id="$gName".*\s+public="0"\s+canjoin="0"/) {
|
|
|
|
$line =~ s/\spublic="0"\s+canjoin="0"/ public="1" canjoin="1"/;
|
|
|
|
}
|
|
|
|
push @newConf, $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write configuration file
|
|
|
|
open(NEWCONF, "> $lwConf");
|
|
|
|
print NEWCONF @newConf;
|
|
|
|
close(NEWCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Start the game earlier or later
|
|
|
|
#
|
|
|
|
sub gameStart {
|
|
|
|
my $gName = shift;
|
|
|
|
my $when = shift;
|
|
|
|
|
|
|
|
return if ($when ne "EARLY" && $when ne "LATE");
|
|
|
|
$when = ($when eq 'EARLY') ? -1 : 1;
|
|
|
|
$when *= 24 * 60 * 60;
|
|
|
|
|
|
|
|
# Read the current configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Generate new configuration
|
|
|
|
my @newConf = ();
|
|
|
|
my $state = 0;
|
|
|
|
foreach my $line (@oldConfiguration) {
|
|
|
|
if ($state == 0 && $line =~ /<Game\s+id="$gName".*\s+public="1"\s+canjoin="1"/) {
|
|
|
|
$state = 1;
|
|
|
|
} elsif ($state == 1) {
|
|
|
|
if ($line =~ /<\/Game>/) {
|
|
|
|
$state = 0;
|
|
|
|
} elsif ($line =~ /<Tick\s+script="[a-z]+"\s+first="([0-9]+)"\s+interval="[0-9]+"\s*\/>/) {
|
|
|
|
my $fTick = $1;
|
|
|
|
$fTick += $when;
|
|
|
|
$line =~ s/\sfirst="[0-9]+"/ first="$fTick"/;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push @newConf, $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write configuration file
|
|
|
|
open(NEWCONF, "> $lwConf");
|
|
|
|
print NEWCONF @newConf;
|
|
|
|
close(NEWCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Set a running game to end
|
|
|
|
#
|
|
|
|
sub gameEnd {
|
|
|
|
my $gName = shift;
|
|
|
|
my $when = shift;
|
|
|
|
|
|
|
|
$when = $when * 60 * 60 + time();
|
|
|
|
|
|
|
|
# Read the current configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Generate new configuration
|
|
|
|
my @newConf = ();
|
|
|
|
my $state = 0;
|
|
|
|
foreach my $line (@oldConfiguration) {
|
|
|
|
if ($state == 0 && $line =~ /<Game\s+id="$gName".*\s+public="1"\s+canjoin="1"/) {
|
|
|
|
$state = 1;
|
|
|
|
} elsif ($state == 1) {
|
|
|
|
if ($line =~ /<\/Game>/) {
|
|
|
|
$state = 0;
|
|
|
|
} elsif ($line =~ /<Tick\s+script="[a-z]+"\s+first="([0-9]+)"\s+interval="[0-9]+"\s*\/>/) {
|
|
|
|
my $fTick = $1;
|
|
|
|
$line =~ s/\sfirst="[0-9]+"/ first="$fTick" last="$when"/;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push @newConf, $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write configuration file
|
|
|
|
open(NEWCONF, "> $lwConf");
|
|
|
|
print NEWCONF @newConf;
|
|
|
|
close(NEWCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Set an ending game's status back to running/victory
|
|
|
|
#
|
|
|
|
sub gameCancelEnd {
|
|
|
|
my $gName = shift;
|
|
|
|
|
|
|
|
# Read the current configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Generate new configuration
|
|
|
|
my @newConf = ();
|
|
|
|
my $state = 0;
|
|
|
|
foreach my $line (@oldConfiguration) {
|
|
|
|
if ($state == 0 && $line =~ /<Game\s+id="$gName".*\s+public="1"\s+canjoin="1"/) {
|
|
|
|
$state = 1;
|
|
|
|
} elsif ($state == 1) {
|
|
|
|
if ($line =~ /<\/Game>/) {
|
|
|
|
$state = 0;
|
|
|
|
} elsif ($line =~ /<Tick\s+script="[a-z]+"\s+/) {
|
|
|
|
$line =~ s/\slast="[0-9]+"//;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push @newConf, $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write configuration file
|
|
|
|
open(NEWCONF, "> $lwConf");
|
|
|
|
print NEWCONF @newConf;
|
|
|
|
close(NEWCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# End the game earlier or later
|
|
|
|
#
|
|
|
|
sub gameChangeEnd {
|
|
|
|
my $gName = shift;
|
|
|
|
my $when = shift;
|
|
|
|
|
|
|
|
return if ($when ne "EARLY" && $when ne "LATE" && $when ne "NOW");
|
|
|
|
if ($when ne 'NOW') {
|
|
|
|
$when = ($when eq 'EARLY') ? -1 : 1;
|
|
|
|
$when *= 24 * 60 * 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Read the current configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Generate new configuration
|
|
|
|
my @newConf = ();
|
|
|
|
my $state = 0;
|
|
|
|
foreach my $line (@oldConfiguration) {
|
|
|
|
if ($state == 0 && $line =~ /<Game\s+id="$gName".*\s+public="1"\s+canjoin="1"/) {
|
|
|
|
$state = 1;
|
|
|
|
} elsif ($state == 1) {
|
|
|
|
if ($line =~ /<\/Game>/) {
|
|
|
|
$state = 0;
|
|
|
|
} elsif ($line =~ /<Tick\s+script="[a-z]+".*\slast="([0-9]+)"/) {
|
|
|
|
my $lTick = $1;
|
|
|
|
if ($when eq 'NOW') {
|
|
|
|
$lTick = time();
|
|
|
|
} else {
|
|
|
|
$lTick += $when;
|
|
|
|
}
|
|
|
|
$line =~ s/\slast="[0-9]+"/ last="$lTick"/;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push @newConf, $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write configuration file
|
|
|
|
open(NEWCONF, "> $lwConf");
|
|
|
|
print NEWCONF @newConf;
|
|
|
|
close(NEWCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Changes the default game
|
|
|
|
#
|
|
|
|
sub setDefaultGame {
|
|
|
|
my $gName = shift;
|
|
|
|
return if ($gName eq "");
|
|
|
|
|
|
|
|
# Read the current configuration
|
|
|
|
open(OLDCONF, "< $lwConf");
|
|
|
|
my @oldConfiguration = <OLDCONF>;
|
|
|
|
close(OLDCONF);
|
|
|
|
|
|
|
|
# Generate new configuration
|
|
|
|
my @newConf = ();
|
|
|
|
foreach my $line (@oldConfiguration) {
|
|
|
|
if ($line =~ /<Games\s+default="[a-z0-9]+"/) {
|
|
|
|
$line =~ s/<Games\s+default="[0-9a-z]+"/<Games default="$gName"/;
|
|
|
|
}
|
|
|
|
push @newConf, $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write configuration file
|
|
|
|
open(NEWCONF, "> $lwConf");
|
|
|
|
print NEWCONF @newConf;
|
|
|
|
close(NEWCONF);
|
|
|
|
}
|