… | |
… | |
27 | use JSON::XS 2.01 (); |
27 | use JSON::XS 2.01 (); |
28 | use BDB (); |
28 | use BDB (); |
29 | use Data::Dumper; |
29 | use Data::Dumper; |
30 | use Digest::MD5; |
30 | use Digest::MD5; |
31 | use Fcntl; |
31 | use Fcntl; |
32 | use YAML::Syck (); |
32 | use YAML (); |
33 | use IO::AIO 2.51 (); |
33 | use IO::AIO 2.51 (); |
34 | use Time::HiRes; |
34 | use Time::HiRes; |
35 | use Compress::LZF; |
35 | use Compress::LZF; |
36 | use Digest::MD5 (); |
36 | use Digest::MD5 (); |
37 | |
37 | |
38 | # configure various modules to our taste |
38 | # configure various modules to our taste |
39 | # |
39 | # |
40 | $Storable::canonical = 1; # reduce rsync transfers |
40 | $Storable::canonical = 1; # reduce rsync transfers |
41 | Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator |
41 | Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator |
42 | Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later |
42 | Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later |
43 | |
|
|
44 | # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode? |
|
|
45 | $YAML::Syck::ImplicitUnicode = 1; |
|
|
46 | |
43 | |
47 | $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority |
44 | $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority |
48 | |
45 | |
49 | sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload |
46 | sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload |
50 | |
47 | |
… | |
… | |
69 | our $TMPDIR = "$LOCALDIR/" . tmpdir; |
66 | our $TMPDIR = "$LOCALDIR/" . tmpdir; |
70 | our $UNIQUEDIR = "$LOCALDIR/" . uniquedir; |
67 | our $UNIQUEDIR = "$LOCALDIR/" . uniquedir; |
71 | our $PLAYERDIR = "$LOCALDIR/" . playerdir; |
68 | our $PLAYERDIR = "$LOCALDIR/" . playerdir; |
72 | our $RANDOMDIR = "$LOCALDIR/random"; |
69 | our $RANDOMDIR = "$LOCALDIR/random"; |
73 | our $BDBDIR = "$LOCALDIR/db"; |
70 | our $BDBDIR = "$LOCALDIR/db"; |
|
|
71 | our %RESOURCE; |
74 | |
72 | |
75 | our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!) |
73 | our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!) |
76 | our $TICK_WATCHER; |
74 | our $TICK_WATCHER; |
77 | our $AIO_POLL_WATCHER; |
75 | our $AIO_POLL_WATCHER; |
78 | our $NEXT_RUNTIME_WRITE; # when should the runtime file be written |
76 | our $NEXT_RUNTIME_WRITE; # when should the runtime file be written |
… | |
… | |
192 | $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge; |
190 | $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge; |
193 | |
191 | |
194 | LOG llevError, $msg; |
192 | LOG llevError, $msg; |
195 | }; |
193 | }; |
196 | } |
194 | } |
|
|
195 | |
|
|
196 | $Coro::State::DIEHOOK = sub { |
|
|
197 | return unless $^S eq 0; # "eq", not "==" |
|
|
198 | |
|
|
199 | if ($Coro::current == $Coro::main) {#d# |
|
|
200 | warn "DIEHOOK called in main context, Coro bug?\n";#d# |
|
|
201 | return;#d# |
|
|
202 | }#d# |
|
|
203 | |
|
|
204 | # kill coroutine otherwise |
|
|
205 | warn Carp::longmess $_[0]; |
|
|
206 | Coro::terminate |
|
|
207 | }; |
|
|
208 | |
|
|
209 | $SIG{__DIE__} = sub { }; #d#? |
197 | |
210 | |
198 | @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable'; |
211 | @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable'; |
199 | @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable'; |
212 | @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable'; |
200 | @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable'; |
213 | @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable'; |
201 | @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable'; |
214 | @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable'; |
… | |
… | |
968 | } |
981 | } |
969 | |
982 | |
970 | 0 |
983 | 0 |
971 | } |
984 | } |
972 | |
985 | |
973 | =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...) |
986 | =item $bool = cf::global->invoke (EVENT_CLASS_XXX, ...) |
974 | |
987 | |
975 | =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...) |
988 | =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...) |
976 | |
989 | |
977 | Generate an object-specific event with the given arguments. |
990 | Generate an object-specific event with the given arguments. |
978 | |
991 | |
… | |
… | |
1303 | my $msg = $@ ? "$v->{path}: $@\n" |
1316 | my $msg = $@ ? "$v->{path}: $@\n" |
1304 | : "$v->{base}: extension inactive.\n"; |
1317 | : "$v->{base}: extension inactive.\n"; |
1305 | |
1318 | |
1306 | if (exists $v->{meta}{mandatory}) { |
1319 | if (exists $v->{meta}{mandatory}) { |
1307 | warn $msg; |
1320 | warn $msg; |
1308 | warn "mandatory extension failed to load, exiting.\n"; |
1321 | cf::cleanup "mandatory extension failed to load, exiting."; |
1309 | exit 1; |
|
|
1310 | } |
1322 | } |
1311 | |
1323 | |
1312 | warn $msg; |
1324 | warn $msg; |
1313 | } |
1325 | } |
1314 | |
1326 | |
… | |
… | |
3149 | { |
3161 | { |
3150 | my $faces = $facedata->{faceinfo}; |
3162 | my $faces = $facedata->{faceinfo}; |
3151 | |
3163 | |
3152 | while (my ($face, $info) = each %$faces) { |
3164 | while (my ($face, $info) = each %$faces) { |
3153 | my $idx = (cf::face::find $face) || cf::face::alloc $face; |
3165 | my $idx = (cf::face::find $face) || cf::face::alloc $face; |
|
|
3166 | |
3154 | cf::face::set_visibility $idx, $info->{visibility}; |
3167 | cf::face::set_visibility $idx, $info->{visibility}; |
3155 | cf::face::set_magicmap $idx, $info->{magicmap}; |
3168 | cf::face::set_magicmap $idx, $info->{magicmap}; |
3156 | cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32}; |
3169 | 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}; |
3170 | cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64}; |
3158 | |
3171 | |
3159 | cf::cede_to_tick; |
3172 | cf::cede_to_tick; |
3160 | } |
3173 | } |
3161 | |
3174 | |
3162 | while (my ($face, $info) = each %$faces) { |
3175 | while (my ($face, $info) = each %$faces) { |
3163 | next unless $info->{smooth}; |
3176 | next unless $info->{smooth}; |
|
|
3177 | |
3164 | my $idx = cf::face::find $face |
3178 | my $idx = cf::face::find $face |
3165 | or next; |
3179 | or next; |
|
|
3180 | |
3166 | if (my $smooth = cf::face::find $info->{smooth}) { |
3181 | if (my $smooth = cf::face::find $info->{smooth}) { |
3167 | cf::face::set_smooth $idx, $smooth; |
3182 | cf::face::set_smooth $idx, $smooth; |
3168 | cf::face::set_smoothlevel $idx, $info->{smoothlevel}; |
3183 | cf::face::set_smoothlevel $idx, $info->{smoothlevel}; |
3169 | } else { |
3184 | } else { |
3170 | warn "smooth face '$info->{smooth}' not found for face '$face'"; |
3185 | warn "smooth face '$info->{smooth}' not found for face '$face'"; |
… | |
… | |
3188 | { |
3203 | { |
3189 | # TODO: for gcfclient pleasure, we should give resources |
3204 | # TODO: for gcfclient pleasure, we should give resources |
3190 | # that gcfclient doesn't grok a >10000 face index. |
3205 | # that gcfclient doesn't grok a >10000 face index. |
3191 | my $res = $facedata->{resource}; |
3206 | my $res = $facedata->{resource}; |
3192 | |
3207 | |
3193 | my $soundconf = delete $res->{"res/sound.conf"}; |
|
|
3194 | |
|
|
3195 | while (my ($name, $info) = each %$res) { |
3208 | while (my ($name, $info) = each %$res) { |
|
|
3209 | if (defined $info->{type}) { |
3196 | my $idx = (cf::face::find $name) || cf::face::alloc $name; |
3210 | my $idx = (cf::face::find $name) || cf::face::alloc $name; |
3197 | my $data; |
3211 | my $data; |
3198 | |
3212 | |
3199 | if ($info->{type} & 1) { |
3213 | if ($info->{type} & 1) { |
3200 | # prepend meta info |
3214 | # prepend meta info |
3201 | |
3215 | |
3202 | my $meta = $enc->encode ({ |
3216 | my $meta = $enc->encode ({ |
3203 | name => $name, |
3217 | name => $name, |
3204 | %{ $info->{meta} || {} }, |
3218 | %{ $info->{meta} || {} }, |
3205 | }); |
3219 | }); |
3206 | |
3220 | |
3207 | $data = pack "(w/a*)*", $meta, $info->{data}; |
3221 | $data = pack "(w/a*)*", $meta, $info->{data}; |
|
|
3222 | } else { |
|
|
3223 | $data = $info->{data}; |
|
|
3224 | } |
|
|
3225 | |
|
|
3226 | cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data; |
|
|
3227 | cf::face::set_type $idx, $info->{type}; |
3208 | } else { |
3228 | } else { |
3209 | $data = $info->{data}; |
3229 | $RESOURCE{$name} = $info; |
3210 | } |
3230 | } |
3211 | |
3231 | |
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; |
3232 | cf::cede_to_tick; |
3216 | } |
3233 | } |
3217 | |
|
|
3218 | if ($soundconf) { |
|
|
3219 | $soundconf = $enc->decode (delete $soundconf->{data}); |
|
|
3220 | |
|
|
3221 | for (0 .. SOUND_CAST_SPELL_0 - 1) { |
|
|
3222 | my $sound = $soundconf->{compat}[$_] |
|
|
3223 | or next; |
|
|
3224 | |
|
|
3225 | my $face = cf::face::find "sound/$sound->[1]"; |
|
|
3226 | cf::sound::set $sound->[0] => $face; |
|
|
3227 | cf::sound::old_sound_index $_, $face; # gcfclient-compat |
|
|
3228 | } |
|
|
3229 | |
|
|
3230 | while (my ($k, $v) = each %{$soundconf->{event}}) { |
|
|
3231 | my $face = cf::face::find "sound/$v"; |
|
|
3232 | cf::sound::set $k => $face; |
|
|
3233 | } |
|
|
3234 | } |
|
|
3235 | } |
3234 | } |
|
|
3235 | |
|
|
3236 | cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE); |
3236 | |
3237 | |
3237 | 1 |
3238 | 1 |
3238 | } |
3239 | } |
|
|
3240 | |
|
|
3241 | cf::global->attach (on_resource_update => sub { |
|
|
3242 | if (my $soundconf = $RESOURCE{"res/sound.conf"}) { |
|
|
3243 | $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data}); |
|
|
3244 | |
|
|
3245 | for (0 .. SOUND_CAST_SPELL_0 - 1) { |
|
|
3246 | my $sound = $soundconf->{compat}[$_] |
|
|
3247 | or next; |
|
|
3248 | |
|
|
3249 | my $face = cf::face::find "sound/$sound->[1]"; |
|
|
3250 | cf::sound::set $sound->[0] => $face; |
|
|
3251 | cf::sound::old_sound_index $_, $face; # gcfclient-compat |
|
|
3252 | } |
|
|
3253 | |
|
|
3254 | while (my ($k, $v) = each %{$soundconf->{event}}) { |
|
|
3255 | my $face = cf::face::find "sound/$v"; |
|
|
3256 | cf::sound::set $k => $face; |
|
|
3257 | } |
|
|
3258 | } |
|
|
3259 | }); |
3239 | |
3260 | |
3240 | register_exticmd fx_want => sub { |
3261 | register_exticmd fx_want => sub { |
3241 | my ($ns, $want) = @_; |
3262 | my ($ns, $want) = @_; |
3242 | |
3263 | |
3243 | while (my ($k, $v) = each %$want) { |
3264 | while (my ($k, $v) = each %$want) { |
… | |
… | |
3298 | sub reload_config { |
3319 | sub reload_config { |
3299 | open my $fh, "<:utf8", "$CONFDIR/config" |
3320 | open my $fh, "<:utf8", "$CONFDIR/config" |
3300 | or return; |
3321 | or return; |
3301 | |
3322 | |
3302 | local $/; |
3323 | local $/; |
3303 | *CFG = YAML::Syck::Load <$fh>; |
3324 | *CFG = YAML::Load <$fh>; |
3304 | |
3325 | |
3305 | $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37]; |
3326 | $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37]; |
3306 | |
3327 | |
3307 | $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset}; |
3328 | $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset}; |
3308 | $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset}; |
3329 | $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset}; |
… | |
… | |
3536 | warn "leaving sync_job"; |
3557 | warn "leaving sync_job"; |
3537 | |
3558 | |
3538 | 1 |
3559 | 1 |
3539 | } or do { |
3560 | } or do { |
3540 | warn $@; |
3561 | warn $@; |
3541 | warn "error while reloading, exiting."; |
3562 | cf::cleanup "error while reloading, exiting."; |
3542 | exit 1; |
|
|
3543 | }; |
3563 | }; |
3544 | |
3564 | |
3545 | warn "reloaded"; |
3565 | warn "reloaded"; |
3546 | }; |
3566 | }; |
3547 | |
3567 | |
… | |
… | |
3550 | sub reload_perl() { |
3570 | sub reload_perl() { |
3551 | # doing reload synchronously and two reloads happen back-to-back, |
3571 | # doing reload synchronously and two reloads happen back-to-back, |
3552 | # coro crashes during coro_state_free->destroy here. |
3572 | # coro crashes during coro_state_free->destroy here. |
3553 | |
3573 | |
3554 | $RELOAD_WATCHER ||= EV::timer 0, 0, sub { |
3574 | $RELOAD_WATCHER ||= EV::timer 0, 0, sub { |
|
|
3575 | do_reload_perl; |
3555 | undef $RELOAD_WATCHER; |
3576 | undef $RELOAD_WATCHER; |
3556 | do_reload_perl; |
|
|
3557 | }; |
3577 | }; |
3558 | } |
3578 | } |
3559 | |
3579 | |
3560 | register_command "reload" => sub { |
3580 | register_command "reload" => sub { |
3561 | my ($who, $arg) = @_; |
3581 | my ($who, $arg) = @_; |