--- deliantra/server/ext/player-env.ext 2007/07/23 16:53:15 1.8 +++ deliantra/server/ext/player-env.ext 2010/05/16 01:58:50 1.20 @@ -3,63 +3,87 @@ # this extension provides "environmental effects", # meaning mostly background music and region messages right now. -sub parse_facelist($$) { - my ($prefix, $list) = @_; +our %MUSIC_GROUP; +our %MUSIC_FACE_CACHE; # cleared by reload_facedata + +sub reload { + cf::trace "loading music config from $DATADIR/music\n"; + + 0 < Coro::AIO::aio_load "$DATADIR/music", my $data + or die "$DATADIR/music $!"; + + my $conf = JSON::XS->new->utf8->relaxed->decode ($data); + %MUSIC_GROUP = %{ $conf->{group} }; + %MUSIC_FACE_CACHE = (); +} + +cf::post_init { + $_[0] ? cf::async { reload } : reload +}; + +sub parse_musiclist($) { + my ($list) = @_; return undef unless defined $list; - #TODO, sort by size (smallest first...) - [ - sort { (cf::face::get_data_size $a) <=> (cf::face::get_data_size $b) } - grep $_, - map +(cf::face::find "$prefix$_" or ((warn "unable to find music file $_"), undef)), - split /\s*,\s*/, $list - ] + my @name = split /\s*,\s*/, $list; + my @face; + + while (@name) { + my $name = shift @name; + + if ($name =~ /^\@(.*)$/) { + my $group = $MUSIC_GROUP{$1} + or cf::error "music group $1 does not exist (referenced in '$list')"; + + unshift @name, @$group; + } else { + my $face = cf::face::find "music/$name" + or "unable to find music face $name (referenced in '$list')"; + + push @face, $face + if $face; + } + } + + # sort by size, smallest first, because it'S the fastest to download... + [ sort { (cf::face::get_data_size $a) <=> (cf::face::get_data_size $b) } @face ] } our %MUSIC_QUEUE; our $MUSIC_SCHEDULER = cf::async_ext { + $Coro::current->{desc} = "music scheduler"; + while () { for (keys %MUSIC_QUEUE) { delete $MUSIC_QUEUE{$_}; - cf::get_slot 0.01, -10; + cf::get_slot 0.01, -10, "music scheduler"; my $pl = cf::player::find_active $_ or next; - $pl->ob->active or next; + my $ob = $pl->ob; my $ns = $pl->ns or next; $ns->extcmd or next; - my $map = $pl->ob->map or next; - - my $faces; + my $map = $ob->map; + my $rgn = $ob->region; - # 1. update map-specific music info - unless (exists $map->{music_faces}) { - $map->{music_faces} = parse_facelist "music/", $map->{music}; - } - - my $faces = $map->{music_faces}; - - # 2. fall back to region if no map-specific music - unless ($faces) { - my $rgn = $pl->ob->region - or next; - - unless (exists $rgn->{music_faces}) { - my $par = $rgn; - while () { - last if exists $par->{music}; - $par = $par->parent - or last; - } + my $id = join "\x00", $map->path, $rgn->name; - $rgn->{music_faces} = parse_facelist "music/", $par->{music}; - } + my $faces = delete $ns->{music_play_once}; + $faces ||= $MUSIC_FACE_CACHE{$id} ||= do { + # 1. map-specific music info + parse_musiclist $map->{music} + or do { + # 2. fall back to region if no map-specific music + $rgn = $rgn->parent + while $rgn && !exists $rgn->{music}; - $faces = $rgn->{music_faces}; - } + parse_musiclist $rgn->{music} + } + ; + }; $faces or next; @@ -70,15 +94,12 @@ $ns->{current_music_faces} = $facestr; - my $msg = { - play => $faces, - }; - my $pri = 0; - push @{$msg->{faces}}, [$_, $pri++, cf::face::get_chksum $_] - for grep $ns->must_send_face ($_), @$faces; + $ns->send_face ($_, --$pri - 110) + for @$faces; + $ns->flush_fx; - $ns->ext_event (music => %$msg); + $ns->ext_msg (ambient_music => $faces); } Coro::schedule unless %MUSIC_QUEUE; } @@ -90,15 +111,23 @@ $pl->ob->message ("You are now " . $new->longname . ". H", $new->longname); - $MUSIC_QUEUE{$pl->ob->name} = undef; + undef $MUSIC_QUEUE{$pl->ob->name}; $MUSIC_SCHEDULER->ready; }, on_map_change => sub { my ($pl, $new) = @_; - $MUSIC_QUEUE{$pl->ob->name} = undef; + undef $MUSIC_QUEUE{$pl->ob->name}; $MUSIC_SCHEDULER->ready; }, ); +sub play_music_once { + my ($pl, $music) = @_; + + $pl->ns->{music_play_once} = parse_musiclist $music; + + undef $MUSIC_QUEUE{$pl->ob->name}; + $MUSIC_SCHEDULER->ready; +}