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.300 by root, Tue Jul 10 06:44:29 2007 UTC vs.
Revision 1.314 by root, Mon Jul 23 16:53:15 2007 UTC

29use Fcntl; 29use Fcntl;
30use YAML::Syck (); 30use YAML::Syck ();
31use IO::AIO 2.32 (); 31use IO::AIO 2.32 ();
32use Time::HiRes; 32use Time::HiRes;
33use Compress::LZF; 33use Compress::LZF;
34use Digest::MD5 ();
34 35
35# configure various modules to our taste 36# configure various modules to our taste
36# 37#
37$Storable::canonical = 1; # reduce rsync transfers 38$Storable::canonical = 1; # reduce rsync transfers
38Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator 39Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
165=item cf::wait_for_tick, cf::wait_for_tick_begin 166=item cf::wait_for_tick, cf::wait_for_tick_begin
166 167
167These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only 168These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
168returns directly I<after> the tick processing (and consequently, can only wake one process 169returns directly I<after> the tick processing (and consequently, can only wake one process
169per tick), while cf::wait_for_tick wakes up all waiters after tick processing. 170per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
171
172=item @cf::INVOKE_RESULTS
173
174This array contains the results of the last C<invoke ()> call. When
175C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
176that call.
170 177
171=back 178=back
172 179
173=cut 180=cut
174 181
315 }; 322 };
316 $TICK_WATCHER->stop; 323 $TICK_WATCHER->stop;
317 $guard 324 $guard
318} 325}
319 326
327=item cf::get_slot $time[, $priority]
328
329Allocate $time seconds of blocking CPU time at priority C<$priority>:
330This 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.
332
333Useful for short background jobs.
334
335=cut
336
337our @SLOT_QUEUE;
338our $SLOT_QUEUE;
339
340$SLOT_QUEUE->cancel if $SLOT_QUEUE;
341$SLOT_QUEUE = Coro::async {
342 my $signal = new Coro::Signal;
343
344 while () {
345 next_job:
346 my $avail = cf::till_tick;
347 if ($avail > 0.01) {
348 for (0 .. $#SLOT_QUEUE) {
349 if ($SLOT_QUEUE[$_][0] < $avail) {
350 my $job = splice @SLOT_QUEUE, $_, 1, ();
351 $job->[2]->send;
352 Coro::cede;
353 goto next_job;
354 }
355 }
356 }
357
358 if (@SLOT_QUEUE) {
359 # we do not use wait_For_tick() as it returns immediately when tick is inactive
360 push @cf::WAIT_FOR_TICK, $signal;
361 $signal->wait;
362 } else {
363 Coro::schedule;
364 }
365 }
366};
367
368sub get_slot($;$) {
369 my ($time, $pri) = @_;
370
371 push @SLOT_QUEUE, [$time, $pri, my $sig = new Coro::Signal];
372 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
373 $SLOT_QUEUE->ready;
374 $sig->wait;
375}
376
320=item cf::async { BLOCK } 377=item cf::async { BLOCK }
321 378
322Currently the same as Coro::async_pool, meaning you cannot use 379Currently the same as Coro::async_pool, meaning you cannot use
323C<on_destroy>, C<join> or other gimmicks on these coroutines. The only 380C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
324thing you are allowed to do is call C<prio> on it. 381thing you are allowed to do is call C<prio> on it.
865 "; 922 ";
866 die if $@; 923 die if $@;
867} 924}
868 925
869our $override; 926our $override;
870our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals? 927our @INVOKE_RESULTS = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
871 928
872sub override { 929sub override {
873 $override = 1; 930 $override = 1;
874 @invoke_results = (); 931 @INVOKE_RESULTS = (@_);
875} 932}
876 933
877sub do_invoke { 934sub do_invoke {
878 my $event = shift; 935 my $event = shift;
879 my $callbacks = shift; 936 my $callbacks = shift;
880 937
881 @invoke_results = (); 938 @INVOKE_RESULTS = ();
882 939
883 local $override; 940 local $override;
884 941
885 for (@$callbacks) { 942 for (@$callbacks) {
886 eval { &{$_->[1]} }; 943 eval { &{$_->[1]} };
903 960
904Generate an object-specific event with the given arguments. 961Generate an object-specific event with the given arguments.
905 962
906This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be 963This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
907removed in future versions), and there is no public API to access override 964removed in future versions), and there is no public API to access override
908results (if you must, access C<@cf::invoke_results> directly). 965results (if you must, access C<@cf::INVOKE_RESULTS> directly).
909 966
910=back 967=back
911 968
912=cut 969=cut
913 970
914############################################################################# 971#############################################################################
915# object support 972# object support
973#
974
975sub _can_merge {
976 my ($ob1, $ob2) = @_;
977
978 local $Storable::canonical = 1;
979 my $fob1 = Storable::freeze $ob1;
980 my $fob2 = Storable::freeze $ob2;
981
982 $fob1 eq $fob2
983}
916 984
917sub reattach { 985sub reattach {
918 # basically do the same as instantiate, without calling instantiate 986 # basically do the same as instantiate, without calling instantiate
919 my ($obj) = @_; 987 my ($obj) = @_;
920 988
1435 s/\s+\n/\n/g; # ws line-ends 1503 s/\s+\n/\n/g; # ws line-ends
1436 s/\n\n+/\n/g; # double lines 1504 s/\n\n+/\n/g; # double lines
1437 s/^\n+//; # beginning lines 1505 s/^\n+//; # beginning lines
1438 s/\n+$//; # ending lines 1506 s/\n+$//; # ending lines
1439 1507
1440 warn $_;#d#
1441 $_ 1508 $_
1442} 1509}
1443 1510
1444sub hintmode { 1511sub hintmode {
1445 $_[0]{hintmode} = $_[1] if @_ > 1; 1512 $_[0]{hintmode} = $_[1] if @_ > 1;
1489 my ($path) = @_; 1556 my ($path) = @_;
1490 1557
1491 my ($match, $specificity); 1558 my ($match, $specificity);
1492 1559
1493 for my $region (list) { 1560 for my $region (list) {
1494 if ($region->match && $path =~ $region->match) { 1561 if ($region->{match} && $path =~ $region->{match}) {
1495 ($match, $specificity) = ($region, $region->specificity) 1562 ($match, $specificity) = ($region, $region->specificity)
1496 if $region->specificity > $specificity; 1563 if $region->specificity > $specificity;
1497 } 1564 }
1498 } 1565 }
1499 1566
1619 $self->init; # pass $1 etc. 1686 $self->init; # pass $1 etc.
1620 return $self; 1687 return $self;
1621 } 1688 }
1622 } 1689 }
1623 1690
1624 Carp::carp "unable to resolve path '$path' (base '$base')."; 1691 Carp::cluck "unable to resolve path '$path' (base '$base').";
1625 () 1692 ()
1626} 1693}
1627 1694
1628sub init { 1695sub init {
1629 my ($self) = @_; 1696 my ($self) = @_;
1907 1974
1908 undef $MAP_PREFETCH{$path}; 1975 undef $MAP_PREFETCH{$path};
1909 $MAP_PREFETCHER ||= cf::async { 1976 $MAP_PREFETCHER ||= cf::async {
1910 while (%MAP_PREFETCH) { 1977 while (%MAP_PREFETCH) {
1911 for my $path (keys %MAP_PREFETCH) { 1978 for my $path (keys %MAP_PREFETCH) {
1912 my $map = find $path 1979 if (my $map = find $path) {
1913 or next;
1914 $map->load; 1980 $map->load;
1981 }
1915 1982
1916 delete $MAP_PREFETCH{$path}; 1983 delete $MAP_PREFETCH{$path};
1917 } 1984 }
1918 } 1985 }
1919 undef $MAP_PREFETCHER; 1986 undef $MAP_PREFETCHER;
2256 2323
2257 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2324 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2258 $self->enter_map ($map, $x, $y); 2325 $self->enter_map ($map, $x, $y);
2259} 2326}
2260 2327
2261=item $player_object->goto ($path, $x, $y[, $check->($map)]) 2328=item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2262 2329
2263Moves the player to the given map-path and coordinates by first freezing 2330Moves the player to the given map-path and coordinates by first freezing
2264her, loading and preparing them map, calling the provided $check callback 2331her, loading and preparing them map, calling the provided $check callback
2265that has to return the map if sucecssful, and then unfreezes the player on 2332that has to return the map if sucecssful, and then unfreezes the player on
2266the new (success) or old (failed) map position. 2333the new (success) or old (failed) map position. In either case, $done will
2334be called at the end of this process.
2267 2335
2268=cut 2336=cut
2269 2337
2270our $GOTOGEN; 2338our $GOTOGEN;
2271 2339
2272sub cf::object::player::goto { 2340sub cf::object::player::goto {
2273 my ($self, $path, $x, $y, $check) = @_; 2341 my ($self, $path, $x, $y, $check, $done) = @_;
2274 2342
2275 # do generation counting so two concurrent goto's will be executed in-order 2343 # do generation counting so two concurrent goto's will be executed in-order
2276 my $gen = $self->{_goto_generation} = ++$GOTOGEN; 2344 my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2277 2345
2278 $self->enter_link; 2346 $self->enter_link;
2298 2366
2299 if ($gen == $self->{_goto_generation}) { 2367 if ($gen == $self->{_goto_generation}) {
2300 delete $self->{_goto_generation}; 2368 delete $self->{_goto_generation};
2301 $self->leave_link ($map, $x, $y); 2369 $self->leave_link ($map, $x, $y);
2302 } 2370 }
2371
2372 $done->() if $done;
2303 })->prio (1); 2373 })->prio (1);
2304} 2374}
2305 2375
2306=item $player_object->enter_exit ($exit_object) 2376=item $player_object->enter_exit ($exit_object)
2307 2377
2416 2486
2417 utf8::encode $text; 2487 utf8::encode $text;
2418 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text); 2488 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2419} 2489}
2420 2490
2421=item $client->send_msg ($color, $type, $msg, [extra...]) 2491=item $client->send_msg ($channel, $msg, $color, [extra...])
2422 2492
2423Send a drawinfo or msg packet to the client, formatting the msg for the 2493Send a drawinfo or msg packet to the client, formatting the msg for the
2424client if neccessary. C<$type> should be a string identifying the type of 2494client if neccessary. C<$type> should be a string identifying the type of
2425the message, with C<log> being the default. If C<$color> is negative, suppress 2495the message, with C<log> being the default. If C<$color> is negative, suppress
2426the message unless the client supports the msg packet. 2496the message unless the client supports the msg packet.
2427 2497
2428=cut 2498=cut
2429 2499
2430sub cf::client::send_msg { 2500sub cf::client::send_msg {
2431 my ($self, $color, $type, $msg, @extra) = @_; 2501 my ($self, $channel, $msg, $color, @extra) = @_;
2432 2502
2433 $msg = $self->pl->expand_cfpod ($msg); 2503 $msg = $self->pl->expand_cfpod ($msg);
2434 2504
2505 $color &= ~cf::NDI_UNIQUE; # just in case...
2506
2507 if (ref $channel) {
2508 # send meta info to client, if not yet sent
2509 unless (exists $self->{channel}{$channel->{id}}) {
2510 $self->{channel}{$channel->{id}} = $channel;
2511 $self->ext_event (channel_info => %$channel);
2512 }
2513
2514 $channel = $channel->{id};
2515 }
2516
2435 return unless @extra || length $msg; 2517 return unless @extra || length $msg;
2436 2518
2437 if ($self->can_msg) { 2519 if ($self->can_msg) {
2438 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $type, $msg, @extra])); 2520 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $channel, $msg, @extra]));
2439 } else { 2521 } else {
2440 # replace some tags by gcfclient-compatible ones 2522 # replace some tags by gcfclient-compatible ones
2441 for ($msg) { 2523 for ($msg) {
2442 1 while 2524 1 while
2443 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/ 2525 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2464 2546
2465=cut 2547=cut
2466 2548
2467sub cf::client::ext_event($$%) { 2549sub cf::client::ext_event($$%) {
2468 my ($self, $type, %msg) = @_; 2550 my ($self, $type, %msg) = @_;
2551
2552 return unless $self->extcmd;
2469 2553
2470 $msg{msgtype} = "event_$type"; 2554 $msg{msgtype} = "event_$type";
2471 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg)); 2555 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg));
2472} 2556}
2473 2557
2720 { 2804 {
2721 my $faces = $facedata->{faceinfo}; 2805 my $faces = $facedata->{faceinfo};
2722 2806
2723 while (my ($face, $info) = each %$faces) { 2807 while (my ($face, $info) = each %$faces) {
2724 my $idx = (cf::face::find $face) || cf::face::alloc $face; 2808 my $idx = (cf::face::find $face) || cf::face::alloc $face;
2725 cf::face::set $idx, $info->{visibility}, $info->{magicmap}; 2809 cf::face::set_visibility $idx, $info->{visibility};
2810 cf::face::set_magicmap $idx, $info->{magicmap};
2726 cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32}; 2811 cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32};
2727 cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64}; 2812 cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64};
2728 Coro::cede; 2813
2814 cf::cede_to_tick;
2729 } 2815 }
2730 2816
2731 while (my ($face, $info) = each %$faces) { 2817 while (my ($face, $info) = each %$faces) {
2732 next unless $info->{smooth}; 2818 next unless $info->{smooth};
2733 my $idx = cf::face::find $face 2819 my $idx = cf::face::find $face
2734 or next; 2820 or next;
2735 if (my $smooth = cf::face::find $info->{smooth}) { 2821 if (my $smooth = cf::face::find $info->{smooth}) {
2822 cf::face::set_smooth $idx, $smooth;
2736 cf::face::set_smooth $idx, $smooth, $info->{smoothlevel}; 2823 cf::face::set_smoothlevel $idx, $info->{smoothlevel};
2737 } else { 2824 } else {
2738 warn "smooth face '$info->{smooth}' not found for face '$face'"; 2825 warn "smooth face '$info->{smooth}' not found for face '$face'";
2739 } 2826 }
2740 Coro::cede; 2827
2828 cf::cede_to_tick;
2741 } 2829 }
2742 } 2830 }
2743 2831
2744 { 2832 {
2745 my $anims = $facedata->{animinfo}; 2833 my $anims = $facedata->{animinfo};
2746 2834
2747 while (my ($anim, $info) = each %$anims) { 2835 while (my ($anim, $info) = each %$anims) {
2748 cf::anim::set $anim, $info->{frames}, $info->{facings}; 2836 cf::anim::set $anim, $info->{frames}, $info->{facings};
2749 Coro::cede; 2837 cf::cede_to_tick;
2750 } 2838 }
2751 2839
2752 cf::anim::invalidate_all; # d'oh 2840 cf::anim::invalidate_all; # d'oh
2841 }
2842
2843 {
2844 # TODO: for gcfclient pleasure, we should give resources
2845 # that gcfclient doesn't grok a >10000 face index.
2846 my $res = $facedata->{resource};
2847 my $enc = JSON::XS->new->utf8->canonical;
2848
2849 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;
2857
2858 if ($name =~ /\.jpg$/) {
2859 cf::face::set_data $idx, 0, $info->{data}, $info->{chksum};#d# temp hack
2860 cf::face::set_data $idx, 1, $info->{data}, $info->{chksum};#d# temp hack
2861 } else {
2862 my $data = pack "(w/a*)*", $meta, $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 }
2868
2869 cf::cede_to_tick;
2870 }
2753 } 2871 }
2754 2872
2755 1 2873 1
2756} 2874}
2757 2875
2758sub reload_regions { 2876sub reload_regions {
2759 load_resource_file "$MAPDIR/regions" 2877 load_resource_file "$MAPDIR/regions"
2760 or die "unable to load regions file\n"; 2878 or die "unable to load regions file\n";
2879
2880 for (cf::region::list) {
2881 $_->{match} = qr/$_->{match}/
2882 if exists $_->{match};
2883 }
2761} 2884}
2762 2885
2763sub reload_facedata { 2886sub reload_facedata {
2764 load_facedata "$DATADIR/facedata" 2887 load_facedata "$DATADIR/facedata"
2765 or die "unable to load facedata\n"; 2888 or die "unable to load facedata\n";

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines