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.337 by root, Tue Aug 21 00:54:01 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 $self->ns->ext_reply ($id, @msg)
1526 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1527} 1511}
1528 1512
1529=item $player->ext_event ($type, %msg) 1513=item $player->ext_msg ($type, @msg)
1530 1514
1531Sends an ext event to the client. 1515Sends an ext event to the client.
1532 1516
1533=cut 1517=cut
1534 1518
1535sub ext_event($$%) { 1519sub ext_msg($$@) {
1536 my ($self, $type, %msg) = @_; 1520 my ($self, $type, @msg) = @_;
1537 1521
1538 $self->ns->ext_event ($type, %msg); 1522 $self->ns->ext_msg ($type, @msg);
1539} 1523}
1540 1524
1541=head3 cf::region 1525=head3 cf::region
1542 1526
1543=over 4 1527=over 4
1904 $self->set_darkness_map; 1888 $self->set_darkness_map;
1905 Coro::cede; 1889 Coro::cede;
1906 $self->activate; 1890 $self->activate;
1907 } 1891 }
1908 1892
1893 $self->{last_save} = $cf::RUNTIME;
1894 $self->last_access ($cf::RUNTIME);
1895
1909 $self->in_memory (cf::MAP_IN_MEMORY); 1896 $self->in_memory (cf::MAP_IN_MEMORY);
1910 } 1897 }
1911 1898
1912 $self->post_load; 1899 $self->post_load;
1913} 1900}
1923 1910
1924 $self 1911 $self
1925} 1912}
1926 1913
1927# find and load all maps in the 3x3 area around a map 1914# find and load all maps in the 3x3 area around a map
1928sub load_diag { 1915sub load_neighbours {
1929 my ($map) = @_; 1916 my ($map) = @_;
1930 1917
1931 my @diag; # diagonal neighbours 1918 my @neigh; # diagonal neighbours
1932 1919
1933 for (0 .. 3) { 1920 for (0 .. 3) {
1934 my $neigh = $map->tile_path ($_) 1921 my $neigh = $map->tile_path ($_)
1935 or next; 1922 or next;
1936 $neigh = find $neigh, $map 1923 $neigh = find $neigh, $map
1937 or next; 1924 or next;
1938 $neigh->load; 1925 $neigh->load;
1939 1926
1927 push @neigh,
1940 push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh], 1928 [$neigh->tile_path (($_ + 3) % 4), $neigh],
1941 [$neigh->tile_path (($_ + 1) % 4), $neigh]; 1929 [$neigh->tile_path (($_ + 1) % 4), $neigh];
1942 } 1930 }
1943 1931
1944 for (@diag) { 1932 for (grep defined $_->[0], @neigh) {
1933 my ($path, $origin) = @$_;
1945 my $neigh = find @$_ 1934 my $neigh = find $path, $origin
1946 or next; 1935 or next;
1947 $neigh->load; 1936 $neigh->load;
1948 } 1937 }
1949} 1938}
1950 1939
2213 2202
2214 } else { 2203 } else {
2215 my $pl = $self->contr; 2204 my $pl = $self->contr;
2216 2205
2217 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) { 2206 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2218 my $diag = $pl->{npc_dialog}; 2207 my $dialog = $pl->{npc_dialog};
2219 $diag->{pl}->ext_reply ( 2208 $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 2209
2226 } else { 2210 } else {
2227 $msg = $npc->name . " says: $msg" if $npc; 2211 $msg = $npc->name . " says: $msg" if $npc;
2228 $self->message ($msg, $flags); 2212 $self->message ($msg, $flags);
2229 } 2213 }
2230 } 2214 }
2215}
2216
2217=item $object->send_msg ($channel, $msg, $color, [extra...])
2218
2219=cut
2220
2221sub cf::object::send_msg {
2222 my $pl = shift->contr
2223 or return;
2224 $pl->send_msg (@_);
2231} 2225}
2232 2226
2233=item $player_object->may ("access") 2227=item $player_object->may ("access")
2234 2228
2235Returns wether the given player is authorized to access resource "access" 2229Returns wether the given player is authorized to access resource "access"
2314 # use -1 or undef as default coordinates, not 0, 0 2308 # use -1 or undef as default coordinates, not 0, 0
2315 ($x, $y) = ($map->enter_x, $map->enter_y) 2309 ($x, $y) = ($map->enter_x, $map->enter_y)
2316 if $x <=0 && $y <= 0; 2310 if $x <=0 && $y <= 0;
2317 2311
2318 $map->load; 2312 $map->load;
2319 $map->load_diag; 2313 $map->load_neighbours;
2320 2314
2321 return unless $self->contr->active; 2315 return unless $self->contr->active;
2322 $self->activate_recursive; 2316 $self->activate_recursive;
2323 2317
2324 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2318 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2500sub cf::client::send_msg { 2494sub cf::client::send_msg {
2501 my ($self, $channel, $msg, $color, @extra) = @_; 2495 my ($self, $channel, $msg, $color, @extra) = @_;
2502 2496
2503 $msg = $self->pl->expand_cfpod ($msg); 2497 $msg = $self->pl->expand_cfpod ($msg);
2504 2498
2505 $color &= ~cf::NDI_UNIQUE; # just in case... 2499 $color &= cf::NDI_CLIENT_MASK; # just in case...
2506 2500
2507 if (ref $channel) { 2501 if (ref $channel) {
2508 # send meta info to client, if not yet sent 2502 # send meta info to client, if not yet sent
2509 unless (exists $self->{channel}{$channel->{id}}) { 2503 unless (exists $self->{channel}{$channel->{id}}) {
2510 $self->{channel}{$channel->{id}} = $channel; 2504 $self->{channel}{$channel->{id}} = $channel;
2511 $self->ext_event (channel_info => %$channel); 2505 $self->ext_msg (channel_info => $channel);
2512 } 2506 }
2513 2507
2514 $channel = $channel->{id}; 2508 $channel = $channel->{id};
2515 } 2509 }
2516 2510
2517 return unless @extra || length $msg; 2511 return unless @extra || length $msg;
2518 2512
2519 if ($self->can_msg) { 2513 if ($self->can_msg) {
2514 # default colour, mask it out
2515 $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2516 if $color & cf::NDI_DEF;
2517
2520 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $channel, $msg, @extra])); 2518 $self->send_packet ("msg " . $self->{json_coder}->encode (
2519 [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2521 } else { 2520 } 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) { 2521 if ($color >= 0) {
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 $color &= cf::NDI_COLOR_MASK;
2533
2534 utf8::encode $msg;
2535
2533 if (0 && $msg =~ /\[/) { 2536 if (0 && $msg =~ /\[/) {
2537 # COMMAND/INFO
2534 $self->send_packet ("drawextinfo $color 4 0 $msg") 2538 $self->send_packet ("drawextinfo $color 10 8 $msg")
2535 } else { 2539 } else {
2536 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g; 2540 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2537 $self->send_packet ("drawinfo $color $msg") 2541 $self->send_packet ("drawinfo $color $msg")
2538 } 2542 }
2539 } 2543 }
2540 } 2544 }
2541} 2545}
2542 2546
2543=item $client->ext_event ($type, %msg) 2547=item $client->ext_msg ($type, @msg)
2544 2548
2545Sends an ext event to the client. 2549Sends an ext event to the client.
2546 2550
2547=cut 2551=cut
2548 2552
2549sub cf::client::ext_event($$%) { 2553sub cf::client::ext_msg($$@) {
2550 my ($self, $type, %msg) = @_; 2554 my ($self, $type, @msg) = @_;
2551 2555
2552 return unless $self->extcmd; 2556 my $extcmd = $self->extcmd;
2553 2557
2558 if ($extcmd == 2) {
2559 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2560 } elsif ($extcmd == 1) { # TODO: remove
2554 $msg{msgtype} = "event_$type"; 2561 push @msg, msgtype => "event_$type";
2555 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg)); 2562 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2563 }
2564}
2565
2566=item $client->ext_reply ($msgid, @msg)
2567
2568Sends an ext reply to the client.
2569
2570=cut
2571
2572sub cf::client::ext_reply($$@) {
2573 my ($self, $id, @msg) = @_;
2574
2575 if ($self->extcmd == 2) {
2576 $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2577 } elsif ($self->ns->extcmd == 1) {
2578 #TODO: version 1, remove
2579 unshift @msg, msgtype => "reply", msgid => $id;
2580 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2581 }
2556} 2582}
2557 2583
2558=item $success = $client->query ($flags, "text", \&cb) 2584=item $success = $client->query ($flags, "text", \&cb)
2559 2585
2560Queues a query to the client, calling the given callback with 2586Queues a query to the client, calling the given callback with
2615 my ($ns, $buf) = @_; 2641 my ($ns, $buf) = @_;
2616 2642
2617 my $msg = eval { $ns->{json_coder}->decode ($buf) }; 2643 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2618 2644
2619 if (ref $msg) { 2645 if (ref $msg) {
2646 my ($type, $reply, @payload) =
2647 "ARRAY" eq ref $msg
2648 ? @$msg
2649 : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2650
2620 if (my $cb = $EXTICMD{$msg->{msgtype}}) { 2651 if (my $cb = $EXTICMD{$type}) {
2621 if (my %reply = $cb->($ns, $msg)) { 2652 my @reply = $cb->($ns, @payload);
2622 $reply{msgid} = $msg->{msgid}; 2653
2623 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply)); 2654 $ns->ext_reply ($reply, @reply)
2624 } 2655 if $reply;
2625 } 2656 }
2626 } else { 2657 } else {
2627 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; 2658 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2628 } 2659 }
2629 2660
2677our $safe = new Safe "safe"; 2708our $safe = new Safe "safe";
2678our $safe_hole = new Safe::Hole; 2709our $safe_hole = new Safe::Hole;
2679 2710
2680$SIG{FPE} = 'IGNORE'; 2711$SIG{FPE} = 'IGNORE';
2681 2712
2682$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 2713$safe->permit_only (Opcode::opset qw(
2714 :base_core :base_mem :base_orig :base_math
2715 grepstart grepwhile mapstart mapwhile
2716 sort time
2717));
2683 2718
2684# here we export the classes and methods available to script code 2719# here we export the classes and methods available to script code
2685 2720
2686=pod 2721=pod
2687 2722
2688The following functions and methods are available within a safe environment: 2723The following functions and methods are available within a safe environment:
2689 2724
2690 cf::object 2725 cf::object
2691 contr pay_amount pay_player map x y force_find force_add 2726 contr pay_amount pay_player map x y force_find force_add
2692 insert remove 2727 insert remove name archname title slaying race
2693 2728
2694 cf::object::player 2729 cf::object::player
2695 player 2730 player
2696 2731
2697 cf::player 2732 cf::player
2702 2737
2703=cut 2738=cut
2704 2739
2705for ( 2740for (
2706 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y 2741 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2707 insert remove)], 2742 insert remove inv name archname title slaying race)],
2708 ["cf::object::player" => qw(player)], 2743 ["cf::object::player" => qw(player)],
2709 ["cf::player" => qw(peaceful)], 2744 ["cf::player" => qw(peaceful)],
2710 ["cf::map" => qw(trigger)], 2745 ["cf::map" => qw(trigger)],
2711) { 2746) {
2712 no strict 'refs'; 2747 no strict 'refs';
2788# the server's init and main functions 2823# the server's init and main functions
2789 2824
2790sub load_facedata($) { 2825sub load_facedata($) {
2791 my ($path) = @_; 2826 my ($path) = @_;
2792 2827
2828 my $enc = JSON::XS->new->utf8->canonical;
2829
2793 warn "loading facedata from $path\n"; 2830 warn "loading facedata from $path\n";
2794 2831
2795 my $facedata; 2832 my $facedata;
2796 0 < aio_load $path, $facedata 2833 0 < aio_load $path, $facedata
2797 or die "$path: $!"; 2834 or die "$path: $!";
2798 2835
2799 $facedata = Coro::Storable::thaw $facedata; 2836 $facedata = Coro::Storable::thaw $facedata;
2800 2837
2801 $facedata->{version} == 2 2838 $facedata->{version} == 2
2802 or cf::cleanup "$path: version mismatch, cannot proceed."; 2839 or cf::cleanup "$path: version mismatch, cannot proceed.";
2840
2841 # patch in the exptable
2842 $facedata->{resource}{"res/exp_table"} = {
2843 type => FT_RSRC,
2844 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
2845 };
2846 cf::cede_to_tick;
2803 2847
2804 { 2848 {
2805 my $faces = $facedata->{faceinfo}; 2849 my $faces = $facedata->{faceinfo};
2806 2850
2807 while (my ($face, $info) = each %$faces) { 2851 while (my ($face, $info) = each %$faces) {
2808 my $idx = (cf::face::find $face) || cf::face::alloc $face; 2852 my $idx = (cf::face::find $face) || cf::face::alloc $face;
2809 cf::face::set_visibility $idx, $info->{visibility}; 2853 cf::face::set_visibility $idx, $info->{visibility};
2810 cf::face::set_magicmap $idx, $info->{magicmap}; 2854 cf::face::set_magicmap $idx, $info->{magicmap};
2811 cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32}; 2855 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
2812 cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64}; 2856 cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
2813 2857
2814 cf::cede_to_tick; 2858 cf::cede_to_tick;
2815 } 2859 }
2816 2860
2817 while (my ($face, $info) = each %$faces) { 2861 while (my ($face, $info) = each %$faces) {
2842 2886
2843 { 2887 {
2844 # TODO: for gcfclient pleasure, we should give resources 2888 # TODO: for gcfclient pleasure, we should give resources
2845 # that gcfclient doesn't grok a >10000 face index. 2889 # that gcfclient doesn't grok a >10000 face index.
2846 my $res = $facedata->{resource}; 2890 my $res = $facedata->{resource};
2847 my $enc = JSON::XS->new->utf8->canonical; 2891
2892 my $soundconf = delete $res->{"res/sound.conf"};
2848 2893
2849 while (my ($name, $info) = each %$res) { 2894 while (my ($name, $info) = each %$res) {
2850 my $meta = $enc->encode ({
2851 name => $name,
2852 type => $info->{type},
2853 copyright => $info->{copyright}, #TODO#
2854 });
2855
2856 my $idx = (cf::face::find $name) || cf::face::alloc $name; 2895 my $idx = (cf::face::find $name) || cf::face::alloc $name;
2896 my $data;
2857 2897
2858 if ($name =~ /\.jpg$/) { 2898 if ($info->{type} & 1) {
2859 cf::face::set_data $idx, 0, $info->{data}, $info->{chksum};#d# temp hack 2899 # prepend meta info
2860 cf::face::set_data $idx, 1, $info->{data}, $info->{chksum};#d# temp hack 2900
2901 my $meta = $enc->encode ({
2902 name => $name,
2903 %{ $info->{meta} || {} },
2904 });
2905
2906 $data = pack "(w/a*)*", $meta, $info->{data};
2861 } else { 2907 } else {
2862 my $data = pack "(w/a*)*", $meta, $info->{data}; 2908 $data = $info->{data};
2863 my $chk = Digest::MD5::md5 "$info->{chksum},$meta"; # mangle data checksum and metadata
2864
2865 cf::face::set_type $idx, 1;
2866 cf::face::set_data $idx, 0, $data, $chk;
2867 } 2909 }
2868 2910
2911 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
2912 cf::face::set_type $idx, $info->{type};
2913
2869 cf::cede_to_tick; 2914 cf::cede_to_tick;
2870 } 2915 }
2916
2917 if ($soundconf) {
2918 $soundconf = $enc->decode (delete $soundconf->{data});
2919
2920 for (0 .. SOUND_CAST_SPELL_0 - 1) {
2921 my $sound = $soundconf->{compat}[$_]
2922 or next;
2923
2924 my $face = cf::face::find "sound/$sound->[1]";
2925 cf::sound::set $sound->[0] => $face;
2926 cf::sound::old_sound_index $_, $face; # gcfclient-compat
2927 }
2928
2929 while (my ($k, $v) = each %{$soundconf->{event}}) {
2930 my $face = cf::face::find "sound/$v";
2931 cf::sound::set $k => $face;
2932 }
2933 }
2871 } 2934 }
2872 2935
2873 1 2936 1
2874} 2937}
2938
2939register_exticmd fx_want => sub {
2940 my ($ns, $want) = @_;
2941
2942 while (my ($k, $v) = each %$want) {
2943 $ns->fx_want ($k, $v);
2944 }
2945};
2875 2946
2876sub reload_regions { 2947sub reload_regions {
2877 load_resource_file "$MAPDIR/regions" 2948 load_resource_file "$MAPDIR/regions"
2878 or die "unable to load regions file\n"; 2949 or die "unable to load regions file\n";
2879 2950

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines