ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.548
Committed: Mon May 10 21:40:22 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.547: +16 -6 lines
Log Message:
fork for yaml_load, fix fork_call

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