… | |
… | |
4 | use strict; |
4 | use strict; |
5 | |
5 | |
6 | use Symbol; |
6 | use Symbol; |
7 | use List::Util; |
7 | use List::Util; |
8 | use Socket; |
8 | use Socket; |
9 | use EV; |
9 | use EV 1.86; |
10 | use Opcode; |
10 | use Opcode; |
11 | use Safe; |
11 | use Safe; |
12 | use Safe::Hole; |
12 | use Safe::Hole; |
13 | use Storable (); |
13 | use Storable (); |
14 | |
14 | |
… | |
… | |
69 | our $TMPDIR = "$LOCALDIR/" . tmpdir; |
69 | our $TMPDIR = "$LOCALDIR/" . tmpdir; |
70 | our $UNIQUEDIR = "$LOCALDIR/" . uniquedir; |
70 | our $UNIQUEDIR = "$LOCALDIR/" . uniquedir; |
71 | our $PLAYERDIR = "$LOCALDIR/" . playerdir; |
71 | our $PLAYERDIR = "$LOCALDIR/" . playerdir; |
72 | our $RANDOMDIR = "$LOCALDIR/random"; |
72 | our $RANDOMDIR = "$LOCALDIR/random"; |
73 | our $BDBDIR = "$LOCALDIR/db"; |
73 | our $BDBDIR = "$LOCALDIR/db"; |
|
|
74 | our %RESOURCE; |
74 | |
75 | |
75 | our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!) |
76 | our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!) |
76 | our $TICK_WATCHER; |
77 | our $TICK_WATCHER; |
77 | our $AIO_POLL_WATCHER; |
78 | our $AIO_POLL_WATCHER; |
78 | our $NEXT_RUNTIME_WRITE; # when should the runtime file be written |
79 | our $NEXT_RUNTIME_WRITE; # when should the runtime file be written |
… | |
… | |
3149 | { |
3150 | { |
3150 | my $faces = $facedata->{faceinfo}; |
3151 | my $faces = $facedata->{faceinfo}; |
3151 | |
3152 | |
3152 | while (my ($face, $info) = each %$faces) { |
3153 | while (my ($face, $info) = each %$faces) { |
3153 | my $idx = (cf::face::find $face) || cf::face::alloc $face; |
3154 | my $idx = (cf::face::find $face) || cf::face::alloc $face; |
|
|
3155 | |
3154 | cf::face::set_visibility $idx, $info->{visibility}; |
3156 | cf::face::set_visibility $idx, $info->{visibility}; |
3155 | cf::face::set_magicmap $idx, $info->{magicmap}; |
3157 | cf::face::set_magicmap $idx, $info->{magicmap}; |
3156 | cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32}; |
3158 | cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32}; |
3157 | cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64}; |
3159 | cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64}; |
3158 | |
3160 | |
3159 | cf::cede_to_tick; |
3161 | cf::cede_to_tick; |
3160 | } |
3162 | } |
3161 | |
3163 | |
3162 | while (my ($face, $info) = each %$faces) { |
3164 | while (my ($face, $info) = each %$faces) { |
3163 | next unless $info->{smooth}; |
3165 | next unless $info->{smooth}; |
|
|
3166 | |
3164 | my $idx = cf::face::find $face |
3167 | my $idx = cf::face::find $face |
3165 | or next; |
3168 | or next; |
|
|
3169 | |
3166 | if (my $smooth = cf::face::find $info->{smooth}) { |
3170 | if (my $smooth = cf::face::find $info->{smooth}) { |
3167 | cf::face::set_smooth $idx, $smooth; |
3171 | cf::face::set_smooth $idx, $smooth; |
3168 | cf::face::set_smoothlevel $idx, $info->{smoothlevel}; |
3172 | cf::face::set_smoothlevel $idx, $info->{smoothlevel}; |
3169 | } else { |
3173 | } else { |
3170 | warn "smooth face '$info->{smooth}' not found for face '$face'"; |
3174 | warn "smooth face '$info->{smooth}' not found for face '$face'"; |
… | |
… | |
3188 | { |
3192 | { |
3189 | # TODO: for gcfclient pleasure, we should give resources |
3193 | # TODO: for gcfclient pleasure, we should give resources |
3190 | # that gcfclient doesn't grok a >10000 face index. |
3194 | # that gcfclient doesn't grok a >10000 face index. |
3191 | my $res = $facedata->{resource}; |
3195 | my $res = $facedata->{resource}; |
3192 | |
3196 | |
3193 | my $soundconf = delete $res->{"res/sound.conf"}; |
|
|
3194 | |
|
|
3195 | while (my ($name, $info) = each %$res) { |
3197 | while (my ($name, $info) = each %$res) { |
|
|
3198 | if (defined $info->{type}) { |
3196 | my $idx = (cf::face::find $name) || cf::face::alloc $name; |
3199 | my $idx = (cf::face::find $name) || cf::face::alloc $name; |
3197 | my $data; |
3200 | my $data; |
3198 | |
3201 | |
3199 | if ($info->{type} & 1) { |
3202 | if ($info->{type} & 1) { |
3200 | # prepend meta info |
3203 | # prepend meta info |
3201 | |
3204 | |
3202 | my $meta = $enc->encode ({ |
3205 | my $meta = $enc->encode ({ |
3203 | name => $name, |
3206 | name => $name, |
3204 | %{ $info->{meta} || {} }, |
3207 | %{ $info->{meta} || {} }, |
3205 | }); |
3208 | }); |
3206 | |
3209 | |
3207 | $data = pack "(w/a*)*", $meta, $info->{data}; |
3210 | $data = pack "(w/a*)*", $meta, $info->{data}; |
|
|
3211 | } else { |
|
|
3212 | $data = $info->{data}; |
|
|
3213 | } |
|
|
3214 | |
|
|
3215 | cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data; |
|
|
3216 | cf::face::set_type $idx, $info->{type}; |
3208 | } else { |
3217 | } else { |
3209 | $data = $info->{data}; |
3218 | $RESOURCE{$name} = $info; |
3210 | } |
3219 | } |
3211 | |
3220 | |
3212 | cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data; |
|
|
3213 | cf::face::set_type $idx, $info->{type}; |
|
|
3214 | |
|
|
3215 | cf::cede_to_tick; |
3221 | cf::cede_to_tick; |
3216 | } |
3222 | } |
3217 | |
3223 | |
3218 | if ($soundconf) { |
3224 | if (my $soundconf = delete $res->{"res/sound.conf"}) { |
3219 | $soundconf = $enc->decode (delete $soundconf->{data}); |
3225 | $soundconf = $enc->decode (delete $soundconf->{data}); |
3220 | |
3226 | |
3221 | for (0 .. SOUND_CAST_SPELL_0 - 1) { |
3227 | for (0 .. SOUND_CAST_SPELL_0 - 1) { |
3222 | my $sound = $soundconf->{compat}[$_] |
3228 | my $sound = $soundconf->{compat}[$_] |
3223 | or next; |
3229 | or next; |
… | |
… | |
3603 | |
3609 | |
3604 | $NOW = $tick_start = EV::now; |
3610 | $NOW = $tick_start = EV::now; |
3605 | |
3611 | |
3606 | cf::server_tick; # one server iteration |
3612 | cf::server_tick; # one server iteration |
3607 | |
3613 | |
3608 | $RUNTIME += $TICK; |
3614 | $RUNTIME += $TICK; |
3609 | $NEXT_TICK += $TICK; |
3615 | $NEXT_TICK = $_[0]->at; |
3610 | |
3616 | |
3611 | if ($NOW >= $NEXT_RUNTIME_WRITE) { |
3617 | if ($NOW >= $NEXT_RUNTIME_WRITE) { |
3612 | $NEXT_RUNTIME_WRITE = $NOW + 10; |
3618 | $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.; |
3613 | Coro::async_pool { |
3619 | Coro::async_pool { |
3614 | $Coro::current->{desc} = "runtime saver"; |
3620 | $Coro::current->{desc} = "runtime saver"; |
3615 | write_runtime |
3621 | write_runtime |
3616 | or warn "ERROR: unable to write runtime file: $!"; |
3622 | or warn "ERROR: unable to write runtime file: $!"; |
3617 | }; |
3623 | }; |
… | |
… | |
3630 | _post_tick; |
3636 | _post_tick; |
3631 | }; |
3637 | }; |
3632 | $TICK_WATCHER->priority (EV::MAXPRI); |
3638 | $TICK_WATCHER->priority (EV::MAXPRI); |
3633 | |
3639 | |
3634 | { |
3640 | { |
|
|
3641 | # configure BDB |
|
|
3642 | |
3635 | BDB::min_parallel 8; |
3643 | BDB::min_parallel 8; |
3636 | BDB::max_poll_reqs $TICK * 0.1; |
3644 | BDB::max_poll_reqs $TICK * 0.1; |
|
|
3645 | $Coro::BDB::WATCHER->priority (1); |
3637 | |
3646 | |
3638 | unless ($DB_ENV) { |
3647 | unless ($DB_ENV) { |
3639 | $DB_ENV = BDB::db_env_create; |
3648 | $DB_ENV = BDB::db_env_create; |
3640 | $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC |
3649 | $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC |
3641 | | BDB::LOG_AUTOREMOVE, 1); |
3650 | | BDB::LOG_AUTOREMOVE, 1); |
… | |
… | |
3668 | BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { }; |
3677 | BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { }; |
3669 | }; |
3678 | }; |
3670 | } |
3679 | } |
3671 | |
3680 | |
3672 | { |
3681 | { |
|
|
3682 | # configure IO::AIO |
|
|
3683 | |
3673 | IO::AIO::min_parallel 8; |
3684 | IO::AIO::min_parallel 8; |
3674 | |
|
|
3675 | undef $Coro::AIO::WATCHER; |
|
|
3676 | IO::AIO::max_poll_time $TICK * 0.1; |
3685 | IO::AIO::max_poll_time $TICK * 0.1; |
3677 | $AIO_POLL_WATCHER = EV::io IO::AIO::poll_fileno, EV::READ, \&IO::AIO::poll_cb; |
3686 | $Coro::AIO::WATCHER->priority (1); |
3678 | } |
3687 | } |
3679 | |
3688 | |
3680 | my $_log_backtrace; |
3689 | my $_log_backtrace; |
3681 | |
3690 | |
3682 | sub _log_backtrace { |
3691 | sub _log_backtrace { |