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.596 by root, Fri Nov 9 20:37:57 2012 UTC vs.
Revision 1.602 by root, Sun Nov 11 18:21:21 2012 UTC

58use BDB (); 58use BDB ();
59use Data::Dumper; 59use Data::Dumper;
60use Fcntl; 60use Fcntl;
61use YAML::XS (); 61use YAML::XS ();
62use IO::AIO (); 62use IO::AIO ();
63use Time::HiRes;
64use Compress::LZF; 63use Compress::LZF;
65use Digest::MD5 (); 64use Digest::MD5 ();
66 65
67AnyEvent::detect; 66AnyEvent::detect;
68 67
252Configuration for the server, loaded from C</etc/deliantra-server/config>, or 251Configuration for the server, loaded from C</etc/deliantra-server/config>, or
253from wherever your confdir points to. 252from wherever your confdir points to.
254 253
255=item cf::wait_for_tick, cf::wait_for_tick_begin 254=item cf::wait_for_tick, cf::wait_for_tick_begin
256 255
257These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only 256These are functions that inhibit the current coroutine one tick.
258returns directly I<after> the tick processing (and consequently, can only wake one thread 257cf::wait_for_tick_begin only returns directly I<after> the tick
258processing (and consequently, can only wake one thread per tick), while
259per tick), while cf::wait_for_tick wakes up all waiters after tick processing. 259cf::wait_for_tick wakes up all waiters after tick processing.
260 260
261Note that cf::Wait_for_tick will immediately return when the server is not 261Note that cf::wait_for_tick will immediately return when the server is not
262ticking, making it suitable for small pauses in threads that need to run 262ticking, making it suitable for small pauses in threads that need to run
263when the server is paused. If that is not applicable (i.e. you I<really> 263when the server is paused. If that is not applicable (i.e. you I<really>
264want to wait, use C<$cf::WAIT_FOR_TICK>). 264want to wait, use C<$cf::WAIT_FOR_TICK>).
265 265
266=item $cf::WAIT_FOR_TICK 266=item $cf::WAIT_FOR_TICK
901 901
902 return db_get cache => "$id/data"; 902 return db_get cache => "$id/data";
903 } 903 }
904 } 904 }
905 905
906 my $t1 = Time::HiRes::time; 906 my $t1 = EV::time;
907 my $data = $process->(\@data); 907 my $data = $process->(\@data);
908 my $t2 = Time::HiRes::time; 908 my $t2 = EV::time;
909 909
910 info "cache: '$id' processed in ", $t2 - $t1, "s\n"; 910 info "cache: '$id' processed in ", $t2 - $t1, "s\n";
911 911
912 db_put cache => "$id/data", $data; 912 db_put cache => "$id/data", $data;
913 db_put cache => "$id/md5" , $md5; 913 db_put cache => "$id/md5" , $md5;
2654 2654
2655Creates and returns a persistent reference to an object that can be stored as a string. 2655Creates and returns a persistent reference to an object that can be stored as a string.
2656 2656
2657=item $ob = cf::object::deref ($refstring) 2657=item $ob = cf::object::deref ($refstring)
2658 2658
2659returns the objetc referenced by refstring. may return undef when it cnanot find the object, 2659returns the objetc referenced by refstring. may return undef when it cannot find the object,
2660even if the object actually exists. May block. 2660even if the object actually exists. May block.
2661 2661
2662=cut 2662=cut
2663 2663
2664sub deref { 2664sub deref {
3519=cut 3519=cut
3520 3520
3521############################################################################# 3521#############################################################################
3522# the server's init and main functions 3522# the server's init and main functions
3523 3523
3524our %FACEHASH; # hash => idx, #d# HACK for http server 3524{
3525 package cf::face;
3525 3526
3527 our %HASH; # hash => idx
3528 our @DATA; # dynamically-created facedata, only faceste 0 used
3529 our @FOFS; # file offset, if > 0
3530 our @SIZE; # size of face, in octets
3531 our @META; # meta hash of face, if any
3532 our $DATAFH; # facedata filehandle
3533
3526# internal api, not fianlised 3534 # internal api, not finalised
3527sub set_face { 3535 sub set {
3528 my ($name, $type, $data) = @_; 3536 my ($name, $type, $data) = @_;
3529 3537
3530 my $idx = cf::face::find $name; 3538 my $idx = cf::face::find $name;
3531 3539
3532 if ($idx) { 3540 if ($idx) {
3533 delete $FACEHASH{cf::face::get_chksum $idx}; 3541 delete $HASH{cf::face::get_chksum $idx};
3534 } else { 3542 } else {
3535 $idx = cf::face::alloc $name; 3543 $idx = cf::face::alloc $name;
3536 } 3544 }
3537 3545
3538 my $hash = cf::face::mangle_chksum Digest::MD5::md5 $data; 3546 my $hash = cf::face::mangle_csum Digest::MD5::md5 $data;
3539 3547
3540 cf::face::set_type $idx, $type; 3548 cf::face::set_type $idx, $type;
3541 cf::face::set_data $idx, 0, $data, $hash; 3549 cf::face::set_csum $idx, 0, $hash;
3542 cf::face::set_meta $idx, $type & 1 ? undef : undef; 3550
3551 # we need to destroy the SV itself, not just modify it, as a running ix
3552 # might hold a reference to it: "delete" achieves that.
3553 delete $FOFS[0][$idx];
3554 delete $DATA[0][$idx];
3555 $DATA[0][$idx] = $data;
3556 $SIZE[0][$idx] = length $data;
3557 delete $META[$idx];
3543 $FACEHASH{$hash} = $idx;#d# 3558 $HASH{$hash} = $idx;#d#
3544 3559
3545 $idx 3560 $idx
3561 }
3562
3563 sub _get_data($$$) {
3564 my ($idx, $set, $cb) = @_;
3565
3566 if (defined $DATA[$set][$idx]) {
3567 $cb->($DATA[$set][$idx]);
3568 } elsif (my $fofs = $FOFS[$set][$idx]) {
3569 my $size = $SIZE[$set][$idx];
3570 my $buf;
3571 IO::AIO::aio_read $DATAFH, $fofs, $size, $buf, 0, sub {
3572 if ($_[0] == $size) {
3573 #cf::debug "read face $idx, $size from $fofs as ", length $buf;#d#
3574 $cb->($buf);
3575 } else {
3576 cf::error "INTERNAL ERROR: unable to read facedata for face $idx#$set ($size, $fofs), ignoring request.";
3577 }
3578 };
3579 } else {
3580 cf::error "requested facedata for unknown face $idx#$set, ignoring.";
3581 }
3582 }
3583
3584 # rather ineffient
3585 sub cf::face::get_data($;$) {
3586 my ($idx, $set) = @_;
3587
3588 _get_data $idx, $set, Coro::rouse_cb;
3589 Coro::rouse_wait
3590 }
3591
3592 sub cf::face::ix {
3593 my ($ns, $set, $idx, $pri) = @_;
3594
3595 _get_data $idx, $set, sub {
3596 $ns->ix_send ($idx, $pri, $_[0]);
3597 };
3598 }
3546} 3599}
3547 3600
3548sub load_facedata($) { 3601sub load_facedata($) {
3549 my ($path) = @_; 3602 my ($path) = @_;
3550 3603
3551 # HACK to clear player env face cache, we need some signal framework
3552 # for this (global event?)
3553 %ext::player_env::MUSIC_FACE_CACHE = ();
3554
3555 my $enc = JSON::XS->new->utf8->canonical->relaxed; 3604 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3556 3605
3557 trace "loading facedata from $path\n"; 3606 trace "loading facedata from $path\n";
3558 3607
3559 my $facedata = decode_storable load_file $path; 3608 my $facedata = decode_storable load_file "$path/faceinfo";
3560 3609
3561 $facedata->{version} == 2 3610 $facedata->{version} == 2
3562 or cf::cleanup "$path: version mismatch, cannot proceed."; 3611 or cf::cleanup "$path/faceinfo: version mismatch, cannot proceed.";
3563 3612
3564 cf::cede_to_tick; 3613 my $fh = aio_open "$DATADIR/facedata", IO::AIO::O_RDONLY, 0
3614 or cf::cleanup "$path/facedata: $!, cannot proceed.";
3615
3616 get_slot 1, -100, "load_facedata"; # make sure we get a very big slot
3617
3618 # BEGIN ATOMIC
3619 # from here on, everything must be atomic - no thread switch allowed
3620 my $t1 = EV::time;
3565 3621
3566 { 3622 {
3567 my $faces = $facedata->{faceinfo}; 3623 my $faces = $facedata->{faceinfo};
3568 3624
3569 for my $face (sort keys %$faces) { 3625 for my $face (sort keys %$faces) {
3570 my $info = $faces->{$face}; 3626 my $info = $faces->{$face};
3571 my $idx = (cf::face::find $face) || cf::face::alloc $face; 3627 my $idx = (cf::face::find $face) || cf::face::alloc $face;
3572 3628
3573 cf::face::set_visibility $idx, $info->{visibility}; 3629 cf::face::set_visibility $idx, $info->{visibility};
3574 cf::face::set_magicmap $idx, $info->{magicmap}; 3630 cf::face::set_magicmap $idx, $info->{magicmap};
3575 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32}; 3631 cf::face::set_csum $idx, 0, $info->{hash64}; $cf::face::SIZE[0][$idx] = $info->{size64}; $cf::face::FOFS[0][$idx] = $info->{fofs64};
3576 cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64}; 3632 cf::face::set_csum $idx, 1, $info->{hash32}; $cf::face::SIZE[1][$idx] = $info->{size32}; $cf::face::FOFS[1][$idx] = $info->{fofs32};
3577 cf::face::set_data $idx, 2, $info->{glyph} , $info->{glyph} ; 3633 cf::face::set_csum $idx, 2, $info->{glyph}; $cf::face::DATA[2][$idx] = $info->{glyph};
3578 $FACEHASH{$info->{hash64}} = $idx;#d# 3634 $cf::face::HASH{$info->{hash64}} = $idx;
3579 3635 delete $cf::face::META[$idx];
3580 cf::cede_to_tick;
3581 } 3636 }
3582 3637
3583 while (my ($face, $info) = each %$faces) { 3638 while (my ($face, $info) = each %$faces) {
3584 next unless $info->{smooth}; 3639 next unless $info->{smooth};
3585 3640
3590 cf::face::set_smooth $idx, $smooth; 3645 cf::face::set_smooth $idx, $smooth;
3591 cf::face::set_smoothlevel $idx, $info->{smoothlevel}; 3646 cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3592 } else { 3647 } else {
3593 error "smooth face '$info->{smooth}' not found for face '$face'"; 3648 error "smooth face '$info->{smooth}' not found for face '$face'";
3594 } 3649 }
3595
3596 cf::cede_to_tick;
3597 } 3650 }
3598 } 3651 }
3599 3652
3600 { 3653 {
3601 my $anims = $facedata->{animinfo}; 3654 my $anims = $facedata->{animinfo};
3602 3655
3603 while (my ($anim, $info) = each %$anims) { 3656 while (my ($anim, $info) = each %$anims) {
3604 cf::anim::set $anim, $info->{frames}, $info->{facings}; 3657 cf::anim::set $anim, $info->{frames}, $info->{facings};
3605 cf::cede_to_tick;
3606 } 3658 }
3607 3659
3608 cf::anim::invalidate_all; # d'oh 3660 cf::anim::invalidate_all; # d'oh
3609 } 3661 }
3610 3662
3615 if (defined (my $type = $info->{type})) { 3667 if (defined (my $type = $info->{type})) {
3616 # TODO: different hash - must free and use new index, or cache ixface data queue 3668 # TODO: different hash - must free and use new index, or cache ixface data queue
3617 my $idx = (cf::face::find $name) || cf::face::alloc $name; 3669 my $idx = (cf::face::find $name) || cf::face::alloc $name;
3618 3670
3619 cf::face::set_type $idx, $type; 3671 cf::face::set_type $idx, $type;
3620 cf::face::set_data $idx, 0, $info->{data}, $info->{hash}; 3672 cf::face::set_csum $idx, 0, $info->{hash};
3673 $cf::face::SIZE[0][$idx] = $info->{size};
3674 $cf::face::FOFS[0][$idx] = $info->{fofs};
3621 cf::face::set_meta $idx, $type & 1 ? undef : $info->{meta}; # preserve meta unless prepended already 3675 $cf::face::META[$idx] = $type & 1 ? undef : $info->{meta}; # preserve meta unless prepended already
3622 $FACEHASH{$info->{hash}} = $idx;#d# 3676 $cf::face::HASH{$info->{hash}} = $idx;
3623 } else { 3677 } else {
3624# $RESOURCE{$name} = $info; # unused 3678# $RESOURCE{$name} = $info; # unused
3625 } 3679 }
3626
3627 cf::cede_to_tick;
3628 } 3680 }
3629 } 3681 }
3682
3683 ($fh, $cf::face::DATAFH) = ($cf::face::DATAFH, $fh);
3684
3685 # HACK to clear player env face cache, we need some signal framework
3686 # for this (global event?)
3687 %ext::player_env::MUSIC_FACE_CACHE = ();
3688
3689 # END ATOMIC
3690
3691 cf::debug "facedata atomic update time ", EV::time - $t1;
3630 3692
3631 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE); 3693 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3694
3695 aio_close $fh if $fh; # close old facedata
3632 3696
3633 1 3697 1
3634} 3698}
3635 3699
3636register_exticmd fx_want => sub { 3700register_exticmd fx_want => sub {
3652} 3716}
3653 3717
3654sub reload_exp_table { 3718sub reload_exp_table {
3655 _reload_exp_table; 3719 _reload_exp_table;
3656 3720
3721 cf::face::set
3657 set_face "res/exp_table" => FT_RSRC, 3722 "res/exp_table" => FT_RSRC,
3658 JSON::XS->new->utf8->canonical->encode ( 3723 JSON::XS->new->utf8->canonical->encode (
3659 [map cf::level_to_min_exp $_, 1 .. cf::settings->max_level] 3724 [map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]
3660 ); 3725 );
3661} 3726}
3662 3727
3663sub reload_materials { 3728sub reload_materials {
3664 _reload_materials; 3729 _reload_materials;
3665} 3730}
3677 if exists $_->{match}; 3742 if exists $_->{match};
3678 } 3743 }
3679} 3744}
3680 3745
3681sub reload_facedata { 3746sub reload_facedata {
3682 load_facedata "$DATADIR/facedata" 3747 load_facedata $DATADIR
3683 or die "unable to load facedata\n"; 3748 or die "unable to load facedata\n";
3684} 3749}
3685 3750
3686sub reload_archetypes { 3751sub reload_archetypes {
3687 load_resource_file "$DATADIR/archetypes" 3752 load_resource_file "$DATADIR/archetypes"
3688 or die "unable to load archetypes\n"; 3753 or die "unable to load archetypes\n";
3689 3754
3755 cf::face::set
3690 set_face "res/skill_info" => FT_RSRC, 3756 "res/skill_info" => FT_RSRC,
3691 JSON::XS->new->utf8->canonical->encode ( 3757 JSON::XS->new->utf8->canonical->encode (
3692 [map [cf::arch::skillvec ($_)->name], 0 .. cf::arch::skillvec_size - 1] 3758 [map [cf::arch::skillvec ($_)->name], 0 .. cf::arch::skillvec_size - 1]
3693 ); 3759 );
3760
3761 cf::face::set
3694 set_face "res/spell_paths" => FT_RSRC, 3762 "res/spell_paths" => FT_RSRC,
3695 JSON::XS->new->utf8->canonical->encode ( 3763 JSON::XS->new->utf8->canonical->encode (
3696 [map [cf::spellpathnames ($_)], 0 .. NRSPELLPATHS - 1] 3764 [map [cf::spellpathnames ($_)], 0 .. NRSPELLPATHS - 1]
3697 ); 3765 );
3698} 3766}
3699 3767
3700sub reload_treasures { 3768sub reload_treasures {
3701 load_resource_file "$DATADIR/treasures" 3769 load_resource_file "$DATADIR/treasures"
3702 or die "unable to load treasurelists\n"; 3770 or die "unable to load treasurelists\n";

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines