snafu has asked for the wisdom of the Perl Monks concerning the following question:

Hey guys, I've been working on this project for a few weeks now and I am just stuck on something that I hope I can get some pointers on.

I am writing a script for a quake server that will allow me to keep track of the players on the server. I eventually will be writing code to allow the players to vote for maps and whatnot but I am having difficulty with this one part.

I am trying to create a hash of arrays that will allow me to keep a tab on the players such that the hash will be formed:

$player_info{$ipaddr} = [ qw($nickname $userid $player_req) ];
I think Im doing something wrong though. Let me give all of the code: <readmore>
# get_player_info(): # intention: Get a listing of all the players in the server # and place that info into a global array. Return # the number of players on the server. sub player_stat() { my $funcName = (caller(0))[3]; my $raw_data = &send(1,"status"); my $watch = 0; my $pass = 0; my $num_players; my $player_name; my %player_info; my $player_nickname; my $player_userid; my $key; my $ipaddr; my @status; # Wow, I am actually starting to get this stuff # I wrote the following without asking anyone # about how to do it. Whats this do you ask? # It takes several lines of data in a single element # of an array and splits each line up into seperate # elements in the array. map { push (@status, $_) } map { split(/\n/,$_) } $raw_data; for ( @status ) { chomp; # eliminate that nasty terminator! :) $_ =~ s/\0//; # Ignore the stuff that comes from this # command for now. Maybe later I will # use this information for something but # I will wait until a more mature release # of this code. if ( $watch ) { # # name userid frags # address rate ping drop # ------------------ ---- ---- ----- # player name userid frags # IPaddr rate ping drop <- record 1 # player name userid frags # IPaddr rate ping drop <- record 2 # # This parses out just the name,userid and IP per record # so that the array is now propagated with # player_name,userid,IPaddr <- from reocrd 1 # player_name,userid,IPaddr <- from record 2 # etc. if ( $_ ne "" ) { if ( $pass == 0 ) { $pass = 1; } if ( $pass == 1 ) { $_ =~ s/\Q( .*)\E\s+([0-9]+)\s+.*// +; $player_nickname = $1; $player_userid = $2; $pass = 2; } elsif ( $pass == 2 ) { ($key = $_ ) =~ /((?:\d+\.){3}\d+)/; $player_info{$1} = [ qw($value1 $value2) ]; $pass = 1; } } } # start counting after we see the "-"'s $watch = 1 if ( /-/ ); }
This is where I am having problems. Im using this lil block to simply check my hash of arrays.
my $d = 0; my $e = 0; my @val; while ( (my $nkey, my @nvalue) = each %player_info ) { #print "Have an array (\@nvalue)\n" if wantarray(@nvalue); print "Key = $nkey and nvalue = "; $e++; for ( @nvalue ) { print "$_\n$_[0]\n$_[0]->[0]\n"; $d++; print join(", ",$_[$e]->[$d]); } print "\n";
block just above here
} return(%player_info); }
When I run this script I get the following:

[qadmin@concon devel]$ ./qservwatch2.pl Key = 192.168.100.10 and nvalue = Use of uninitialized value in concat +enation (.) at ./qservwatch2.pl line 522. Use of uninitialized value in concatenation (.) at ./qservwatch2.pl li +ne 522. ARRAY(0x8373ebc) Use of uninitialized value in join at ./qservwatch2.pl line 524.

I have tried everything I can find but I think I am confusing myself somehow and am just plain doing something wrong. I have been reading out of the Programming Perl (second edition) book. It has been great. In fact, I was reading in it the other day that I can use DB_File with tie() to an undef'ed file that would allow me to create a DBM in memory. Would this be a better route to go? Considering that players are not allowed to exceed 20 at a time so it wouldn't be that big as far as a memory footprint is concerned.

TIA guys. Please be gentle. Im still learning this stuff and will be for about...oh, 40 years =P

----------
- Jim

