ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.565
Committed: Sat Apr 30 05:41:17 2011 UTC (13 years ago) by root
Branch: MAIN
Changes since 1.564: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.484 #
2 root 1.412 # This file is part of Deliantra, the Roguelike Realtime MMORPG.
3     #
4 root 1.563 # Copyright (©) 2006,2007,2008,2009,2010,2011 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.561
1561     last;
1562 root 1.278 }
1563     }
1564     };
1565 root 1.1 }
1566    
1567 root 1.8 #############################################################################
1568 root 1.70
1569 root 1.281 =back
1570    
1571 root 1.70 =head2 CORE EXTENSIONS
1572    
1573 root 1.447 Functions and methods that extend core deliantra objects.
1574 root 1.70
1575 root 1.143 =cut
1576    
1577     package cf::player;
1578    
1579 root 1.154 use Coro::AIO;
1580    
1581 root 1.95 =head3 cf::player
1582    
1583 root 1.70 =over 4
1584 root 1.22
1585 root 1.361 =item cf::player::num_playing
1586    
1587     Returns the official number of playing players, as per the Crossfire metaserver rules.
1588    
1589     =cut
1590    
1591     sub num_playing {
1592     scalar grep
1593     $_->ob->map
1594     && !$_->hidden
1595     && !$_->ob->flag (cf::FLAG_WIZ),
1596     cf::player::list
1597     }
1598    
1599 root 1.143 =item cf::player::find $login
1600 root 1.23
1601 root 1.143 Returns the given player object, loading it if necessary (might block).
1602 root 1.23
1603     =cut
1604    
1605 root 1.145 sub playerdir($) {
1606 root 1.253 "$PLAYERDIR/"
1607 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1608     }
1609    
1610 root 1.143 sub path($) {
1611 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1612    
1613 root 1.234 (playerdir $login) . "/playerdata"
1614 root 1.143 }
1615    
1616     sub find_active($) {
1617     $cf::PLAYER{$_[0]}
1618     and $cf::PLAYER{$_[0]}->active
1619     and $cf::PLAYER{$_[0]}
1620     }
1621    
1622     sub exists($) {
1623     my ($login) = @_;
1624    
1625     $cf::PLAYER{$login}
1626 root 1.452 or !aio_stat path $login
1627 root 1.143 }
1628    
1629     sub find($) {
1630     return $cf::PLAYER{$_[0]} || do {
1631     my $login = $_[0];
1632    
1633     my $guard = cf::lock_acquire "user_find:$login";
1634    
1635 root 1.151 $cf::PLAYER{$_[0]} || do {
1636 root 1.234 # rename old playerfiles to new ones
1637     #TODO: remove when no longer required
1638     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1639     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1640     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1641     aio_unlink +(playerdir $login) . "/$login.pl";
1642    
1643 root 1.356 my $f = new_from_file cf::object::thawer path $login
1644 root 1.151 or return;
1645 root 1.356
1646     my $pl = cf::player::load_pl $f
1647     or return;
1648 root 1.427
1649 root 1.356 local $cf::PLAYER_LOADING{$login} = $pl;
1650     $f->resolve_delayed_derefs;
1651 root 1.151 $cf::PLAYER{$login} = $pl
1652     }
1653     }
1654 root 1.143 }
1655    
1656 root 1.511 cf::player->attach (
1657     on_load => sub {
1658     my ($pl, $path) = @_;
1659    
1660     # restore slots saved in save, below
1661     my $slots = delete $pl->{_slots};
1662    
1663     $pl->ob->current_weapon ($slots->[0]);
1664     $pl->combat_ob ($slots->[1]);
1665     $pl->ranged_ob ($slots->[2]);
1666     },
1667     );
1668    
1669 root 1.143 sub save($) {
1670     my ($pl) = @_;
1671    
1672     return if $pl->{deny_save};
1673    
1674     my $path = path $pl;
1675     my $guard = cf::lock_acquire "user_save:$path";
1676    
1677     return if $pl->{deny_save};
1678 root 1.146
1679 root 1.154 aio_mkdir playerdir $pl, 0770;
1680 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1681    
1682 root 1.420 cf::get_slot 0.01;
1683    
1684 root 1.511 # save slots, to be restored later
1685     local $pl->{_slots} = [$pl->ob->current_weapon, $pl->combat_ob, $pl->ranged_ob];
1686    
1687 root 1.143 $pl->save_pl ($path);
1688 root 1.346 cf::cede_to_tick;
1689 root 1.143 }
1690    
1691     sub new($) {
1692     my ($login) = @_;
1693    
1694     my $self = create;
1695    
1696     $self->ob->name ($login);
1697     $self->{deny_save} = 1;
1698    
1699     $cf::PLAYER{$login} = $self;
1700    
1701     $self
1702 root 1.23 }
1703    
1704 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1705    
1706     =cut
1707    
1708     sub send_msg {
1709     my $ns = shift->ns
1710     or return;
1711     $ns->send_msg (@_);
1712     }
1713    
1714 root 1.154 =item $pl->quit_character
1715    
1716     Nukes the player without looking back. If logged in, the connection will
1717     be destroyed. May block for a long time.
1718    
1719     =cut
1720    
1721 root 1.145 sub quit_character {
1722     my ($pl) = @_;
1723    
1724 root 1.220 my $name = $pl->ob->name;
1725    
1726 root 1.145 $pl->{deny_save} = 1;
1727 root 1.443 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1728 root 1.145
1729 root 1.549 $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->ns;
1730 root 1.145 $pl->deactivate;
1731 root 1.547
1732 root 1.432 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1733 root 1.549 $pl->invoke (cf::EVENT_PLAYER_QUIT) if $pl->ns;
1734 root 1.547 ext::highscore::check ($pl->ob);
1735    
1736 root 1.145 $pl->ns->destroy if $pl->ns;
1737    
1738     my $path = playerdir $pl;
1739     my $temp = "$path~$cf::RUNTIME~deleting~";
1740 root 1.154 aio_rename $path, $temp;
1741 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1742     $pl->destroy;
1743 root 1.220
1744     my $prefix = qr<^~\Q$name\E/>;
1745    
1746     # nuke player maps
1747     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1748    
1749 root 1.150 IO::AIO::aio_rmtree $temp;
1750 root 1.145 }
1751    
1752 pippijn 1.221 =item $pl->kick
1753    
1754     Kicks a player out of the game. This destroys the connection.
1755    
1756     =cut
1757    
1758     sub kick {
1759     my ($pl, $kicker) = @_;
1760    
1761     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1762     $pl->killer ("kicked");
1763     $pl->ns->destroy;
1764     }
1765    
1766 root 1.154 =item cf::player::list_logins
1767    
1768     Returns am arrayref of all valid playernames in the system, can take a
1769     while and may block, so not sync_job-capable, ever.
1770    
1771     =cut
1772    
1773     sub list_logins {
1774 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1775 root 1.154 or return [];
1776    
1777     my @logins;
1778    
1779     for my $login (@$dirs) {
1780 root 1.354 my $path = path $login;
1781    
1782     # a .pst is a dead give-away for a valid player
1783 root 1.427 # if no pst file found, open and chekc for blocked users
1784     if (aio_stat "$path.pst") {
1785 root 1.354 my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1786     aio_read $fh, 0, 512, my $buf, 0 or next;
1787     $buf !~ /^password -------------$/m or next; # official not-valid tag
1788     }
1789 root 1.154
1790     utf8::decode $login;
1791     push @logins, $login;
1792     }
1793    
1794     \@logins
1795     }
1796    
1797     =item $player->maps
1798    
1799 root 1.523 =item cf::player::maps $login
1800    
1801 root 1.166 Returns an arrayref of map paths that are private for this
1802 root 1.154 player. May block.
1803    
1804     =cut
1805    
1806     sub maps($) {
1807     my ($pl) = @_;
1808    
1809 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1810    
1811 root 1.154 my $files = aio_readdir playerdir $pl
1812     or return;
1813    
1814     my @paths;
1815    
1816     for (@$files) {
1817     utf8::decode $_;
1818     next if /\.(?:pl|pst)$/;
1819 root 1.158 next unless /^$PATH_SEP/o;
1820 root 1.154
1821 root 1.565 push @paths, cf::map::normalise "~$pl/$_";
1822 root 1.154 }
1823    
1824     \@paths
1825     }
1826    
1827 root 1.447 =item $protocol_xml = $player->expand_cfpod ($cfpod)
1828 root 1.283
1829 root 1.447 Expand deliantra pod fragments into protocol xml.
1830 root 1.283
1831 root 1.316 =item $player->ext_reply ($msgid, @msg)
1832 root 1.95
1833     Sends an ext reply to the player.
1834    
1835     =cut
1836    
1837 root 1.316 sub ext_reply($$@) {
1838     my ($self, $id, @msg) = @_;
1839 root 1.95
1840 root 1.336 $self->ns->ext_reply ($id, @msg)
1841 root 1.95 }
1842    
1843 root 1.316 =item $player->ext_msg ($type, @msg)
1844 root 1.231
1845     Sends an ext event to the client.
1846    
1847     =cut
1848    
1849 root 1.316 sub ext_msg($$@) {
1850     my ($self, $type, @msg) = @_;
1851 root 1.231
1852 root 1.316 $self->ns->ext_msg ($type, @msg);
1853 root 1.231 }
1854    
1855 root 1.238 =head3 cf::region
1856    
1857     =over 4
1858    
1859     =cut
1860    
1861     package cf::region;
1862    
1863     =item cf::region::find_by_path $path
1864    
1865 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1866 root 1.238
1867     =cut
1868    
1869     sub find_by_path($) {
1870     my ($path) = @_;
1871    
1872 root 1.523 $path =~ s/^~[^\/]*//; # skip ~login
1873    
1874 root 1.238 my ($match, $specificity);
1875    
1876     for my $region (list) {
1877 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1878 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1879     if $region->specificity > $specificity;
1880     }
1881     }
1882    
1883     $match
1884     }
1885 root 1.143
1886 root 1.95 =back
1887    
1888 root 1.110 =head3 cf::map
1889    
1890     =over 4
1891    
1892     =cut
1893    
1894     package cf::map;
1895    
1896     use Fcntl;
1897     use Coro::AIO;
1898    
1899 root 1.166 use overload
1900 root 1.173 '""' => \&as_string,
1901     fallback => 1;
1902 root 1.166
1903 root 1.133 our $MAX_RESET = 3600;
1904     our $DEFAULT_RESET = 3000;
1905 root 1.110
1906     sub generate_random_map {
1907 root 1.166 my ($self, $rmp) = @_;
1908 root 1.418
1909     my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM
1910    
1911 root 1.556 $self->_create_random_map ($rmp);
1912 root 1.110 }
1913    
1914 root 1.187 =item cf::map->register ($regex, $prio)
1915    
1916     Register a handler for the map path matching the given regex at the
1917     givne priority (higher is better, built-in handlers have priority 0, the
1918     default).
1919    
1920     =cut
1921    
1922 root 1.166 sub register {
1923 root 1.187 my (undef, $regex, $prio) = @_;
1924 root 1.166 my $pkg = caller;
1925    
1926     push @{"$pkg\::ISA"}, __PACKAGE__;
1927    
1928 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1929 root 1.166 }
1930    
1931     # also paths starting with '/'
1932 root 1.524 $EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1933 root 1.166
1934 root 1.170 sub thawer_merge {
1935 root 1.172 my ($self, $merge) = @_;
1936    
1937 root 1.170 # we have to keep some variables in memory intact
1938 root 1.172 local $self->{path};
1939     local $self->{load_path};
1940 root 1.170
1941 root 1.172 $self->SUPER::thawer_merge ($merge);
1942 root 1.170 }
1943    
1944 root 1.166 sub normalise {
1945     my ($path, $base) = @_;
1946    
1947 root 1.543 $path = "$path"; # make sure it's a string
1948 root 1.192
1949 root 1.199 $path =~ s/\.map$//;
1950    
1951 root 1.166 # map plan:
1952     #
1953     # /! non-realised random map exit (special hack!)
1954     # {... are special paths that are not being touched
1955     # ?xxx/... are special absolute paths
1956     # ?random/... random maps
1957     # /... normal maps
1958     # ~user/... per-player map of a specific user
1959 elmex 1.564 # !up !down for quad maps, or other maps with up/down layers
1960 root 1.166
1961     $path =~ s/$PATH_SEP/\//go;
1962    
1963     # treat it as relative path if it starts with
1964     # something that looks reasonable
1965     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1966     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1967    
1968     $base =~ s{[^/]+/?$}{};
1969     $path = "$base/$path";
1970 elmex 1.564
1971     } elsif ($path eq '!up') {
1972     $base && ref $base
1973     or Carp::carp "normalise called with relative tile path and no base: '$path'";
1974    
1975     my $uppth = $base->tile_path (cf::TILE_UP);
1976     $path = $uppth if $uppth;
1977    
1978     } elsif ($path eq '!down') {
1979     $base && ref $base
1980     or Carp::carp "normalise called with relative tile path and no base: '$path'";
1981    
1982     my $dpth = $base->tile_path (cf::TILE_DOWN);
1983     $path = $dpth if $dpth;
1984 root 1.166 }
1985    
1986     for ($path) {
1987     redo if s{/\.?/}{/};
1988     redo if s{/[^/]+/\.\./}{/};
1989     }
1990    
1991     $path
1992     }
1993    
1994     sub new_from_path {
1995     my (undef, $path, $base) = @_;
1996    
1997     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1998    
1999     $path = normalise $path, $base;
2000    
2001 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
2002     if ($path =~ $EXT_MAP{$pkg}[1]) {
2003 root 1.166 my $self = bless cf::map::new, $pkg;
2004     $self->{path} = $path; $self->path ($path);
2005     $self->init; # pass $1 etc.
2006     return $self;
2007     }
2008     }
2009    
2010 root 1.543 Carp::cluck "unable to resolve path '$path' (base '$base')";
2011 root 1.166 ()
2012     }
2013    
2014 root 1.561 # may re-bless or do other evil things
2015 root 1.166 sub init {
2016     my ($self) = @_;
2017    
2018     $self
2019     }
2020    
2021     sub as_string {
2022     my ($self) = @_;
2023    
2024     "$self->{path}"
2025     }
2026    
2027     # the displayed name, this is a one way mapping
2028     sub visible_name {
2029     &as_string
2030     }
2031    
2032     # the original (read-only) location
2033     sub load_path {
2034     my ($self) = @_;
2035    
2036 root 1.254 "$MAPDIR/$self->{path}.map"
2037 root 1.166 }
2038    
2039     # the temporary/swap location
2040     sub save_path {
2041     my ($self) = @_;
2042    
2043 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
2044 root 1.254 "$TMPDIR/$path.map"
2045 root 1.166 }
2046    
2047     # the unique path, undef == no special unique path
2048     sub uniq_path {
2049     my ($self) = @_;
2050    
2051 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
2052 root 1.253 "$UNIQUEDIR/$path"
2053 root 1.166 }
2054    
2055 root 1.275 sub decay_objects {
2056     my ($self) = @_;
2057    
2058     return if $self->{deny_reset};
2059    
2060     $self->do_decay_objects;
2061     }
2062    
2063 root 1.166 sub unlink_save {
2064     my ($self) = @_;
2065    
2066     utf8::encode (my $save = $self->save_path);
2067 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
2068     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
2069 root 1.166 }
2070    
2071     sub load_header_from($) {
2072     my ($self, $path) = @_;
2073 root 1.110
2074     utf8::encode $path;
2075 root 1.356 my $f = new_from_file cf::object::thawer $path
2076     or return;
2077 root 1.110
2078 root 1.356 $self->_load_header ($f)
2079 root 1.110 or return;
2080    
2081 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
2082     $f->resolve_delayed_derefs;
2083    
2084 root 1.166 $self->{load_path} = $path;
2085 root 1.135
2086 root 1.166 1
2087     }
2088 root 1.110
2089 root 1.188 sub load_header_orig {
2090 root 1.166 my ($self) = @_;
2091 root 1.110
2092 root 1.166 $self->load_header_from ($self->load_path)
2093 root 1.110 }
2094    
2095 root 1.188 sub load_header_temp {
2096 root 1.166 my ($self) = @_;
2097 root 1.110
2098 root 1.166 $self->load_header_from ($self->save_path)
2099     }
2100 root 1.110
2101 root 1.188 sub prepare_temp {
2102     my ($self) = @_;
2103    
2104     $self->last_access ((delete $self->{last_access})
2105     || $cf::RUNTIME); #d#
2106     # safety
2107     $self->{instantiate_time} = $cf::RUNTIME
2108     if $self->{instantiate_time} > $cf::RUNTIME;
2109     }
2110    
2111     sub prepare_orig {
2112     my ($self) = @_;
2113    
2114     $self->{load_original} = 1;
2115     $self->{instantiate_time} = $cf::RUNTIME;
2116     $self->last_access ($cf::RUNTIME);
2117     $self->instantiate;
2118     }
2119    
2120 root 1.166 sub load_header {
2121     my ($self) = @_;
2122 root 1.110
2123 root 1.188 if ($self->load_header_temp) {
2124     $self->prepare_temp;
2125 root 1.166 } else {
2126 root 1.188 $self->load_header_orig
2127 root 1.166 or return;
2128 root 1.188 $self->prepare_orig;
2129 root 1.166 }
2130 root 1.120
2131 root 1.275 $self->{deny_reset} = 1
2132     if $self->no_reset;
2133    
2134 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
2135     unless $self->default_region;
2136    
2137 root 1.166 1
2138     }
2139 root 1.110
2140 root 1.166 sub find;
2141     sub find {
2142     my ($path, $origin) = @_;
2143 root 1.134
2144 root 1.543 cf::cede_to_tick;
2145    
2146 elmex 1.564 $path = normalise $path, $origin;
2147 root 1.110
2148 root 1.459 my $guard1 = cf::lock_acquire "map_data:$path";#d#remove
2149     my $guard2 = cf::lock_acquire "map_find:$path";
2150 root 1.110
2151 root 1.166 $cf::MAP{$path} || do {
2152     my $map = new_from_path cf::map $path
2153     or return;
2154 root 1.110
2155 root 1.116 $map->{last_save} = $cf::RUNTIME;
2156 root 1.110
2157 root 1.166 $map->load_header
2158     or return;
2159 root 1.134
2160 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2161 root 1.185 # doing this can freeze the server in a sync job, obviously
2162     #$cf::WAIT_FOR_TICK->wait;
2163 root 1.429 undef $guard2;
2164 root 1.358 undef $guard1;
2165 root 1.112 $map->reset;
2166 root 1.192 return find $path;
2167 root 1.112 }
2168 root 1.110
2169 root 1.166 $cf::MAP{$path} = $map
2170 root 1.110 }
2171     }
2172    
2173 root 1.500 sub pre_load { }
2174     #sub post_load { } # XS
2175 root 1.188
2176 root 1.110 sub load {
2177     my ($self) = @_;
2178    
2179 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2180    
2181 root 1.120 my $path = $self->{path};
2182    
2183 root 1.256 {
2184 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2185 root 1.256
2186 root 1.357 return unless $self->valid;
2187 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2188 root 1.110
2189 root 1.256 $self->in_memory (cf::MAP_LOADING);
2190 root 1.110
2191 root 1.256 $self->alloc;
2192 root 1.188
2193 root 1.256 $self->pre_load;
2194 root 1.346 cf::cede_to_tick;
2195 root 1.188
2196 root 1.562 if (exists $self->{load_path}) {
2197     my $f = new_from_file cf::object::thawer $self->{load_path};
2198     $f->skip_block;
2199     $self->_load_objects ($f)
2200     or return;
2201    
2202     $self->post_load_original
2203     if delete $self->{load_original};
2204    
2205     if (my $uniq = $self->uniq_path) {
2206     utf8::encode $uniq;
2207     unless (aio_stat $uniq) {
2208     if (my $f = new_from_file cf::object::thawer $uniq) {
2209     $self->clear_unique_items;
2210     $self->_load_objects ($f);
2211     $f->resolve_delayed_derefs;
2212     }
2213 root 1.356 }
2214 root 1.256 }
2215 root 1.562
2216     $f->resolve_delayed_derefs;
2217 root 1.110 }
2218    
2219 root 1.346 cf::cede_to_tick;
2220 root 1.256 # now do the right thing for maps
2221     $self->link_multipart_objects;
2222 root 1.110 $self->difficulty ($self->estimate_difficulty)
2223     unless $self->difficulty;
2224 root 1.346 cf::cede_to_tick;
2225 root 1.256
2226     unless ($self->{deny_activate}) {
2227     $self->decay_objects;
2228     $self->fix_auto_apply;
2229     $self->update_buttons;
2230 root 1.346 cf::cede_to_tick;
2231 root 1.256 $self->activate;
2232     }
2233    
2234 root 1.325 $self->{last_save} = $cf::RUNTIME;
2235     $self->last_access ($cf::RUNTIME);
2236 root 1.324
2237 root 1.420 $self->in_memory (cf::MAP_ACTIVE);
2238 root 1.110 }
2239    
2240 root 1.188 $self->post_load;
2241 root 1.553
2242     1
2243 root 1.166 }
2244    
2245 root 1.507 # customize the map for a given player, i.e.
2246     # return the _real_ map. used by e.g. per-player
2247     # maps to change the path to ~playername/mappath
2248 root 1.166 sub customise_for {
2249     my ($self, $ob) = @_;
2250    
2251     return find "~" . $ob->name . "/" . $self->{path}
2252     if $self->per_player;
2253 root 1.134
2254 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2255     # if $self->per_party;
2256    
2257 root 1.166 $self
2258 root 1.110 }
2259    
2260 root 1.157 # find and load all maps in the 3x3 area around a map
2261 root 1.333 sub load_neighbours {
2262 root 1.157 my ($map) = @_;
2263    
2264 root 1.333 my @neigh; # diagonal neighbours
2265 root 1.157
2266     for (0 .. 3) {
2267     my $neigh = $map->tile_path ($_)
2268     or next;
2269     $neigh = find $neigh, $map
2270     or next;
2271     $neigh->load;
2272    
2273 root 1.527 # now find the diagonal neighbours
2274 root 1.333 push @neigh,
2275     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2276     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2277 root 1.157 }
2278    
2279 root 1.333 for (grep defined $_->[0], @neigh) {
2280     my ($path, $origin) = @$_;
2281     my $neigh = find $path, $origin
2282 root 1.157 or next;
2283     $neigh->load;
2284     }
2285     }
2286    
2287 root 1.133 sub find_sync {
2288 root 1.110 my ($path, $origin) = @_;
2289    
2290 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_find_sync"
2291     if $Coro::current == $Coro::main;
2292    
2293     find $path, $origin
2294 root 1.133 }
2295    
2296     sub do_load_sync {
2297     my ($map) = @_;
2298 root 1.110
2299 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_load_sync"
2300 root 1.342 if $Coro::current == $Coro::main;
2301 root 1.339
2302 root 1.534 $map->load;
2303 root 1.110 }
2304    
2305 root 1.157 our %MAP_PREFETCH;
2306 root 1.183 our $MAP_PREFETCHER = undef;
2307 root 1.157
2308     sub find_async {
2309 root 1.339 my ($path, $origin, $load) = @_;
2310 root 1.157
2311 elmex 1.564 $path = normalise $path, $origin;
2312 root 1.157
2313 root 1.166 if (my $map = $cf::MAP{$path}) {
2314 root 1.420 return $map if !$load || $map->in_memory == cf::MAP_ACTIVE;
2315 root 1.157 }
2316    
2317 root 1.339 $MAP_PREFETCH{$path} |= $load;
2318    
2319 root 1.183 $MAP_PREFETCHER ||= cf::async {
2320 root 1.374 $Coro::current->{desc} = "map prefetcher";
2321    
2322 root 1.183 while (%MAP_PREFETCH) {
2323 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2324     if (my $map = find $k) {
2325     $map->load if $v;
2326 root 1.308 }
2327 root 1.183
2328 root 1.339 delete $MAP_PREFETCH{$k};
2329 root 1.183 }
2330     }
2331     undef $MAP_PREFETCHER;
2332     };
2333 root 1.189 $MAP_PREFETCHER->prio (6);
2334 root 1.157
2335     ()
2336     }
2337    
2338 root 1.518 # common code, used by both ->save and ->swapout
2339     sub _save {
2340 root 1.110 my ($self) = @_;
2341    
2342     $self->{last_save} = $cf::RUNTIME;
2343    
2344     return unless $self->dirty;
2345    
2346 root 1.166 my $save = $self->save_path; utf8::encode $save;
2347     my $uniq = $self->uniq_path; utf8::encode $uniq;
2348 root 1.117
2349 root 1.110 $self->{load_path} = $save;
2350    
2351     return if $self->{deny_save};
2352    
2353 root 1.132 local $self->{last_access} = $self->last_access;#d#
2354    
2355 root 1.143 cf::async {
2356 root 1.374 $Coro::current->{desc} = "map player save";
2357 root 1.143 $_->contr->save for $self->players;
2358     };
2359    
2360 root 1.420 cf::get_slot 0.02;
2361    
2362 root 1.110 if ($uniq) {
2363 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2364     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2365 root 1.110 } else {
2366 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2367 root 1.110 }
2368     }
2369    
2370 root 1.518 sub save {
2371     my ($self) = @_;
2372    
2373     my $lock = cf::lock_acquire "map_data:$self->{path}";
2374    
2375     $self->_save;
2376     }
2377    
2378 root 1.110 sub swap_out {
2379     my ($self) = @_;
2380    
2381 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2382 root 1.137
2383 root 1.420 return if $self->in_memory != cf::MAP_ACTIVE;
2384 root 1.110 return if $self->{deny_save};
2385 root 1.518 return if $self->players;
2386 root 1.110
2387 root 1.518 # first deactivate the map and "unlink" it from the core
2388     $self->deactivate;
2389     $_->clear_links_to ($self) for values %cf::MAP;
2390 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2391    
2392 root 1.518 # then atomically save
2393     $self->_save;
2394    
2395     # then free the map
2396 root 1.110 $self->clear;
2397     }
2398    
2399 root 1.112 sub reset_at {
2400     my ($self) = @_;
2401 root 1.110
2402     # TODO: safety, remove and allow resettable per-player maps
2403 root 1.114 return 1e99 if $self->{deny_reset};
2404 root 1.110
2405 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2406 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2407 root 1.110
2408 root 1.112 $time + $to
2409     }
2410    
2411     sub should_reset {
2412     my ($self) = @_;
2413    
2414     $self->reset_at <= $cf::RUNTIME
2415 root 1.111 }
2416    
2417 root 1.110 sub reset {
2418     my ($self) = @_;
2419    
2420 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2421 root 1.137
2422 root 1.110 return if $self->players;
2423    
2424 root 1.532 cf::trace "resetting map ", $self->path, "\n";
2425 root 1.110
2426 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2427    
2428     # need to save uniques path
2429     unless ($self->{deny_save}) {
2430     my $uniq = $self->uniq_path; utf8::encode $uniq;
2431    
2432     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2433     if $uniq;
2434     }
2435    
2436 root 1.111 delete $cf::MAP{$self->path};
2437 root 1.110
2438 root 1.358 $self->deactivate;
2439 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2440 root 1.167 $self->clear;
2441    
2442 root 1.166 $self->unlink_save;
2443 root 1.111 $self->destroy;
2444 root 1.110 }
2445    
2446 root 1.114 my $nuke_counter = "aaaa";
2447    
2448     sub nuke {
2449     my ($self) = @_;
2450    
2451 root 1.349 {
2452     my $lock = cf::lock_acquire "map_data:$self->{path}";
2453    
2454     delete $cf::MAP{$self->path};
2455 root 1.174
2456 root 1.351 $self->unlink_save;
2457    
2458 root 1.524 bless $self, "cf::map::wrap";
2459 root 1.349 delete $self->{deny_reset};
2460     $self->{deny_save} = 1;
2461     $self->reset_timeout (1);
2462     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2463 root 1.174
2464 root 1.349 $cf::MAP{$self->path} = $self;
2465     }
2466 root 1.174
2467 root 1.114 $self->reset; # polite request, might not happen
2468     }
2469    
2470 root 1.276 =item $maps = cf::map::tmp_maps
2471    
2472     Returns an arrayref with all map paths of currently instantiated and saved
2473 root 1.277 maps. May block.
2474 root 1.276
2475     =cut
2476    
2477     sub tmp_maps() {
2478     [
2479     map {
2480     utf8::decode $_;
2481 root 1.277 /\.map$/
2482 root 1.276 ? normalise $_
2483     : ()
2484     } @{ aio_readdir $TMPDIR or [] }
2485     ]
2486     }
2487    
2488 root 1.277 =item $maps = cf::map::random_maps
2489    
2490     Returns an arrayref with all map paths of currently instantiated and saved
2491     random maps. May block.
2492    
2493     =cut
2494    
2495     sub random_maps() {
2496     [
2497     map {
2498     utf8::decode $_;
2499     /\.map$/
2500     ? normalise "?random/$_"
2501     : ()
2502     } @{ aio_readdir $RANDOMDIR or [] }
2503     ]
2504     }
2505    
2506 root 1.158 =item cf::map::unique_maps
2507    
2508 root 1.166 Returns an arrayref of paths of all shared maps that have
2509 root 1.158 instantiated unique items. May block.
2510    
2511     =cut
2512    
2513     sub unique_maps() {
2514 root 1.276 [
2515     map {
2516     utf8::decode $_;
2517 root 1.419 s/\.map$//; # TODO future compatibility hack
2518     /\.pst$/ || !/^$PATH_SEP/o # TODO unique maps apparebntly lack the .map suffix :/
2519     ? ()
2520     : normalise $_
2521 root 1.276 } @{ aio_readdir $UNIQUEDIR or [] }
2522     ]
2523 root 1.158 }
2524    
2525 root 1.489 =item cf::map::static_maps
2526    
2527     Returns an arrayref if paths of all static maps (all preinstalled F<.map>
2528 root 1.491 file in the shared directory excluding F</styles> and F</editor>). May
2529     block.
2530 root 1.489
2531     =cut
2532    
2533     sub static_maps() {
2534     my @dirs = "";
2535     my @maps;
2536    
2537     while (@dirs) {
2538     my $dir = shift @dirs;
2539    
2540 root 1.491 next if $dir eq "/styles" || $dir eq "/editor";
2541 root 1.490
2542 root 1.489 my ($dirs, $files) = Coro::AIO::aio_scandir "$MAPDIR$dir", 2
2543     or return;
2544    
2545     for (@$files) {
2546     s/\.map$// or next;
2547     utf8::decode $_;
2548     push @maps, "$dir/$_";
2549     }
2550    
2551     push @dirs, map "$dir/$_", @$dirs;
2552     }
2553    
2554     \@maps
2555     }
2556    
2557 root 1.155 =back
2558    
2559     =head3 cf::object
2560    
2561     =cut
2562    
2563     package cf::object;
2564    
2565     =over 4
2566    
2567     =item $ob->inv_recursive
2568 root 1.110
2569 root 1.419 Returns the inventory of the object I<and> their inventories, recursively,
2570     but I<not> the object itself.
2571 root 1.110
2572 root 1.155 =cut
2573 root 1.144
2574 root 1.155 sub inv_recursive_;
2575     sub inv_recursive_ {
2576     map { $_, inv_recursive_ $_->inv } @_
2577     }
2578 root 1.110
2579 root 1.155 sub inv_recursive {
2580     inv_recursive_ inv $_[0]
2581 root 1.110 }
2582    
2583 root 1.356 =item $ref = $ob->ref
2584    
2585 root 1.419 Creates and returns a persistent reference to an object that can be stored as a string.
2586 root 1.356
2587     =item $ob = cf::object::deref ($refstring)
2588    
2589     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2590     even if the object actually exists. May block.
2591    
2592     =cut
2593    
2594     sub deref {
2595     my ($ref) = @_;
2596    
2597 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2598 root 1.356 my ($uuid, $name) = ($1, $2);
2599     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2600     or return;
2601     $pl->ob->uuid eq $uuid
2602     or return;
2603    
2604     $pl->ob
2605     } else {
2606     warn "$ref: cannot resolve object reference\n";
2607     undef
2608     }
2609     }
2610    
2611 root 1.110 package cf;
2612    
2613     =back
2614    
2615 root 1.95 =head3 cf::object::player
2616    
2617     =over 4
2618    
2619 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2620 root 1.28
2621     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2622     can be C<undef>. Does the right thing when the player is currently in a
2623     dialogue with the given NPC character.
2624    
2625     =cut
2626    
2627 root 1.428 our $SAY_CHANNEL = {
2628     id => "say",
2629     title => "Map",
2630     reply => "say ",
2631 root 1.468 tooltip => "Things said to and replied from NPCs near you and other players on the same map only.",
2632 root 1.428 };
2633    
2634     our $CHAT_CHANNEL = {
2635     id => "chat",
2636     title => "Chat",
2637     reply => "chat ",
2638     tooltip => "Player chat and shouts, global to the server.",
2639     };
2640    
2641 root 1.22 # rough implementation of a future "reply" method that works
2642     # with dialog boxes.
2643 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2644 root 1.23 sub cf::object::player::reply($$$;$) {
2645     my ($self, $npc, $msg, $flags) = @_;
2646    
2647     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2648 root 1.22
2649 root 1.24 if ($self->{record_replies}) {
2650     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2651 elmex 1.282
2652 root 1.24 } else {
2653 elmex 1.282 my $pl = $self->contr;
2654    
2655     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2656 root 1.316 my $dialog = $pl->{npc_dialog};
2657     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2658 elmex 1.282
2659     } else {
2660     $msg = $npc->name . " says: $msg" if $npc;
2661 root 1.428 $self->send_msg ($SAY_CHANNEL => $msg, $flags);
2662 elmex 1.282 }
2663 root 1.24 }
2664 root 1.22 }
2665    
2666 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2667    
2668     =cut
2669    
2670     sub cf::object::send_msg {
2671     my $pl = shift->contr
2672     or return;
2673     $pl->send_msg (@_);
2674     }
2675    
2676 root 1.79 =item $player_object->may ("access")
2677    
2678     Returns wether the given player is authorized to access resource "access"
2679     (e.g. "command_wizcast").
2680    
2681     =cut
2682    
2683     sub cf::object::player::may {
2684     my ($self, $access) = @_;
2685    
2686     $self->flag (cf::FLAG_WIZ) ||
2687     (ref $cf::CFG{"may_$access"}
2688     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2689     : $cf::CFG{"may_$access"})
2690     }
2691 root 1.70
2692 root 1.115 =item $player_object->enter_link
2693    
2694     Freezes the player and moves him/her to a special map (C<{link}>).
2695    
2696 root 1.446 The player should be reasonably safe there for short amounts of time (e.g.
2697     for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2698 root 1.527 though, as the player cannot control the character while it is on the link
2699 root 1.446 map.
2700 root 1.115
2701 root 1.166 Will never block.
2702    
2703 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2704    
2705 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2706     map. If the map is not valid (or omitted), the player will be moved back
2707     to the location he/she was before the call to C<enter_link>, or, if that
2708     fails, to the emergency map position.
2709 root 1.115
2710     Might block.
2711    
2712     =cut
2713    
2714 root 1.166 sub link_map {
2715     unless ($LINK_MAP) {
2716     $LINK_MAP = cf::map::find "{link}"
2717 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2718 root 1.166 $LINK_MAP->load;
2719     }
2720    
2721     $LINK_MAP
2722     }
2723    
2724 root 1.110 sub cf::object::player::enter_link {
2725     my ($self) = @_;
2726    
2727 root 1.259 $self->deactivate_recursive;
2728 root 1.258
2729 root 1.527 ++$self->{_link_recursion};
2730    
2731 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2732 root 1.110
2733 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2734 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2735 root 1.110
2736 root 1.519 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2737 root 1.110 }
2738    
2739     sub cf::object::player::leave_link {
2740     my ($self, $map, $x, $y) = @_;
2741    
2742 root 1.270 return unless $self->contr->active;
2743    
2744 root 1.110 my $link_pos = delete $self->{_link_pos};
2745    
2746     unless ($map) {
2747     # restore original map position
2748     ($map, $x, $y) = @{ $link_pos || [] };
2749 root 1.133 $map = cf::map::find $map;
2750 root 1.110
2751     unless ($map) {
2752     ($map, $x, $y) = @$EMERGENCY_POSITION;
2753 root 1.133 $map = cf::map::find $map
2754 root 1.110 or die "FATAL: cannot load emergency map\n";
2755     }
2756     }
2757    
2758     ($x, $y) = (-1, -1)
2759     unless (defined $x) && (defined $y);
2760    
2761     # use -1 or undef as default coordinates, not 0, 0
2762     ($x, $y) = ($map->enter_x, $map->enter_y)
2763 root 1.492 if $x <= 0 && $y <= 0;
2764 root 1.110
2765     $map->load;
2766 root 1.333 $map->load_neighbours;
2767 root 1.110
2768 root 1.143 return unless $self->contr->active;
2769 root 1.215
2770     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2771 root 1.527 if ($self->enter_map ($map, $x, $y)) {
2772     # entering was successful
2773     delete $self->{_link_recursion};
2774     # only activate afterwards, to support waiting in hooks
2775     $self->activate_recursive;
2776     }
2777 root 1.476
2778 root 1.110 }
2779    
2780 root 1.527 =item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2781 root 1.268
2782     Moves the player to the given map-path and coordinates by first freezing
2783     her, loading and preparing them map, calling the provided $check callback
2784     that has to return the map if sucecssful, and then unfreezes the player on
2785 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2786     be called at the end of this process.
2787 root 1.110
2788 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2789     it needs a loaded map it has to call C<< ->load >>.
2790    
2791 root 1.110 =cut
2792    
2793 root 1.270 our $GOTOGEN;
2794    
2795 root 1.136 sub cf::object::player::goto {
2796 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2797 root 1.268
2798 root 1.527 if ($self->{_link_recursion} >= $MAX_LINKS) {
2799 root 1.532 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2800 root 1.527 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2801     ($path, $x, $y) = @$EMERGENCY_POSITION;
2802     }
2803    
2804 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2805     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2806    
2807 root 1.110 $self->enter_link;
2808    
2809 root 1.140 (async {
2810 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2811    
2812 root 1.365 # *tag paths override both path and x|y
2813     if ($path =~ /^\*(.*)$/) {
2814     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2815     my $ob = $obs[rand @obs];
2816 root 1.366
2817 root 1.367 # see if we actually can go there
2818 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2819     $ob = $obs[rand @obs];
2820 root 1.369 } else {
2821     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2822 root 1.368 }
2823 root 1.369 # else put us there anyways for now #d#
2824 root 1.366
2825 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2826 root 1.369 } else {
2827     ($path, $x, $y) = (undef, undef, undef);
2828 root 1.365 }
2829     }
2830    
2831 root 1.197 my $map = eval {
2832 elmex 1.564 my $map = defined $path ? cf::map::find $path, $self->map : undef;
2833 root 1.268
2834     if ($map) {
2835     $map = $map->customise_for ($self);
2836 root 1.527 $map = $check->($map, $x, $y, $self) if $check && $map;
2837 root 1.268 } else {
2838 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2839 root 1.268 }
2840    
2841 root 1.197 $map
2842 root 1.268 };
2843    
2844     if ($@) {
2845     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2846     LOG llevError | logBacktrace, Carp::longmess $@;
2847     }
2848 root 1.115
2849 root 1.270 if ($gen == $self->{_goto_generation}) {
2850     delete $self->{_goto_generation};
2851     $self->leave_link ($map, $x, $y);
2852     }
2853 root 1.306
2854 root 1.527 $done->($self) if $done;
2855 root 1.110 })->prio (1);
2856     }
2857    
2858     =item $player_object->enter_exit ($exit_object)
2859    
2860     =cut
2861    
2862     sub parse_random_map_params {
2863     my ($spec) = @_;
2864    
2865     my $rmp = { # defaults
2866 root 1.181 xsize => (cf::rndm 15, 40),
2867     ysize => (cf::rndm 15, 40),
2868     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2869 root 1.182 #layout => string,
2870 root 1.110 };
2871    
2872     for (split /\n/, $spec) {
2873     my ($k, $v) = split /\s+/, $_, 2;
2874    
2875     $rmp->{lc $k} = $v if (length $k) && (length $v);
2876     }
2877    
2878     $rmp
2879     }
2880    
2881     sub prepare_random_map {
2882     my ($exit) = @_;
2883    
2884     # all this does is basically replace the /! path by
2885     # a new random map path (?random/...) with a seed
2886     # that depends on the exit object
2887    
2888     my $rmp = parse_random_map_params $exit->msg;
2889    
2890     if ($exit->map) {
2891 root 1.198 $rmp->{region} = $exit->region->name;
2892 root 1.110 $rmp->{origin_map} = $exit->map->path;
2893     $rmp->{origin_x} = $exit->x;
2894     $rmp->{origin_y} = $exit->y;
2895 root 1.430
2896     $exit->map->touch;
2897 root 1.110 }
2898    
2899     $rmp->{random_seed} ||= $exit->random_seed;
2900    
2901 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2902 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2903 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2904 root 1.110
2905 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2906 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2907 root 1.177 undef $fh;
2908     aio_rename "$meta~", $meta;
2909 root 1.110
2910 root 1.430 my $slaying = "?random/$md5";
2911    
2912     if ($exit->valid) {
2913     $exit->slaying ("?random/$md5");
2914     $exit->msg (undef);
2915     }
2916 root 1.110 }
2917     }
2918    
2919     sub cf::object::player::enter_exit {
2920     my ($self, $exit) = @_;
2921    
2922     return unless $self->type == cf::PLAYER;
2923    
2924 root 1.430 $self->enter_link;
2925    
2926     (async {
2927     $Coro::current->{desc} = "enter_exit";
2928    
2929     unless (eval {
2930     $self->deactivate_recursive; # just to be sure
2931 root 1.195
2932 root 1.430 # random map handling
2933     {
2934     my $guard = cf::lock_acquire "exit_prepare:$exit";
2935 root 1.195
2936 root 1.430 prepare_random_map $exit
2937     if $exit->slaying eq "/!";
2938     }
2939 root 1.110
2940 elmex 1.564 my $map = cf::map::normalise $exit->slaying, $exit->map;
2941 root 1.430 my $x = $exit->stats->hp;
2942     my $y = $exit->stats->sp;
2943 root 1.296
2944 root 1.430 $self->goto ($map, $x, $y);
2945 root 1.374
2946 root 1.430 # if exit is damned, update players death & WoR home-position
2947     $self->contr->savebed ($map, $x, $y)
2948     if $exit->flag (cf::FLAG_DAMNED);
2949 root 1.110
2950 root 1.430 1
2951 root 1.110 }) {
2952 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2953 root 1.233 . "I'll try to bring you back to the map you were before. "
2954     . "Please report this to the dungeon master!",
2955     cf::NDI_UNIQUE | cf::NDI_RED);
2956 root 1.110
2957 root 1.532 error "ERROR in enter_exit: $@";
2958 root 1.110 $self->leave_link;
2959     }
2960     })->prio (1);
2961     }
2962    
2963 root 1.95 =head3 cf::client
2964    
2965     =over 4
2966    
2967     =item $client->send_drawinfo ($text, $flags)
2968    
2969     Sends a drawinfo packet to the client. Circumvents output buffering so
2970     should not be used under normal circumstances.
2971    
2972 root 1.70 =cut
2973    
2974 root 1.95 sub cf::client::send_drawinfo {
2975     my ($self, $text, $flags) = @_;
2976    
2977     utf8::encode $text;
2978 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2979 root 1.95 }
2980    
2981 root 1.494 =item $client->send_big_packet ($pkt)
2982    
2983     Like C<send_packet>, but tries to compress large packets, and fragments
2984     them as required.
2985    
2986     =cut
2987    
2988     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2989    
2990     sub cf::client::send_big_packet {
2991     my ($self, $pkt) = @_;
2992    
2993     # try lzf for large packets
2994     $pkt = "lzf " . Compress::LZF::compress $pkt
2995     if 1024 <= length $pkt and $self->{can_lzf};
2996    
2997     # split very large packets
2998     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2999     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
3000     $pkt = "frag";
3001     }
3002    
3003     $self->send_packet ($pkt);
3004     }
3005    
3006 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
3007 root 1.283
3008     Send a drawinfo or msg packet to the client, formatting the msg for the
3009     client if neccessary. C<$type> should be a string identifying the type of
3010     the message, with C<log> being the default. If C<$color> is negative, suppress
3011     the message unless the client supports the msg packet.
3012    
3013     =cut
3014    
3015 root 1.391 # non-persistent channels (usually the info channel)
3016 root 1.350 our %CHANNEL = (
3017 root 1.486 "c/motd" => {
3018     id => "infobox",
3019     title => "MOTD",
3020     reply => undef,
3021     tooltip => "The message of the day",
3022     },
3023 root 1.350 "c/identify" => {
3024 root 1.375 id => "infobox",
3025 root 1.350 title => "Identify",
3026     reply => undef,
3027     tooltip => "Items recently identified",
3028     },
3029 root 1.352 "c/examine" => {
3030 root 1.375 id => "infobox",
3031 root 1.352 title => "Examine",
3032     reply => undef,
3033     tooltip => "Signs and other items you examined",
3034     },
3035 root 1.487 "c/shopinfo" => {
3036     id => "infobox",
3037     title => "Shop Info",
3038     reply => undef,
3039     tooltip => "What your bargaining skill tells you about the shop",
3040     },
3041 root 1.389 "c/book" => {
3042     id => "infobox",
3043     title => "Book",
3044     reply => undef,
3045     tooltip => "The contents of a note or book",
3046     },
3047 root 1.375 "c/lookat" => {
3048     id => "infobox",
3049     title => "Look",
3050     reply => undef,
3051     tooltip => "What you saw there",
3052     },
3053 root 1.390 "c/who" => {
3054     id => "infobox",
3055     title => "Players",
3056     reply => undef,
3057     tooltip => "Shows players who are currently online",
3058     },
3059     "c/body" => {
3060     id => "infobox",
3061     title => "Body Parts",
3062     reply => undef,
3063     tooltip => "Shows which body parts you posess and are available",
3064     },
3065 root 1.465 "c/statistics" => {
3066     id => "infobox",
3067     title => "Statistics",
3068     reply => undef,
3069     tooltip => "Shows your primary statistics",
3070     },
3071 root 1.450 "c/skills" => {
3072     id => "infobox",
3073     title => "Skills",
3074     reply => undef,
3075     tooltip => "Shows your experience per skill and item power",
3076     },
3077 root 1.470 "c/shopitems" => {
3078     id => "infobox",
3079     title => "Shop Items",
3080     reply => undef,
3081     tooltip => "Shows the items currently for sale in this shop",
3082     },
3083 root 1.465 "c/resistances" => {
3084     id => "infobox",
3085     title => "Resistances",
3086     reply => undef,
3087     tooltip => "Shows your resistances",
3088     },
3089     "c/pets" => {
3090     id => "infobox",
3091     title => "Pets",
3092     reply => undef,
3093     tooltip => "Shows information abotu your pets/a specific pet",
3094     },
3095 root 1.471 "c/perceiveself" => {
3096     id => "infobox",
3097     title => "Perceive Self",
3098     reply => undef,
3099     tooltip => "You gained detailed knowledge about yourself",
3100     },
3101 root 1.390 "c/uptime" => {
3102     id => "infobox",
3103     title => "Uptime",
3104     reply => undef,
3105 root 1.391 tooltip => "How long the server has been running since last restart",
3106 root 1.390 },
3107     "c/mapinfo" => {
3108     id => "infobox",
3109     title => "Map Info",
3110     reply => undef,
3111     tooltip => "Information related to the maps",
3112     },
3113 root 1.426 "c/party" => {
3114     id => "party",
3115     title => "Party",
3116     reply => "gsay ",
3117     tooltip => "Messages and chat related to your party",
3118     },
3119 root 1.464 "c/death" => {
3120     id => "death",
3121     title => "Death",
3122     reply => undef,
3123     tooltip => "Reason for and more info about your most recent death",
3124     },
3125 root 1.462 "c/say" => $SAY_CHANNEL,
3126     "c/chat" => $CHAT_CHANNEL,
3127 root 1.350 );
3128    
3129 root 1.283 sub cf::client::send_msg {
3130 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
3131 root 1.283
3132 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
3133     unless $color & cf::NDI_VERBATIM;
3134 root 1.283
3135 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
3136 root 1.311
3137 root 1.350 # check predefined channels, for the benefit of C
3138 root 1.375 if ($CHANNEL{$channel}) {
3139     $channel = $CHANNEL{$channel};
3140    
3141 root 1.463 $self->ext_msg (channel_info => $channel);
3142 root 1.375 $channel = $channel->{id};
3143 root 1.350
3144 root 1.375 } elsif (ref $channel) {
3145 root 1.311 # send meta info to client, if not yet sent
3146     unless (exists $self->{channel}{$channel->{id}}) {
3147     $self->{channel}{$channel->{id}} = $channel;
3148 root 1.463 $self->ext_msg (channel_info => $channel);
3149 root 1.311 }
3150    
3151     $channel = $channel->{id};
3152     }
3153    
3154 root 1.313 return unless @extra || length $msg;
3155    
3156 root 1.463 # default colour, mask it out
3157     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3158     if $color & cf::NDI_DEF;
3159    
3160     my $pkt = "msg "
3161     . $self->{json_coder}->encode (
3162     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3163     );
3164    
3165 root 1.494 $self->send_big_packet ($pkt);
3166 root 1.283 }
3167    
3168 root 1.316 =item $client->ext_msg ($type, @msg)
3169 root 1.232
3170 root 1.287 Sends an ext event to the client.
3171 root 1.232
3172     =cut
3173    
3174 root 1.316 sub cf::client::ext_msg($$@) {
3175     my ($self, $type, @msg) = @_;
3176 root 1.232
3177 root 1.343 if ($self->extcmd == 2) {
3178 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3179 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3180 root 1.316 push @msg, msgtype => "event_$type";
3181 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3182 root 1.316 }
3183 root 1.232 }
3184 root 1.95
3185 root 1.336 =item $client->ext_reply ($msgid, @msg)
3186    
3187     Sends an ext reply to the client.
3188    
3189     =cut
3190    
3191     sub cf::client::ext_reply($$@) {
3192     my ($self, $id, @msg) = @_;
3193    
3194     if ($self->extcmd == 2) {
3195 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3196 root 1.343 } elsif ($self->extcmd == 1) {
3197 root 1.336 #TODO: version 1, remove
3198     unshift @msg, msgtype => "reply", msgid => $id;
3199 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3200 root 1.336 }
3201     }
3202    
3203 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3204    
3205     Queues a query to the client, calling the given callback with
3206     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3207     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3208    
3209 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3210     become reliable at some point in the future.
3211 root 1.95
3212     =cut
3213    
3214     sub cf::client::query {
3215     my ($self, $flags, $text, $cb) = @_;
3216    
3217     return unless $self->state == ST_PLAYING
3218     || $self->state == ST_SETUP
3219     || $self->state == ST_CUSTOM;
3220    
3221     $self->state (ST_CUSTOM);
3222    
3223     utf8::encode $text;
3224     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3225    
3226     $self->send_packet ($self->{query_queue}[0][0])
3227     if @{ $self->{query_queue} } == 1;
3228 root 1.287
3229     1
3230 root 1.95 }
3231    
3232     cf::client->attach (
3233 root 1.290 on_connect => sub {
3234     my ($ns) = @_;
3235    
3236     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3237     },
3238 root 1.95 on_reply => sub {
3239     my ($ns, $msg) = @_;
3240    
3241     # this weird shuffling is so that direct followup queries
3242     # get handled first
3243 root 1.128 my $queue = delete $ns->{query_queue}
3244 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3245 root 1.95
3246     (shift @$queue)->[1]->($msg);
3247 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3248 root 1.95
3249     push @{ $ns->{query_queue} }, @$queue;
3250    
3251     if (@{ $ns->{query_queue} } == @$queue) {
3252     if (@$queue) {
3253     $ns->send_packet ($ns->{query_queue}[0][0]);
3254     } else {
3255 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3256 root 1.95 }
3257     }
3258     },
3259 root 1.287 on_exticmd => sub {
3260     my ($ns, $buf) = @_;
3261    
3262 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3263 root 1.287
3264     if (ref $msg) {
3265 root 1.316 my ($type, $reply, @payload) =
3266     "ARRAY" eq ref $msg
3267     ? @$msg
3268     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3269    
3270 root 1.338 my @reply;
3271    
3272 root 1.316 if (my $cb = $EXTICMD{$type}) {
3273 root 1.338 @reply = $cb->($ns, @payload);
3274     }
3275    
3276     $ns->ext_reply ($reply, @reply)
3277     if $reply;
3278 root 1.316
3279 root 1.287 } else {
3280 root 1.532 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3281 root 1.287 }
3282    
3283     cf::override;
3284     },
3285 root 1.95 );
3286    
3287 root 1.140 =item $client->async (\&cb)
3288 root 1.96
3289     Create a new coroutine, running the specified callback. The coroutine will
3290     be automatically cancelled when the client gets destroyed (e.g. on logout,
3291     or loss of connection).
3292    
3293     =cut
3294    
3295 root 1.140 sub cf::client::async {
3296 root 1.96 my ($self, $cb) = @_;
3297    
3298 root 1.140 my $coro = &Coro::async ($cb);
3299 root 1.103
3300     $coro->on_destroy (sub {
3301 root 1.96 delete $self->{_coro}{$coro+0};
3302 root 1.103 });
3303 root 1.96
3304     $self->{_coro}{$coro+0} = $coro;
3305 root 1.103
3306     $coro
3307 root 1.96 }
3308    
3309     cf::client->attach (
3310 root 1.509 on_client_destroy => sub {
3311 root 1.96 my ($ns) = @_;
3312    
3313 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3314 root 1.96 },
3315     );
3316    
3317 root 1.95 =back
3318    
3319 root 1.70
3320     =head2 SAFE SCRIPTING
3321    
3322     Functions that provide a safe environment to compile and execute
3323     snippets of perl code without them endangering the safety of the server
3324     itself. Looping constructs, I/O operators and other built-in functionality
3325     is not available in the safe scripting environment, and the number of
3326 root 1.79 functions and methods that can be called is greatly reduced.
3327 root 1.70
3328     =cut
3329 root 1.23
3330 root 1.42 our $safe = new Safe "safe";
3331 root 1.23 our $safe_hole = new Safe::Hole;
3332    
3333     $SIG{FPE} = 'IGNORE';
3334    
3335 root 1.328 $safe->permit_only (Opcode::opset qw(
3336 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3337 root 1.328 grepstart grepwhile mapstart mapwhile
3338     sort time
3339     ));
3340 root 1.23
3341 root 1.25 # here we export the classes and methods available to script code
3342    
3343 root 1.70 =pod
3344    
3345 root 1.228 The following functions and methods are available within a safe environment:
3346 root 1.70
3347 root 1.297 cf::object
3348 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3349 root 1.425 insert remove name archname title slaying race decrease split
3350 root 1.466 value
3351 root 1.297
3352     cf::object::player
3353     player
3354    
3355     cf::player
3356     peaceful
3357    
3358     cf::map
3359     trigger
3360 root 1.70
3361     =cut
3362    
3363 root 1.25 for (
3364 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3365 elmex 1.431 insert remove inv nrof name archname title slaying race
3366 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3367 root 1.25 ["cf::object::player" => qw(player)],
3368 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3369 elmex 1.91 ["cf::map" => qw(trigger)],
3370 root 1.25 ) {
3371     my ($pkg, @funs) = @$_;
3372 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3373 root 1.25 for @funs;
3374     }
3375 root 1.23
3376 root 1.70 =over 4
3377    
3378     =item @retval = safe_eval $code, [var => value, ...]
3379    
3380     Compiled and executes the given perl code snippet. additional var/value
3381     pairs result in temporary local (my) scalar variables of the given name
3382     that are available in the code snippet. Example:
3383    
3384     my $five = safe_eval '$first + $second', first => 1, second => 4;
3385    
3386     =cut
3387    
3388 root 1.23 sub safe_eval($;@) {
3389     my ($code, %vars) = @_;
3390    
3391     my $qcode = $code;
3392     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3393     $qcode =~ s/\n/\\n/g;
3394    
3395 root 1.466 %vars = (_dummy => 0) unless %vars;
3396    
3397 root 1.499 my @res;
3398 root 1.23 local $_;
3399    
3400 root 1.42 my $eval =
3401 root 1.23 "do {\n"
3402     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3403     . "#line 0 \"{$qcode}\"\n"
3404     . $code
3405     . "\n}"
3406 root 1.25 ;
3407    
3408 root 1.499 if ($CFG{safe_eval}) {
3409     sub_generation_inc;
3410     local @safe::cf::_safe_eval_args = values %vars;
3411     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3412     sub_generation_inc;
3413     } else {
3414     local @cf::_safe_eval_args = values %vars;
3415     @res = wantarray ? eval eval : scalar eval $eval;
3416     }
3417 root 1.25
3418 root 1.42 if ($@) {
3419 root 1.532 warn "$@",
3420     "while executing safe code '$code'\n",
3421     "with arguments " . (join " ", %vars) . "\n";
3422 root 1.42 }
3423    
3424 root 1.25 wantarray ? @res : $res[0]
3425 root 1.23 }
3426    
3427 root 1.69 =item cf::register_script_function $function => $cb
3428    
3429     Register a function that can be called from within map/npc scripts. The
3430     function should be reasonably secure and should be put into a package name
3431     like the extension.
3432    
3433     Example: register a function that gets called whenever a map script calls
3434     C<rent::overview>, as used by the C<rent> extension.
3435    
3436     cf::register_script_function "rent::overview" => sub {
3437     ...
3438     };
3439    
3440     =cut
3441    
3442 root 1.23 sub register_script_function {
3443     my ($fun, $cb) = @_;
3444    
3445 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3446     *$fun = $safe_hole->wrap ($cb);
3447 root 1.23 }
3448    
3449 root 1.70 =back
3450    
3451 root 1.71 =cut
3452    
3453 root 1.23 #############################################################################
3454 root 1.203 # the server's init and main functions
3455    
3456 root 1.246 sub load_facedata($) {
3457     my ($path) = @_;
3458 root 1.223
3459 root 1.348 # HACK to clear player env face cache, we need some signal framework
3460     # for this (global event?)
3461     %ext::player_env::MUSIC_FACE_CACHE = ();
3462    
3463 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3464 root 1.334
3465 root 1.532 trace "loading facedata from $path\n";
3466 root 1.223
3467 root 1.560 my $facedata = decode_storable load_file $path;
3468 root 1.223
3469 root 1.236 $facedata->{version} == 2
3470 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3471    
3472 root 1.334 # patch in the exptable
3473 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3474 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3475     type => FT_RSRC,
3476 root 1.500 data => $exp_table,
3477     hash => (Digest::MD5::md5 $exp_table),
3478 root 1.334 };
3479     cf::cede_to_tick;
3480    
3481 root 1.236 {
3482     my $faces = $facedata->{faceinfo};
3483    
3484     while (my ($face, $info) = each %$faces) {
3485     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3486 root 1.405
3487 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3488     cf::face::set_magicmap $idx, $info->{magicmap};
3489 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3490     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3491 root 1.558 cf::face::set_data $idx, 2, $info->{glyph} , $info->{glyph} ;
3492 root 1.302
3493     cf::cede_to_tick;
3494 root 1.236 }
3495    
3496     while (my ($face, $info) = each %$faces) {
3497     next unless $info->{smooth};
3498 root 1.405
3499 root 1.236 my $idx = cf::face::find $face
3500     or next;
3501 root 1.405
3502 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3503 root 1.302 cf::face::set_smooth $idx, $smooth;
3504     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3505 root 1.236 } else {
3506 root 1.532 error "smooth face '$info->{smooth}' not found for face '$face'";
3507 root 1.236 }
3508 root 1.302
3509     cf::cede_to_tick;
3510 root 1.236 }
3511 root 1.223 }
3512    
3513 root 1.236 {
3514     my $anims = $facedata->{animinfo};
3515    
3516     while (my ($anim, $info) = each %$anims) {
3517     cf::anim::set $anim, $info->{frames}, $info->{facings};
3518 root 1.302 cf::cede_to_tick;
3519 root 1.225 }
3520 root 1.236
3521     cf::anim::invalidate_all; # d'oh
3522 root 1.225 }
3523    
3524 root 1.302 {
3525     my $res = $facedata->{resource};
3526    
3527     while (my ($name, $info) = each %$res) {
3528 root 1.405 if (defined $info->{type}) {
3529     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3530    
3531 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3532 root 1.405 cf::face::set_type $idx, $info->{type};
3533 root 1.337 } else {
3534 root 1.530 $RESOURCE{$name} = $info; # unused
3535 root 1.307 }
3536 root 1.302
3537     cf::cede_to_tick;
3538     }
3539 root 1.406 }
3540    
3541     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3542 root 1.321
3543 root 1.406 1
3544     }
3545    
3546 root 1.318 register_exticmd fx_want => sub {
3547     my ($ns, $want) = @_;
3548    
3549     while (my ($k, $v) = each %$want) {
3550     $ns->fx_want ($k, $v);
3551     }
3552     };
3553    
3554 root 1.423 sub load_resource_file($) {
3555 root 1.424 my $guard = lock_acquire "load_resource_file";
3556    
3557 root 1.423 my $status = load_resource_file_ $_[0];
3558     get_slot 0.1, 100;
3559     cf::arch::commit_load;
3560 root 1.424
3561 root 1.423 $status
3562     }
3563    
3564 root 1.253 sub reload_regions {
3565 root 1.348 # HACK to clear player env face cache, we need some signal framework
3566     # for this (global event?)
3567     %ext::player_env::MUSIC_FACE_CACHE = ();
3568    
3569 root 1.253 load_resource_file "$MAPDIR/regions"
3570     or die "unable to load regions file\n";
3571 root 1.304
3572     for (cf::region::list) {
3573     $_->{match} = qr/$_->{match}/
3574     if exists $_->{match};
3575     }
3576 root 1.253 }
3577    
3578 root 1.246 sub reload_facedata {
3579 root 1.253 load_facedata "$DATADIR/facedata"
3580 root 1.246 or die "unable to load facedata\n";
3581     }
3582    
3583     sub reload_archetypes {
3584 root 1.253 load_resource_file "$DATADIR/archetypes"
3585 root 1.246 or die "unable to load archetypes\n";
3586 root 1.241 }
3587    
3588 root 1.246 sub reload_treasures {
3589 root 1.253 load_resource_file "$DATADIR/treasures"
3590 root 1.246 or die "unable to load treasurelists\n";
3591 root 1.241 }
3592    
3593 root 1.530 sub reload_sound {
3594 root 1.532 trace "loading sound config from $DATADIR/sound\n";
3595 root 1.531
3596 root 1.560 my $soundconf = JSON::XS->new->utf8->relaxed->decode (load_file "$DATADIR/sound");
3597 root 1.530
3598     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3599     my $sound = $soundconf->{compat}[$_]
3600     or next;
3601    
3602     my $face = cf::face::find "sound/$sound->[1]";
3603     cf::sound::set $sound->[0] => $face;
3604     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3605     }
3606    
3607     while (my ($k, $v) = each %{$soundconf->{event}}) {
3608     my $face = cf::face::find "sound/$v";
3609     cf::sound::set $k => $face;
3610     }
3611     }
3612    
3613 root 1.223 sub reload_resources {
3614 root 1.532 trace "reloading resource files...\n";
3615 root 1.245
3616 root 1.545 reload_exp_table;
3617     reload_materials;
3618 root 1.246 reload_facedata;
3619 root 1.530 reload_sound;
3620 root 1.246 reload_archetypes;
3621 root 1.423 reload_regions;
3622 root 1.246 reload_treasures;
3623 root 1.245
3624 root 1.532 trace "finished reloading resource files\n";
3625 root 1.223 }
3626    
3627 root 1.345 sub reload_config {
3628 root 1.532 trace "reloading config file...\n";
3629 root 1.485
3630 root 1.560 my $config = load_file "$CONFDIR/config";
3631 root 1.546 utf8::decode $config;
3632 root 1.560 *CFG = decode_yaml $config;
3633 root 1.131
3634 root 1.527 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3635 root 1.131
3636 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3637     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3638    
3639 root 1.131 if (exists $CFG{mlockall}) {
3640     eval {
3641 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3642 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3643     };
3644     warn $@ if $@;
3645     }
3646 root 1.72 }
3647    
3648 root 1.445 sub pidfile() {
3649     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3650     or die "$PIDFILE: $!";
3651     flock $fh, &Fcntl::LOCK_EX
3652     or die "$PIDFILE: flock: $!";
3653     $fh
3654     }
3655    
3656     # make sure only one server instance is running at any one time
3657     sub atomic {
3658     my $fh = pidfile;
3659    
3660     my $pid = <$fh>;
3661     kill 9, $pid if $pid > 0;
3662    
3663     seek $fh, 0, 0;
3664     print $fh $$;
3665     }
3666    
3667 root 1.474 sub main_loop {
3668 root 1.532 trace "EV::loop starting\n";
3669 root 1.474 if (1) {
3670     EV::loop;
3671     }
3672 root 1.532 trace "EV::loop returned\n";
3673 root 1.474 goto &main_loop unless $REALLY_UNLOOP;
3674     }
3675    
3676 root 1.39 sub main {
3677 root 1.453 cf::init_globals; # initialise logging
3678    
3679     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3680 root 1.561 LOG llevInfo, "Copyright (C) 2005-2011 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3681 root 1.453 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3682     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3683    
3684     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3685 root 1.445
3686 root 1.108 # we must not ever block the main coroutine
3687     local $Coro::idle = sub {
3688 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3689 root 1.175 (async {
3690 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3691 root 1.396 EV::loop EV::LOOP_ONESHOT;
3692 root 1.175 })->prio (Coro::PRIO_MAX);
3693 root 1.108 };
3694    
3695 root 1.453 evthread_start IO::AIO::poll_fileno;
3696    
3697     cf::sync_job {
3698 root 1.543 cf::incloader::init ();
3699 root 1.540
3700 root 1.515 cf::init_anim;
3701     cf::init_attackmess;
3702     cf::init_dynamic;
3703    
3704 root 1.495 cf::load_settings;
3705    
3706 root 1.453 reload_resources;
3707 root 1.423 reload_config;
3708     db_init;
3709 root 1.453
3710     cf::init_uuid;
3711     cf::init_signals;
3712     cf::init_skills;
3713    
3714     cf::init_beforeplay;
3715    
3716     atomic;
3717    
3718 root 1.423 load_extensions;
3719    
3720 root 1.453 utime time, time, $RUNTIMEFILE;
3721 root 1.183
3722 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3723 root 1.475 use POSIX ();
3724 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3725 root 1.445
3726 root 1.550 cf::_post_init 0;
3727 root 1.453 };
3728 root 1.445
3729 root 1.516 cf::object::thawer::errors_are_fatal 0;
3730 root 1.532 info "parse errors in files are no longer fatal from this point on.\n";
3731 root 1.516
3732 root 1.540 my $free_main; $free_main = EV::idle sub {
3733     undef $free_main;
3734     undef &main; # free gobs of memory :)
3735     };
3736    
3737     goto &main_loop;
3738 root 1.34 }
3739    
3740     #############################################################################
3741 root 1.155 # initialisation and cleanup
3742    
3743     # install some emergency cleanup handlers
3744     BEGIN {
3745 root 1.396 our %SIGWATCHER = ();
3746 root 1.155 for my $signal (qw(INT HUP TERM)) {
3747 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3748 root 1.396 cf::cleanup "SIG$signal";
3749     };
3750 root 1.155 }
3751     }
3752    
3753 root 1.417 sub write_runtime_sync {
3754 root 1.512 my $t0 = AE::time;
3755 root 1.506
3756 root 1.281 # first touch the runtime file to show we are still running:
3757     # the fsync below can take a very very long time.
3758    
3759 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3760 root 1.281
3761     my $guard = cf::lock_acquire "write_runtime";
3762    
3763 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3764 root 1.281 or return;
3765    
3766     my $value = $cf::RUNTIME + 90 + 10;
3767     # 10 is the runtime save interval, for a monotonic clock
3768     # 60 allows for the watchdog to kill the server.
3769    
3770     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3771     and return;
3772    
3773     # always fsync - this file is important
3774     aio_fsync $fh
3775     and return;
3776    
3777     # touch it again to show we are up-to-date
3778     aio_utime $fh, undef, undef;
3779    
3780     close $fh
3781     or return;
3782    
3783 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3784 root 1.281 and return;
3785    
3786 root 1.532 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3787 root 1.281
3788     1
3789     }
3790    
3791 root 1.416 our $uuid_lock;
3792     our $uuid_skip;
3793    
3794     sub write_uuid_sync($) {
3795     $uuid_skip ||= $_[0];
3796    
3797     return if $uuid_lock;
3798     local $uuid_lock = 1;
3799    
3800     my $uuid = "$LOCALDIR/uuid";
3801    
3802     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3803     or return;
3804    
3805 root 1.454 my $value = uuid_seq uuid_cur;
3806    
3807 root 1.455 unless ($value) {
3808 root 1.532 info "cowardly refusing to write zero uuid value!\n";
3809 root 1.454 return;
3810     }
3811    
3812     my $value = uuid_str $value + $uuid_skip;
3813 root 1.416 $uuid_skip = 0;
3814    
3815     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3816     and return;
3817    
3818     # always fsync - this file is important
3819     aio_fsync $fh
3820     and return;
3821    
3822     close $fh
3823     or return;
3824    
3825     aio_rename "$uuid~", $uuid
3826     and return;
3827    
3828 root 1.532 trace "uuid file written ($value).\n";
3829 root 1.416
3830     1
3831    
3832     }
3833    
3834     sub write_uuid($$) {
3835     my ($skip, $sync) = @_;
3836    
3837     $sync ? write_uuid_sync $skip
3838     : async { write_uuid_sync $skip };
3839     }
3840    
3841 root 1.156 sub emergency_save() {
3842 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3843    
3844 root 1.532 info "emergency_perl_save: enter\n";
3845 root 1.155
3846 root 1.534 # this is a trade-off: we want to be very quick here, so
3847     # save all maps without fsync, and later call a global sync
3848     # (which in turn might be very very slow)
3849     local $USE_FSYNC = 0;
3850    
3851 root 1.155 cf::sync_job {
3852 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3853    
3854 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3855     # refcount bugs in for. also avoids problems with players
3856 root 1.167 # and maps saved/destroyed asynchronously.
3857 root 1.532 info "emergency_perl_save: begin player save\n";
3858 root 1.155 for my $login (keys %cf::PLAYER) {
3859     my $pl = $cf::PLAYER{$login} or next;
3860     $pl->valid or next;
3861 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3862 root 1.155 $pl->save;
3863     }
3864 root 1.532 info "emergency_perl_save: end player save\n";
3865 root 1.155
3866 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3867    
3868 root 1.532 info "emergency_perl_save: begin map save\n";
3869 root 1.155 for my $path (keys %cf::MAP) {
3870     my $map = $cf::MAP{$path} or next;
3871     $map->valid or next;
3872     $map->save;
3873     }
3874 root 1.532 info "emergency_perl_save: end map save\n";
3875 root 1.208
3876 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3877    
3878 root 1.532 info "emergency_perl_save: begin database checkpoint\n";
3879 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3880 root 1.532 info "emergency_perl_save: end database checkpoint\n";
3881 root 1.416
3882 root 1.532 info "emergency_perl_save: begin write uuid\n";
3883 root 1.416 write_uuid_sync 1;
3884 root 1.532 info "emergency_perl_save: end write uuid\n";
3885 root 1.155
3886 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3887    
3888     trace "emergency_perl_save: syncing database to disk";
3889     BDB::db_env_txn_checkpoint $DB_ENV;
3890    
3891 root 1.536 info "emergency_perl_save: starting sync\n";
3892 root 1.535 IO::AIO::aio_sync sub {
3893 root 1.536 info "emergency_perl_save: finished sync\n";
3894 root 1.535 };
3895    
3896     cf::write_runtime_sync; # external watchdog should not bark
3897    
3898     trace "emergency_perl_save: flushing outstanding aio requests";
3899     while (IO::AIO::nreqs || BDB::nreqs) {
3900     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3901     }
3902    
3903     cf::write_runtime_sync; # external watchdog should not bark
3904 root 1.457 };
3905    
3906 root 1.532 info "emergency_perl_save: leave\n";
3907 root 1.155 }
3908 root 1.22
3909 root 1.211 sub post_cleanup {
3910     my ($make_core) = @_;
3911    
3912 root 1.535 IO::AIO::flush;
3913    
3914 root 1.532 error Carp::longmess "post_cleanup backtrace"
3915 root 1.211 if $make_core;
3916 root 1.445
3917     my $fh = pidfile;
3918     unlink $PIDFILE if <$fh> == $$;
3919 root 1.211 }
3920    
3921 root 1.441 # a safer delete_package, copied from Symbol
3922     sub clear_package($) {
3923     my $pkg = shift;
3924    
3925     # expand to full symbol table name if needed
3926     unless ($pkg =~ /^main::.*::$/) {
3927     $pkg = "main$pkg" if $pkg =~ /^::/;
3928     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3929     $pkg .= '::' unless $pkg =~ /::$/;
3930     }
3931    
3932     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3933     my $stem_symtab = *{$stem}{HASH};
3934    
3935     defined $stem_symtab and exists $stem_symtab->{$leaf}
3936     or return;
3937    
3938     # clear all symbols
3939     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3940     for my $name (keys %$leaf_symtab) {
3941     _gv_clear *{"$pkg$name"};
3942     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3943     }
3944     }
3945    
3946 root 1.246 sub do_reload_perl() {
3947 root 1.106 # can/must only be called in main
3948 root 1.543 unless (in_main) {
3949 root 1.532 error "can only reload from main coroutine";
3950 root 1.106 return;
3951     }
3952    
3953 root 1.441 return if $RELOAD++;
3954    
3955 root 1.512 my $t1 = AE::time;
3956 root 1.457
3957 root 1.441 while ($RELOAD) {
3958 root 1.543 cf::get_slot 0.1, -1, "reload_perl";
3959 root 1.550 info "perl_reload: reloading...";
3960 root 1.103
3961 root 1.550 trace "perl_reload: entering sync_job";
3962 root 1.212
3963 root 1.441 cf::sync_job {
3964 root 1.543 #cf::emergency_save;
3965 root 1.183
3966 root 1.550 trace "perl_reload: cancelling all extension coros";
3967 root 1.441 $_->cancel for values %EXT_CORO;
3968     %EXT_CORO = ();
3969 root 1.223
3970 root 1.550 trace "perl_reload: removing commands";
3971 root 1.441 %COMMAND = ();
3972 root 1.103
3973 root 1.550 trace "perl_reload: removing ext/exti commands";
3974 root 1.441 %EXTCMD = ();
3975     %EXTICMD = ();
3976 root 1.159
3977 root 1.550 trace "perl_reload: unloading/nuking all extensions";
3978 root 1.441 for my $pkg (@EXTS) {
3979 root 1.532 trace "... unloading $pkg";
3980 root 1.159
3981 root 1.441 if (my $cb = $pkg->can ("unload")) {
3982     eval {
3983     $cb->($pkg);
3984     1
3985 root 1.532 } or error "$pkg unloaded, but with errors: $@";
3986 root 1.441 }
3987 root 1.159
3988 root 1.532 trace "... clearing $pkg";
3989 root 1.441 clear_package $pkg;
3990 root 1.159 }
3991    
3992 root 1.550 trace "perl_reload: unloading all perl modules loaded from $LIBDIR";
3993 root 1.441 while (my ($k, $v) = each %INC) {
3994     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3995 root 1.65
3996 root 1.532 trace "... unloading $k";
3997 root 1.441 delete $INC{$k};
3998 root 1.65
3999 root 1.441 $k =~ s/\.pm$//;
4000     $k =~ s/\//::/g;
4001 root 1.65
4002 root 1.441 if (my $cb = $k->can ("unload_module")) {
4003     $cb->();
4004     }
4005 root 1.65
4006 root 1.441 clear_package $k;
4007 root 1.65 }
4008    
4009 root 1.550 trace "perl_reload: getting rid of safe::, as good as possible";
4010 root 1.441 clear_package "safe::$_"
4011     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
4012 root 1.65
4013 root 1.550 trace "perl_reload: unloading cf.pm \"a bit\"";
4014 root 1.441 delete $INC{"cf.pm"};
4015 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
4016 root 1.65
4017 root 1.441 # don't, removes xs symbols, too,
4018     # and global variables created in xs
4019     #clear_package __PACKAGE__;
4020 root 1.65
4021 root 1.550 info "perl_reload: unload completed, starting to reload now";
4022 root 1.65
4023 root 1.550 trace "perl_reload: reloading cf.pm";
4024 root 1.441 require cf;
4025 root 1.483 cf::_connect_to_perl_1;
4026 root 1.183
4027 root 1.550 trace "perl_reload: loading config and database again";
4028 root 1.441 cf::reload_config;
4029 root 1.100
4030 root 1.550 trace "perl_reload: loading extensions";
4031 root 1.441 cf::load_extensions;
4032 root 1.65
4033 root 1.457 if ($REATTACH_ON_RELOAD) {
4034 root 1.550 trace "perl_reload: reattaching attachments to objects/players";
4035 root 1.457 _global_reattach; # objects, sockets
4036 root 1.550 trace "perl_reload: reattaching attachments to maps";
4037 root 1.457 reattach $_ for values %MAP;
4038 root 1.550 trace "perl_reload: reattaching attachments to players";
4039 root 1.457 reattach $_ for values %PLAYER;
4040     }
4041 root 1.65
4042 root 1.550 cf::_post_init 1;
4043 root 1.453
4044 root 1.550 trace "perl_reload: leaving sync_job";
4045 root 1.183
4046 root 1.441 1
4047     } or do {
4048 root 1.532 error $@;
4049 root 1.550 cf::cleanup "perl_reload: error, exiting.";
4050 root 1.441 };
4051 root 1.183
4052 root 1.441 --$RELOAD;
4053     }
4054 root 1.457
4055 root 1.512 $t1 = AE::time - $t1;
4056 root 1.550 info "perl_reload: completed in ${t1}s\n";
4057 root 1.65 };
4058    
4059 root 1.175 our $RELOAD_WATCHER; # used only during reload
4060    
4061 root 1.246 sub reload_perl() {
4062     # doing reload synchronously and two reloads happen back-to-back,
4063     # coro crashes during coro_state_free->destroy here.
4064    
4065 root 1.457 $RELOAD_WATCHER ||= cf::async {
4066     Coro::AIO::aio_wait cache_extensions;
4067    
4068 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
4069 root 1.457 do_reload_perl;
4070     undef $RELOAD_WATCHER;
4071     };
4072 root 1.396 };
4073 root 1.246 }
4074    
4075 root 1.111 register_command "reload" => sub {
4076 root 1.65 my ($who, $arg) = @_;
4077    
4078     if ($who->flag (FLAG_WIZ)) {
4079 root 1.175 $who->message ("reloading server.");
4080 root 1.374 async {
4081     $Coro::current->{desc} = "perl_reload";
4082     reload_perl;
4083     };
4084 root 1.65 }
4085     };
4086    
4087 root 1.540 #############################################################################
4088 root 1.17
4089 root 1.183 my $bug_warning = 0;
4090    
4091 root 1.239 our @WAIT_FOR_TICK;
4092     our @WAIT_FOR_TICK_BEGIN;
4093    
4094 root 1.546 sub wait_for_tick() {
4095 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4096 root 1.241
4097 root 1.239 my $signal = new Coro::Signal;
4098     push @WAIT_FOR_TICK, $signal;
4099     $signal->wait;
4100     }
4101    
4102 root 1.546 sub wait_for_tick_begin() {
4103 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4104 root 1.241
4105 root 1.239 my $signal = new Coro::Signal;
4106     push @WAIT_FOR_TICK_BEGIN, $signal;
4107     $signal->wait;
4108     }
4109    
4110 root 1.412 sub tick {
4111 root 1.396 if ($Coro::current != $Coro::main) {
4112     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
4113     unless ++$bug_warning > 10;
4114     return;
4115     }
4116    
4117     cf::server_tick; # one server iteration
4118 root 1.245
4119 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
4120 root 1.502
4121 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
4122 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
4123 root 1.396 Coro::async_pool {
4124     $Coro::current->{desc} = "runtime saver";
4125 root 1.417 write_runtime_sync
4126 root 1.532 or error "ERROR: unable to write runtime file: $!";
4127 root 1.396 };
4128     }
4129 root 1.265
4130 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
4131     $sig->send;
4132     }
4133     while (my $sig = shift @WAIT_FOR_TICK) {
4134     $sig->send;
4135     }
4136 root 1.265
4137 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
4138 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
4139 root 1.265
4140 root 1.412 if (0) {
4141     if ($NEXT_TICK) {
4142     my $jitter = $TICK_START - $NEXT_TICK;
4143     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
4144 root 1.532 debug "jitter $JITTER\n";#d#
4145 root 1.412 }
4146     }
4147     }
4148 root 1.35
4149 root 1.206 {
4150 root 1.401 # configure BDB
4151    
4152 root 1.503 BDB::min_parallel 16;
4153 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
4154 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
4155 root 1.77
4156 root 1.206 unless ($DB_ENV) {
4157     $DB_ENV = BDB::db_env_create;
4158 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
4159     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
4160     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
4161 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
4162     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
4163 root 1.206
4164 root 1.534 cf::sync_job {
4165     eval {
4166     BDB::db_env_open
4167     $DB_ENV,
4168     $BDBDIR,
4169     BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4170     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4171     0666;
4172 root 1.208
4173 root 1.534 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4174     };
4175 root 1.533
4176 root 1.534 cf::cleanup "db_env_open(db): $@" if $@;
4177     };
4178 root 1.206 }
4179 root 1.363
4180 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4181     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4182     };
4183     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4184     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4185     };
4186     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4187     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4188     };
4189 root 1.206 }
4190    
4191     {
4192 root 1.401 # configure IO::AIO
4193    
4194 root 1.206 IO::AIO::min_parallel 8;
4195     IO::AIO::max_poll_time $TICK * 0.1;
4196 root 1.435 undef $AnyEvent::AIO::WATCHER;
4197 root 1.206 }
4198 root 1.108
4199 root 1.552 our $_log_backtrace;
4200     our $_log_backtrace_last;
4201 root 1.262
4202 root 1.260 sub _log_backtrace {
4203     my ($msg, @addr) = @_;
4204    
4205 root 1.552 $msg =~ s/\n$//;
4206 root 1.260
4207 root 1.552 if ($_log_backtrace_last eq $msg) {
4208     LOG llevInfo, "[ABT] $msg\n";
4209     LOG llevInfo, "[ABT] [duplicate, suppressed]\n";
4210 root 1.262 # limit the # of concurrent backtraces
4211 root 1.552 } elsif ($_log_backtrace < 2) {
4212     $_log_backtrace_last = $msg;
4213 root 1.262 ++$_log_backtrace;
4214 root 1.446 my $perl_bt = Carp::longmess $msg;
4215 root 1.262 async {
4216 root 1.374 $Coro::current->{desc} = "abt $msg";
4217    
4218 root 1.262 my @bt = fork_call {
4219     @addr = map { sprintf "%x", $_ } @addr;
4220     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4221     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4222     or die "addr2line: $!";
4223    
4224     my @funcs;
4225     my @res = <$fh>;
4226     chomp for @res;
4227     while (@res) {
4228     my ($func, $line) = splice @res, 0, 2, ();
4229     push @funcs, "[$func] $line";
4230     }
4231 root 1.260
4232 root 1.262 @funcs
4233     };
4234 root 1.260
4235 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4236     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4237 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4238     --$_log_backtrace;
4239     };
4240     } else {
4241 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4242 root 1.552 LOG llevInfo, "[ABT] [overload, suppressed]\n";
4243 root 1.262 }
4244 root 1.260 }
4245    
4246 root 1.249 # load additional modules
4247 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4248 root 1.483 cf::_connect_to_perl_2;
4249 root 1.249
4250 root 1.125 END { cf::emergency_save }
4251    
4252 root 1.1 1
4253