#!/usr/bin/perl -w # JnBSync is a frontend for jumpnbump # Copyright (c) 2003 David Baelde # This software is under General Public License Version 2003-10-15-12-05 # Version 2003-10-15-12-05 use strict ; use Getopt::Long ; use Socket ; ############################################################################### # Default values # ############################################################################### my $login = $ENV{LOGNAME} ; my $jnb = "jumpnbump -scaleup" ; my $data = "/usr/share/games/jumpnbump/jumpbump.dat" ; my $player ; my $waitfor = 3 ; my $width = 80 ; sub tmp_file { return "./jnbsync.tmp.dat" ; } ############################################################################### # Parsing # ############################################################################### my $port = 26411+1 ; my $server = "" ; my $help = 0 ; my $result = GetOptions ("waitfor=i" => \$waitfor, "player=s" => \$player, "data=s" => \$data, "connect=s" => \$server, "login=s" => \$login, "port=i" => \$port, "width=i" => \$width, "help" => \$help ) ; if ($help || !$result) { print " JnBSync is a frontend for jumpnbump Copyright (c) 2003 David Baelde This software is under General Public License Version 2003-10-15-12-05 jnbsync -data Server side : Specify the level in which you'll play. Clients doesn't need to have it, it will be uploaded. Specify a directory, the script will choose a random .dat in it. -waitfor [0-4] Server side : Wait for N clients before starting the game. You can type in \"go\" while waiting for a client in order to start the game anyway. Defaults to $waitfor. -connect Client side : Specify the server. -port Both sides : Change the default port. Defaults to $port. -player [0-3] Both sides : Try to have player #N. If it fails, you will be kicked. The best is to let the server choose a player for you (default). Defaults to 0 for the server. -login Both sides : Specify a login. Defaults to your logname. -width Both sides : Set the terminal width. Defaults to $width. Many default values can be set up by editing the Perl script. " ; exit 1 ; } ############################################################################### # Get ready ... # ############################################################################### $| = 1 ; my ($i,$p) ; my $max_id = 3 ; sub abort { print shift (), "\n" ; exit 1 ; } sub min { my ($a,$b) = (shift,shift) ; return ($a>$b?$b:$a) ; } # Init player data my @players = () ; sub is_valid { my $p = shift ; return 0 if ($p>$max_id || $p<0) ; return 1 ; } sub alloc_player { my $p = shift ; if (defined $p) { return 0 if ($p>$max_id || $p<0) ; if ($players[$p]) { return 0 ; }else{ $players[$p] = 1 ; return 1 ; } }else{ for $p (0..$max_id) { next if $players[$p] ; $players[$p] = 1 ; return $p ; } return -1 ; } } sub exists_player { my $p ; my $i = 0 ; for $p (0..$max_id) { $i++ unless $players[$p] ; } return $i ; } sub free_player { my $p = shift ; $players[$p] = 0 ; } if (defined $player) { abort "Wrong player $p !" if $player<0 || $player>3 ; } ############################################################################### # Server side # ############################################################################### sub fhbits { local $_ ; my @fhlist = split ' ', $_[0] ; my $bits = '' ; for (@fhlist) { vec ($bits,fileno($_),1) = 1; } return $bits; } sub server { unless (defined $player) { $player = 0 ; } alloc_player $player ; $waitfor = min ($waitfor,3) ; abort "Don't use jnbsync if there is no expected client !" unless $waitfor ; $max_id = $waitfor ; # Data if (-d $data) { my @list = `ls $data/*.dat` ; $data = $list[int(rand(@list))] ; chomp $data ; print "I choosed \"$data\"\n" ; } # Start the server print "Local player : #$player\n" ; print "Waiting for : $waitfor player",($waitfor>1?"s":""),"\n" ; print "Max id : $max_id\n" ; print "Level data : $data\n" ; print "\n" ; socket SERVER, PF_INET, SOCK_STREAM, (getprotobyname "tcp") || die "socket : $!" ; bind SERVER, (sockaddr_in $port, INADDR_ANY) || die "bind : $!" ; listen SERVER, SOMAXCONN || die "listen : $!" ; my $logins = "Player $player is $login\n" ; my @clients = () ; WAITING: while (exists_player) { print "Waiting (",exists_player,") ...\n" ; my $rin = fhbits ('STDIN SERVER') ; while (1) { my $rout = $rin ; select ($rout, undef, undef, undef) ; if (vec($rout,fileno(STDIN),1)) { $_ = ; next unless /go/ ; $waitfor -= exists_player ; last WAITING ; }else{ last ; } } my $addr = accept(CLIENT,SERVER) || die "accept : $!" ; my ($port,$iaddr) = sockaddr_in($addr); my $name = (gethostbyaddr($iaddr,AF_INET) || "(null)"); print " Got a connection from $name [", inet_ntoa($iaddr), "]" ; # The client allocs a player my $client ; my $clogin ; my $ok = 1 ; my $request = ; print " -> $request" ; if ($request =~ /(\w+) claims to be ([0123])/) { $client = $2 ; $clogin = $1 ; undef $client unless alloc_player $client ; } if ($request =~ /(\w+) wants a player/) { $clogin = $1 ; $p = alloc_player ; if (is_valid $p) { $client = $p ; } } select CLIENT ; $| = 1 ; select STDOUT ; print " ", (defined $client?"Player $client":"No player"), " were allocated for $clogin.\n" ; if (defined $client) { $logins .= "Player $client is $clogin\n" ; push @clients, $iaddr ; print CLIENT "You have player $client\n" ; print " Sending data ... " ; open DATA, $data ; print CLIENT while ; close DATA ; print "OK!\n" ; } else { print CLIENT "Go away !\n" ; } close CLIENT ; } close SERVER ; print '='x$width , "\n" ; print $logins ; print '='x$width , "\n" ; system "$jnb -server $waitfor -player $player -dat $data &" ; sleep 2 ; my $iaddr ; for $iaddr (@clients) { socket (CLIENT, PF_INET, SOCK_STREAM, (getprotobyname "tcp")) || die "socket : $!" ; print "Connecting to ", (gethostbyaddr($iaddr,AF_INET) || "(null)"), " (", inet_ntoa($iaddr), ") ..." ; connect (CLIENT, sockaddr_in($port+1,$iaddr)) || die "connect : $!" ; print "done.\n" ; print CLIENT $logins ; close CLIENT ; } } ############################################################################### # Client side # ############################################################################### sub client { my $addr = sockaddr_in($port, (inet_aton $server)) ; socket (SERVER, PF_INET, SOCK_STREAM, (getprotobyname "tcp")) || die "socket : $!" ; print "Connecting ... " ; connect (SERVER, $addr) || die "\nconnect : $!" ; print "OK.\n" ; select SERVER ; $| = 1 ; select STDOUT ; if ($player) { print SERVER "$login claims to be $player\n" ; } else { print SERVER "$login wants a player","\n" ; } print "Request sent -> " ; my $player = ; print $player ; if ($player =~ /You have player ([0123])/) { $player = $1 ; }else{ undef $player ; } if (defined $player) { print "Receiving the level ..." ; my $tmp = tmp_file ; open DATA, ">$tmp" ; print DATA while ; close DATA ; close SERVER ; print " done.\n" ; socket CLIENT, PF_INET, SOCK_STREAM, (getprotobyname "tcp") || die "socket : $!" ; bind CLIENT, (sockaddr_in (($port+1), INADDR_ANY)) || die "bind : $!" ; listen CLIENT, 1 || die "listen : $!" ; print "Waiting for server to come back ... " ; accept SERVER,CLIENT ; print "OK.\n" ; print '='x$width , "\n" ; print while ; print '='x$width , "\n" ; close SERVER ; close CLIENT ; exec "$jnb -connect $server -dat $tmp -player $player" ; }else{ close SERVER ; } } ############################################################################### # Play ! # ############################################################################### if ($server) { client ; } else { server ; }