ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.546
Committed: Thu May 6 22:57:49 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.545: +15 -11 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.484 #
2 root 1.412 # This file is part of Deliantra, the Roguelike Realtime MMORPG.
3     #
4 root 1.504 # Copyright (©) 2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.412 #
6 root 1.484 # Deliantra is free software: you can redistribute it and/or modify it under
7     # the terms of the Affero GNU General Public License as published by the
8     # Free Software Foundation, either version 3 of the License, or (at your
9     # option) any later version.
10 root 1.412 #
11     # This program is distributed in the hope that it will be useful,
12     # but WITHOUT ANY WARRANTY; without even the implied warranty of
13     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     # GNU General Public License for more details.
15     #
16 root 1.484 # You should have received a copy of the Affero GNU General Public License
17     # and the GNU General Public License along with this program. If not, see
18     # <http://www.gnu.org/licenses/>.
19 root 1.412 #
20     # The authors can be reached via e-mail to <support@deliantra.net>
21 root 1.484 #
22 root 1.412
23 root 1.1 package cf;
24    
25 root 1.539 use common::sense;
26 root 1.96
27 root 1.1 use Symbol;
28     use List::Util;
29 root 1.250 use Socket;
30 root 1.433 use EV;
31 root 1.23 use Opcode;
32     use Safe;
33     use Safe::Hole;
34 root 1.385 use Storable ();
35 root 1.493 use Carp ();
36 root 1.19
37 root 1.458 use Guard ();
38 root 1.433 use Coro ();
39 root 1.224 use Coro::State;
40 root 1.250 use Coro::Handle;
41 root 1.441 use Coro::EV;
42 root 1.434 use Coro::AnyEvent;
43 root 1.96 use Coro::Timer;
44     use Coro::Signal;
45     use Coro::Semaphore;
46 root 1.459 use Coro::SemaphoreSet;
47 root 1.433 use Coro::AnyEvent;
48 root 1.105 use Coro::AIO;
49 root 1.437 use Coro::BDB 1.6;
50 root 1.237 use Coro::Storable;
51 root 1.332 use Coro::Util ();
52 root 1.96
53 root 1.398 use JSON::XS 2.01 ();
54 root 1.206 use BDB ();
55 root 1.154 use Data::Dumper;
56 root 1.105 use Fcntl;
57 root 1.484 use YAML::XS ();
58 root 1.433 use IO::AIO ();
59 root 1.32 use Time::HiRes;
60 root 1.208 use Compress::LZF;
61 root 1.302 use Digest::MD5 ();
62 root 1.208
63 root 1.433 AnyEvent::detect;
64    
65 root 1.227 # configure various modules to our taste
66     #
67 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
68 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
69 root 1.227
70 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
71 root 1.1
72 root 1.449 # make sure c-lzf reinitialises itself
73     Compress::LZF::set_serializer "Storable", "Storable::net_mstore", "Storable::mretrieve";
74     Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
75 root 1.448
76 root 1.474 # strictly for debugging
77     $SIG{QUIT} = sub { Carp::cluck "SIGQUIT" };
78    
79 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
80    
81 root 1.540 our @ORIG_INC;
82    
83 root 1.85 our %COMMAND = ();
84     our %COMMAND_TIME = ();
85 root 1.159
86     our @EXTS = (); # list of extension package names
87 root 1.85 our %EXTCMD = ();
88 root 1.287 our %EXTICMD = ();
89 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
90 root 1.161 our %EXT_MAP = (); # pluggable maps
91 root 1.85
92 root 1.451 our $RELOAD; # number of reloads so far, non-zero while in reload
93 root 1.1 our @EVENT;
94 root 1.479 our @REFLECT; # set by XS
95     our %REFLECT; # set by us
96 root 1.253
97 root 1.445 our $CONFDIR = confdir;
98     our $DATADIR = datadir;
99     our $LIBDIR = "$DATADIR/ext";
100     our $PODDIR = "$DATADIR/pod";
101     our $MAPDIR = "$DATADIR/" . mapdir;
102     our $LOCALDIR = localdir;
103     our $TMPDIR = "$LOCALDIR/" . tmpdir;
104     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
105     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
106     our $RANDOMDIR = "$LOCALDIR/random";
107     our $BDBDIR = "$LOCALDIR/db";
108     our $PIDFILE = "$LOCALDIR/pid";
109     our $RUNTIMEFILE = "$LOCALDIR/runtime";
110    
111 root 1.530 our %RESOURCE; # unused
112 root 1.1
113 root 1.527 our $OUTPUT_RATE_MIN = 3000;
114     our $OUTPUT_RATE_MAX = 1000000;
115    
116     our $MAX_LINKS = 32; # how many chained exits to follow
117 root 1.528 our $VERBOSE_IO = 1;
118 root 1.513
119 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
120 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
121 root 1.35 our $NEXT_TICK;
122 root 1.469 our $USE_FSYNC = 1; # use fsync to write maps - default on
123 root 1.35
124 root 1.371 our $BDB_DEADLOCK_WATCHER;
125 root 1.363 our $BDB_CHECKPOINT_WATCHER;
126     our $BDB_TRICKLE_WATCHER;
127 root 1.206 our $DB_ENV;
128    
129 root 1.543 our @EXTRA_MODULES = qw(pod match mapscript incloader);
130 root 1.466
131 root 1.70 our %CFG;
132    
133 root 1.84 our $UPTIME; $UPTIME ||= time;
134 root 1.103 our $RUNTIME;
135 root 1.399 our $NOW;
136 root 1.103
137 root 1.356 our (%PLAYER, %PLAYER_LOADING); # all users
138     our (%MAP, %MAP_LOADING ); # all maps
139 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
140 root 1.103
141 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
142     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
143    
144 root 1.265 our $LOAD; # a number between 0 (idle) and 1 (too many objects)
145     our $LOADAVG; # same thing, but with alpha-smoothing
146 root 1.412 our $JITTER; # average jitter
147     our $TICK_START; # for load detecting purposes
148 root 1.265
149 root 1.453 our @POST_INIT;
150    
151 root 1.474 our $REATTACH_ON_RELOAD; # set to true to force object reattach on reload (slow)
152     our $REALLY_UNLOOP; # never set to true, please :)
153 root 1.457
154 root 1.103 binmode STDOUT;
155     binmode STDERR;
156    
157     # read virtual server time, if available
158 root 1.445 unless ($RUNTIME || !-e $RUNTIMEFILE) {
159     open my $fh, "<", $RUNTIMEFILE
160     or die "unable to read $RUNTIMEFILE file: $!";
161 root 1.103 $RUNTIME = <$fh> + 0.;
162     }
163    
164 root 1.467 eval "sub TICK() { $TICK } 1" or die;
165    
166 root 1.253 mkdir $_
167     for $LOCALDIR, $TMPDIR, $UNIQUEDIR, $PLAYERDIR, $RANDOMDIR, $BDBDIR;
168 root 1.103
169 root 1.131 our $EMERGENCY_POSITION;
170 root 1.110
171 root 1.199 sub cf::map::normalise;
172    
173 root 1.522 sub in_main() {
174     $Coro::current == $Coro::main
175     }
176    
177 root 1.70 #############################################################################
178    
179 root 1.479 %REFLECT = ();
180     for (@REFLECT) {
181     my $reflect = JSON::XS::decode_json $_;
182     $REFLECT{$reflect->{class}} = $reflect;
183     }
184    
185 root 1.480 # this is decidedly evil
186 root 1.482 $REFLECT{object}{flags} = { map +($_ => undef), grep $_, map /^FLAG_([A-Z0-9_]+)$/ && lc $1, keys %{"cf::"} };
187 root 1.480
188 root 1.479 #############################################################################
189    
190 root 1.70 =head2 GLOBAL VARIABLES
191    
192     =over 4
193    
194 root 1.83 =item $cf::UPTIME
195    
196     The timestamp of the server start (so not actually an uptime).
197    
198 root 1.103 =item $cf::RUNTIME
199    
200     The time this server has run, starts at 0 and is increased by $cf::TICK on
201     every server tick.
202    
203 root 1.253 =item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR
204     $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR
205     $cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR
206    
207     Various directories - "/etc", read-only install directory, perl-library
208     directory, pod-directory, read-only maps directory, "/var", "/var/tmp",
209     unique-items directory, player file directory, random maps directory and
210     database environment.
211 root 1.70
212 root 1.103 =item $cf::NOW
213    
214     The time of the last (current) server tick.
215    
216 root 1.70 =item $cf::TICK
217    
218     The interval between server ticks, in seconds.
219    
220 root 1.265 =item $cf::LOADAVG
221    
222     The current CPU load on the server (alpha-smoothed), as a value between 0
223     (none) and 1 (overloaded), indicating how much time is spent on processing
224     objects per tick. Healthy values are < 0.5.
225    
226     =item $cf::LOAD
227    
228     The raw value load value from the last tick.
229    
230 root 1.70 =item %cf::CFG
231    
232 root 1.395 Configuration for the server, loaded from C</etc/deliantra-server/config>, or
233 root 1.70 from wherever your confdir points to.
234    
235 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
236 root 1.155
237 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
238 root 1.546 returns directly I<after> the tick processing (and consequently, can only wake one thread
239 root 1.239 per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
240 root 1.155
241 root 1.546 =cut
242    
243     sub wait_for_tick();
244     sub wait_for_tick_begin();
245    
246 elmex 1.310 =item @cf::INVOKE_RESULTS
247    
248 root 1.488 This array contains the results of the last C<invoke ()> call. When
249 elmex 1.310 C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
250     that call.
251    
252 root 1.479 =item %cf::REFLECT
253    
254     Contains, for each (C++) class name, a hash reference with information
255 root 1.480 about object members (methods, scalars, arrays and flags) and other
256     metadata, which is useful for introspection.
257 root 1.479
258 root 1.70 =back
259    
260     =cut
261    
262 root 1.532 sub error(@) { LOG llevError, join "", @_ }
263     sub warn (@) { LOG llevWarn , join "", @_ }
264     sub info (@) { LOG llevInfo , join "", @_ }
265     sub debug(@) { LOG llevDebug, join "", @_ }
266     sub trace(@) { LOG llevTrace, join "", @_ }
267    
268 root 1.477 $Coro::State::WARNHOOK = sub {
269     my $msg = join "", @_;
270 root 1.103
271 root 1.477 $msg .= "\n"
272     unless $msg =~ /\n$/;
273 root 1.1
274 root 1.477 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
275 root 1.250
276 root 1.532 LOG llevWarn, $msg;
277 root 1.477 };
278 root 1.1
279 root 1.407 $Coro::State::DIEHOOK = sub {
280 root 1.410 return unless $^S eq 0; # "eq", not "=="
281    
282 root 1.532 error Carp::longmess $_[0];
283 root 1.477
284 root 1.522 if (in_main) {#d#
285 root 1.532 error "DIEHOOK called in main context, Coro bug?\n";#d#
286 root 1.410 return;#d#
287     }#d#
288    
289     # kill coroutine otherwise
290     Coro::terminate
291 root 1.407 };
292    
293 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
294     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
295     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
296     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
297     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
298 root 1.273 @safe::cf::arch::ISA = @cf::arch::ISA = 'cf::object';
299     @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; # not really true (yet)
300 root 1.25
301 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
302 root 1.25 # within the Safe compartment.
303 root 1.86 for my $pkg (qw(
304 root 1.100 cf::global cf::attachable
305 root 1.86 cf::object cf::object::player
306 root 1.89 cf::client cf::player
307 root 1.86 cf::arch cf::living
308 root 1.440 cf::map cf::mapspace
309     cf::party cf::region
310 root 1.86 )) {
311 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
312 root 1.25 }
313 root 1.1
314 root 1.396 $EV::DIED = sub {
315 root 1.493 Carp::cluck "error in event callback: @_";
316 root 1.18 };
317    
318 root 1.281 #############################################################################
319    
320 root 1.70 =head2 UTILITY FUNCTIONS
321    
322     =over 4
323    
324 root 1.154 =item dumpval $ref
325    
326 root 1.70 =cut
327 root 1.44
328 root 1.154 sub dumpval {
329     eval {
330     local $SIG{__DIE__};
331     my $d;
332     if (1) {
333     $d = new Data::Dumper([$_[0]], ["*var"]);
334     $d->Terse(1);
335     $d->Indent(2);
336     $d->Quotekeys(0);
337     $d->Useqq(1);
338     #$d->Bless(...);
339     $d->Seen($_[1]) if @_ > 1;
340     $d = $d->Dump();
341     }
342     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
343     $d
344     } || "[unable to dump $_[0]: '$@']";
345     }
346    
347 root 1.398 =item $ref = cf::decode_json $json
348 root 1.70
349     Converts a JSON string into the corresponding perl data structure.
350    
351 root 1.398 =item $json = cf::encode_json $ref
352 root 1.70
353     Converts a perl data structure into its JSON representation.
354    
355 root 1.287 =cut
356    
357 root 1.290 our $json_coder = JSON::XS->new->utf8->max_size (1e6); # accept ~1mb max
358 root 1.287
359 root 1.398 sub encode_json($) { $json_coder->encode ($_[0]) }
360     sub decode_json($) { $json_coder->decode ($_[0]) }
361 root 1.287
362 root 1.453 =item cf::post_init { BLOCK }
363    
364     Execute the given codeblock, I<after> all extensions have been (re-)loaded,
365     but I<before> the server starts ticking again.
366    
367     The cdoeblock will have a single boolean argument to indicate whether this
368     is a reload or not.
369    
370     =cut
371    
372     sub post_init(&) {
373     push @POST_INIT, shift;
374     }
375    
376 root 1.120 =item cf::lock_wait $string
377    
378     Wait until the given lock is available. See cf::lock_acquire.
379    
380     =item my $lock = cf::lock_acquire $string
381    
382     Wait until the given lock is available and then acquires it and returns
383 root 1.458 a L<Guard> object. If the guard object gets destroyed (goes out of scope,
384 root 1.120 for example when the coroutine gets canceled), the lock is automatically
385     returned.
386    
387 root 1.347 Locks are *not* recursive, locking from the same coro twice results in a
388     deadlocked coro.
389    
390 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
391     uses map_find and cf::map::load uses map_load).
392 root 1.120
393 root 1.253 =item $locked = cf::lock_active $string
394    
395     Return true if the lock is currently active, i.e. somebody has locked it.
396    
397 root 1.120 =cut
398    
399 root 1.459 our $LOCKS = new Coro::SemaphoreSet;
400 root 1.120
401     sub lock_wait($) {
402 root 1.459 $LOCKS->wait ($_[0]);
403 root 1.120 }
404    
405     sub lock_acquire($) {
406 root 1.459 $LOCKS->guard ($_[0])
407 root 1.120 }
408    
409 root 1.253 sub lock_active($) {
410 root 1.459 $LOCKS->count ($_[0]) < 1
411 root 1.253 }
412    
413 root 1.133 sub freeze_mainloop {
414 root 1.412 tick_inhibit_inc;
415 root 1.133
416 root 1.458 &Guard::guard (\&tick_inhibit_dec);
417 root 1.133 }
418    
419 root 1.396 =item cf::periodic $interval, $cb
420    
421     Like EV::periodic, but randomly selects a starting point so that the actions
422 root 1.514 get spread over time.
423 root 1.396
424     =cut
425    
426     sub periodic($$) {
427     my ($interval, $cb) = @_;
428    
429     my $start = rand List::Util::min 180, $interval;
430    
431     EV::periodic $start, $interval, 0, $cb
432     }
433    
434 root 1.315 =item cf::get_slot $time[, $priority[, $name]]
435 root 1.314
436     Allocate $time seconds of blocking CPU time at priority C<$priority>:
437     This call blocks and returns only when you have at least C<$time> seconds
438     of cpu time till the next tick. The slot is only valid till the next cede.
439    
440 root 1.315 The optional C<$name> can be used to identify the job to run. It might be
441     used for statistical purposes and should identify the same time-class.
442    
443 root 1.314 Useful for short background jobs.
444    
445     =cut
446    
447     our @SLOT_QUEUE;
448     our $SLOT_QUEUE;
449 root 1.502 our $SLOT_DECAY = 0.9;
450 root 1.314
451     $SLOT_QUEUE->cancel if $SLOT_QUEUE;
452     $SLOT_QUEUE = Coro::async {
453 root 1.374 $Coro::current->desc ("timeslot manager");
454    
455 root 1.314 my $signal = new Coro::Signal;
456 root 1.502 my $busy;
457 root 1.314
458     while () {
459     next_job:
460 root 1.502
461 root 1.314 my $avail = cf::till_tick;
462 root 1.502
463     for (0 .. $#SLOT_QUEUE) {
464     if ($SLOT_QUEUE[$_][0] <= $avail) {
465     $busy = 0;
466     my $job = splice @SLOT_QUEUE, $_, 1, ();
467     $job->[2]->send;
468     Coro::cede;
469     goto next_job;
470     } else {
471     $SLOT_QUEUE[$_][0] *= $SLOT_DECAY;
472 root 1.314 }
473     }
474    
475     if (@SLOT_QUEUE) {
476 root 1.380 # we do not use wait_for_tick() as it returns immediately when tick is inactive
477 root 1.314 push @cf::WAIT_FOR_TICK, $signal;
478     $signal->wait;
479     } else {
480 root 1.502 $busy = 0;
481 root 1.314 Coro::schedule;
482     }
483     }
484     };
485    
486 root 1.315 sub get_slot($;$$) {
487 root 1.423 return if tick_inhibit || $Coro::current == $Coro::main;
488 root 1.420
489 root 1.315 my ($time, $pri, $name) = @_;
490    
491 root 1.502 $time = clamp $time, 0.01, $TICK * .6;
492    
493 root 1.315 my $sig = new Coro::Signal;
494 root 1.314
495 root 1.315 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
496 root 1.314 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
497     $SLOT_QUEUE->ready;
498     $sig->wait;
499     }
500    
501 root 1.140 =item cf::async { BLOCK }
502    
503     Currently the same as Coro::async_pool, meaning you cannot use
504     C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
505     thing you are allowed to do is call C<prio> on it.
506    
507     =cut
508    
509     BEGIN { *async = \&Coro::async_pool }
510    
511 root 1.106 =item cf::sync_job { BLOCK }
512    
513 root 1.394 The design of Deliantra requires that the main coroutine ($Coro::main)
514     is always able to handle events or runnable, as Deliantra is only
515 root 1.281 partly reentrant. Thus "blocking" it by e.g. waiting for I/O is not
516     acceptable.
517 root 1.106
518     If it must be done, put the blocking parts into C<sync_job>. This will run
519     the given BLOCK in another coroutine while waiting for the result. The
520     server will be frozen during this time, so the block should either finish
521     fast or be very important.
522    
523     =cut
524    
525 root 1.105 sub sync_job(&) {
526     my ($job) = @_;
527    
528 root 1.534 if (in_main) {
529 root 1.512 my $time = AE::time;
530 root 1.265
531 root 1.112 # this is the main coro, too bad, we have to block
532     # till the operation succeeds, freezing the server :/
533    
534 root 1.534 #LOG llevError, Carp::longmess "sync job";#d#
535 root 1.379
536 root 1.133 my $freeze_guard = freeze_mainloop;
537 root 1.112
538     my $busy = 1;
539     my @res;
540    
541 root 1.140 (async {
542 root 1.374 $Coro::current->desc ("sync job coro");
543 root 1.112 @res = eval { $job->() };
544 root 1.532 error $@ if $@;
545 root 1.112 undef $busy;
546     })->prio (Coro::PRIO_MAX);
547    
548 root 1.105 while ($busy) {
549 root 1.387 if (Coro::nready) {
550     Coro::cede_notself;
551     } else {
552 root 1.396 EV::loop EV::LOOP_ONESHOT;
553 root 1.387 }
554 root 1.105 }
555 root 1.112
556 root 1.512 my $time = AE::time - $time;
557 root 1.265
558 root 1.412 $TICK_START += $time; # do not account sync jobs to server load
559 root 1.265
560 root 1.112 wantarray ? @res : $res[0]
561 root 1.105 } else {
562 root 1.112 # we are in another coroutine, how wonderful, everything just works
563    
564     $job->()
565 root 1.105 }
566     }
567    
568 root 1.140 =item $coro = cf::async_ext { BLOCK }
569 root 1.103
570 root 1.159 Like async, but this coro is automatically being canceled when the
571 root 1.140 extension calling this is being unloaded.
572 root 1.103
573     =cut
574    
575 root 1.140 sub async_ext(&) {
576 root 1.103 my $cb = shift;
577    
578 root 1.140 my $coro = &Coro::async ($cb);
579 root 1.103
580     $coro->on_destroy (sub {
581     delete $EXT_CORO{$coro+0};
582     });
583     $EXT_CORO{$coro+0} = $coro;
584    
585     $coro
586     }
587    
588 root 1.281 =item fork_call { }, $args
589    
590     Executes the given code block with the given arguments in a seperate
591     process, returning the results. Everything must be serialisable with
592     Coro::Storable. May, of course, block. Note that the executed sub may
593 root 1.396 never block itself or use any form of event handling.
594 root 1.281
595     =cut
596    
597     sub fork_call(&@) {
598     my ($cb, @args) = @_;
599    
600 root 1.332 # we seemingly have to make a local copy of the whole thing,
601     # otherwise perl prematurely frees the stuff :/
602 root 1.355 # TODO: investigate and fix (likely this will be rather laborious)
603 root 1.281
604 root 1.332 my @res = Coro::Util::fork_eval {
605 root 1.281 reset_signals;
606 root 1.332 &$cb
607     }, @args;
608 root 1.298
609 root 1.332 wantarray ? @res : $res[-1]
610 root 1.281 }
611    
612 root 1.514 sub objinfo {
613     (
614     "counter value" => cf::object::object_count,
615     "objects created" => cf::object::create_count,
616     "objects destroyed" => cf::object::destroy_count,
617     "freelist size" => cf::object::free_count,
618     "allocated objects" => cf::object::objects_size,
619     "active objects" => cf::object::actives_size,
620     )
621     }
622    
623 root 1.415 =item $coin = coin_from_name $name
624    
625     =cut
626    
627     our %coin_alias = (
628     "silver" => "silvercoin",
629     "silvercoin" => "silvercoin",
630     "silvercoins" => "silvercoin",
631     "gold" => "goldcoin",
632     "goldcoin" => "goldcoin",
633     "goldcoins" => "goldcoin",
634     "platinum" => "platinacoin",
635     "platinumcoin" => "platinacoin",
636     "platinumcoins" => "platinacoin",
637     "platina" => "platinacoin",
638     "platinacoin" => "platinacoin",
639     "platinacoins" => "platinacoin",
640     "royalty" => "royalty",
641     "royalties" => "royalty",
642     );
643    
644     sub coin_from_name($) {
645     $coin_alias{$_[0]}
646     ? cf::arch::find $coin_alias{$_[0]}
647     : undef
648     }
649    
650 root 1.281 =item $value = cf::db_get $family => $key
651    
652     Returns a single value from the environment database.
653    
654     =item cf::db_put $family => $key => $value
655    
656     Stores the given C<$value> in the family. It can currently store binary
657     data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary).
658    
659 root 1.363 =item $db = cf::db_table "name"
660    
661     Create and/or open a new database table. The string must not be "db" and must be unique
662     within each server.
663    
664 root 1.281 =cut
665    
666 root 1.363 sub db_table($) {
667 root 1.534 cf::error "db_get called from main context"
668     if $Coro::current == $Coro::main;
669    
670 root 1.363 my ($name) = @_;
671     my $db = BDB::db_create $DB_ENV;
672    
673     eval {
674     $db->set_flags (BDB::CHKSUM);
675    
676     utf8::encode $name;
677     BDB::db_open $db, undef, $name, undef, BDB::BTREE,
678     BDB::CREATE | BDB::AUTO_COMMIT, 0666;
679     cf::cleanup "db_open(db): $!" if $!;
680     };
681     cf::cleanup "db_open(db): $@" if $@;
682    
683     $db
684     }
685    
686 root 1.281 our $DB;
687    
688     sub db_init {
689 root 1.534 $DB ||= db_table "db";
690 root 1.281 }
691    
692     sub db_get($$) {
693     my $key = "$_[0]/$_[1]";
694    
695 root 1.534 cf::error "db_get called from main context"
696     if $Coro::current == $Coro::main;
697    
698     BDB::db_get $DB, undef, $key, my $data;
699 root 1.281
700 root 1.534 $! ? ()
701     : $data
702 root 1.281 }
703    
704     sub db_put($$$) {
705     BDB::dbreq_pri 4;
706     BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
707     }
708    
709     =item cf::cache $id => [$paths...], $processversion => $process
710    
711     Generic caching function that returns the value of the resource $id,
712     caching and regenerating as required.
713    
714     This function can block.
715    
716     =cut
717    
718     sub cache {
719     my ($id, $src, $processversion, $process) = @_;
720    
721     my $meta =
722     join "\x00",
723     $processversion,
724     map {
725     aio_stat $_
726     and Carp::croak "$_: $!";
727 root 1.264
728 root 1.281 ($_, (stat _)[7,9])
729     } @$src;
730 root 1.264
731 root 1.281 my $dbmeta = db_get cache => "$id/meta";
732     if ($dbmeta ne $meta) {
733     # changed, we may need to process
734 root 1.264
735 root 1.281 my @data;
736     my $md5;
737 root 1.219
738 root 1.281 for (0 .. $#$src) {
739     0 <= aio_load $src->[$_], $data[$_]
740     or Carp::croak "$src->[$_]: $!";
741     }
742 root 1.108
743 root 1.281 # if processing is expensive, check
744     # checksum first
745     if (1) {
746     $md5 =
747     join "\x00",
748     $processversion,
749     map {
750 root 1.346 cf::cede_to_tick;
751 root 1.281 ($src->[$_], Digest::MD5::md5_hex $data[$_])
752     } 0.. $#$src;
753    
754 root 1.186
755 root 1.281 my $dbmd5 = db_get cache => "$id/md5";
756     if ($dbmd5 eq $md5) {
757     db_put cache => "$id/meta", $meta;
758 root 1.108
759 root 1.281 return db_get cache => "$id/data";
760     }
761     }
762 root 1.108
763 root 1.281 my $t1 = Time::HiRes::time;
764     my $data = $process->(\@data);
765     my $t2 = Time::HiRes::time;
766 root 1.264
767 root 1.532 info "cache: '$id' processed in ", $t2 - $t1, "s\n";
768 root 1.108
769 root 1.281 db_put cache => "$id/data", $data;
770     db_put cache => "$id/md5" , $md5;
771     db_put cache => "$id/meta", $meta;
772 root 1.108
773 root 1.281 return $data;
774     }
775 root 1.263
776 root 1.281 db_get cache => "$id/data"
777 root 1.108 }
778    
779 root 1.230 =item cf::datalog type => key => value, ...
780    
781     Log a datalog packet of the given type with the given key-value pairs.
782    
783     =cut
784    
785     sub datalog($@) {
786     my ($type, %kv) = @_;
787 root 1.532 info "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
788 root 1.230 }
789    
790 root 1.70 =back
791    
792 root 1.71 =cut
793    
794 root 1.44 #############################################################################
795 root 1.39
796 root 1.93 =head2 ATTACHABLE OBJECTS
797    
798 root 1.447 Many objects in deliantra are so-called attachable objects. That means you can
799 root 1.94 attach callbacks/event handlers (a collection of which is called an "attachment")
800     to it. All such attachable objects support the following methods.
801    
802     In the following description, CLASS can be any of C<global>, C<object>
803     C<player>, C<client> or C<map> (i.e. the attachable objects in
804 root 1.394 Deliantra).
805 root 1.55
806     =over 4
807    
808 root 1.94 =item $attachable->attach ($attachment, key => $value...)
809    
810     =item $attachable->detach ($attachment)
811    
812     Attach/detach a pre-registered attachment to a specific object and give it
813     the specified key/value pairs as arguments.
814    
815     Example, attach a minesweeper attachment to the given object, making it a
816     10x10 minesweeper game:
817 root 1.46
818 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
819 root 1.53
820 root 1.93 =item $bool = $attachable->attached ($name)
821 root 1.46
822 root 1.93 Checks wether the named attachment is currently attached to the object.
823 root 1.46
824 root 1.94 =item cf::CLASS->attach ...
825 root 1.46
826 root 1.94 =item cf::CLASS->detach ...
827 root 1.92
828 root 1.94 Define an anonymous attachment and attach it to all objects of the given
829     CLASS. See the next function for an explanation of its arguments.
830 root 1.92
831 root 1.93 You can attach to global events by using the C<cf::global> class.
832 root 1.92
833 root 1.94 Example, log all player logins:
834    
835     cf::player->attach (
836     on_login => sub {
837     my ($pl) = @_;
838     ...
839     },
840     );
841    
842     Example, attach to the jeweler skill:
843    
844     cf::object->attach (
845     type => cf::SKILL,
846     subtype => cf::SK_JEWELER,
847     on_use_skill => sub {
848     my ($sk, $ob, $part, $dir, $msg) = @_;
849     ...
850     },
851     );
852    
853     =item cf::CLASS::attachment $name, ...
854    
855     Register an attachment by C<$name> through which attachable objects of the
856     given CLASS can refer to this attachment.
857    
858 root 1.447 Some classes such as deliantra maps and objects can specify attachments
859 root 1.94 that are attached at load/instantiate time, thus the need for a name.
860    
861     These calls expect any number of the following handler/hook descriptions:
862 root 1.46
863     =over 4
864    
865     =item prio => $number
866    
867     Set the priority for all following handlers/hooks (unless overwritten
868     by another C<prio> setting). Lower priority handlers get executed
869     earlier. The default priority is C<0>, and many built-in handlers are
870     registered at priority C<-1000>, so lower priorities should not be used
871     unless you know what you are doing.
872    
873 root 1.93 =item type => $type
874    
875     (Only for C<< cf::object->attach >> calls), limits the attachment to the
876     given type of objects only (the additional parameter C<subtype> can be
877     used to further limit to the given subtype).
878    
879 root 1.46 =item on_I<event> => \&cb
880    
881     Call the given code reference whenever the named event happens (event is
882     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
883     handlers are recognised generally depends on the type of object these
884     handlers attach to).
885    
886     See F<include/eventinc.h> for the full list of events supported, and their
887     class.
888    
889     =item package => package::
890    
891     Look for sub functions of the name C<< on_I<event> >> in the given
892     package and register them. Only handlers for eevents supported by the
893     object/class are recognised.
894    
895     =back
896    
897 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
898     event handler when a monster attacks:
899    
900     cf::object::attachment sockpuppet =>
901     on_skill_attack => sub {
902     my ($self, $victim) = @_;
903     ...
904     }
905     }
906    
907 root 1.96 =item $attachable->valid
908    
909     Just because you have a perl object does not mean that the corresponding
910     C-level object still exists. If you try to access an object that has no
911     valid C counterpart anymore you get an exception at runtime. This method
912     can be used to test for existence of the C object part without causing an
913     exception.
914    
915 root 1.39 =cut
916    
917 root 1.40 # the following variables are defined in .xs and must not be re-created
918 root 1.100 our @CB_GLOBAL = (); # registry for all global events
919     our @CB_ATTACHABLE = (); # registry for all attachables
920     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
921     our @CB_PLAYER = ();
922     our @CB_CLIENT = ();
923     our @CB_TYPE = (); # registry for type (cf-object class) based events
924     our @CB_MAP = ();
925 root 1.39
926 root 1.45 my %attachment;
927    
928 root 1.170 sub cf::attachable::thawer_merge {
929     # simply override everything except _meta
930     local $_[0]{_meta};
931     %{$_[0]} = %{$_[1]};
932     }
933    
934 root 1.93 sub _attach_cb($$$$) {
935     my ($registry, $event, $prio, $cb) = @_;
936 root 1.39
937     use sort 'stable';
938    
939     $cb = [$prio, $cb];
940    
941     @{$registry->[$event]} = sort
942     { $a->[0] cmp $b->[0] }
943     @{$registry->[$event] || []}, $cb;
944     }
945    
946 root 1.100 # hack
947     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
948    
949 root 1.39 # attach handles attaching event callbacks
950     # the only thing the caller has to do is pass the correct
951     # registry (== where the callback attaches to).
952 root 1.93 sub _attach {
953 root 1.45 my ($registry, $klass, @arg) = @_;
954 root 1.39
955 root 1.93 my $object_type;
956 root 1.39 my $prio = 0;
957     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
958    
959 root 1.100 #TODO: get rid of this hack
960     if ($attachable_klass{$klass}) {
961     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
962     }
963    
964 root 1.45 while (@arg) {
965     my $type = shift @arg;
966 root 1.39
967     if ($type eq "prio") {
968 root 1.45 $prio = shift @arg;
969 root 1.39
970 root 1.93 } elsif ($type eq "type") {
971     $object_type = shift @arg;
972     $registry = $CB_TYPE[$object_type] ||= [];
973    
974     } elsif ($type eq "subtype") {
975     defined $object_type or Carp::croak "subtype specified without type";
976     my $object_subtype = shift @arg;
977 root 1.267 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_TYPES] ||= [];
978 root 1.93
979 root 1.39 } elsif ($type eq "package") {
980 root 1.45 my $pkg = shift @arg;
981 root 1.39
982     while (my ($name, $id) = each %cb_id) {
983     if (my $cb = $pkg->can ($name)) {
984 root 1.93 _attach_cb $registry, $id, $prio, $cb;
985 root 1.39 }
986     }
987    
988     } elsif (exists $cb_id{$type}) {
989 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
990 root 1.39
991     } elsif (ref $type) {
992 root 1.532 error "attaching objects not supported, ignoring.\n";
993 root 1.39
994     } else {
995 root 1.45 shift @arg;
996 root 1.532 error "attach argument '$type' not supported, ignoring.\n";
997 root 1.39 }
998     }
999     }
1000    
1001 root 1.93 sub _object_attach {
1002 root 1.48 my ($obj, $name, %arg) = @_;
1003 root 1.46
1004 root 1.55 return if exists $obj->{_attachment}{$name};
1005    
1006 root 1.46 if (my $attach = $attachment{$name}) {
1007     my $registry = $obj->registry;
1008    
1009 root 1.47 for (@$attach) {
1010     my ($klass, @attach) = @$_;
1011 root 1.93 _attach $registry, $klass, @attach;
1012 root 1.47 }
1013 root 1.46
1014 root 1.48 $obj->{$name} = \%arg;
1015 root 1.46 } else {
1016 root 1.532 info "object uses attachment '$name' which is not available, postponing.\n";
1017 root 1.46 }
1018    
1019 root 1.50 $obj->{_attachment}{$name} = undef;
1020 root 1.46 }
1021    
1022 root 1.93 sub cf::attachable::attach {
1023     if (ref $_[0]) {
1024     _object_attach @_;
1025     } else {
1026     _attach shift->_attach_registry, @_;
1027     }
1028 root 1.267 _recalc_want;
1029 root 1.55 };
1030 root 1.46
1031 root 1.54 # all those should be optimised
1032 root 1.93 sub cf::attachable::detach {
1033 root 1.54 my ($obj, $name) = @_;
1034 root 1.46
1035 root 1.93 if (ref $obj) {
1036     delete $obj->{_attachment}{$name};
1037     reattach ($obj);
1038     } else {
1039     Carp::croak "cannot, currently, detach class attachments";
1040     }
1041 root 1.267 _recalc_want;
1042 root 1.55 };
1043    
1044 root 1.93 sub cf::attachable::attached {
1045 root 1.55 my ($obj, $name) = @_;
1046    
1047     exists $obj->{_attachment}{$name}
1048 root 1.39 }
1049    
1050 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
1051 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
1052     sub cf::\L$klass\E::_attach_registry {
1053     (\\\@CB_$klass, KLASS_$klass)
1054     }
1055 root 1.45
1056 root 1.93 sub cf::\L$klass\E::attachment {
1057     my \$name = shift;
1058 root 1.39
1059 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
1060     }
1061     ";
1062     die if $@;
1063 root 1.52 }
1064    
1065 root 1.39 our $override;
1066 elmex 1.310 our @INVOKE_RESULTS = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
1067 root 1.39
1068 root 1.45 sub override {
1069     $override = 1;
1070 elmex 1.310 @INVOKE_RESULTS = (@_);
1071 root 1.39 }
1072    
1073 root 1.45 sub do_invoke {
1074 root 1.39 my $event = shift;
1075 root 1.40 my $callbacks = shift;
1076 root 1.39
1077 elmex 1.310 @INVOKE_RESULTS = ();
1078 root 1.45
1079 root 1.39 local $override;
1080    
1081 root 1.40 for (@$callbacks) {
1082 root 1.39 eval { &{$_->[1]} };
1083    
1084     if ($@) {
1085 root 1.532 error "$@", "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
1086 root 1.39 override;
1087     }
1088    
1089     return 1 if $override;
1090     }
1091    
1092     0
1093     }
1094    
1095 root 1.406 =item $bool = cf::global->invoke (EVENT_CLASS_XXX, ...)
1096 root 1.55
1097 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
1098 root 1.55
1099 root 1.96 Generate an object-specific event with the given arguments.
1100 root 1.55
1101 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
1102 root 1.55 removed in future versions), and there is no public API to access override
1103 elmex 1.310 results (if you must, access C<@cf::INVOKE_RESULTS> directly).
1104 root 1.55
1105     =back
1106    
1107 root 1.71 =cut
1108    
1109 root 1.70 #############################################################################
1110 root 1.45 # object support
1111 root 1.312
1112 root 1.386 sub _object_equal($$);
1113     sub _object_equal($$) {
1114     my ($a, $b) = @_;
1115    
1116     return 0 unless (ref $a) eq (ref $b);
1117    
1118     if ("HASH" eq ref $a) {
1119     my @ka = keys %$a;
1120     my @kb = keys %$b;
1121    
1122     return 0 if @ka != @kb;
1123    
1124     for (0 .. $#ka) {
1125     return 0 unless $ka[$_] eq $kb[$_];
1126     return 0 unless _object_equal $a->{$ka[$_]}, $b->{$kb[$_]};
1127     }
1128    
1129     } elsif ("ARRAY" eq ref $a) {
1130    
1131     return 0 if @$a != @$b;
1132    
1133     for (0 .. $#$a) {
1134     return 0 unless _object_equal $a->[$_], $b->[$_];
1135     }
1136    
1137     } elsif ($a ne $b) {
1138     return 0;
1139     }
1140    
1141     1
1142     }
1143    
1144     our $SLOW_MERGES;#d#
1145 root 1.312 sub _can_merge {
1146     my ($ob1, $ob2) = @_;
1147    
1148 root 1.386 ++$SLOW_MERGES;#d#
1149 root 1.312
1150 root 1.386 # we do the slow way here
1151     return _object_equal $ob1, $ob2
1152 root 1.312 }
1153 root 1.45
1154 root 1.102 sub reattach {
1155     # basically do the same as instantiate, without calling instantiate
1156     my ($obj) = @_;
1157    
1158 root 1.441 # no longer needed after getting rid of delete_package?
1159     #bless $obj, ref $obj; # re-bless in case extensions have been reloaded
1160 root 1.169
1161 root 1.102 my $registry = $obj->registry;
1162    
1163     @$registry = ();
1164    
1165     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
1166    
1167     for my $name (keys %{ $obj->{_attachment} || {} }) {
1168     if (my $attach = $attachment{$name}) {
1169     for (@$attach) {
1170     my ($klass, @attach) = @$_;
1171     _attach $registry, $klass, @attach;
1172     }
1173     } else {
1174 root 1.532 info "object uses attachment '$name' that is not available, postponing.\n";
1175 root 1.102 }
1176     }
1177     }
1178    
1179 root 1.100 cf::attachable->attach (
1180     prio => -1000000,
1181     on_instantiate => sub {
1182     my ($obj, $data) = @_;
1183 root 1.45
1184 root 1.398 $data = decode_json $data;
1185 root 1.45
1186 root 1.100 for (@$data) {
1187     my ($name, $args) = @$_;
1188 root 1.49
1189 root 1.100 $obj->attach ($name, %{$args || {} });
1190     }
1191     },
1192 root 1.102 on_reattach => \&reattach,
1193 root 1.100 on_clone => sub {
1194     my ($src, $dst) = @_;
1195    
1196     @{$dst->registry} = @{$src->registry};
1197    
1198     %$dst = %$src;
1199    
1200     %{$dst->{_attachment}} = %{$src->{_attachment}}
1201     if exists $src->{_attachment};
1202     },
1203     );
1204 root 1.45
1205 root 1.46 sub object_freezer_save {
1206 root 1.59 my ($filename, $rdata, $objs) = @_;
1207 root 1.46
1208 root 1.105 sync_job {
1209     if (length $$rdata) {
1210 root 1.362 utf8::decode (my $decname = $filename);
1211 root 1.532 trace sprintf "saving %s (%d,%d)\n",
1212     $decname, length $$rdata, scalar @$objs
1213 root 1.527 if $VERBOSE_IO;
1214 root 1.60
1215 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1216 root 1.427 aio_chmod $fh, SAVE_MODE;
1217 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1218 root 1.473 if ($cf::USE_FSYNC) {
1219     aio_sync_file_range $fh, 0, 0, IO::AIO::SYNC_FILE_RANGE_WAIT_BEFORE | IO::AIO::SYNC_FILE_RANGE_WRITE | IO::AIO::SYNC_FILE_RANGE_WAIT_AFTER;
1220     aio_fsync $fh;
1221     }
1222 root 1.427 aio_close $fh;
1223 root 1.105
1224     if (@$objs) {
1225     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
1226 root 1.427 aio_chmod $fh, SAVE_MODE;
1227 root 1.388 my $data = Coro::Storable::nfreeze { version => 1, objs => $objs };
1228 root 1.105 aio_write $fh, 0, (length $data), $data, 0;
1229 root 1.473 if ($cf::USE_FSYNC) {
1230     aio_sync_file_range $fh, 0, 0, IO::AIO::SYNC_FILE_RANGE_WAIT_BEFORE | IO::AIO::SYNC_FILE_RANGE_WRITE | IO::AIO::SYNC_FILE_RANGE_WAIT_AFTER;
1231     aio_fsync $fh;
1232     }
1233 root 1.427 aio_close $fh;
1234 root 1.105 aio_rename "$filename.pst~", "$filename.pst";
1235     }
1236     } else {
1237     aio_unlink "$filename.pst";
1238     }
1239    
1240     aio_rename "$filename~", $filename;
1241 root 1.456
1242     $filename =~ s%/[^/]+$%%;
1243 root 1.457 aio_pathsync $filename if $cf::USE_FSYNC;
1244 root 1.60 } else {
1245 root 1.532 error "unable to save objects: $filename~: $!\n";
1246 root 1.60 }
1247 root 1.59 } else {
1248 root 1.105 aio_unlink $filename;
1249     aio_unlink "$filename.pst";
1250 root 1.59 }
1251 root 1.356 };
1252 root 1.45 }
1253    
1254 root 1.80 sub object_freezer_as_string {
1255     my ($rdata, $objs) = @_;
1256    
1257     use Data::Dumper;
1258    
1259 root 1.81 $$rdata . Dumper $objs
1260 root 1.80 }
1261    
1262 root 1.46 sub object_thawer_load {
1263     my ($filename) = @_;
1264    
1265 root 1.105 my ($data, $av);
1266 root 1.61
1267 root 1.105 (aio_load $filename, $data) >= 0
1268     or return;
1269 root 1.61
1270 root 1.105 unless (aio_stat "$filename.pst") {
1271     (aio_load "$filename.pst", $av) >= 0
1272     or return;
1273 root 1.356
1274 root 1.388 my $st = eval { Coro::Storable::thaw $av };
1275 root 1.380 $av = $st->{objs};
1276 root 1.61 }
1277 root 1.45
1278 root 1.362 utf8::decode (my $decname = $filename);
1279 root 1.532 trace sprintf "loading %s (%d,%d)\n",
1280     $decname, length $data, scalar @{$av || []}
1281 root 1.527 if $VERBOSE_IO;
1282 root 1.356
1283     ($data, $av)
1284 root 1.45 }
1285    
1286 root 1.281 =head2 COMMAND CALLBACKS
1287    
1288     =over 4
1289    
1290     =cut
1291    
1292 root 1.45 #############################################################################
1293 root 1.85 # command handling &c
1294 root 1.39
1295 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
1296 root 1.1
1297 root 1.85 Register a callback for execution when the client sends the user command
1298     $name.
1299 root 1.5
1300 root 1.85 =cut
1301 root 1.5
1302 root 1.85 sub register_command {
1303     my ($name, $cb) = @_;
1304 root 1.5
1305 root 1.85 my $caller = caller;
1306     #warn "registering command '$name/$time' to '$caller'";
1307 root 1.1
1308 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
1309 root 1.1 }
1310    
1311 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
1312 root 1.1
1313 root 1.287 Register a callback for execution when the client sends an (synchronous)
1314     extcmd packet. Ext commands will be processed in the order they are
1315     received by the server, like other user commands. The first argument is
1316     the logged-in player. Ext commands can only be processed after a player
1317     has logged in successfully.
1318    
1319     If the callback returns something, it is sent back as if reply was being
1320     called.
1321    
1322     =item cf::register_exticmd $name => \&callback($ns,$packet);
1323    
1324     Register a callback for execution when the client sends an (asynchronous)
1325     exticmd packet. Exti commands are processed by the server as soon as they
1326     are received, i.e. out of order w.r.t. other commands. The first argument
1327     is a client socket. Exti commands can be received anytime, even before
1328     log-in.
1329 root 1.1
1330 root 1.85 If the callback returns something, it is sent back as if reply was being
1331     called.
1332 root 1.1
1333 root 1.85 =cut
1334 root 1.1
1335 root 1.16 sub register_extcmd {
1336     my ($name, $cb) = @_;
1337    
1338 root 1.159 $EXTCMD{$name} = $cb;
1339 root 1.16 }
1340    
1341 root 1.287 sub register_exticmd {
1342     my ($name, $cb) = @_;
1343    
1344     $EXTICMD{$name} = $cb;
1345     }
1346    
1347 root 1.457 use File::Glob ();
1348    
1349 root 1.93 cf::player->attach (
1350 root 1.509 on_unknown_command => sub {
1351 root 1.85 my ($pl, $name, $params) = @_;
1352    
1353     my $cb = $COMMAND{$name}
1354     or return;
1355    
1356     for my $cmd (@$cb) {
1357     $cmd->[1]->($pl->ob, $params);
1358     }
1359    
1360     cf::override;
1361     },
1362     on_extcmd => sub {
1363     my ($pl, $buf) = @_;
1364    
1365 root 1.290 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1366 root 1.85
1367     if (ref $msg) {
1368 root 1.316 my ($type, $reply, @payload) =
1369     "ARRAY" eq ref $msg
1370     ? @$msg
1371     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1372    
1373 root 1.338 my @reply;
1374    
1375 root 1.316 if (my $cb = $EXTCMD{$type}) {
1376 root 1.338 @reply = $cb->($pl, @payload);
1377     }
1378    
1379     $pl->ext_reply ($reply, @reply)
1380     if $reply;
1381 root 1.316
1382 root 1.85 } else {
1383 root 1.532 error "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1384 root 1.85 }
1385    
1386     cf::override;
1387     },
1388 root 1.93 );
1389 root 1.85
1390 root 1.457 # "readahead" all extensions
1391     sub cache_extensions {
1392     my $grp = IO::AIO::aio_group;
1393    
1394 root 1.472 add $grp IO::AIO::aio_readdirx $LIBDIR, IO::AIO::READDIR_STAT_ORDER, sub {
1395 root 1.457 for (grep /\.ext$/, @{$_[0]}) {
1396     add $grp IO::AIO::aio_load "$LIBDIR/$_", my $data;
1397     }
1398     };
1399    
1400     $grp
1401     }
1402    
1403 root 1.278 sub load_extensions {
1404 root 1.532 info "loading extensions...";
1405    
1406 root 1.278 cf::sync_job {
1407     my %todo;
1408    
1409     for my $path (<$LIBDIR/*.ext>) {
1410     next unless -r $path;
1411    
1412     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1413     my $base = $1;
1414     my $pkg = $1;
1415     $pkg =~ s/[^[:word:]]/_/g;
1416     $pkg = "ext::$pkg";
1417    
1418     open my $fh, "<:utf8", $path
1419     or die "$path: $!";
1420    
1421     my $source = do { local $/; <$fh> };
1422 root 1.1
1423 root 1.278 my %ext = (
1424     path => $path,
1425     base => $base,
1426     pkg => $pkg,
1427     );
1428 root 1.1
1429 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1430     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1431 root 1.1
1432 root 1.278 $ext{source} =
1433 root 1.538 "package $pkg; use common::sense;\n"
1434 root 1.278 . "#line 1 \"$path\"\n{\n"
1435     . $source
1436     . "\n};\n1";
1437 root 1.1
1438 root 1.278 $todo{$base} = \%ext;
1439 root 1.166 }
1440 root 1.1
1441 root 1.505 my $pass = 0;
1442 root 1.278 my %done;
1443     while (%todo) {
1444     my $progress;
1445    
1446 root 1.505 ++$pass;
1447    
1448     ext:
1449 root 1.278 while (my ($k, $v) = each %todo) {
1450 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1451 root 1.505 next ext
1452 root 1.278 unless exists $done{$_};
1453     }
1454    
1455 root 1.532 trace "... pass $pass, loading '$k' into '$v->{pkg}'\n";
1456 root 1.505
1457     my $active = eval $v->{source};
1458    
1459     if (length $@) {
1460 root 1.532 error "$v->{path}: $@\n";
1461 root 1.505
1462     cf::cleanup "mandatory extension '$k' failed to load, exiting."
1463     if exists $v->{meta}{mandatory};
1464 root 1.521
1465     warn "$v->{base}: optional extension cannot be loaded, skipping.\n";
1466     delete $todo{$k};
1467 root 1.505 } else {
1468     $done{$k} = delete $todo{$k};
1469     push @EXTS, $v->{pkg};
1470     $progress = 1;
1471 root 1.278
1472 root 1.532 info "$v->{base}: extension inactive.\n"
1473 root 1.505 unless $active;
1474 root 1.278 }
1475 root 1.505 }
1476    
1477     unless ($progress) {
1478     warn "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n";
1479 root 1.278
1480 root 1.505 while (my ($k, $v) = each %todo) {
1481     cf::cleanup "mandatory extension '$k' has unresolved dependencies, exiting."
1482     if exists $v->{meta}{mandatory};
1483     }
1484 root 1.278 }
1485     }
1486     };
1487 root 1.1 }
1488    
1489 root 1.8 #############################################################################
1490 root 1.70
1491 root 1.281 =back
1492    
1493 root 1.70 =head2 CORE EXTENSIONS
1494    
1495 root 1.447 Functions and methods that extend core deliantra objects.
1496 root 1.70
1497 root 1.143 =cut
1498    
1499     package cf::player;
1500    
1501 root 1.154 use Coro::AIO;
1502    
1503 root 1.95 =head3 cf::player
1504    
1505 root 1.70 =over 4
1506 root 1.22
1507 root 1.361 =item cf::player::num_playing
1508    
1509     Returns the official number of playing players, as per the Crossfire metaserver rules.
1510    
1511     =cut
1512    
1513     sub num_playing {
1514     scalar grep
1515     $_->ob->map
1516     && !$_->hidden
1517     && !$_->ob->flag (cf::FLAG_WIZ),
1518     cf::player::list
1519     }
1520    
1521 root 1.143 =item cf::player::find $login
1522 root 1.23
1523 root 1.143 Returns the given player object, loading it if necessary (might block).
1524 root 1.23
1525     =cut
1526    
1527 root 1.145 sub playerdir($) {
1528 root 1.253 "$PLAYERDIR/"
1529 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1530     }
1531    
1532 root 1.143 sub path($) {
1533 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1534    
1535 root 1.234 (playerdir $login) . "/playerdata"
1536 root 1.143 }
1537    
1538     sub find_active($) {
1539     $cf::PLAYER{$_[0]}
1540     and $cf::PLAYER{$_[0]}->active
1541     and $cf::PLAYER{$_[0]}
1542     }
1543    
1544     sub exists($) {
1545     my ($login) = @_;
1546    
1547     $cf::PLAYER{$login}
1548 root 1.452 or !aio_stat path $login
1549 root 1.143 }
1550    
1551     sub find($) {
1552     return $cf::PLAYER{$_[0]} || do {
1553     my $login = $_[0];
1554    
1555     my $guard = cf::lock_acquire "user_find:$login";
1556    
1557 root 1.151 $cf::PLAYER{$_[0]} || do {
1558 root 1.234 # rename old playerfiles to new ones
1559     #TODO: remove when no longer required
1560     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1561     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1562     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1563     aio_unlink +(playerdir $login) . "/$login.pl";
1564    
1565 root 1.356 my $f = new_from_file cf::object::thawer path $login
1566 root 1.151 or return;
1567 root 1.356
1568     my $pl = cf::player::load_pl $f
1569     or return;
1570 root 1.427
1571 root 1.356 local $cf::PLAYER_LOADING{$login} = $pl;
1572     $f->resolve_delayed_derefs;
1573 root 1.151 $cf::PLAYER{$login} = $pl
1574     }
1575     }
1576 root 1.143 }
1577    
1578 root 1.511 cf::player->attach (
1579     on_load => sub {
1580     my ($pl, $path) = @_;
1581    
1582     # restore slots saved in save, below
1583     my $slots = delete $pl->{_slots};
1584    
1585     $pl->ob->current_weapon ($slots->[0]);
1586     $pl->combat_ob ($slots->[1]);
1587     $pl->ranged_ob ($slots->[2]);
1588     },
1589     );
1590    
1591 root 1.143 sub save($) {
1592     my ($pl) = @_;
1593    
1594     return if $pl->{deny_save};
1595    
1596     my $path = path $pl;
1597     my $guard = cf::lock_acquire "user_save:$path";
1598    
1599     return if $pl->{deny_save};
1600 root 1.146
1601 root 1.154 aio_mkdir playerdir $pl, 0770;
1602 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1603    
1604 root 1.420 cf::get_slot 0.01;
1605    
1606 root 1.511 # save slots, to be restored later
1607     local $pl->{_slots} = [$pl->ob->current_weapon, $pl->combat_ob, $pl->ranged_ob];
1608    
1609 root 1.143 $pl->save_pl ($path);
1610 root 1.346 cf::cede_to_tick;
1611 root 1.143 }
1612    
1613     sub new($) {
1614     my ($login) = @_;
1615    
1616     my $self = create;
1617    
1618     $self->ob->name ($login);
1619     $self->{deny_save} = 1;
1620    
1621     $cf::PLAYER{$login} = $self;
1622    
1623     $self
1624 root 1.23 }
1625    
1626 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1627    
1628     =cut
1629    
1630     sub send_msg {
1631     my $ns = shift->ns
1632     or return;
1633     $ns->send_msg (@_);
1634     }
1635    
1636 root 1.154 =item $pl->quit_character
1637    
1638     Nukes the player without looking back. If logged in, the connection will
1639     be destroyed. May block for a long time.
1640    
1641     =cut
1642    
1643 root 1.145 sub quit_character {
1644     my ($pl) = @_;
1645    
1646 root 1.220 my $name = $pl->ob->name;
1647    
1648 root 1.145 $pl->{deny_save} = 1;
1649 root 1.443 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1650 root 1.145
1651     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1652     $pl->deactivate;
1653 root 1.432 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1654 root 1.145 $pl->invoke (cf::EVENT_PLAYER_QUIT);
1655     $pl->ns->destroy if $pl->ns;
1656    
1657     my $path = playerdir $pl;
1658     my $temp = "$path~$cf::RUNTIME~deleting~";
1659 root 1.154 aio_rename $path, $temp;
1660 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1661     $pl->destroy;
1662 root 1.220
1663     my $prefix = qr<^~\Q$name\E/>;
1664    
1665     # nuke player maps
1666     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1667    
1668 root 1.150 IO::AIO::aio_rmtree $temp;
1669 root 1.145 }
1670    
1671 pippijn 1.221 =item $pl->kick
1672    
1673     Kicks a player out of the game. This destroys the connection.
1674    
1675     =cut
1676    
1677     sub kick {
1678     my ($pl, $kicker) = @_;
1679    
1680     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1681     $pl->killer ("kicked");
1682     $pl->ns->destroy;
1683     }
1684    
1685 root 1.154 =item cf::player::list_logins
1686    
1687     Returns am arrayref of all valid playernames in the system, can take a
1688     while and may block, so not sync_job-capable, ever.
1689    
1690     =cut
1691    
1692     sub list_logins {
1693 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1694 root 1.154 or return [];
1695    
1696     my @logins;
1697    
1698     for my $login (@$dirs) {
1699 root 1.354 my $path = path $login;
1700    
1701     # a .pst is a dead give-away for a valid player
1702 root 1.427 # if no pst file found, open and chekc for blocked users
1703     if (aio_stat "$path.pst") {
1704 root 1.354 my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1705     aio_read $fh, 0, 512, my $buf, 0 or next;
1706     $buf !~ /^password -------------$/m or next; # official not-valid tag
1707     }
1708 root 1.154
1709     utf8::decode $login;
1710     push @logins, $login;
1711     }
1712    
1713     \@logins
1714     }
1715    
1716     =item $player->maps
1717    
1718 root 1.523 =item cf::player::maps $login
1719    
1720 root 1.166 Returns an arrayref of map paths that are private for this
1721 root 1.154 player. May block.
1722    
1723     =cut
1724    
1725     sub maps($) {
1726     my ($pl) = @_;
1727    
1728 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1729    
1730 root 1.154 my $files = aio_readdir playerdir $pl
1731     or return;
1732    
1733     my @paths;
1734    
1735     for (@$files) {
1736     utf8::decode $_;
1737     next if /\.(?:pl|pst)$/;
1738 root 1.158 next unless /^$PATH_SEP/o;
1739 root 1.154
1740 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1741 root 1.154 }
1742    
1743     \@paths
1744     }
1745    
1746 root 1.447 =item $protocol_xml = $player->expand_cfpod ($cfpod)
1747 root 1.283
1748 root 1.447 Expand deliantra pod fragments into protocol xml.
1749 root 1.283
1750 root 1.316 =item $player->ext_reply ($msgid, @msg)
1751 root 1.95
1752     Sends an ext reply to the player.
1753    
1754     =cut
1755    
1756 root 1.316 sub ext_reply($$@) {
1757     my ($self, $id, @msg) = @_;
1758 root 1.95
1759 root 1.336 $self->ns->ext_reply ($id, @msg)
1760 root 1.95 }
1761    
1762 root 1.316 =item $player->ext_msg ($type, @msg)
1763 root 1.231
1764     Sends an ext event to the client.
1765    
1766     =cut
1767    
1768 root 1.316 sub ext_msg($$@) {
1769     my ($self, $type, @msg) = @_;
1770 root 1.231
1771 root 1.316 $self->ns->ext_msg ($type, @msg);
1772 root 1.231 }
1773    
1774 root 1.238 =head3 cf::region
1775    
1776     =over 4
1777    
1778     =cut
1779    
1780     package cf::region;
1781    
1782     =item cf::region::find_by_path $path
1783    
1784 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1785 root 1.238
1786     =cut
1787    
1788     sub find_by_path($) {
1789     my ($path) = @_;
1790    
1791 root 1.523 $path =~ s/^~[^\/]*//; # skip ~login
1792    
1793 root 1.238 my ($match, $specificity);
1794    
1795     for my $region (list) {
1796 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1797 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1798     if $region->specificity > $specificity;
1799     }
1800     }
1801    
1802     $match
1803     }
1804 root 1.143
1805 root 1.95 =back
1806    
1807 root 1.110 =head3 cf::map
1808    
1809     =over 4
1810    
1811     =cut
1812    
1813     package cf::map;
1814    
1815     use Fcntl;
1816     use Coro::AIO;
1817    
1818 root 1.166 use overload
1819 root 1.173 '""' => \&as_string,
1820     fallback => 1;
1821 root 1.166
1822 root 1.133 our $MAX_RESET = 3600;
1823     our $DEFAULT_RESET = 3000;
1824 root 1.110
1825     sub generate_random_map {
1826 root 1.166 my ($self, $rmp) = @_;
1827 root 1.418
1828     my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM
1829    
1830 root 1.110 # mit "rum" bekleckern, nicht
1831 root 1.166 $self->_create_random_map (
1832 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1833 root 1.508 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle}, $rmp->{miningstyle},
1834 root 1.110 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1835     $rmp->{exit_on_final_map},
1836     $rmp->{xsize}, $rmp->{ysize},
1837     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1838     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1839     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1840     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1841     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1842 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1843     )
1844 root 1.110 }
1845    
1846 root 1.187 =item cf::map->register ($regex, $prio)
1847    
1848     Register a handler for the map path matching the given regex at the
1849     givne priority (higher is better, built-in handlers have priority 0, the
1850     default).
1851    
1852     =cut
1853    
1854 root 1.166 sub register {
1855 root 1.187 my (undef, $regex, $prio) = @_;
1856 root 1.166 my $pkg = caller;
1857    
1858     push @{"$pkg\::ISA"}, __PACKAGE__;
1859    
1860 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1861 root 1.166 }
1862    
1863     # also paths starting with '/'
1864 root 1.524 $EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1865 root 1.166
1866 root 1.170 sub thawer_merge {
1867 root 1.172 my ($self, $merge) = @_;
1868    
1869 root 1.170 # we have to keep some variables in memory intact
1870 root 1.172 local $self->{path};
1871     local $self->{load_path};
1872 root 1.170
1873 root 1.172 $self->SUPER::thawer_merge ($merge);
1874 root 1.170 }
1875    
1876 root 1.166 sub normalise {
1877     my ($path, $base) = @_;
1878    
1879 root 1.543 $path = "$path"; # make sure it's a string
1880 root 1.192
1881 root 1.199 $path =~ s/\.map$//;
1882    
1883 root 1.166 # map plan:
1884     #
1885     # /! non-realised random map exit (special hack!)
1886     # {... are special paths that are not being touched
1887     # ?xxx/... are special absolute paths
1888     # ?random/... random maps
1889     # /... normal maps
1890     # ~user/... per-player map of a specific user
1891    
1892     $path =~ s/$PATH_SEP/\//go;
1893    
1894     # treat it as relative path if it starts with
1895     # something that looks reasonable
1896     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1897     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1898    
1899     $base =~ s{[^/]+/?$}{};
1900     $path = "$base/$path";
1901     }
1902    
1903     for ($path) {
1904     redo if s{//}{/};
1905     redo if s{/\.?/}{/};
1906     redo if s{/[^/]+/\.\./}{/};
1907     }
1908    
1909     $path
1910     }
1911    
1912     sub new_from_path {
1913     my (undef, $path, $base) = @_;
1914    
1915     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1916    
1917     $path = normalise $path, $base;
1918    
1919 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1920     if ($path =~ $EXT_MAP{$pkg}[1]) {
1921 root 1.166 my $self = bless cf::map::new, $pkg;
1922     $self->{path} = $path; $self->path ($path);
1923     $self->init; # pass $1 etc.
1924     return $self;
1925     }
1926     }
1927    
1928 root 1.543 Carp::cluck "unable to resolve path '$path' (base '$base')";
1929 root 1.166 ()
1930     }
1931    
1932     sub init {
1933     my ($self) = @_;
1934    
1935     $self
1936     }
1937    
1938     sub as_string {
1939     my ($self) = @_;
1940    
1941     "$self->{path}"
1942     }
1943    
1944     # the displayed name, this is a one way mapping
1945     sub visible_name {
1946     &as_string
1947     }
1948    
1949     # the original (read-only) location
1950     sub load_path {
1951     my ($self) = @_;
1952    
1953 root 1.254 "$MAPDIR/$self->{path}.map"
1954 root 1.166 }
1955    
1956     # the temporary/swap location
1957     sub save_path {
1958     my ($self) = @_;
1959    
1960 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1961 root 1.254 "$TMPDIR/$path.map"
1962 root 1.166 }
1963    
1964     # the unique path, undef == no special unique path
1965     sub uniq_path {
1966     my ($self) = @_;
1967    
1968 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1969 root 1.253 "$UNIQUEDIR/$path"
1970 root 1.166 }
1971    
1972 root 1.275 sub decay_objects {
1973     my ($self) = @_;
1974    
1975     return if $self->{deny_reset};
1976    
1977     $self->do_decay_objects;
1978     }
1979    
1980 root 1.166 sub unlink_save {
1981     my ($self) = @_;
1982    
1983     utf8::encode (my $save = $self->save_path);
1984 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1985     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1986 root 1.166 }
1987    
1988     sub load_header_from($) {
1989     my ($self, $path) = @_;
1990 root 1.110
1991     utf8::encode $path;
1992 root 1.356 my $f = new_from_file cf::object::thawer $path
1993     or return;
1994 root 1.110
1995 root 1.356 $self->_load_header ($f)
1996 root 1.110 or return;
1997    
1998 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1999     $f->resolve_delayed_derefs;
2000    
2001 root 1.166 $self->{load_path} = $path;
2002 root 1.135
2003 root 1.166 1
2004     }
2005 root 1.110
2006 root 1.188 sub load_header_orig {
2007 root 1.166 my ($self) = @_;
2008 root 1.110
2009 root 1.166 $self->load_header_from ($self->load_path)
2010 root 1.110 }
2011    
2012 root 1.188 sub load_header_temp {
2013 root 1.166 my ($self) = @_;
2014 root 1.110
2015 root 1.166 $self->load_header_from ($self->save_path)
2016     }
2017 root 1.110
2018 root 1.188 sub prepare_temp {
2019     my ($self) = @_;
2020    
2021     $self->last_access ((delete $self->{last_access})
2022     || $cf::RUNTIME); #d#
2023     # safety
2024     $self->{instantiate_time} = $cf::RUNTIME
2025     if $self->{instantiate_time} > $cf::RUNTIME;
2026     }
2027    
2028     sub prepare_orig {
2029     my ($self) = @_;
2030    
2031     $self->{load_original} = 1;
2032     $self->{instantiate_time} = $cf::RUNTIME;
2033     $self->last_access ($cf::RUNTIME);
2034     $self->instantiate;
2035     }
2036    
2037 root 1.166 sub load_header {
2038     my ($self) = @_;
2039 root 1.110
2040 root 1.188 if ($self->load_header_temp) {
2041     $self->prepare_temp;
2042 root 1.166 } else {
2043 root 1.188 $self->load_header_orig
2044 root 1.166 or return;
2045 root 1.188 $self->prepare_orig;
2046 root 1.166 }
2047 root 1.120
2048 root 1.275 $self->{deny_reset} = 1
2049     if $self->no_reset;
2050    
2051 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
2052     unless $self->default_region;
2053    
2054 root 1.166 1
2055     }
2056 root 1.110
2057 root 1.166 sub find;
2058     sub find {
2059     my ($path, $origin) = @_;
2060 root 1.134
2061 root 1.543 cf::cede_to_tick;
2062    
2063 root 1.166 $path = normalise $path, $origin && $origin->path;
2064 root 1.110
2065 root 1.459 my $guard1 = cf::lock_acquire "map_data:$path";#d#remove
2066     my $guard2 = cf::lock_acquire "map_find:$path";
2067 root 1.110
2068 root 1.166 $cf::MAP{$path} || do {
2069     my $map = new_from_path cf::map $path
2070     or return;
2071 root 1.110
2072 root 1.116 $map->{last_save} = $cf::RUNTIME;
2073 root 1.110
2074 root 1.166 $map->load_header
2075     or return;
2076 root 1.134
2077 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2078 root 1.185 # doing this can freeze the server in a sync job, obviously
2079     #$cf::WAIT_FOR_TICK->wait;
2080 root 1.429 undef $guard2;
2081 root 1.358 undef $guard1;
2082 root 1.112 $map->reset;
2083 root 1.192 return find $path;
2084 root 1.112 }
2085 root 1.110
2086 root 1.166 $cf::MAP{$path} = $map
2087 root 1.110 }
2088     }
2089    
2090 root 1.500 sub pre_load { }
2091     #sub post_load { } # XS
2092 root 1.188
2093 root 1.110 sub load {
2094     my ($self) = @_;
2095    
2096 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2097    
2098 root 1.120 my $path = $self->{path};
2099    
2100 root 1.256 {
2101 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2102 root 1.256
2103 root 1.357 return unless $self->valid;
2104 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2105 root 1.110
2106 root 1.256 $self->in_memory (cf::MAP_LOADING);
2107 root 1.110
2108 root 1.256 $self->alloc;
2109 root 1.188
2110 root 1.256 $self->pre_load;
2111 root 1.346 cf::cede_to_tick;
2112 root 1.188
2113 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2114     $f->skip_block;
2115     $self->_load_objects ($f)
2116 root 1.256 or return;
2117 root 1.110
2118 root 1.439 $self->post_load_original
2119 root 1.256 if delete $self->{load_original};
2120 root 1.111
2121 root 1.256 if (my $uniq = $self->uniq_path) {
2122     utf8::encode $uniq;
2123 root 1.356 unless (aio_stat $uniq) {
2124     if (my $f = new_from_file cf::object::thawer $uniq) {
2125     $self->clear_unique_items;
2126     $self->_load_objects ($f);
2127     $f->resolve_delayed_derefs;
2128     }
2129 root 1.256 }
2130 root 1.110 }
2131    
2132 root 1.356 $f->resolve_delayed_derefs;
2133    
2134 root 1.346 cf::cede_to_tick;
2135 root 1.256 # now do the right thing for maps
2136     $self->link_multipart_objects;
2137 root 1.110 $self->difficulty ($self->estimate_difficulty)
2138     unless $self->difficulty;
2139 root 1.346 cf::cede_to_tick;
2140 root 1.256
2141     unless ($self->{deny_activate}) {
2142     $self->decay_objects;
2143     $self->fix_auto_apply;
2144     $self->update_buttons;
2145 root 1.346 cf::cede_to_tick;
2146 root 1.256 $self->activate;
2147     }
2148    
2149 root 1.325 $self->{last_save} = $cf::RUNTIME;
2150     $self->last_access ($cf::RUNTIME);
2151 root 1.324
2152 root 1.420 $self->in_memory (cf::MAP_ACTIVE);
2153 root 1.110 }
2154    
2155 root 1.188 $self->post_load;
2156 root 1.166 }
2157    
2158 root 1.507 # customize the map for a given player, i.e.
2159     # return the _real_ map. used by e.g. per-player
2160     # maps to change the path to ~playername/mappath
2161 root 1.166 sub customise_for {
2162     my ($self, $ob) = @_;
2163    
2164     return find "~" . $ob->name . "/" . $self->{path}
2165     if $self->per_player;
2166 root 1.134
2167 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2168     # if $self->per_party;
2169    
2170 root 1.166 $self
2171 root 1.110 }
2172    
2173 root 1.157 # find and load all maps in the 3x3 area around a map
2174 root 1.333 sub load_neighbours {
2175 root 1.157 my ($map) = @_;
2176    
2177 root 1.333 my @neigh; # diagonal neighbours
2178 root 1.157
2179     for (0 .. 3) {
2180     my $neigh = $map->tile_path ($_)
2181     or next;
2182     $neigh = find $neigh, $map
2183     or next;
2184     $neigh->load;
2185    
2186 root 1.527 # now find the diagonal neighbours
2187 root 1.333 push @neigh,
2188     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2189     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2190 root 1.157 }
2191    
2192 root 1.333 for (grep defined $_->[0], @neigh) {
2193     my ($path, $origin) = @$_;
2194     my $neigh = find $path, $origin
2195 root 1.157 or next;
2196     $neigh->load;
2197     }
2198     }
2199    
2200 root 1.133 sub find_sync {
2201 root 1.110 my ($path, $origin) = @_;
2202    
2203 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_find_sync"
2204     if $Coro::current == $Coro::main;
2205    
2206     find $path, $origin
2207 root 1.133 }
2208    
2209     sub do_load_sync {
2210     my ($map) = @_;
2211 root 1.110
2212 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_load_sync"
2213 root 1.342 if $Coro::current == $Coro::main;
2214 root 1.339
2215 root 1.534 $map->load;
2216 root 1.110 }
2217    
2218 root 1.157 our %MAP_PREFETCH;
2219 root 1.183 our $MAP_PREFETCHER = undef;
2220 root 1.157
2221     sub find_async {
2222 root 1.339 my ($path, $origin, $load) = @_;
2223 root 1.157
2224 root 1.166 $path = normalise $path, $origin && $origin->{path};
2225 root 1.157
2226 root 1.166 if (my $map = $cf::MAP{$path}) {
2227 root 1.420 return $map if !$load || $map->in_memory == cf::MAP_ACTIVE;
2228 root 1.157 }
2229    
2230 root 1.339 $MAP_PREFETCH{$path} |= $load;
2231    
2232 root 1.183 $MAP_PREFETCHER ||= cf::async {
2233 root 1.374 $Coro::current->{desc} = "map prefetcher";
2234    
2235 root 1.183 while (%MAP_PREFETCH) {
2236 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2237     if (my $map = find $k) {
2238     $map->load if $v;
2239 root 1.308 }
2240 root 1.183
2241 root 1.339 delete $MAP_PREFETCH{$k};
2242 root 1.183 }
2243     }
2244     undef $MAP_PREFETCHER;
2245     };
2246 root 1.189 $MAP_PREFETCHER->prio (6);
2247 root 1.157
2248     ()
2249     }
2250    
2251 root 1.518 # common code, used by both ->save and ->swapout
2252     sub _save {
2253 root 1.110 my ($self) = @_;
2254    
2255     $self->{last_save} = $cf::RUNTIME;
2256    
2257     return unless $self->dirty;
2258    
2259 root 1.166 my $save = $self->save_path; utf8::encode $save;
2260     my $uniq = $self->uniq_path; utf8::encode $uniq;
2261 root 1.117
2262 root 1.110 $self->{load_path} = $save;
2263    
2264     return if $self->{deny_save};
2265    
2266 root 1.132 local $self->{last_access} = $self->last_access;#d#
2267    
2268 root 1.143 cf::async {
2269 root 1.374 $Coro::current->{desc} = "map player save";
2270 root 1.143 $_->contr->save for $self->players;
2271     };
2272    
2273 root 1.420 cf::get_slot 0.02;
2274    
2275 root 1.110 if ($uniq) {
2276 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2277     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2278 root 1.110 } else {
2279 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2280 root 1.110 }
2281     }
2282    
2283 root 1.518 sub save {
2284     my ($self) = @_;
2285    
2286     my $lock = cf::lock_acquire "map_data:$self->{path}";
2287    
2288     $self->_save;
2289     }
2290    
2291 root 1.110 sub swap_out {
2292     my ($self) = @_;
2293    
2294 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2295 root 1.137
2296 root 1.420 return if $self->in_memory != cf::MAP_ACTIVE;
2297 root 1.110 return if $self->{deny_save};
2298 root 1.518 return if $self->players;
2299 root 1.110
2300 root 1.518 # first deactivate the map and "unlink" it from the core
2301     $self->deactivate;
2302     $_->clear_links_to ($self) for values %cf::MAP;
2303 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2304    
2305 root 1.518 # then atomically save
2306     $self->_save;
2307    
2308     # then free the map
2309 root 1.110 $self->clear;
2310     }
2311    
2312 root 1.112 sub reset_at {
2313     my ($self) = @_;
2314 root 1.110
2315     # TODO: safety, remove and allow resettable per-player maps
2316 root 1.114 return 1e99 if $self->{deny_reset};
2317 root 1.110
2318 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2319 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2320 root 1.110
2321 root 1.112 $time + $to
2322     }
2323    
2324     sub should_reset {
2325     my ($self) = @_;
2326    
2327     $self->reset_at <= $cf::RUNTIME
2328 root 1.111 }
2329    
2330 root 1.110 sub reset {
2331     my ($self) = @_;
2332    
2333 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2334 root 1.137
2335 root 1.110 return if $self->players;
2336    
2337 root 1.532 cf::trace "resetting map ", $self->path, "\n";
2338 root 1.110
2339 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2340    
2341     # need to save uniques path
2342     unless ($self->{deny_save}) {
2343     my $uniq = $self->uniq_path; utf8::encode $uniq;
2344    
2345     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2346     if $uniq;
2347     }
2348    
2349 root 1.111 delete $cf::MAP{$self->path};
2350 root 1.110
2351 root 1.358 $self->deactivate;
2352 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2353 root 1.167 $self->clear;
2354    
2355 root 1.166 $self->unlink_save;
2356 root 1.111 $self->destroy;
2357 root 1.110 }
2358    
2359 root 1.114 my $nuke_counter = "aaaa";
2360    
2361     sub nuke {
2362     my ($self) = @_;
2363    
2364 root 1.349 {
2365     my $lock = cf::lock_acquire "map_data:$self->{path}";
2366    
2367     delete $cf::MAP{$self->path};
2368 root 1.174
2369 root 1.351 $self->unlink_save;
2370    
2371 root 1.524 bless $self, "cf::map::wrap";
2372 root 1.349 delete $self->{deny_reset};
2373     $self->{deny_save} = 1;
2374     $self->reset_timeout (1);
2375     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2376 root 1.174
2377 root 1.349 $cf::MAP{$self->path} = $self;
2378     }
2379 root 1.174
2380 root 1.114 $self->reset; # polite request, might not happen
2381     }
2382    
2383 root 1.276 =item $maps = cf::map::tmp_maps
2384    
2385     Returns an arrayref with all map paths of currently instantiated and saved
2386 root 1.277 maps. May block.
2387 root 1.276
2388     =cut
2389    
2390     sub tmp_maps() {
2391     [
2392     map {
2393     utf8::decode $_;
2394 root 1.277 /\.map$/
2395 root 1.276 ? normalise $_
2396     : ()
2397     } @{ aio_readdir $TMPDIR or [] }
2398     ]
2399     }
2400    
2401 root 1.277 =item $maps = cf::map::random_maps
2402    
2403     Returns an arrayref with all map paths of currently instantiated and saved
2404     random maps. May block.
2405    
2406     =cut
2407    
2408     sub random_maps() {
2409     [
2410     map {
2411     utf8::decode $_;
2412     /\.map$/
2413     ? normalise "?random/$_"
2414     : ()
2415     } @{ aio_readdir $RANDOMDIR or [] }
2416     ]
2417     }
2418    
2419 root 1.158 =item cf::map::unique_maps
2420    
2421 root 1.166 Returns an arrayref of paths of all shared maps that have
2422 root 1.158 instantiated unique items. May block.
2423    
2424     =cut
2425    
2426     sub unique_maps() {
2427 root 1.276 [
2428     map {
2429     utf8::decode $_;
2430 root 1.419 s/\.map$//; # TODO future compatibility hack
2431     /\.pst$/ || !/^$PATH_SEP/o # TODO unique maps apparebntly lack the .map suffix :/
2432     ? ()
2433     : normalise $_
2434 root 1.276 } @{ aio_readdir $UNIQUEDIR or [] }
2435     ]
2436 root 1.158 }
2437    
2438 root 1.489 =item cf::map::static_maps
2439    
2440     Returns an arrayref if paths of all static maps (all preinstalled F<.map>
2441 root 1.491 file in the shared directory excluding F</styles> and F</editor>). May
2442     block.
2443 root 1.489
2444     =cut
2445    
2446     sub static_maps() {
2447     my @dirs = "";
2448     my @maps;
2449    
2450     while (@dirs) {
2451     my $dir = shift @dirs;
2452    
2453 root 1.491 next if $dir eq "/styles" || $dir eq "/editor";
2454 root 1.490
2455 root 1.489 my ($dirs, $files) = Coro::AIO::aio_scandir "$MAPDIR$dir", 2
2456     or return;
2457    
2458     for (@$files) {
2459     s/\.map$// or next;
2460     utf8::decode $_;
2461     push @maps, "$dir/$_";
2462     }
2463    
2464     push @dirs, map "$dir/$_", @$dirs;
2465     }
2466    
2467     \@maps
2468     }
2469    
2470 root 1.155 =back
2471    
2472     =head3 cf::object
2473    
2474     =cut
2475    
2476     package cf::object;
2477    
2478     =over 4
2479    
2480     =item $ob->inv_recursive
2481 root 1.110
2482 root 1.419 Returns the inventory of the object I<and> their inventories, recursively,
2483     but I<not> the object itself.
2484 root 1.110
2485 root 1.155 =cut
2486 root 1.144
2487 root 1.155 sub inv_recursive_;
2488     sub inv_recursive_ {
2489     map { $_, inv_recursive_ $_->inv } @_
2490     }
2491 root 1.110
2492 root 1.155 sub inv_recursive {
2493     inv_recursive_ inv $_[0]
2494 root 1.110 }
2495    
2496 root 1.356 =item $ref = $ob->ref
2497    
2498 root 1.419 Creates and returns a persistent reference to an object that can be stored as a string.
2499 root 1.356
2500     =item $ob = cf::object::deref ($refstring)
2501    
2502     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2503     even if the object actually exists. May block.
2504    
2505     =cut
2506    
2507     sub deref {
2508     my ($ref) = @_;
2509    
2510 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2511 root 1.356 my ($uuid, $name) = ($1, $2);
2512     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2513     or return;
2514     $pl->ob->uuid eq $uuid
2515     or return;
2516    
2517     $pl->ob
2518     } else {
2519     warn "$ref: cannot resolve object reference\n";
2520     undef
2521     }
2522     }
2523    
2524 root 1.110 package cf;
2525    
2526     =back
2527    
2528 root 1.95 =head3 cf::object::player
2529    
2530     =over 4
2531    
2532 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2533 root 1.28
2534     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2535     can be C<undef>. Does the right thing when the player is currently in a
2536     dialogue with the given NPC character.
2537    
2538     =cut
2539    
2540 root 1.428 our $SAY_CHANNEL = {
2541     id => "say",
2542     title => "Map",
2543     reply => "say ",
2544 root 1.468 tooltip => "Things said to and replied from NPCs near you and other players on the same map only.",
2545 root 1.428 };
2546    
2547     our $CHAT_CHANNEL = {
2548     id => "chat",
2549     title => "Chat",
2550     reply => "chat ",
2551     tooltip => "Player chat and shouts, global to the server.",
2552     };
2553    
2554 root 1.22 # rough implementation of a future "reply" method that works
2555     # with dialog boxes.
2556 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2557 root 1.23 sub cf::object::player::reply($$$;$) {
2558     my ($self, $npc, $msg, $flags) = @_;
2559    
2560     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2561 root 1.22
2562 root 1.24 if ($self->{record_replies}) {
2563     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2564 elmex 1.282
2565 root 1.24 } else {
2566 elmex 1.282 my $pl = $self->contr;
2567    
2568     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2569 root 1.316 my $dialog = $pl->{npc_dialog};
2570     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2571 elmex 1.282
2572     } else {
2573     $msg = $npc->name . " says: $msg" if $npc;
2574 root 1.428 $self->send_msg ($SAY_CHANNEL => $msg, $flags);
2575 elmex 1.282 }
2576 root 1.24 }
2577 root 1.22 }
2578    
2579 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2580    
2581     =cut
2582    
2583     sub cf::object::send_msg {
2584     my $pl = shift->contr
2585     or return;
2586     $pl->send_msg (@_);
2587     }
2588    
2589 root 1.79 =item $player_object->may ("access")
2590    
2591     Returns wether the given player is authorized to access resource "access"
2592     (e.g. "command_wizcast").
2593    
2594     =cut
2595    
2596     sub cf::object::player::may {
2597     my ($self, $access) = @_;
2598    
2599     $self->flag (cf::FLAG_WIZ) ||
2600     (ref $cf::CFG{"may_$access"}
2601     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2602     : $cf::CFG{"may_$access"})
2603     }
2604 root 1.70
2605 root 1.115 =item $player_object->enter_link
2606    
2607     Freezes the player and moves him/her to a special map (C<{link}>).
2608    
2609 root 1.446 The player should be reasonably safe there for short amounts of time (e.g.
2610     for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2611 root 1.527 though, as the player cannot control the character while it is on the link
2612 root 1.446 map.
2613 root 1.115
2614 root 1.166 Will never block.
2615    
2616 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2617    
2618 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2619     map. If the map is not valid (or omitted), the player will be moved back
2620     to the location he/she was before the call to C<enter_link>, or, if that
2621     fails, to the emergency map position.
2622 root 1.115
2623     Might block.
2624    
2625     =cut
2626    
2627 root 1.166 sub link_map {
2628     unless ($LINK_MAP) {
2629     $LINK_MAP = cf::map::find "{link}"
2630 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2631 root 1.166 $LINK_MAP->load;
2632     }
2633    
2634     $LINK_MAP
2635     }
2636    
2637 root 1.110 sub cf::object::player::enter_link {
2638     my ($self) = @_;
2639    
2640 root 1.259 $self->deactivate_recursive;
2641 root 1.258
2642 root 1.527 ++$self->{_link_recursion};
2643    
2644 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2645 root 1.110
2646 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2647 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2648 root 1.110
2649 root 1.519 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2650 root 1.110 }
2651    
2652     sub cf::object::player::leave_link {
2653     my ($self, $map, $x, $y) = @_;
2654    
2655 root 1.270 return unless $self->contr->active;
2656    
2657 root 1.110 my $link_pos = delete $self->{_link_pos};
2658    
2659     unless ($map) {
2660     # restore original map position
2661     ($map, $x, $y) = @{ $link_pos || [] };
2662 root 1.133 $map = cf::map::find $map;
2663 root 1.110
2664     unless ($map) {
2665     ($map, $x, $y) = @$EMERGENCY_POSITION;
2666 root 1.133 $map = cf::map::find $map
2667 root 1.110 or die "FATAL: cannot load emergency map\n";
2668     }
2669     }
2670    
2671     ($x, $y) = (-1, -1)
2672     unless (defined $x) && (defined $y);
2673    
2674     # use -1 or undef as default coordinates, not 0, 0
2675     ($x, $y) = ($map->enter_x, $map->enter_y)
2676 root 1.492 if $x <= 0 && $y <= 0;
2677 root 1.110
2678     $map->load;
2679 root 1.333 $map->load_neighbours;
2680 root 1.110
2681 root 1.143 return unless $self->contr->active;
2682 root 1.215
2683     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2684 root 1.527 if ($self->enter_map ($map, $x, $y)) {
2685     # entering was successful
2686     delete $self->{_link_recursion};
2687     # only activate afterwards, to support waiting in hooks
2688     $self->activate_recursive;
2689     }
2690 root 1.476
2691 root 1.110 }
2692    
2693 root 1.527 =item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2694 root 1.268
2695     Moves the player to the given map-path and coordinates by first freezing
2696     her, loading and preparing them map, calling the provided $check callback
2697     that has to return the map if sucecssful, and then unfreezes the player on
2698 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2699     be called at the end of this process.
2700 root 1.110
2701 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2702     it needs a loaded map it has to call C<< ->load >>.
2703    
2704 root 1.110 =cut
2705    
2706 root 1.270 our $GOTOGEN;
2707    
2708 root 1.136 sub cf::object::player::goto {
2709 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2710 root 1.268
2711 root 1.527 if ($self->{_link_recursion} >= $MAX_LINKS) {
2712 root 1.532 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2713 root 1.527 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2714     ($path, $x, $y) = @$EMERGENCY_POSITION;
2715     }
2716    
2717 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2718     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2719    
2720 root 1.110 $self->enter_link;
2721    
2722 root 1.140 (async {
2723 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2724    
2725 root 1.365 # *tag paths override both path and x|y
2726     if ($path =~ /^\*(.*)$/) {
2727     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2728     my $ob = $obs[rand @obs];
2729 root 1.366
2730 root 1.367 # see if we actually can go there
2731 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2732     $ob = $obs[rand @obs];
2733 root 1.369 } else {
2734     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2735 root 1.368 }
2736 root 1.369 # else put us there anyways for now #d#
2737 root 1.366
2738 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2739 root 1.369 } else {
2740     ($path, $x, $y) = (undef, undef, undef);
2741 root 1.365 }
2742     }
2743    
2744 root 1.197 my $map = eval {
2745 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2746 root 1.268
2747     if ($map) {
2748     $map = $map->customise_for ($self);
2749 root 1.527 $map = $check->($map, $x, $y, $self) if $check && $map;
2750 root 1.268 } else {
2751 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2752 root 1.268 }
2753    
2754 root 1.197 $map
2755 root 1.268 };
2756    
2757     if ($@) {
2758     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2759     LOG llevError | logBacktrace, Carp::longmess $@;
2760     }
2761 root 1.115
2762 root 1.270 if ($gen == $self->{_goto_generation}) {
2763     delete $self->{_goto_generation};
2764     $self->leave_link ($map, $x, $y);
2765     }
2766 root 1.306
2767 root 1.527 $done->($self) if $done;
2768 root 1.110 })->prio (1);
2769     }
2770    
2771     =item $player_object->enter_exit ($exit_object)
2772    
2773     =cut
2774    
2775     sub parse_random_map_params {
2776     my ($spec) = @_;
2777    
2778     my $rmp = { # defaults
2779 root 1.181 xsize => (cf::rndm 15, 40),
2780     ysize => (cf::rndm 15, 40),
2781     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2782 root 1.182 #layout => string,
2783 root 1.110 };
2784    
2785     for (split /\n/, $spec) {
2786     my ($k, $v) = split /\s+/, $_, 2;
2787    
2788     $rmp->{lc $k} = $v if (length $k) && (length $v);
2789     }
2790    
2791     $rmp
2792     }
2793    
2794     sub prepare_random_map {
2795     my ($exit) = @_;
2796    
2797     # all this does is basically replace the /! path by
2798     # a new random map path (?random/...) with a seed
2799     # that depends on the exit object
2800    
2801     my $rmp = parse_random_map_params $exit->msg;
2802    
2803     if ($exit->map) {
2804 root 1.198 $rmp->{region} = $exit->region->name;
2805 root 1.110 $rmp->{origin_map} = $exit->map->path;
2806     $rmp->{origin_x} = $exit->x;
2807     $rmp->{origin_y} = $exit->y;
2808 root 1.430
2809     $exit->map->touch;
2810 root 1.110 }
2811    
2812     $rmp->{random_seed} ||= $exit->random_seed;
2813    
2814 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2815 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2816 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2817 root 1.110
2818 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2819 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2820 root 1.177 undef $fh;
2821     aio_rename "$meta~", $meta;
2822 root 1.110
2823 root 1.430 my $slaying = "?random/$md5";
2824    
2825     if ($exit->valid) {
2826     $exit->slaying ("?random/$md5");
2827     $exit->msg (undef);
2828     }
2829 root 1.110 }
2830     }
2831    
2832     sub cf::object::player::enter_exit {
2833     my ($self, $exit) = @_;
2834    
2835     return unless $self->type == cf::PLAYER;
2836    
2837 root 1.430 $self->enter_link;
2838    
2839     (async {
2840     $Coro::current->{desc} = "enter_exit";
2841    
2842     unless (eval {
2843     $self->deactivate_recursive; # just to be sure
2844 root 1.195
2845 root 1.430 # random map handling
2846     {
2847     my $guard = cf::lock_acquire "exit_prepare:$exit";
2848 root 1.195
2849 root 1.430 prepare_random_map $exit
2850     if $exit->slaying eq "/!";
2851     }
2852 root 1.110
2853 root 1.430 my $map = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2854     my $x = $exit->stats->hp;
2855     my $y = $exit->stats->sp;
2856 root 1.296
2857 root 1.430 $self->goto ($map, $x, $y);
2858 root 1.374
2859 root 1.430 # if exit is damned, update players death & WoR home-position
2860     $self->contr->savebed ($map, $x, $y)
2861     if $exit->flag (cf::FLAG_DAMNED);
2862 root 1.110
2863 root 1.430 1
2864 root 1.110 }) {
2865 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2866 root 1.233 . "I'll try to bring you back to the map you were before. "
2867     . "Please report this to the dungeon master!",
2868     cf::NDI_UNIQUE | cf::NDI_RED);
2869 root 1.110
2870 root 1.532 error "ERROR in enter_exit: $@";
2871 root 1.110 $self->leave_link;
2872     }
2873     })->prio (1);
2874     }
2875    
2876 root 1.95 =head3 cf::client
2877    
2878     =over 4
2879    
2880     =item $client->send_drawinfo ($text, $flags)
2881    
2882     Sends a drawinfo packet to the client. Circumvents output buffering so
2883     should not be used under normal circumstances.
2884    
2885 root 1.70 =cut
2886    
2887 root 1.95 sub cf::client::send_drawinfo {
2888     my ($self, $text, $flags) = @_;
2889    
2890     utf8::encode $text;
2891 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2892 root 1.95 }
2893    
2894 root 1.494 =item $client->send_big_packet ($pkt)
2895    
2896     Like C<send_packet>, but tries to compress large packets, and fragments
2897     them as required.
2898    
2899     =cut
2900    
2901     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2902    
2903     sub cf::client::send_big_packet {
2904     my ($self, $pkt) = @_;
2905    
2906     # try lzf for large packets
2907     $pkt = "lzf " . Compress::LZF::compress $pkt
2908     if 1024 <= length $pkt and $self->{can_lzf};
2909    
2910     # split very large packets
2911     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2912     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2913     $pkt = "frag";
2914     }
2915    
2916     $self->send_packet ($pkt);
2917     }
2918    
2919 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2920 root 1.283
2921     Send a drawinfo or msg packet to the client, formatting the msg for the
2922     client if neccessary. C<$type> should be a string identifying the type of
2923     the message, with C<log> being the default. If C<$color> is negative, suppress
2924     the message unless the client supports the msg packet.
2925    
2926     =cut
2927    
2928 root 1.391 # non-persistent channels (usually the info channel)
2929 root 1.350 our %CHANNEL = (
2930 root 1.486 "c/motd" => {
2931     id => "infobox",
2932     title => "MOTD",
2933     reply => undef,
2934     tooltip => "The message of the day",
2935     },
2936 root 1.350 "c/identify" => {
2937 root 1.375 id => "infobox",
2938 root 1.350 title => "Identify",
2939     reply => undef,
2940     tooltip => "Items recently identified",
2941     },
2942 root 1.352 "c/examine" => {
2943 root 1.375 id => "infobox",
2944 root 1.352 title => "Examine",
2945     reply => undef,
2946     tooltip => "Signs and other items you examined",
2947     },
2948 root 1.487 "c/shopinfo" => {
2949     id => "infobox",
2950     title => "Shop Info",
2951     reply => undef,
2952     tooltip => "What your bargaining skill tells you about the shop",
2953     },
2954 root 1.389 "c/book" => {
2955     id => "infobox",
2956     title => "Book",
2957     reply => undef,
2958     tooltip => "The contents of a note or book",
2959     },
2960 root 1.375 "c/lookat" => {
2961     id => "infobox",
2962     title => "Look",
2963     reply => undef,
2964     tooltip => "What you saw there",
2965     },
2966 root 1.390 "c/who" => {
2967     id => "infobox",
2968     title => "Players",
2969     reply => undef,
2970     tooltip => "Shows players who are currently online",
2971     },
2972     "c/body" => {
2973     id => "infobox",
2974     title => "Body Parts",
2975     reply => undef,
2976     tooltip => "Shows which body parts you posess and are available",
2977     },
2978 root 1.465 "c/statistics" => {
2979     id => "infobox",
2980     title => "Statistics",
2981     reply => undef,
2982     tooltip => "Shows your primary statistics",
2983     },
2984 root 1.450 "c/skills" => {
2985     id => "infobox",
2986     title => "Skills",
2987     reply => undef,
2988     tooltip => "Shows your experience per skill and item power",
2989     },
2990 root 1.470 "c/shopitems" => {
2991     id => "infobox",
2992     title => "Shop Items",
2993     reply => undef,
2994     tooltip => "Shows the items currently for sale in this shop",
2995     },
2996 root 1.465 "c/resistances" => {
2997     id => "infobox",
2998     title => "Resistances",
2999     reply => undef,
3000     tooltip => "Shows your resistances",
3001     },
3002     "c/pets" => {
3003     id => "infobox",
3004     title => "Pets",
3005     reply => undef,
3006     tooltip => "Shows information abotu your pets/a specific pet",
3007     },
3008 root 1.471 "c/perceiveself" => {
3009     id => "infobox",
3010     title => "Perceive Self",
3011     reply => undef,
3012     tooltip => "You gained detailed knowledge about yourself",
3013     },
3014 root 1.390 "c/uptime" => {
3015     id => "infobox",
3016     title => "Uptime",
3017     reply => undef,
3018 root 1.391 tooltip => "How long the server has been running since last restart",
3019 root 1.390 },
3020     "c/mapinfo" => {
3021     id => "infobox",
3022     title => "Map Info",
3023     reply => undef,
3024     tooltip => "Information related to the maps",
3025     },
3026 root 1.426 "c/party" => {
3027     id => "party",
3028     title => "Party",
3029     reply => "gsay ",
3030     tooltip => "Messages and chat related to your party",
3031     },
3032 root 1.464 "c/death" => {
3033     id => "death",
3034     title => "Death",
3035     reply => undef,
3036     tooltip => "Reason for and more info about your most recent death",
3037     },
3038 root 1.462 "c/say" => $SAY_CHANNEL,
3039     "c/chat" => $CHAT_CHANNEL,
3040 root 1.350 );
3041    
3042 root 1.283 sub cf::client::send_msg {
3043 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
3044 root 1.283
3045 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
3046     unless $color & cf::NDI_VERBATIM;
3047 root 1.283
3048 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
3049 root 1.311
3050 root 1.350 # check predefined channels, for the benefit of C
3051 root 1.375 if ($CHANNEL{$channel}) {
3052     $channel = $CHANNEL{$channel};
3053    
3054 root 1.463 $self->ext_msg (channel_info => $channel);
3055 root 1.375 $channel = $channel->{id};
3056 root 1.350
3057 root 1.375 } elsif (ref $channel) {
3058 root 1.311 # send meta info to client, if not yet sent
3059     unless (exists $self->{channel}{$channel->{id}}) {
3060     $self->{channel}{$channel->{id}} = $channel;
3061 root 1.463 $self->ext_msg (channel_info => $channel);
3062 root 1.311 }
3063    
3064     $channel = $channel->{id};
3065     }
3066    
3067 root 1.313 return unless @extra || length $msg;
3068    
3069 root 1.463 # default colour, mask it out
3070     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3071     if $color & cf::NDI_DEF;
3072    
3073     my $pkt = "msg "
3074     . $self->{json_coder}->encode (
3075     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3076     );
3077    
3078 root 1.494 $self->send_big_packet ($pkt);
3079 root 1.283 }
3080    
3081 root 1.316 =item $client->ext_msg ($type, @msg)
3082 root 1.232
3083 root 1.287 Sends an ext event to the client.
3084 root 1.232
3085     =cut
3086    
3087 root 1.316 sub cf::client::ext_msg($$@) {
3088     my ($self, $type, @msg) = @_;
3089 root 1.232
3090 root 1.343 if ($self->extcmd == 2) {
3091 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3092 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3093 root 1.316 push @msg, msgtype => "event_$type";
3094 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3095 root 1.316 }
3096 root 1.232 }
3097 root 1.95
3098 root 1.336 =item $client->ext_reply ($msgid, @msg)
3099    
3100     Sends an ext reply to the client.
3101    
3102     =cut
3103    
3104     sub cf::client::ext_reply($$@) {
3105     my ($self, $id, @msg) = @_;
3106    
3107     if ($self->extcmd == 2) {
3108 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3109 root 1.343 } elsif ($self->extcmd == 1) {
3110 root 1.336 #TODO: version 1, remove
3111     unshift @msg, msgtype => "reply", msgid => $id;
3112 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3113 root 1.336 }
3114     }
3115    
3116 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3117    
3118     Queues a query to the client, calling the given callback with
3119     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3120     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3121    
3122 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3123     become reliable at some point in the future.
3124 root 1.95
3125     =cut
3126    
3127     sub cf::client::query {
3128     my ($self, $flags, $text, $cb) = @_;
3129    
3130     return unless $self->state == ST_PLAYING
3131     || $self->state == ST_SETUP
3132     || $self->state == ST_CUSTOM;
3133    
3134     $self->state (ST_CUSTOM);
3135    
3136     utf8::encode $text;
3137     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3138    
3139     $self->send_packet ($self->{query_queue}[0][0])
3140     if @{ $self->{query_queue} } == 1;
3141 root 1.287
3142     1
3143 root 1.95 }
3144    
3145     cf::client->attach (
3146 root 1.290 on_connect => sub {
3147     my ($ns) = @_;
3148    
3149     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3150     },
3151 root 1.95 on_reply => sub {
3152     my ($ns, $msg) = @_;
3153    
3154     # this weird shuffling is so that direct followup queries
3155     # get handled first
3156 root 1.128 my $queue = delete $ns->{query_queue}
3157 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3158 root 1.95
3159     (shift @$queue)->[1]->($msg);
3160 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3161 root 1.95
3162     push @{ $ns->{query_queue} }, @$queue;
3163    
3164     if (@{ $ns->{query_queue} } == @$queue) {
3165     if (@$queue) {
3166     $ns->send_packet ($ns->{query_queue}[0][0]);
3167     } else {
3168 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3169 root 1.95 }
3170     }
3171     },
3172 root 1.287 on_exticmd => sub {
3173     my ($ns, $buf) = @_;
3174    
3175 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3176 root 1.287
3177     if (ref $msg) {
3178 root 1.316 my ($type, $reply, @payload) =
3179     "ARRAY" eq ref $msg
3180     ? @$msg
3181     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3182    
3183 root 1.338 my @reply;
3184    
3185 root 1.316 if (my $cb = $EXTICMD{$type}) {
3186 root 1.338 @reply = $cb->($ns, @payload);
3187     }
3188    
3189     $ns->ext_reply ($reply, @reply)
3190     if $reply;
3191 root 1.316
3192 root 1.287 } else {
3193 root 1.532 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3194 root 1.287 }
3195    
3196     cf::override;
3197     },
3198 root 1.95 );
3199    
3200 root 1.140 =item $client->async (\&cb)
3201 root 1.96
3202     Create a new coroutine, running the specified callback. The coroutine will
3203     be automatically cancelled when the client gets destroyed (e.g. on logout,
3204     or loss of connection).
3205    
3206     =cut
3207    
3208 root 1.140 sub cf::client::async {
3209 root 1.96 my ($self, $cb) = @_;
3210    
3211 root 1.140 my $coro = &Coro::async ($cb);
3212 root 1.103
3213     $coro->on_destroy (sub {
3214 root 1.96 delete $self->{_coro}{$coro+0};
3215 root 1.103 });
3216 root 1.96
3217     $self->{_coro}{$coro+0} = $coro;
3218 root 1.103
3219     $coro
3220 root 1.96 }
3221    
3222     cf::client->attach (
3223 root 1.509 on_client_destroy => sub {
3224 root 1.96 my ($ns) = @_;
3225    
3226 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3227 root 1.96 },
3228     );
3229    
3230 root 1.95 =back
3231    
3232 root 1.70
3233     =head2 SAFE SCRIPTING
3234    
3235     Functions that provide a safe environment to compile and execute
3236     snippets of perl code without them endangering the safety of the server
3237     itself. Looping constructs, I/O operators and other built-in functionality
3238     is not available in the safe scripting environment, and the number of
3239 root 1.79 functions and methods that can be called is greatly reduced.
3240 root 1.70
3241     =cut
3242 root 1.23
3243 root 1.42 our $safe = new Safe "safe";
3244 root 1.23 our $safe_hole = new Safe::Hole;
3245    
3246     $SIG{FPE} = 'IGNORE';
3247    
3248 root 1.328 $safe->permit_only (Opcode::opset qw(
3249 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3250 root 1.328 grepstart grepwhile mapstart mapwhile
3251     sort time
3252     ));
3253 root 1.23
3254 root 1.25 # here we export the classes and methods available to script code
3255    
3256 root 1.70 =pod
3257    
3258 root 1.228 The following functions and methods are available within a safe environment:
3259 root 1.70
3260 root 1.297 cf::object
3261 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3262 root 1.425 insert remove name archname title slaying race decrease split
3263 root 1.466 value
3264 root 1.297
3265     cf::object::player
3266     player
3267    
3268     cf::player
3269     peaceful
3270    
3271     cf::map
3272     trigger
3273 root 1.70
3274     =cut
3275    
3276 root 1.25 for (
3277 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3278 elmex 1.431 insert remove inv nrof name archname title slaying race
3279 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3280 root 1.25 ["cf::object::player" => qw(player)],
3281 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3282 elmex 1.91 ["cf::map" => qw(trigger)],
3283 root 1.25 ) {
3284     my ($pkg, @funs) = @$_;
3285 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3286 root 1.25 for @funs;
3287     }
3288 root 1.23
3289 root 1.70 =over 4
3290    
3291     =item @retval = safe_eval $code, [var => value, ...]
3292    
3293     Compiled and executes the given perl code snippet. additional var/value
3294     pairs result in temporary local (my) scalar variables of the given name
3295     that are available in the code snippet. Example:
3296    
3297     my $five = safe_eval '$first + $second', first => 1, second => 4;
3298    
3299     =cut
3300    
3301 root 1.23 sub safe_eval($;@) {
3302     my ($code, %vars) = @_;
3303    
3304     my $qcode = $code;
3305     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3306     $qcode =~ s/\n/\\n/g;
3307    
3308 root 1.466 %vars = (_dummy => 0) unless %vars;
3309    
3310 root 1.499 my @res;
3311 root 1.23 local $_;
3312    
3313 root 1.42 my $eval =
3314 root 1.23 "do {\n"
3315     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3316     . "#line 0 \"{$qcode}\"\n"
3317     . $code
3318     . "\n}"
3319 root 1.25 ;
3320    
3321 root 1.499 if ($CFG{safe_eval}) {
3322     sub_generation_inc;
3323     local @safe::cf::_safe_eval_args = values %vars;
3324     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3325     sub_generation_inc;
3326     } else {
3327     local @cf::_safe_eval_args = values %vars;
3328     @res = wantarray ? eval eval : scalar eval $eval;
3329     }
3330 root 1.25
3331 root 1.42 if ($@) {
3332 root 1.532 warn "$@",
3333     "while executing safe code '$code'\n",
3334     "with arguments " . (join " ", %vars) . "\n";
3335 root 1.42 }
3336    
3337 root 1.25 wantarray ? @res : $res[0]
3338 root 1.23 }
3339    
3340 root 1.69 =item cf::register_script_function $function => $cb
3341    
3342     Register a function that can be called from within map/npc scripts. The
3343     function should be reasonably secure and should be put into a package name
3344     like the extension.
3345    
3346     Example: register a function that gets called whenever a map script calls
3347     C<rent::overview>, as used by the C<rent> extension.
3348    
3349     cf::register_script_function "rent::overview" => sub {
3350     ...
3351     };
3352    
3353     =cut
3354    
3355 root 1.23 sub register_script_function {
3356     my ($fun, $cb) = @_;
3357    
3358 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3359     *$fun = $safe_hole->wrap ($cb);
3360 root 1.23 }
3361    
3362 root 1.70 =back
3363    
3364 root 1.71 =cut
3365    
3366 root 1.23 #############################################################################
3367 root 1.203 # the server's init and main functions
3368    
3369 root 1.246 sub load_facedata($) {
3370     my ($path) = @_;
3371 root 1.223
3372 root 1.348 # HACK to clear player env face cache, we need some signal framework
3373     # for this (global event?)
3374     %ext::player_env::MUSIC_FACE_CACHE = ();
3375    
3376 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3377 root 1.334
3378 root 1.532 trace "loading facedata from $path\n";
3379 root 1.223
3380 root 1.546 0 < aio_load $path, my $facedata
3381 root 1.223 or die "$path: $!";
3382    
3383 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3384 root 1.223
3385 root 1.236 $facedata->{version} == 2
3386 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3387    
3388 root 1.334 # patch in the exptable
3389 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3390 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3391     type => FT_RSRC,
3392 root 1.500 data => $exp_table,
3393     hash => (Digest::MD5::md5 $exp_table),
3394 root 1.334 };
3395     cf::cede_to_tick;
3396    
3397 root 1.236 {
3398     my $faces = $facedata->{faceinfo};
3399    
3400     while (my ($face, $info) = each %$faces) {
3401     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3402 root 1.405
3403 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3404     cf::face::set_magicmap $idx, $info->{magicmap};
3405 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3406     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3407 root 1.302
3408     cf::cede_to_tick;
3409 root 1.236 }
3410    
3411     while (my ($face, $info) = each %$faces) {
3412     next unless $info->{smooth};
3413 root 1.405
3414 root 1.236 my $idx = cf::face::find $face
3415     or next;
3416 root 1.405
3417 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3418 root 1.302 cf::face::set_smooth $idx, $smooth;
3419     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3420 root 1.236 } else {
3421 root 1.532 error "smooth face '$info->{smooth}' not found for face '$face'";
3422 root 1.236 }
3423 root 1.302
3424     cf::cede_to_tick;
3425 root 1.236 }
3426 root 1.223 }
3427    
3428 root 1.236 {
3429     my $anims = $facedata->{animinfo};
3430    
3431     while (my ($anim, $info) = each %$anims) {
3432     cf::anim::set $anim, $info->{frames}, $info->{facings};
3433 root 1.302 cf::cede_to_tick;
3434 root 1.225 }
3435 root 1.236
3436     cf::anim::invalidate_all; # d'oh
3437 root 1.225 }
3438    
3439 root 1.302 {
3440     my $res = $facedata->{resource};
3441    
3442     while (my ($name, $info) = each %$res) {
3443 root 1.405 if (defined $info->{type}) {
3444     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3445    
3446 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3447 root 1.405 cf::face::set_type $idx, $info->{type};
3448 root 1.337 } else {
3449 root 1.530 $RESOURCE{$name} = $info; # unused
3450 root 1.307 }
3451 root 1.302
3452     cf::cede_to_tick;
3453     }
3454 root 1.406 }
3455    
3456     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3457 root 1.321
3458 root 1.406 1
3459     }
3460    
3461 root 1.318 register_exticmd fx_want => sub {
3462     my ($ns, $want) = @_;
3463    
3464     while (my ($k, $v) = each %$want) {
3465     $ns->fx_want ($k, $v);
3466     }
3467     };
3468    
3469 root 1.423 sub load_resource_file($) {
3470 root 1.424 my $guard = lock_acquire "load_resource_file";
3471    
3472 root 1.423 my $status = load_resource_file_ $_[0];
3473     get_slot 0.1, 100;
3474     cf::arch::commit_load;
3475 root 1.424
3476 root 1.423 $status
3477     }
3478    
3479 root 1.253 sub reload_regions {
3480 root 1.348 # HACK to clear player env face cache, we need some signal framework
3481     # for this (global event?)
3482     %ext::player_env::MUSIC_FACE_CACHE = ();
3483    
3484 root 1.253 load_resource_file "$MAPDIR/regions"
3485     or die "unable to load regions file\n";
3486 root 1.304
3487     for (cf::region::list) {
3488     $_->{match} = qr/$_->{match}/
3489     if exists $_->{match};
3490     }
3491 root 1.253 }
3492    
3493 root 1.246 sub reload_facedata {
3494 root 1.253 load_facedata "$DATADIR/facedata"
3495 root 1.246 or die "unable to load facedata\n";
3496     }
3497    
3498     sub reload_archetypes {
3499 root 1.253 load_resource_file "$DATADIR/archetypes"
3500 root 1.246 or die "unable to load archetypes\n";
3501 root 1.241 }
3502    
3503 root 1.246 sub reload_treasures {
3504 root 1.253 load_resource_file "$DATADIR/treasures"
3505 root 1.246 or die "unable to load treasurelists\n";
3506 root 1.241 }
3507    
3508 root 1.530 sub reload_sound {
3509 root 1.532 trace "loading sound config from $DATADIR/sound\n";
3510 root 1.531
3511 root 1.530 0 < Coro::AIO::aio_load "$DATADIR/sound", my $data
3512     or die "$DATADIR/sound $!";
3513    
3514     my $soundconf = JSON::XS->new->utf8->relaxed->decode ($data);
3515    
3516     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3517     my $sound = $soundconf->{compat}[$_]
3518     or next;
3519    
3520     my $face = cf::face::find "sound/$sound->[1]";
3521     cf::sound::set $sound->[0] => $face;
3522     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3523     }
3524    
3525     while (my ($k, $v) = each %{$soundconf->{event}}) {
3526     my $face = cf::face::find "sound/$v";
3527     cf::sound::set $k => $face;
3528     }
3529     }
3530    
3531 root 1.223 sub reload_resources {
3532 root 1.532 trace "reloading resource files...\n";
3533 root 1.245
3534 root 1.545 reload_exp_table;
3535     reload_materials;
3536 root 1.246 reload_facedata;
3537 root 1.530 reload_sound;
3538 root 1.246 reload_archetypes;
3539 root 1.423 reload_regions;
3540 root 1.246 reload_treasures;
3541 root 1.245
3542 root 1.532 trace "finished reloading resource files\n";
3543 root 1.223 }
3544    
3545 root 1.345 sub reload_config {
3546 root 1.532 trace "reloading config file...\n";
3547 root 1.485
3548 root 1.546 0 < aio_load "$CONFDIR/config", my $config
3549     or die "$CONFDIR/config: $!";
3550    
3551     utf8::decode $config;
3552 root 1.72
3553 root 1.546 cf::get_slot 0.1, 10, "reload_config"; # yaml might be slow...
3554     *CFG = YAML::XS::Load $config;
3555 root 1.131
3556 root 1.527 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3557 root 1.131
3558 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3559     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3560    
3561 root 1.131 if (exists $CFG{mlockall}) {
3562     eval {
3563 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3564 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3565     };
3566     warn $@ if $@;
3567     }
3568 root 1.72 }
3569    
3570 root 1.445 sub pidfile() {
3571     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3572     or die "$PIDFILE: $!";
3573     flock $fh, &Fcntl::LOCK_EX
3574     or die "$PIDFILE: flock: $!";
3575     $fh
3576     }
3577    
3578     # make sure only one server instance is running at any one time
3579     sub atomic {
3580     my $fh = pidfile;
3581    
3582     my $pid = <$fh>;
3583     kill 9, $pid if $pid > 0;
3584    
3585     seek $fh, 0, 0;
3586     print $fh $$;
3587     }
3588    
3589 root 1.474 sub main_loop {
3590 root 1.532 trace "EV::loop starting\n";
3591 root 1.474 if (1) {
3592     EV::loop;
3593     }
3594 root 1.532 trace "EV::loop returned\n";
3595 root 1.474 goto &main_loop unless $REALLY_UNLOOP;
3596     }
3597    
3598 root 1.39 sub main {
3599 root 1.453 cf::init_globals; # initialise logging
3600    
3601     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3602 root 1.540 LOG llevInfo, "Copyright (C) 2005-2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3603 root 1.453 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3604     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3605    
3606     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3607 root 1.445
3608 root 1.108 # we must not ever block the main coroutine
3609     local $Coro::idle = sub {
3610 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3611 root 1.175 (async {
3612 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3613 root 1.396 EV::loop EV::LOOP_ONESHOT;
3614 root 1.175 })->prio (Coro::PRIO_MAX);
3615 root 1.108 };
3616    
3617 root 1.453 evthread_start IO::AIO::poll_fileno;
3618    
3619     cf::sync_job {
3620 root 1.543 cf::incloader::init ();
3621 root 1.540
3622 root 1.515 cf::init_anim;
3623     cf::init_attackmess;
3624     cf::init_dynamic;
3625    
3626 root 1.495 cf::load_settings;
3627    
3628 root 1.453 reload_resources;
3629 root 1.423 reload_config;
3630     db_init;
3631 root 1.453
3632     cf::init_uuid;
3633     cf::init_signals;
3634     cf::init_skills;
3635    
3636     cf::init_beforeplay;
3637    
3638     atomic;
3639    
3640 root 1.423 load_extensions;
3641    
3642 root 1.453 utime time, time, $RUNTIMEFILE;
3643 root 1.183
3644 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3645 root 1.475 use POSIX ();
3646 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3647 root 1.445
3648 root 1.453 (pop @POST_INIT)->(0) while @POST_INIT;
3649     };
3650 root 1.445
3651 root 1.516 cf::object::thawer::errors_are_fatal 0;
3652 root 1.532 info "parse errors in files are no longer fatal from this point on.\n";
3653 root 1.516
3654 root 1.540 my $free_main; $free_main = EV::idle sub {
3655     undef $free_main;
3656     undef &main; # free gobs of memory :)
3657     };
3658    
3659     goto &main_loop;
3660 root 1.34 }
3661    
3662     #############################################################################
3663 root 1.155 # initialisation and cleanup
3664    
3665     # install some emergency cleanup handlers
3666     BEGIN {
3667 root 1.396 our %SIGWATCHER = ();
3668 root 1.155 for my $signal (qw(INT HUP TERM)) {
3669 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3670 root 1.396 cf::cleanup "SIG$signal";
3671     };
3672 root 1.155 }
3673     }
3674    
3675 root 1.417 sub write_runtime_sync {
3676 root 1.512 my $t0 = AE::time;
3677 root 1.506
3678 root 1.281 # first touch the runtime file to show we are still running:
3679     # the fsync below can take a very very long time.
3680    
3681 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3682 root 1.281
3683     my $guard = cf::lock_acquire "write_runtime";
3684    
3685 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3686 root 1.281 or return;
3687    
3688     my $value = $cf::RUNTIME + 90 + 10;
3689     # 10 is the runtime save interval, for a monotonic clock
3690     # 60 allows for the watchdog to kill the server.
3691    
3692     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3693     and return;
3694    
3695     # always fsync - this file is important
3696     aio_fsync $fh
3697     and return;
3698    
3699     # touch it again to show we are up-to-date
3700     aio_utime $fh, undef, undef;
3701    
3702     close $fh
3703     or return;
3704    
3705 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3706 root 1.281 and return;
3707    
3708 root 1.532 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3709 root 1.281
3710     1
3711     }
3712    
3713 root 1.416 our $uuid_lock;
3714     our $uuid_skip;
3715    
3716     sub write_uuid_sync($) {
3717     $uuid_skip ||= $_[0];
3718    
3719     return if $uuid_lock;
3720     local $uuid_lock = 1;
3721    
3722     my $uuid = "$LOCALDIR/uuid";
3723    
3724     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3725     or return;
3726    
3727 root 1.454 my $value = uuid_seq uuid_cur;
3728    
3729 root 1.455 unless ($value) {
3730 root 1.532 info "cowardly refusing to write zero uuid value!\n";
3731 root 1.454 return;
3732     }
3733    
3734     my $value = uuid_str $value + $uuid_skip;
3735 root 1.416 $uuid_skip = 0;
3736    
3737     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3738     and return;
3739    
3740     # always fsync - this file is important
3741     aio_fsync $fh
3742     and return;
3743    
3744     close $fh
3745     or return;
3746    
3747     aio_rename "$uuid~", $uuid
3748     and return;
3749    
3750 root 1.532 trace "uuid file written ($value).\n";
3751 root 1.416
3752     1
3753    
3754     }
3755    
3756     sub write_uuid($$) {
3757     my ($skip, $sync) = @_;
3758    
3759     $sync ? write_uuid_sync $skip
3760     : async { write_uuid_sync $skip };
3761     }
3762    
3763 root 1.156 sub emergency_save() {
3764 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3765    
3766 root 1.532 info "emergency_perl_save: enter\n";
3767 root 1.155
3768 root 1.534 # this is a trade-off: we want to be very quick here, so
3769     # save all maps without fsync, and later call a global sync
3770     # (which in turn might be very very slow)
3771     local $USE_FSYNC = 0;
3772    
3773 root 1.155 cf::sync_job {
3774 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3775    
3776 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3777     # refcount bugs in for. also avoids problems with players
3778 root 1.167 # and maps saved/destroyed asynchronously.
3779 root 1.532 info "emergency_perl_save: begin player save\n";
3780 root 1.155 for my $login (keys %cf::PLAYER) {
3781     my $pl = $cf::PLAYER{$login} or next;
3782     $pl->valid or next;
3783 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3784 root 1.155 $pl->save;
3785     }
3786 root 1.532 info "emergency_perl_save: end player save\n";
3787 root 1.155
3788 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3789    
3790 root 1.532 info "emergency_perl_save: begin map save\n";
3791 root 1.155 for my $path (keys %cf::MAP) {
3792     my $map = $cf::MAP{$path} or next;
3793     $map->valid or next;
3794     $map->save;
3795     }
3796 root 1.532 info "emergency_perl_save: end map save\n";
3797 root 1.208
3798 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3799    
3800 root 1.532 info "emergency_perl_save: begin database checkpoint\n";
3801 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3802 root 1.532 info "emergency_perl_save: end database checkpoint\n";
3803 root 1.416
3804 root 1.532 info "emergency_perl_save: begin write uuid\n";
3805 root 1.416 write_uuid_sync 1;
3806 root 1.532 info "emergency_perl_save: end write uuid\n";
3807 root 1.155
3808 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3809    
3810     trace "emergency_perl_save: syncing database to disk";
3811     BDB::db_env_txn_checkpoint $DB_ENV;
3812    
3813 root 1.536 info "emergency_perl_save: starting sync\n";
3814 root 1.535 IO::AIO::aio_sync sub {
3815 root 1.536 info "emergency_perl_save: finished sync\n";
3816 root 1.535 };
3817    
3818     cf::write_runtime_sync; # external watchdog should not bark
3819    
3820     trace "emergency_perl_save: flushing outstanding aio requests";
3821     while (IO::AIO::nreqs || BDB::nreqs) {
3822     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3823     }
3824    
3825     cf::write_runtime_sync; # external watchdog should not bark
3826 root 1.457 };
3827    
3828 root 1.532 info "emergency_perl_save: leave\n";
3829 root 1.155 }
3830 root 1.22
3831 root 1.211 sub post_cleanup {
3832     my ($make_core) = @_;
3833    
3834 root 1.535 IO::AIO::flush;
3835    
3836 root 1.532 error Carp::longmess "post_cleanup backtrace"
3837 root 1.211 if $make_core;
3838 root 1.445
3839     my $fh = pidfile;
3840     unlink $PIDFILE if <$fh> == $$;
3841 root 1.211 }
3842    
3843 root 1.441 # a safer delete_package, copied from Symbol
3844     sub clear_package($) {
3845     my $pkg = shift;
3846    
3847     # expand to full symbol table name if needed
3848     unless ($pkg =~ /^main::.*::$/) {
3849     $pkg = "main$pkg" if $pkg =~ /^::/;
3850     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3851     $pkg .= '::' unless $pkg =~ /::$/;
3852     }
3853    
3854     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3855     my $stem_symtab = *{$stem}{HASH};
3856    
3857     defined $stem_symtab and exists $stem_symtab->{$leaf}
3858     or return;
3859    
3860     # clear all symbols
3861     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3862     for my $name (keys %$leaf_symtab) {
3863     _gv_clear *{"$pkg$name"};
3864     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3865     }
3866     }
3867    
3868 root 1.246 sub do_reload_perl() {
3869 root 1.106 # can/must only be called in main
3870 root 1.543 unless (in_main) {
3871 root 1.532 error "can only reload from main coroutine";
3872 root 1.106 return;
3873     }
3874    
3875 root 1.441 return if $RELOAD++;
3876    
3877 root 1.512 my $t1 = AE::time;
3878 root 1.457
3879 root 1.441 while ($RELOAD) {
3880 root 1.543 cf::get_slot 0.1, -1, "reload_perl";
3881 root 1.532 info "reloading...";
3882 root 1.103
3883 root 1.532 trace "entering sync_job";
3884 root 1.212
3885 root 1.441 cf::sync_job {
3886 root 1.543 #cf::emergency_save;
3887 root 1.183
3888 root 1.532 trace "cancelling all extension coros";
3889 root 1.441 $_->cancel for values %EXT_CORO;
3890     %EXT_CORO = ();
3891 root 1.223
3892 root 1.532 trace "removing commands";
3893 root 1.441 %COMMAND = ();
3894 root 1.103
3895 root 1.532 trace "removing ext/exti commands";
3896 root 1.441 %EXTCMD = ();
3897     %EXTICMD = ();
3898 root 1.159
3899 root 1.532 trace "unloading/nuking all extensions";
3900 root 1.441 for my $pkg (@EXTS) {
3901 root 1.532 trace "... unloading $pkg";
3902 root 1.159
3903 root 1.441 if (my $cb = $pkg->can ("unload")) {
3904     eval {
3905     $cb->($pkg);
3906     1
3907 root 1.532 } or error "$pkg unloaded, but with errors: $@";
3908 root 1.441 }
3909 root 1.159
3910 root 1.532 trace "... clearing $pkg";
3911 root 1.441 clear_package $pkg;
3912 root 1.159 }
3913    
3914 root 1.532 trace "unloading all perl modules loaded from $LIBDIR";
3915 root 1.441 while (my ($k, $v) = each %INC) {
3916     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3917 root 1.65
3918 root 1.532 trace "... unloading $k";
3919 root 1.441 delete $INC{$k};
3920 root 1.65
3921 root 1.441 $k =~ s/\.pm$//;
3922     $k =~ s/\//::/g;
3923 root 1.65
3924 root 1.441 if (my $cb = $k->can ("unload_module")) {
3925     $cb->();
3926     }
3927 root 1.65
3928 root 1.441 clear_package $k;
3929 root 1.65 }
3930    
3931 root 1.532 trace "getting rid of safe::, as good as possible";
3932 root 1.441 clear_package "safe::$_"
3933     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3934 root 1.65
3935 root 1.532 trace "unloading cf.pm \"a bit\"";
3936 root 1.441 delete $INC{"cf.pm"};
3937 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3938 root 1.65
3939 root 1.441 # don't, removes xs symbols, too,
3940     # and global variables created in xs
3941     #clear_package __PACKAGE__;
3942 root 1.65
3943 root 1.532 info "unload completed, starting to reload now";
3944 root 1.65
3945 root 1.532 trace "reloading cf.pm";
3946 root 1.441 require cf;
3947 root 1.483 cf::_connect_to_perl_1;
3948 root 1.183
3949 root 1.532 trace "loading config and database again";
3950 root 1.441 cf::reload_config;
3951 root 1.100
3952 root 1.532 trace "loading extensions";
3953 root 1.441 cf::load_extensions;
3954 root 1.65
3955 root 1.457 if ($REATTACH_ON_RELOAD) {
3956 root 1.532 trace "reattaching attachments to objects/players";
3957 root 1.457 _global_reattach; # objects, sockets
3958 root 1.532 trace "reattaching attachments to maps";
3959 root 1.457 reattach $_ for values %MAP;
3960 root 1.532 trace "reattaching attachments to players";
3961 root 1.457 reattach $_ for values %PLAYER;
3962     }
3963 root 1.65
3964 root 1.532 trace "running post_init jobs";
3965 root 1.453 (pop @POST_INIT)->(1) while @POST_INIT;
3966    
3967 root 1.532 trace "leaving sync_job";
3968 root 1.183
3969 root 1.441 1
3970     } or do {
3971 root 1.532 error $@;
3972 root 1.441 cf::cleanup "error while reloading, exiting.";
3973     };
3974 root 1.183
3975 root 1.532 info "reloaded";
3976 root 1.441 --$RELOAD;
3977     }
3978 root 1.457
3979 root 1.512 $t1 = AE::time - $t1;
3980 root 1.532 info "reload completed in ${t1}s\n";
3981 root 1.65 };
3982    
3983 root 1.175 our $RELOAD_WATCHER; # used only during reload
3984    
3985 root 1.246 sub reload_perl() {
3986     # doing reload synchronously and two reloads happen back-to-back,
3987     # coro crashes during coro_state_free->destroy here.
3988    
3989 root 1.457 $RELOAD_WATCHER ||= cf::async {
3990     Coro::AIO::aio_wait cache_extensions;
3991    
3992 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
3993 root 1.457 do_reload_perl;
3994     undef $RELOAD_WATCHER;
3995     };
3996 root 1.396 };
3997 root 1.246 }
3998    
3999 root 1.111 register_command "reload" => sub {
4000 root 1.65 my ($who, $arg) = @_;
4001    
4002     if ($who->flag (FLAG_WIZ)) {
4003 root 1.175 $who->message ("reloading server.");
4004 root 1.374 async {
4005     $Coro::current->{desc} = "perl_reload";
4006     reload_perl;
4007     };
4008 root 1.65 }
4009     };
4010    
4011 root 1.540 #############################################################################
4012 root 1.17
4013 root 1.183 my $bug_warning = 0;
4014    
4015 root 1.239 our @WAIT_FOR_TICK;
4016     our @WAIT_FOR_TICK_BEGIN;
4017    
4018 root 1.546 sub wait_for_tick() {
4019 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4020 root 1.241
4021 root 1.239 my $signal = new Coro::Signal;
4022     push @WAIT_FOR_TICK, $signal;
4023     $signal->wait;
4024     }
4025    
4026 root 1.546 sub wait_for_tick_begin() {
4027 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4028 root 1.241
4029 root 1.239 my $signal = new Coro::Signal;
4030     push @WAIT_FOR_TICK_BEGIN, $signal;
4031     $signal->wait;
4032     }
4033    
4034 root 1.412 sub tick {
4035 root 1.396 if ($Coro::current != $Coro::main) {
4036     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
4037     unless ++$bug_warning > 10;
4038     return;
4039     }
4040    
4041     cf::server_tick; # one server iteration
4042 root 1.245
4043 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
4044 root 1.502
4045 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
4046 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
4047 root 1.396 Coro::async_pool {
4048     $Coro::current->{desc} = "runtime saver";
4049 root 1.417 write_runtime_sync
4050 root 1.532 or error "ERROR: unable to write runtime file: $!";
4051 root 1.396 };
4052     }
4053 root 1.265
4054 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
4055     $sig->send;
4056     }
4057     while (my $sig = shift @WAIT_FOR_TICK) {
4058     $sig->send;
4059     }
4060 root 1.265
4061 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
4062 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
4063 root 1.265
4064 root 1.412 if (0) {
4065     if ($NEXT_TICK) {
4066     my $jitter = $TICK_START - $NEXT_TICK;
4067     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
4068 root 1.532 debug "jitter $JITTER\n";#d#
4069 root 1.412 }
4070     }
4071     }
4072 root 1.35
4073 root 1.206 {
4074 root 1.401 # configure BDB
4075    
4076 root 1.503 BDB::min_parallel 16;
4077 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
4078 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
4079 root 1.77
4080 root 1.206 unless ($DB_ENV) {
4081     $DB_ENV = BDB::db_env_create;
4082 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
4083     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
4084     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
4085 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
4086     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
4087 root 1.206
4088 root 1.534 cf::sync_job {
4089     eval {
4090     BDB::db_env_open
4091     $DB_ENV,
4092     $BDBDIR,
4093     BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4094     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4095     0666;
4096 root 1.208
4097 root 1.534 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4098     };
4099 root 1.533
4100 root 1.534 cf::cleanup "db_env_open(db): $@" if $@;
4101     };
4102 root 1.206 }
4103 root 1.363
4104 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4105     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4106     };
4107     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4108     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4109     };
4110     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4111     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4112     };
4113 root 1.206 }
4114    
4115     {
4116 root 1.401 # configure IO::AIO
4117    
4118 root 1.206 IO::AIO::min_parallel 8;
4119     IO::AIO::max_poll_time $TICK * 0.1;
4120 root 1.435 undef $AnyEvent::AIO::WATCHER;
4121 root 1.206 }
4122 root 1.108
4123 root 1.262 my $_log_backtrace;
4124    
4125 root 1.260 sub _log_backtrace {
4126     my ($msg, @addr) = @_;
4127    
4128 root 1.262 $msg =~ s/\n//;
4129 root 1.260
4130 root 1.262 # limit the # of concurrent backtraces
4131     if ($_log_backtrace < 2) {
4132     ++$_log_backtrace;
4133 root 1.446 my $perl_bt = Carp::longmess $msg;
4134 root 1.262 async {
4135 root 1.374 $Coro::current->{desc} = "abt $msg";
4136    
4137 root 1.262 my @bt = fork_call {
4138     @addr = map { sprintf "%x", $_ } @addr;
4139     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4140     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4141     or die "addr2line: $!";
4142    
4143     my @funcs;
4144     my @res = <$fh>;
4145     chomp for @res;
4146     while (@res) {
4147     my ($func, $line) = splice @res, 0, 2, ();
4148     push @funcs, "[$func] $line";
4149     }
4150 root 1.260
4151 root 1.262 @funcs
4152     };
4153 root 1.260
4154 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4155     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4156 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4157     --$_log_backtrace;
4158     };
4159     } else {
4160 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4161 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
4162     }
4163 root 1.260 }
4164    
4165 root 1.249 # load additional modules
4166 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4167 root 1.483 cf::_connect_to_perl_2;
4168 root 1.249
4169 root 1.125 END { cf::emergency_save }
4170    
4171 root 1.1 1
4172