--- deliantra/server/lib/cf.pm 2011/08/13 20:20:30 1.575 +++ deliantra/server/lib/cf.pm 2012/11/21 12:47:09 1.612 @@ -1,22 +1,22 @@ # # This file is part of Deliantra, the Roguelike Realtime MMORPG. -# -# Copyright (©) 2006,2007,2008,2009,2010,2011 Marc Alexander Lehmann / Robin Redeker / the Deliantra team -# +# +# Copyright (©) 2006,2007,2008,2009,2010,2011,2012 Marc Alexander Lehmann / Robin Redeker / the Deliantra team +# # Deliantra is free software: you can redistribute it and/or modify it under # the terms of the Affero GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the Affero GNU General Public License # and the GNU General Public License along with this program. If not, see # . -# +# # The authors can be reached via e-mail to # @@ -34,7 +34,10 @@ use Storable (); use Carp (); -use Guard (); +use AnyEvent (); +use AnyEvent::IO (); +use AnyEvent::DNS (); + use Coro (); use Coro::State; use Coro::Handle; @@ -50,13 +53,13 @@ use Coro::Storable; use Coro::Util (); +use Guard (); use JSON::XS 2.01 (); use BDB (); use Data::Dumper; use Fcntl; use YAML::XS (); use IO::AIO (); -use Time::HiRes; use Compress::LZF; use Digest::MD5 (); @@ -85,7 +88,9 @@ our @EXTS = (); # list of extension package names our %EXTCMD = (); +our %EXTACMD = (); our %EXTICMD = (); +our %EXTIACMD = (); our %EXT_CORO = (); # coroutines bound to extensions our %EXT_MAP = (); # pluggable maps @@ -110,7 +115,7 @@ our $PIDFILE = "$LOCALDIR/pid"; our $RUNTIMEFILE = "$LOCALDIR/runtime"; -our %RESOURCE; # unused +#our %RESOURCE; # unused our $OUTPUT_RATE_MIN = 3000; our $OUTPUT_RATE_MAX = 1000000; @@ -131,6 +136,7 @@ our @EXTRA_MODULES = qw(pod match mapscript incloader); our %CFG; +our %EXT_CFG; # cfgkeyname => [var-ref, defaultvalue] our $UPTIME; $UPTIME ||= time; our $RUNTIME = 0; @@ -221,9 +227,9 @@ The time this server has run, starts at 0 and is increased by $cf::TICK on every server tick. -=item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR +=item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR -$cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR +$cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR Various directories - "/etc", read-only install directory, perl-library directory, pod-directory, read-only maps directory, "/var", "/var/tmp", @@ -247,11 +253,12 @@ =item cf::wait_for_tick, cf::wait_for_tick_begin -These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only -returns directly I the tick processing (and consequently, can only wake one thread -per tick), while cf::wait_for_tick wakes up all waiters after tick processing. +These are functions that inhibit the current coroutine one tick. +cf::wait_for_tick_begin only returns directly I the tick +processing (and consequently, can only wake one thread per tick), while +cf::wait_for_tick wakes up all waiters after tick processing. -Note that cf::Wait_for_tick will immediately return when the server is not +Note that cf::wait_for_tick will immediately return when the server is not ticking, making it suitable for small pauses in threads that need to run when the server is paused. If that is not applicable (i.e. you I want to wait, use C<$cf::WAIT_FOR_TICK>). @@ -337,7 +344,7 @@ } $EV::DIED = sub { - Carp::cluck "error in event callback: @_"; + warn "error in event callback: $@"; }; ############################################################################# @@ -374,7 +381,7 @@ } || "[unable to dump $_[0]: '$@']"; } -=item $scalar = load_file $path +=item $scalar = cf::load_file $path Loads the given file from path and returns its contents. Croaks on error and can block. @@ -388,6 +395,44 @@ $data } +=item $success = cf::replace_file $path, $data, $sync + +Atomically replaces the file at the given $path with new $data, and +optionally $sync the data to disk before replacing the file. + +=cut + +sub replace_file($$;$) { + my ($path, $data, $sync) = @_; + + my $lock = cf::lock_acquire ("replace_file:$path"); + + my $fh = aio_open "$path~", Fcntl::O_WRONLY | Fcntl::O_CREAT | Fcntl::O_TRUNC, 0644 + or return; + + $data = $data->() if ref $data; + + length $data == aio_write $fh, 0, (length $data), $data, 0 + or return; + + !$sync + or !aio_fsync $fh + or return; + + aio_close $fh + and return; + + aio_rename "$path~", $path + and return; + + if ($sync) { + $path =~ s%/[^/]*$%%; + aio_pathsync $path; + } + + 1 +} + =item $ref = cf::decode_json $json Converts a JSON string into the corresponding perl data structure. @@ -530,7 +575,7 @@ C<$time> seconds of cpu time till the next tick. The slot is only valid till the next cede. -Background jobs should use a priority les than zero, interactive jobs +Background jobs should use a priority less than zero, interactive jobs should use 100 or more. The optional C<$name> can be used to identify the job to run. It might be @@ -554,6 +599,8 @@ while () { next_job: + Coro::cede; + my $avail = cf::till_tick; for (0 .. $#SLOT_QUEUE) { @@ -561,7 +608,6 @@ $busy = 0; my $job = splice @SLOT_QUEUE, $_, 1, (); $job->[2]->send; - Coro::cede; goto next_job; } else { $SLOT_QUEUE[$_][0] *= $SLOT_DECAY; @@ -569,8 +615,7 @@ } if (@SLOT_QUEUE) { - # we do not use wait_for_tick() as it returns immediately when tick is inactive - $WAIT_FOR_TICK->wait; + wait_for_tick; } else { $busy = 0; Coro::schedule; @@ -696,10 +741,6 @@ sub fork_call(&@) { my ($cb, @args) = @_; - # we seemingly have to make a local copy of the whole thing, - # otherwise perl prematurely frees the stuff :/ - # TODO: investigate and fix (likely this will be rather laborious) - my @res = Coro::Util::fork_eval { cf::post_fork; &$cb @@ -858,9 +899,9 @@ } } - my $t1 = Time::HiRes::time; + my $t1 = EV::time; my $data = $process->(\@data); - my $t2 = Time::HiRes::time; + my $t2 = EV::time; info "cache: '$id' processed in ", $t2 - $t1, "s\n"; @@ -1390,7 +1431,7 @@ ############################################################################# # command handling &c -=item cf::register_command $name => \&callback($ob,$args); +=item cf::register_command $name => \&callback($ob,$args) Register a callback for execution when the client sends the user command $name. @@ -1406,7 +1447,7 @@ push @{ $COMMAND{$name} }, [$caller, $cb]; } -=item cf::register_extcmd $name => \&callback($pl,$packet); +=item cf::register_extcmd $name => \&callback($pl,@args) Register a callback for execution when the client sends an (synchronous) extcmd packet. Ext commands will be processed in the order they are @@ -1414,10 +1455,14 @@ the logged-in player. Ext commands can only be processed after a player has logged in successfully. -If the callback returns something, it is sent back as if reply was being -called. +The values will be sent back to the client. + +=item cf::register_async_extcmd $name => \&callback($pl,$reply->(...),@args) -=item cf::register_exticmd $name => \&callback($ns,$packet); +Same as C, but instead of returning values, the +callback needs to clal the C<$reply> function. + +=item cf::register_exticmd $name => \&callback($ns,@args) Register a callback for execution when the client sends an (asynchronous) exticmd packet. Exti commands are processed by the server as soon as they @@ -1425,23 +1470,39 @@ is a client socket. Exti commands can be received anytime, even before log-in. -If the callback returns something, it is sent back as if reply was being -called. +The values will be sent back to the client. + +=item cf::register_async_exticmd $name => \&callback($ns,$reply->(...),@args) + +Same as C, but instead of returning values, the +callback needs to clal the C<$reply> function. =cut -sub register_extcmd { +sub register_extcmd($$) { my ($name, $cb) = @_; $EXTCMD{$name} = $cb; } -sub register_exticmd { +sub register_async_extcmd($$) { + my ($name, $cb) = @_; + + $EXTACMD{$name} = $cb; +} + +sub register_exticmd($$) { my ($name, $cb) = @_; $EXTICMD{$name} = $cb; } +sub register_async_exticmd($$) { + my ($name, $cb) = @_; + + $EXTIACMD{$name} = $cb; +} + use File::Glob (); cf::player->attach ( @@ -1463,19 +1524,27 @@ my $msg = eval { $pl->ns->{json_coder}->decode ($buf) }; if (ref $msg) { - my ($type, $reply, @payload) = - "ARRAY" eq ref $msg - ? @$msg - : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove + my ($type, $reply, @payload) = @$msg; # version 1 used %type, $id, %$hash - my @reply; + if (my $cb = $EXTACMD{$type}) { + $cb->( + $pl, + sub { + $pl->ext_msg ("reply-$reply", @_) + if $reply; + }, + @payload + ); + } else { + my @reply; - if (my $cb = $EXTCMD{$type}) { - @reply = $cb->($pl, @payload); - } + if (my $cb = $EXTCMD{$type}) { + @reply = $cb->($pl, @payload); + } - $pl->ext_reply ($reply, @reply) - if $reply; + $pl->ext_msg ("reply-$reply", @reply) + if $reply; + } } else { error "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n"; @@ -1498,9 +1567,22 @@ $grp } +sub _ext_cfg_reg($$$$) { + my ($rvar, $varname, $cfgname, $default) = @_; + + $cfgname = lc $varname + unless length $cfgname; + + $EXT_CFG{$cfgname} = [$rvar, $default]; + + $$rvar = exists $CFG{$cfgname} ? $CFG{$cfgname} : $default; +} + sub load_extensions { info "loading extensions..."; + %EXT_CFG = (); + cf::sync_job { my %todo; @@ -1552,7 +1634,16 @@ trace "... pass $pass, loading '$k' into '$v->{pkg}'\n"; - my $active = eval $v->{source}; + my $source = $v->{source}; + + # support "CONF varname :confname = default" pseudo-statements + $source =~ s{ + ^ CONF \s+ ([^\s:=]+) \s* (?:: \s* ([^\s:=]+) \s* )? = ([^\n#]+) + }{ + "our \$$1; BEGIN { cf::_ext_cfg_reg \\\$$1, q\x00$1\x00, q\x00$2\x00, $3 }"; + }gmxe; + + my $active = eval $source; if (length $@) { error "$v->{path}: $@\n"; @@ -1850,18 +1941,6 @@ Expand deliantra pod fragments into protocol xml. -=item $player->ext_reply ($msgid, @msg) - -Sends an ext reply to the player. - -=cut - -sub ext_reply($$@) { - my ($self, $id, @msg) = @_; - - $self->ns->ext_reply ($id, @msg) -} - =item $player->ext_msg ($type, @msg) Sends an ext event to the client. @@ -2573,7 +2652,7 @@ =item $ob = cf::object::deref ($refstring) -returns the objetc referenced by refstring. may return undef when it cnanot find the object, +returns the objetc referenced by refstring. may return undef when it cannot find the object, even if the object actually exists. May block. =cut @@ -2965,20 +3044,6 @@ =over 4 -=item $client->send_drawinfo ($text, $flags) - -Sends a drawinfo packet to the client. Circumvents output buffering so -should not be used under normal circumstances. - -=cut - -sub cf::client::send_drawinfo { - my ($self, $text, $flags) = @_; - - utf8::encode $text; - $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text); -} - =item $client->send_big_packet ($pkt) Like C, but tries to compress large packets, and fragments @@ -3006,9 +3071,9 @@ =item $client->send_msg ($channel, $msg, $color, [extra...]) -Send a drawinfo or msg packet to the client, formatting the msg for the -client if neccessary. C<$type> should be a string identifying the type of -the message, with C being the default. If C<$color> is negative, suppress +Send a msg packet to the client, formatting the msg for the client if +necessary. C<$type> should be a string identifying the type of the +message, with C being the default. If C<$color> is negative, suppress the message unless the client supports the msg packet. =cut @@ -3123,6 +3188,12 @@ reply => undef, tooltip => "Reason for and more info about your most recent death", }, + "c/fatal" => { + id => "fatal", + title => "Fatal Error", + reply => undef, + tooltip => "Reason for the server disconnect", + }, "c/say" => $SAY_CHANNEL, "c/chat" => $CHAT_CHANNEL, ); @@ -3175,30 +3246,7 @@ sub cf::client::ext_msg($$@) { my ($self, $type, @msg) = @_; - if ($self->extcmd == 2) { - $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg])); - } elsif ($self->extcmd == 1) { # TODO: remove - push @msg, msgtype => "event_$type"; - $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg})); - } -} - -=item $client->ext_reply ($msgid, @msg) - -Sends an ext reply to the client. - -=cut - -sub cf::client::ext_reply($$@) { - my ($self, $id, @msg) = @_; - - if ($self->extcmd == 2) { - $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg])); - } elsif ($self->extcmd == 1) { - #TODO: version 1, remove - unshift @msg, msgtype => "reply", msgid => $id; - $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg})); - } + $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg])); } =item $success = $client->query ($flags, "text", \&cb) @@ -3230,6 +3278,44 @@ 1 } +=item $client->update_command_faces + +=cut + +our %COMMAND_FACE; + +sub cf::client::update_command_faces { + my ($self) = @_; + + my @faces = grep $_, + $COMMAND_FACE{standard}, + $COMMAND_FACE{emote}, + $COMMAND_FACE{skill}, + $self->pl->ob->flag (cf::FLAG_WIZ) ? $COMMAND_FACE{dm} : (), + ; + + $self->send_face ($_) + for @faces; + $self->flush_fx; + + $self->ext_msg (command_list => @faces); +} + +=item cf::client::set_command_face $type, $commands + +=cut + +sub cf::client::set_command_face { + my ($type, $list) = @_; + + my $idx = &cf::face::set ( #d# ugly forward reference + "command_list/$type" => cf::FT_RSRC, + JSON::XS->new->utf8->encode ([ sort @$list ]) + ); + + $COMMAND_FACE{$type} = $idx; +} + cf::client->attach ( on_connect => sub { my ($ns) = @_; @@ -3263,20 +3349,27 @@ my $msg = eval { $ns->{json_coder}->decode ($buf) }; if (ref $msg) { - my ($type, $reply, @payload) = - "ARRAY" eq ref $msg - ? @$msg - : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove + my ($type, $reply, @payload) = @$msg; # version 1 used %type, $id, %$hash - my @reply; - - if (my $cb = $EXTICMD{$type}) { - @reply = $cb->($ns, @payload); - } + if (my $cb = $EXTIACMD{$type}) { + $cb->( + $ns, + sub { + $ns->ext_msg ("reply-$reply", @_) + if $reply; + }, + @payload + ); + } else { + my @reply; - $ns->ext_reply ($reply, @reply) - if $reply; + if (my $cb = $EXTICMD{$type}) { + @reply = $cb->($ns, @payload); + } + $ns->ext_msg ("reply-$reply", @reply) + if $reply; + } } else { error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; } @@ -3454,44 +3547,118 @@ ############################################################################# # the server's init and main functions +{ + package cf::face; + + our %HASH; # hash => idx + our @DATA; # dynamically-created facedata, only faceste 0 used + our @FOFS; # file offset, if > 0 + our @SIZE; # size of face, in octets + our @META; # meta hash of face, if any + our $DATAFH; # facedata filehandle + + # internal api, not finalised + sub set { + my ($name, $type, $data) = @_; + + my $idx = cf::face::find $name; + + if ($idx) { + delete $HASH{cf::face::get_csum $idx}; + } else { + $idx = cf::face::alloc $name; + } + + my $hash = cf::face::mangle_csum Digest::MD5::md5 $data; + + cf::face::set_type $idx, $type; + cf::face::set_csum $idx, 0, $hash; + + # we need to destroy the SV itself, not just modify it, as a running ix + # might hold a reference to it: "delete" achieves that. + delete $FOFS[0][$idx]; + delete $DATA[0][$idx]; + $DATA[0][$idx] = $data; + $SIZE[0][$idx] = length $data; + delete $META[$idx]; + $HASH{$hash} = $idx;#d# + + $idx + } + + sub _get_data($$$) { + my ($idx, $set, $cb) = @_; + + if (defined $DATA[$set][$idx]) { + $cb->($DATA[$set][$idx]); + } elsif (my $fofs = $FOFS[$set][$idx]) { + my $size = $SIZE[$set][$idx]; + my $buf; + IO::AIO::aio_read $DATAFH, $fofs, $size, $buf, 0, sub { + if ($_[0] == $size) { + #cf::debug "read face $idx, $size from $fofs as ", length $buf;#d# + $cb->($buf); + } else { + cf::error "INTERNAL ERROR: unable to read facedata for face $idx#$set ($size, $fofs), ignoring request."; + } + }; + } else { + cf::error "requested facedata for unknown face $idx#$set, ignoring."; + } + } + + # rather ineffient + sub cf::face::get_data($;$) { + my ($idx, $set) = @_; + + _get_data $idx, $set, Coro::rouse_cb; + Coro::rouse_wait + } + + sub cf::face::ix { + my ($ns, $set, $idx, $pri) = @_; + + _get_data $idx, $set, sub { + $ns->ix_send ($idx, $pri, $_[0]); + }; + } +} + sub load_facedata($) { my ($path) = @_; - # HACK to clear player env face cache, we need some signal framework - # for this (global event?) - %ext::player_env::MUSIC_FACE_CACHE = (); - my $enc = JSON::XS->new->utf8->canonical->relaxed; trace "loading facedata from $path\n"; - my $facedata = decode_storable load_file $path; + my $facedata = decode_storable load_file "$path/faceinfo"; $facedata->{version} == 2 - or cf::cleanup "$path: version mismatch, cannot proceed."; + or cf::cleanup "$path/faceinfo: version mismatch, cannot proceed."; - # patch in the exptable - my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]); - $facedata->{resource}{"res/exp_table"} = { - type => FT_RSRC, - data => $exp_table, - hash => (Digest::MD5::md5 $exp_table), - }; - cf::cede_to_tick; + my $fh = aio_open "$DATADIR/facedata", IO::AIO::O_RDONLY, 0 + or cf::cleanup "$path/facedata: $!, cannot proceed."; + + get_slot 1, -100, "load_facedata"; # make sure we get a very big slot + + # BEGIN ATOMIC + # from here on, everything must be atomic - no thread switch allowed + my $t1 = EV::time; { my $faces = $facedata->{faceinfo}; - while (my ($face, $info) = each %$faces) { + for my $face (sort keys %$faces) { + my $info = $faces->{$face}; my $idx = (cf::face::find $face) || cf::face::alloc $face; cf::face::set_visibility $idx, $info->{visibility}; cf::face::set_magicmap $idx, $info->{magicmap}; - cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32}; - cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64}; - cf::face::set_data $idx, 2, $info->{glyph} , $info->{glyph} ; - - cf::cede_to_tick; + cf::face::set_csum $idx, 0, $info->{hash64}; $cf::face::SIZE[0][$idx] = $info->{size64}; $cf::face::FOFS[0][$idx] = $info->{fofs64}; + cf::face::set_csum $idx, 1, $info->{hash32}; $cf::face::SIZE[1][$idx] = $info->{size32}; $cf::face::FOFS[1][$idx] = $info->{fofs32}; + cf::face::set_csum $idx, 2, $info->{glyph}; $cf::face::DATA[2][$idx] = $info->{glyph}; + $cf::face::HASH{$info->{hash64}} = $idx; + delete $cf::face::META[$idx]; } while (my ($face, $info) = each %$faces) { @@ -3506,8 +3673,6 @@ } else { error "smooth face '$info->{smooth}' not found for face '$face'"; } - - cf::cede_to_tick; } } @@ -3516,7 +3681,6 @@ while (my ($anim, $info) = each %$anims) { cf::anim::set $anim, $info->{frames}, $info->{facings}; - cf::cede_to_tick; } cf::anim::invalidate_all; # d'oh @@ -3526,21 +3690,36 @@ my $res = $facedata->{resource}; while (my ($name, $info) = each %$res) { - if (defined $info->{type}) { + if (defined (my $type = $info->{type})) { + # TODO: different hash - must free and use new index, or cache ixface data queue my $idx = (cf::face::find $name) || cf::face::alloc $name; - cf::face::set_data $idx, 0, $info->{data}, $info->{hash}; - cf::face::set_type $idx, $info->{type}; + cf::face::set_type $idx, $type; + cf::face::set_csum $idx, 0, $info->{hash}; + $cf::face::SIZE[0][$idx] = $info->{size}; + $cf::face::FOFS[0][$idx] = $info->{fofs}; + $cf::face::META[$idx] = $type & 1 ? undef : $info->{meta}; # preserve meta unless prepended already + $cf::face::HASH{$info->{hash}} = $idx; } else { - $RESOURCE{$name} = $info; # unused +# $RESOURCE{$name} = $info; # unused } - - cf::cede_to_tick; } } + ($fh, $cf::face::DATAFH) = ($cf::face::DATAFH, $fh); + + # HACK to clear player env face cache, we need some signal framework + # for this (global event?) + %ext::player_env::MUSIC_FACE_CACHE = (); + + # END ATOMIC + + cf::debug "facedata atomic update time ", EV::time - $t1; + cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE); + aio_close $fh if $fh; # close old facedata + 1 } @@ -3562,6 +3741,20 @@ $status } +sub reload_exp_table { + _reload_exp_table; + + cf::face::set + "res/exp_table" => FT_RSRC, + JSON::XS->new->utf8->canonical->encode ( + [map cf::level_to_min_exp $_, 1 .. cf::settings->max_level] + ); +} + +sub reload_materials { + _reload_materials; +} + sub reload_regions { # HACK to clear player env face cache, we need some signal framework # for this (global event?) @@ -3577,13 +3770,39 @@ } sub reload_facedata { - load_facedata "$DATADIR/facedata" + load_facedata $DATADIR or die "unable to load facedata\n"; } sub reload_archetypes { load_resource_file "$DATADIR/archetypes" or die "unable to load archetypes\n"; + + cf::face::set + "res/skill_info" => FT_RSRC, + JSON::XS->new->utf8->canonical->encode ( + [map [cf::arch::skillvec ($_)->name], 0 .. cf::arch::skillvec_size - 1] + ); + + cf::face::set + "res/spell_paths" => FT_RSRC, + JSON::XS->new->utf8->canonical->encode ( + [map [cf::spellpathnames ($_)], 0 .. NRSPELLPATHS - 1] + ); + + # command completion + my @commands; + + for (0..cf::arch::skillvec_size - 1) { + my $skill = cf::arch::skillvec $_; + my $name = $skill->name; + my $flags = cf::skill_flags $skill->subtype; + + push @commands, "ready_skill $name" if $flags & (SF_COMBAT | SF_RANGED | SF_GRACE); + push @commands, "use_skill $name" if $flags & (SF_USE | SF_AUTARK | SF_GRACE); + } + + cf::client::set_command_face skill => \@commands; } sub reload_treasures { @@ -3614,9 +3833,9 @@ sub reload_resources { trace "reloading resource files...\n"; - reload_exp_table; reload_materials; reload_facedata; + reload_exp_table; reload_sound; reload_archetypes; reload_regions; @@ -3678,14 +3897,14 @@ cf::init_globals; # initialise logging LOG llevInfo, "Welcome to Deliantra, v" . VERSION; - LOG llevInfo, "Copyright (C) 2005-2011 Marc Alexander Lehmann / Robin Redeker / the Deliantra team."; + LOG llevInfo, "Copyright (C) 2005-2012 Marc Alexander Lehmann / Robin Redeker / the Deliantra team."; LOG llevInfo, "Copyright (C) 1994 Mark Wedel."; LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen."; $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority # we must not ever block the main coroutine - local $Coro::idle = sub { + $Coro::idle = sub { Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d# (async { $Coro::current->{desc} = "IDLE BUG HANDLER"; @@ -3698,6 +3917,8 @@ cf::sync_job { cf::incloader::init (); + db_init; + cf::init_anim; cf::init_attackmess; cf::init_dynamic; @@ -3706,7 +3927,6 @@ reload_resources; reload_config; - db_init; cf::init_uuid; cf::init_signals; @@ -4141,6 +4361,7 @@ { # configure BDB + info "initialising database"; BDB::min_parallel 16; BDB::max_poll_reqs $TICK * 0.1; @@ -4179,14 +4400,18 @@ $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub { BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { }; }; + + info "database initialised"; } { # configure IO::AIO + info "initialising aio"; IO::AIO::min_parallel 8; IO::AIO::max_poll_time $TICK * 0.1; undef $AnyEvent::AIO::WATCHER; + info "aio initialised"; } our $_log_backtrace;