ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.551
Committed: Sun May 16 13:48:07 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.550: +0 -1 lines
Log Message:
fix crash with layoutoption RM_WALL_OFF ins erver and don't use it in the maps

File Contents

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