Replies are listed 'Best First'.
Re: hash of arrays -- Im missing something
by Zaxo (Archbishop) on Jul 25, 2001 at 09:49 UTC

    I see a couple of problems that use warnings; use strict; would expose right away. First,

    $player_info{$ipaddr} = [ qw($nickname $userid $player_req) ]; # and... $player_info{$1} = [ qw($value1 $value2) ];
    should be
    $player_info{$ipaddr} = [ $nickname, $userid, $player_req ]; # and... $player_info{$1} = [ $value1, $value2 ];
    because qw() does not interpolate the value of variables.

    Second,

    while ( (my $nkey, my @nvalue) = each %player_info ) { #stuff }
    is incorrect, the second of the pair is a scalar arrayref:
    while ( (my $nkey, my $nvalue) = each %player_info ) { # stuff with $nvalue->[$ndx] }
    Sorry, I didn't follow what you were doing in that block.

    After Compline,
    Zaxo

      Excellent. The $_ = [ $value1, $value2 ]; was very helpful. The script is using warnings. Nothing more than the dreaded unitialized value was ever popped out. Of course, that is because my regex was skewed. Its all fixed now.

      Thanks!

      ----------
      - Jim

Re: hash of arrays -- Im missing something
by japhy (Canon) on Jul 25, 2001 at 10:53 UTC
    In addition to the other bugs, I think:
    for ( @nvalue ) { print "$_\n$_[0]\n$_[0]->[0]\n"; $d++; print join(", ",$_[$e]->[$d]); } print "\n";
    should be written as:
    for ( @nvalue ) { print "$_\n$_->[0]\n$_->[0][0]\n"; $d++; print join(", ",$_->[$e][$d]); # XXX: join() ? } print "\n";
    The problem is that
    $_[0]
    means "element 0 of the @_ array", whereas
    $_->[0]
    means "element 0 of the array referred to by $_".

    _____________________________________________________
    Jeff japhy Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Excellent Japhy. This helps out immensely. I really get futsed up with the references and dereferences and the syntax for both. This helps out quite a bit. Thanks.

      ----------
      - Jim

Re: hash of arrays -- Im missing something
by jepri (Parson) on Jul 25, 2001 at 09:59 UTC
    Well, the obvious problem is that you are getting an array reference back from %player_info. The [] brackets construct a reference to an array. So when you do:

    $player_info{$1}= [ qw($value1 $value2) ]; (my $nkey, my @nvalue) = each %player_info

    You are actually putting a reference into $player{$1} info and then retreiving it into @nvalue. @nvalue then has one element, the arrayref i.e. $nvalue[0]=ARRAY(0xblahblahblah).

    To do this right, you need to catch the reference like this:

    (my $nkey, my $nvalue_ref) = each %player_info ; my @nvalue = @{$nvalue};

    However that won't make a difference, because you aren't actually putting anything into your array - you never put any values into $value1, $value2! Are you using strict?

    Also, your parsing routine is a little complicated. Rather than doing the passes, strip the header before the loop, then do a =~ /(stuff)\s(more stuff)\s(stuff2)\n(even more stuff)....\n/

    Also this:

    [ qw($value1 $value2) ];

    should be:

    [ $value1, $value2 ];

    Also (this is a personal style choice, but) this is can be done differently (TIMTOWTDI)

    (my $nkey, my $nvalue_ref) my ($nkey, $nvalue_ref)

    HTH

    Update: Typical. Two people beat me. I must type slow, or think slow :) Corrected my reasoning on the qw().

    ____________________
    Jeremy
    I didn't believe in evil until I dated it.

Re: hash of arrays -- Im missing something
by physi (Friar) on Jul 25, 2001 at 09:52 UTC
    I guess your problem is, that you build an anonymous array in your hash. When you want to get back the values from that array, you must do the following:
    $player_info{$ipaddr} = [$nickname, $userid, $player_req]; for $key ( keys $player_info) { print @{$player_info{$key}} }
    or just fill the hash in an other way:
    push @{$player_info{$ipaddr}} , ($nickname,$userid,$player_req); print ${$player_info{$ipaddr}}[0]; ## gives the nickname ...
    Remember you have a reference as a hash value, so you must deref it with adding a ${...}[index] or a @{...} to get access to the values of the array inside your hash.
    Hope this helps.

    update woahh Zaxo is right here with the qw-part, changed this.

    ----------------------------------- --the good, the bad and the physi-- -----------------------------------
      Once I used the help Zaxo gave me I was able to use your help to figure out what I was doing wrong. Thank you!

      ----------
      - Jim

Re: hash of arrays -- Im missing something
by andye (Curate) on Jul 25, 2001 at 16:03 UTC
    Hi snafu, good points from many others, just thought I'd point out that with
    map { push (@status, $_) } map { split(/\n/,$_) } $raw_data;
    you're throwing away the results of the outside map. Wouldn't this...
    @status = split /\n/, $raw_data;
    do the same thing? Or am I misunderstanding?

    andy.

    update: or even

    foreach (split /\n/, $raw_data) { ... }
Re: hash of arrays -- Im missing something
by snafu (Chaplain) on Jul 26, 2001 at 07:29 UTC
    I appreciate everybodys' assistance on this. I have now solved the problem because of your help. For this, I am truly grateful.

    ----------
    - Jim