ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
(Generate patch)

Comparing deliantra/server/lib/cf.pm (file contents):
Revision 1.314 by root, Mon Jul 23 16:53:15 2007 UTC vs.
Revision 1.333 by root, Sun Aug 19 09:14:50 2007 UTC

10use Event; 10use Event;
11use Opcode; 11use Opcode;
12use Safe; 12use Safe;
13use Safe::Hole; 13use Safe::Hole;
14 14
15use Coro 3.61 (); 15use Coro 3.64 ();
16use Coro::State; 16use Coro::State;
17use Coro::Handle; 17use Coro::Handle;
18use Coro::Event; 18use Coro::Event;
19use Coro::Timer; 19use Coro::Timer;
20use Coro::Signal; 20use Coro::Signal;
21use Coro::Semaphore; 21use Coro::Semaphore;
22use Coro::AIO; 22use Coro::AIO;
23use Coro::Storable; 23use Coro::Storable;
24use Coro::Util ();
24 25
25use JSON::XS 1.4 (); 26use JSON::XS 1.4 ();
26use BDB (); 27use BDB ();
27use Data::Dumper; 28use Data::Dumper;
28use Digest::MD5; 29use Digest::MD5;
322 }; 323 };
323 $TICK_WATCHER->stop; 324 $TICK_WATCHER->stop;
324 $guard 325 $guard
325} 326}
326 327
327=item cf::get_slot $time[, $priority] 328=item cf::get_slot $time[, $priority[, $name]]
328 329
329Allocate $time seconds of blocking CPU time at priority C<$priority>: 330Allocate $time seconds of blocking CPU time at priority C<$priority>:
330This call blocks and returns only when you have at least C<$time> seconds 331This call blocks and returns only when you have at least C<$time> seconds
331of cpu time till the next tick. The slot is only valid till the next cede. 332of cpu time till the next tick. The slot is only valid till the next cede.
333
334The optional C<$name> can be used to identify the job to run. It might be
335used for statistical purposes and should identify the same time-class.
332 336
333Useful for short background jobs. 337Useful for short background jobs.
334 338
335=cut 339=cut
336 340
363 Coro::schedule; 367 Coro::schedule;
364 } 368 }
365 } 369 }
366}; 370};
367 371
368sub get_slot($;$) { 372sub get_slot($;$$) {
369 my ($time, $pri) = @_; 373 my ($time, $pri, $name) = @_;
370 374
375 $time = $TICK * .6 if $time > $TICK * .6;
376 my $sig = new Coro::Signal;
377
371 push @SLOT_QUEUE, [$time, $pri, my $sig = new Coro::Signal]; 378 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
372 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE; 379 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
373 $SLOT_QUEUE->ready; 380 $SLOT_QUEUE->ready;
374 $sig->wait; 381 $sig->wait;
375} 382}
376 383
466Coro::Storable. May, of course, block. Note that the executed sub may 473Coro::Storable. May, of course, block. Note that the executed sub may
467never block itself or use any form of Event handling. 474never block itself or use any form of Event handling.
468 475
469=cut 476=cut
470 477
471sub _store_scalar {
472 open my $fh, ">", \my $buf
473 or die "fork_call: cannot open fh-to-buf in child : $!";
474 Storable::store_fd $_[0], $fh;
475 close $fh;
476
477 $buf
478}
479
480sub fork_call(&@) { 478sub fork_call(&@) {
481 my ($cb, @args) = @_; 479 my ($cb, @args) = @_;
482 480
483# socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC 481 # we seemingly have to make a local copy of the whole thing,
484# or die "socketpair: $!"; 482 # otherwise perl prematurely frees the stuff :/
485 pipe my $fh1, my $fh2 483 # TODO: investigate and fix (liekly this will be rather laborious)
486 or die "pipe: $!";
487 484
488 if (my $pid = fork) { 485 my @res = Coro::Util::fork_eval {
489 close $fh2;
490
491 my $res = (Coro::Handle::unblock $fh1)->readline (undef);
492 warn "pst<$res>" unless $res =~ /^pst/;
493 $res = Coro::Storable::thaw $res;
494
495 waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
496
497 Carp::confess $$res unless "ARRAY" eq ref $res;
498
499 return wantarray ? @$res : $res->[-1];
500 } else {
501 reset_signals; 486 reset_signals;
502 local $SIG{__WARN__}; 487 &$cb
503 local $SIG{__DIE__}; 488 }, @args;
504 # just in case, this hack effectively disables event
505 # in the child. cleaner and slower would be canceling all watchers,
506 # but this works for the time being.
507 local $Coro::idle;
508 $Coro::current->prio (Coro::PRIO_MAX);
509 489
510 eval { 490 wantarray ? @res : $res[-1]
511 close $fh1;
512
513 my @res = eval { $cb->(@args) };
514
515 syswrite $fh2, _store_scalar $@ ? \"$@" : \@res;
516 close $fh2;
517 };
518
519 warn $@ if $@;
520 _exit 0;
521 }
522} 491}
523 492
524=item $value = cf::db_get $family => $key 493=item $value = cf::db_get $family => $key
525 494
526Returns a single value from the environment database. 495Returns a single value from the environment database.
1175 my ($pl, $buf) = @_; 1144 my ($pl, $buf) = @_;
1176 1145
1177 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) }; 1146 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1178 1147
1179 if (ref $msg) { 1148 if (ref $msg) {
1149 my ($type, $reply, @payload) =
1150 "ARRAY" eq ref $msg
1151 ? @$msg
1152 : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1153
1180 if (my $cb = $EXTCMD{$msg->{msgtype}}) { 1154 if (my $cb = $EXTCMD{$type}) {
1181 if (my %reply = $cb->($pl, $msg)) { 1155 my @reply = $cb->($pl, @payload);
1156
1182 $pl->ext_reply ($msg->{msgid}, %reply); 1157 $pl->ext_reply ($reply, @reply)
1183 } 1158 if $reply;
1184 } 1159 }
1185 } else { 1160 } else {
1186 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n"; 1161 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1187 } 1162 }
1188 1163
1358 $self->{deny_save} = 1; 1333 $self->{deny_save} = 1;
1359 1334
1360 $cf::PLAYER{$login} = $self; 1335 $cf::PLAYER{$login} = $self;
1361 1336
1362 $self 1337 $self
1338}
1339
1340=item $player->send_msg ($channel, $msg, $color, [extra...])
1341
1342=cut
1343
1344sub send_msg {
1345 my $ns = shift->ns
1346 or return;
1347 $ns->send_msg (@_);
1363} 1348}
1364 1349
1365=item $pl->quit_character 1350=item $pl->quit_character
1366 1351
1367Nukes the player without looking back. If logged in, the connection will 1352Nukes the player without looking back. If logged in, the connection will
1511sub hintmode { 1496sub hintmode {
1512 $_[0]{hintmode} = $_[1] if @_ > 1; 1497 $_[0]{hintmode} = $_[1] if @_ > 1;
1513 $_[0]{hintmode} 1498 $_[0]{hintmode}
1514} 1499}
1515 1500
1516=item $player->ext_reply ($msgid, %msg) 1501=item $player->ext_reply ($msgid, @msg)
1517 1502
1518Sends an ext reply to the player. 1503Sends an ext reply to the player.
1519 1504
1520=cut 1505=cut
1521 1506
1522sub ext_reply($$%) { 1507sub ext_reply($$@) {
1523 my ($self, $id, %msg) = @_; 1508 my ($self, $id, @msg) = @_;
1524 1509
1525 $msg{msgid} = $id; 1510 if ($self->ns->extcmd == 2) {
1511 $self->send ("ext " . $self->ns->{json_coder}->encode (["reply-$id", @msg]));
1512 } elsif ($self->ns->extcmd == 1) {
1513 #TODO: version 1, remove
1514 unshift @msg, msgtype => "reply", msgid => $id;
1526 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg)); 1515 $self->send ("ext " . $self->ns->{json_coder}->encode ({@msg}));
1516 }
1527} 1517}
1528 1518
1529=item $player->ext_event ($type, %msg) 1519=item $player->ext_msg ($type, @msg)
1530 1520
1531Sends an ext event to the client. 1521Sends an ext event to the client.
1532 1522
1533=cut 1523=cut
1534 1524
1535sub ext_event($$%) { 1525sub ext_msg($$@) {
1536 my ($self, $type, %msg) = @_; 1526 my ($self, $type, @msg) = @_;
1537 1527
1538 $self->ns->ext_event ($type, %msg); 1528 $self->ns->ext_msg ($type, @msg);
1539} 1529}
1540 1530
1541=head3 cf::region 1531=head3 cf::region
1542 1532
1543=over 4 1533=over 4
1904 $self->set_darkness_map; 1894 $self->set_darkness_map;
1905 Coro::cede; 1895 Coro::cede;
1906 $self->activate; 1896 $self->activate;
1907 } 1897 }
1908 1898
1899 $self->{last_save} = $cf::RUNTIME;
1900 $self->last_access ($cf::RUNTIME);
1901
1909 $self->in_memory (cf::MAP_IN_MEMORY); 1902 $self->in_memory (cf::MAP_IN_MEMORY);
1910 } 1903 }
1911 1904
1912 $self->post_load; 1905 $self->post_load;
1913} 1906}
1923 1916
1924 $self 1917 $self
1925} 1918}
1926 1919
1927# find and load all maps in the 3x3 area around a map 1920# find and load all maps in the 3x3 area around a map
1928sub load_diag { 1921sub load_neighbours {
1929 my ($map) = @_; 1922 my ($map) = @_;
1930 1923
1931 my @diag; # diagonal neighbours 1924 my @neigh; # diagonal neighbours
1932 1925
1933 for (0 .. 3) { 1926 for (0 .. 3) {
1934 my $neigh = $map->tile_path ($_) 1927 my $neigh = $map->tile_path ($_)
1935 or next; 1928 or next;
1936 $neigh = find $neigh, $map 1929 $neigh = find $neigh, $map
1937 or next; 1930 or next;
1938 $neigh->load; 1931 $neigh->load;
1939 1932
1933 push @neigh,
1940 push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh], 1934 [$neigh->tile_path (($_ + 3) % 4), $neigh],
1941 [$neigh->tile_path (($_ + 1) % 4), $neigh]; 1935 [$neigh->tile_path (($_ + 1) % 4), $neigh];
1942 } 1936 }
1943 1937
1944 for (@diag) { 1938 for (grep defined $_->[0], @neigh) {
1939 my ($path, $origin) = @$_;
1945 my $neigh = find @$_ 1940 my $neigh = find $path, $origin
1946 or next; 1941 or next;
1947 $neigh->load; 1942 $neigh->load;
1948 } 1943 }
1949} 1944}
1950 1945
2213 2208
2214 } else { 2209 } else {
2215 my $pl = $self->contr; 2210 my $pl = $self->contr;
2216 2211
2217 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) { 2212 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2218 my $diag = $pl->{npc_dialog}; 2213 my $dialog = $pl->{npc_dialog};
2219 $diag->{pl}->ext_reply ( 2214 $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2220 $diag->{id},
2221 msgtype => "reply",
2222 msg => $diag->{pl}->expand_cfpod ($msg),
2223 add_topics => []
2224 );
2225 2215
2226 } else { 2216 } else {
2227 $msg = $npc->name . " says: $msg" if $npc; 2217 $msg = $npc->name . " says: $msg" if $npc;
2228 $self->message ($msg, $flags); 2218 $self->message ($msg, $flags);
2229 } 2219 }
2230 } 2220 }
2221}
2222
2223=item $object->send_msg ($channel, $msg, $color, [extra...])
2224
2225=cut
2226
2227sub cf::object::send_msg {
2228 my $pl = shift->contr
2229 or return;
2230 $pl->send_msg (@_);
2231} 2231}
2232 2232
2233=item $player_object->may ("access") 2233=item $player_object->may ("access")
2234 2234
2235Returns wether the given player is authorized to access resource "access" 2235Returns wether the given player is authorized to access resource "access"
2314 # use -1 or undef as default coordinates, not 0, 0 2314 # use -1 or undef as default coordinates, not 0, 0
2315 ($x, $y) = ($map->enter_x, $map->enter_y) 2315 ($x, $y) = ($map->enter_x, $map->enter_y)
2316 if $x <=0 && $y <= 0; 2316 if $x <=0 && $y <= 0;
2317 2317
2318 $map->load; 2318 $map->load;
2319 $map->load_diag; 2319 $map->load_neighbours;
2320 2320
2321 return unless $self->contr->active; 2321 return unless $self->contr->active;
2322 $self->activate_recursive; 2322 $self->activate_recursive;
2323 2323
2324 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2324 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2500sub cf::client::send_msg { 2500sub cf::client::send_msg {
2501 my ($self, $channel, $msg, $color, @extra) = @_; 2501 my ($self, $channel, $msg, $color, @extra) = @_;
2502 2502
2503 $msg = $self->pl->expand_cfpod ($msg); 2503 $msg = $self->pl->expand_cfpod ($msg);
2504 2504
2505 $color &= ~cf::NDI_UNIQUE; # just in case... 2505 $color &= cf::NDI_CLIENT_MASK; # just in case...
2506 2506
2507 if (ref $channel) { 2507 if (ref $channel) {
2508 # send meta info to client, if not yet sent 2508 # send meta info to client, if not yet sent
2509 unless (exists $self->{channel}{$channel->{id}}) { 2509 unless (exists $self->{channel}{$channel->{id}}) {
2510 $self->{channel}{$channel->{id}} = $channel; 2510 $self->{channel}{$channel->{id}} = $channel;
2511 $self->ext_event (channel_info => %$channel); 2511 $self->ext_msg (channel_info => $channel);
2512 } 2512 }
2513 2513
2514 $channel = $channel->{id}; 2514 $channel = $channel->{id};
2515 } 2515 }
2516 2516
2517 return unless @extra || length $msg; 2517 return unless @extra || length $msg;
2518 2518
2519 if ($self->can_msg) { 2519 if ($self->can_msg) {
2520 # default colour, mask it out
2521 $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2522 if $color & cf::NDI_DEF;
2523
2520 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $channel, $msg, @extra])); 2524 $self->send_packet ("msg " . $self->{json_coder}->encode (
2525 [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2521 } else { 2526 } else {
2522 # replace some tags by gcfclient-compatible ones
2523 for ($msg) {
2524 1 while
2525 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2526 || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2527 || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2528 || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2529 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2530 }
2531
2532 if ($color >= 0) { 2527 if ($color >= 0) {
2528 # replace some tags by gcfclient-compatible ones
2529 for ($msg) {
2530 1 while
2531 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2532 || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2533 || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2534 || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2535 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2536 }
2537
2538 $color &= cf::NDI_COLOR_MASK;
2539
2540 utf8::encode $msg;
2541
2533 if (0 && $msg =~ /\[/) { 2542 if (0 && $msg =~ /\[/) {
2543 # COMMAND/INFO
2534 $self->send_packet ("drawextinfo $color 4 0 $msg") 2544 $self->send_packet ("drawextinfo $color 10 8 $msg")
2535 } else { 2545 } else {
2536 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g; 2546 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2537 $self->send_packet ("drawinfo $color $msg") 2547 $self->send_packet ("drawinfo $color $msg")
2538 } 2548 }
2539 } 2549 }
2540 } 2550 }
2541} 2551}
2542 2552
2543=item $client->ext_event ($type, %msg) 2553=item $client->ext_msg ($type, @msg)
2544 2554
2545Sends an ext event to the client. 2555Sends an ext event to the client.
2546 2556
2547=cut 2557=cut
2548 2558
2549sub cf::client::ext_event($$%) { 2559sub cf::client::ext_msg($$@) {
2550 my ($self, $type, %msg) = @_; 2560 my ($self, $type, @msg) = @_;
2551 2561
2552 return unless $self->extcmd; 2562 my $extcmd = $self->extcmd;
2553 2563
2564 if ($extcmd == 2) {
2565 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2566 } elsif ($extcmd == 1) { # TODO: remove
2554 $msg{msgtype} = "event_$type"; 2567 push @msg, msgtype => "event_$type";
2555 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg)); 2568 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2569 }
2556} 2570}
2557 2571
2558=item $success = $client->query ($flags, "text", \&cb) 2572=item $success = $client->query ($flags, "text", \&cb)
2559 2573
2560Queues a query to the client, calling the given callback with 2574Queues a query to the client, calling the given callback with
2615 my ($ns, $buf) = @_; 2629 my ($ns, $buf) = @_;
2616 2630
2617 my $msg = eval { $ns->{json_coder}->decode ($buf) }; 2631 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2618 2632
2619 if (ref $msg) { 2633 if (ref $msg) {
2634 my ($type, $reply, @payload) =
2635 "ARRAY" eq ref $msg
2636 ? @$msg
2637 : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2638
2620 if (my $cb = $EXTICMD{$msg->{msgtype}}) { 2639 if (my $cb = $EXTICMD{$type}) {
2621 if (my %reply = $cb->($ns, $msg)) { 2640 my @reply = $cb->($ns, @payload);
2622 $reply{msgid} = $msg->{msgid}; 2641
2623 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply)); 2642 $ns->ext_reply ($reply, @reply)
2624 } 2643 if $reply;
2625 } 2644 }
2626 } else { 2645 } else {
2627 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; 2646 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2628 } 2647 }
2629 2648
2677our $safe = new Safe "safe"; 2696our $safe = new Safe "safe";
2678our $safe_hole = new Safe::Hole; 2697our $safe_hole = new Safe::Hole;
2679 2698
2680$SIG{FPE} = 'IGNORE'; 2699$SIG{FPE} = 'IGNORE';
2681 2700
2682$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 2701$safe->permit_only (Opcode::opset qw(
2702 :base_core :base_mem :base_orig :base_math
2703 grepstart grepwhile mapstart mapwhile
2704 sort time
2705));
2683 2706
2684# here we export the classes and methods available to script code 2707# here we export the classes and methods available to script code
2685 2708
2686=pod 2709=pod
2687 2710
2688The following functions and methods are available within a safe environment: 2711The following functions and methods are available within a safe environment:
2689 2712
2690 cf::object 2713 cf::object
2691 contr pay_amount pay_player map x y force_find force_add 2714 contr pay_amount pay_player map x y force_find force_add
2692 insert remove 2715 insert remove name archname title slaying race
2693 2716
2694 cf::object::player 2717 cf::object::player
2695 player 2718 player
2696 2719
2697 cf::player 2720 cf::player
2702 2725
2703=cut 2726=cut
2704 2727
2705for ( 2728for (
2706 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y 2729 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2707 insert remove)], 2730 insert remove inv name archname title slaying race)],
2708 ["cf::object::player" => qw(player)], 2731 ["cf::object::player" => qw(player)],
2709 ["cf::player" => qw(peaceful)], 2732 ["cf::player" => qw(peaceful)],
2710 ["cf::map" => qw(trigger)], 2733 ["cf::map" => qw(trigger)],
2711) { 2734) {
2712 no strict 'refs'; 2735 no strict 'refs';
2844 # TODO: for gcfclient pleasure, we should give resources 2867 # TODO: for gcfclient pleasure, we should give resources
2845 # that gcfclient doesn't grok a >10000 face index. 2868 # that gcfclient doesn't grok a >10000 face index.
2846 my $res = $facedata->{resource}; 2869 my $res = $facedata->{resource};
2847 my $enc = JSON::XS->new->utf8->canonical; 2870 my $enc = JSON::XS->new->utf8->canonical;
2848 2871
2872 my $soundconf = delete $res->{"res/sound.conf"};
2873
2849 while (my ($name, $info) = each %$res) { 2874 while (my ($name, $info) = each %$res) {
2850 my $meta = $enc->encode ({ 2875 my $meta = $enc->encode ({
2851 name => $name, 2876 name => $name,
2852 type => $info->{type}, 2877 %{ $info->{meta} || {} },
2853 copyright => $info->{copyright}, #TODO#
2854 }); 2878 });
2855 2879
2856 my $idx = (cf::face::find $name) || cf::face::alloc $name; 2880 my $idx = (cf::face::find $name) || cf::face::alloc $name;
2857 2881
2858 if ($name =~ /\.jpg$/) { 2882 if ($info->{type} & 1) {
2859 cf::face::set_data $idx, 0, $info->{data}, $info->{chksum};#d# temp hack 2883 # prepend meta info
2860 cf::face::set_data $idx, 1, $info->{data}, $info->{chksum};#d# temp hack 2884
2861 } else {
2862 my $data = pack "(w/a*)*", $meta, $info->{data}; 2885 my $data = pack "(w/a*)*", $meta, $info->{data};
2863 my $chk = Digest::MD5::md5 "$info->{chksum},$meta"; # mangle data checksum and metadata 2886 my $chk = Digest::MD5::md5 "$info->{chksum},$meta"; # mangle data checksum and metadata
2864 2887
2865 cf::face::set_type $idx, 1;
2866 cf::face::set_data $idx, 0, $data, $chk; 2888 cf::face::set_data $idx, 0, $data, $chk;
2889 } else {
2890 cf::face::set_data $idx, 0, $info->{data}, $info->{chksum};
2867 } 2891 }
2868 2892
2893 cf::face::set_type $idx, $info->{type};
2894
2869 cf::cede_to_tick; 2895 cf::cede_to_tick;
2870 } 2896 }
2897
2898 if ($soundconf) {
2899 $soundconf = $enc->decode (delete $soundconf->{data});
2900
2901 for (0 .. SOUND_CAST_SPELL_0 - 1) {
2902 my $sound = $soundconf->{compat}[$_]
2903 or next;
2904
2905 my $face = cf::face::find "sound/$sound->[1]";
2906 cf::sound::set $sound->[0] => $face;
2907 cf::sound::old_sound_index $_, $face; # gcfclient-compat
2908 }
2909
2910 while (my ($k, $v) = each %{$soundconf->{event}}) {
2911 my $face = cf::face::find "sound/$v";
2912 cf::sound::set $k => $face;
2913 }
2914 }
2871 } 2915 }
2872 2916
2873 1 2917 1
2874} 2918}
2919
2920register_exticmd fx_want => sub {
2921 my ($ns, $want) = @_;
2922
2923 while (my ($k, $v) = each %$want) {
2924 $ns->fx_want ($k, $v);
2925 }
2926};
2875 2927
2876sub reload_regions { 2928sub reload_regions {
2877 load_resource_file "$MAPDIR/regions" 2929 load_resource_file "$MAPDIR/regions"
2878 or die "unable to load regions file\n"; 2930 or die "unable to load regions file\n";
2879 2931

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines