ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.560
Committed: Fri Apr 22 02:03:12 2011 UTC (13 years, 1 month ago) by root
Branch: MAIN
Changes since 1.559: +54 -21 lines
Log Message:
move gridmap to arch, refactor cf.pm a bit

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