ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.554
Committed: Sat Jun 26 22:10:18 2010 UTC (13 years, 11 months ago) by root
Branch: MAIN
Changes since 1.553: +5 -4 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.484 #
2 root 1.412 # This file is part of Deliantra, the Roguelike Realtime MMORPG.
3     #
4 root 1.504 # Copyright (©) 2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.412 #
6 root 1.484 # Deliantra is free software: you can redistribute it and/or modify it under
7     # the terms of the Affero GNU General Public License as published by the
8     # Free Software Foundation, either version 3 of the License, or (at your
9     # option) any later version.
10 root 1.412 #
11     # This program is distributed in the hope that it will be useful,
12     # but WITHOUT ANY WARRANTY; without even the implied warranty of
13     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     # GNU General Public License for more details.
15     #
16 root 1.484 # You should have received a copy of the Affero GNU General Public License
17     # and the GNU General Public License along with this program. If not, see
18     # <http://www.gnu.org/licenses/>.
19 root 1.412 #
20     # The authors can be reached via e-mail to <support@deliantra.net>
21 root 1.484 #
22 root 1.412
23 root 1.1 package cf;
24    
25 root 1.539 use common::sense;
26 root 1.96
27 root 1.1 use Symbol;
28     use List::Util;
29 root 1.250 use Socket;
30 root 1.433 use EV;
31 root 1.23 use Opcode;
32     use Safe;
33     use Safe::Hole;
34 root 1.385 use Storable ();
35 root 1.493 use Carp ();
36 root 1.19
37 root 1.458 use Guard ();
38 root 1.433 use Coro ();
39 root 1.224 use Coro::State;
40 root 1.250 use Coro::Handle;
41 root 1.441 use Coro::EV;
42 root 1.434 use Coro::AnyEvent;
43 root 1.96 use Coro::Timer;
44     use Coro::Signal;
45     use Coro::Semaphore;
46 root 1.459 use Coro::SemaphoreSet;
47 root 1.433 use Coro::AnyEvent;
48 root 1.105 use Coro::AIO;
49 root 1.437 use Coro::BDB 1.6;
50 root 1.237 use Coro::Storable;
51 root 1.332 use Coro::Util ();
52 root 1.96
53 root 1.398 use JSON::XS 2.01 ();
54 root 1.206 use BDB ();
55 root 1.154 use Data::Dumper;
56 root 1.105 use Fcntl;
57 root 1.484 use YAML::XS ();
58 root 1.433 use IO::AIO ();
59 root 1.32 use Time::HiRes;
60 root 1.208 use Compress::LZF;
61 root 1.302 use Digest::MD5 ();
62 root 1.208
63 root 1.433 AnyEvent::detect;
64    
65 root 1.227 # configure various modules to our taste
66     #
67 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
68 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
69 root 1.227
70 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
71 root 1.1
72 root 1.449 # make sure c-lzf reinitialises itself
73     Compress::LZF::set_serializer "Storable", "Storable::net_mstore", "Storable::mretrieve";
74     Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
75 root 1.448
76 root 1.474 # strictly for debugging
77     $SIG{QUIT} = sub { Carp::cluck "SIGQUIT" };
78    
79 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
80    
81 root 1.540 our @ORIG_INC;
82    
83 root 1.85 our %COMMAND = ();
84     our %COMMAND_TIME = ();
85 root 1.159
86     our @EXTS = (); # list of extension package names
87 root 1.85 our %EXTCMD = ();
88 root 1.287 our %EXTICMD = ();
89 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
90 root 1.161 our %EXT_MAP = (); # pluggable maps
91 root 1.85
92 root 1.451 our $RELOAD; # number of reloads so far, non-zero while in reload
93 root 1.1 our @EVENT;
94 root 1.479 our @REFLECT; # set by XS
95     our %REFLECT; # set by us
96 root 1.253
97 root 1.445 our $CONFDIR = confdir;
98     our $DATADIR = datadir;
99     our $LIBDIR = "$DATADIR/ext";
100     our $PODDIR = "$DATADIR/pod";
101     our $MAPDIR = "$DATADIR/" . mapdir;
102     our $LOCALDIR = localdir;
103     our $TMPDIR = "$LOCALDIR/" . tmpdir;
104     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
105     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
106     our $RANDOMDIR = "$LOCALDIR/random";
107     our $BDBDIR = "$LOCALDIR/db";
108     our $PIDFILE = "$LOCALDIR/pid";
109     our $RUNTIMEFILE = "$LOCALDIR/runtime";
110    
111 root 1.530 our %RESOURCE; # unused
112 root 1.1
113 root 1.527 our $OUTPUT_RATE_MIN = 3000;
114     our $OUTPUT_RATE_MAX = 1000000;
115    
116     our $MAX_LINKS = 32; # how many chained exits to follow
117 root 1.528 our $VERBOSE_IO = 1;
118 root 1.513
119 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
120 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
121 root 1.35 our $NEXT_TICK;
122 root 1.469 our $USE_FSYNC = 1; # use fsync to write maps - default on
123 root 1.35
124 root 1.371 our $BDB_DEADLOCK_WATCHER;
125 root 1.363 our $BDB_CHECKPOINT_WATCHER;
126     our $BDB_TRICKLE_WATCHER;
127 root 1.206 our $DB_ENV;
128    
129 root 1.543 our @EXTRA_MODULES = qw(pod match mapscript incloader);
130 root 1.466
131 root 1.70 our %CFG;
132    
133 root 1.84 our $UPTIME; $UPTIME ||= time;
134 root 1.103 our $RUNTIME;
135 root 1.399 our $NOW;
136 root 1.103
137 root 1.356 our (%PLAYER, %PLAYER_LOADING); # all users
138     our (%MAP, %MAP_LOADING ); # all maps
139 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
140 root 1.103
141 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
142     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
143    
144 root 1.265 our $LOAD; # a number between 0 (idle) and 1 (too many objects)
145     our $LOADAVG; # same thing, but with alpha-smoothing
146 root 1.412 our $JITTER; # average jitter
147     our $TICK_START; # for load detecting purposes
148 root 1.265
149 root 1.453 our @POST_INIT;
150    
151 root 1.474 our $REATTACH_ON_RELOAD; # set to true to force object reattach on reload (slow)
152     our $REALLY_UNLOOP; # never set to true, please :)
153 root 1.457
154 root 1.103 binmode STDOUT;
155     binmode STDERR;
156    
157     # read virtual server time, if available
158 root 1.445 unless ($RUNTIME || !-e $RUNTIMEFILE) {
159     open my $fh, "<", $RUNTIMEFILE
160     or die "unable to read $RUNTIMEFILE file: $!";
161 root 1.103 $RUNTIME = <$fh> + 0.;
162     }
163    
164 root 1.467 eval "sub TICK() { $TICK } 1" or die;
165    
166 root 1.253 mkdir $_
167     for $LOCALDIR, $TMPDIR, $UNIQUEDIR, $PLAYERDIR, $RANDOMDIR, $BDBDIR;
168 root 1.103
169 root 1.131 our $EMERGENCY_POSITION;
170 root 1.110
171 root 1.199 sub cf::map::normalise;
172    
173 root 1.522 sub in_main() {
174     $Coro::current == $Coro::main
175     }
176    
177 root 1.70 #############################################################################
178    
179 root 1.479 %REFLECT = ();
180     for (@REFLECT) {
181     my $reflect = JSON::XS::decode_json $_;
182     $REFLECT{$reflect->{class}} = $reflect;
183     }
184    
185 root 1.480 # this is decidedly evil
186 root 1.482 $REFLECT{object}{flags} = { map +($_ => undef), grep $_, map /^FLAG_([A-Z0-9_]+)$/ && lc $1, keys %{"cf::"} };
187 root 1.480
188 root 1.479 #############################################################################
189    
190 root 1.70 =head2 GLOBAL VARIABLES
191    
192     =over 4
193    
194 root 1.83 =item $cf::UPTIME
195    
196     The timestamp of the server start (so not actually an uptime).
197    
198 root 1.103 =item $cf::RUNTIME
199    
200     The time this server has run, starts at 0 and is increased by $cf::TICK on
201     every server tick.
202    
203 root 1.253 =item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR
204     $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR
205     $cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR
206    
207     Various directories - "/etc", read-only install directory, perl-library
208     directory, pod-directory, read-only maps directory, "/var", "/var/tmp",
209     unique-items directory, player file directory, random maps directory and
210     database environment.
211 root 1.70
212 root 1.103 =item $cf::NOW
213    
214     The time of the last (current) server tick.
215    
216 root 1.70 =item $cf::TICK
217    
218     The interval between server ticks, in seconds.
219    
220 root 1.265 =item $cf::LOADAVG
221    
222     The current CPU load on the server (alpha-smoothed), as a value between 0
223     (none) and 1 (overloaded), indicating how much time is spent on processing
224     objects per tick. Healthy values are < 0.5.
225    
226     =item $cf::LOAD
227    
228     The raw value load value from the last tick.
229    
230 root 1.70 =item %cf::CFG
231    
232 root 1.395 Configuration for the server, loaded from C</etc/deliantra-server/config>, or
233 root 1.70 from wherever your confdir points to.
234    
235 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
236 root 1.155
237 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
238 root 1.546 returns directly I<after> the tick processing (and consequently, can only wake one thread
239 root 1.239 per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
240 root 1.155
241 root 1.546 =cut
242    
243     sub wait_for_tick();
244     sub wait_for_tick_begin();
245    
246 elmex 1.310 =item @cf::INVOKE_RESULTS
247    
248 root 1.488 This array contains the results of the last C<invoke ()> call. When
249 elmex 1.310 C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
250     that call.
251    
252 root 1.479 =item %cf::REFLECT
253    
254     Contains, for each (C++) class name, a hash reference with information
255 root 1.480 about object members (methods, scalars, arrays and flags) and other
256     metadata, which is useful for introspection.
257 root 1.479
258 root 1.70 =back
259    
260     =cut
261    
262 root 1.532 sub error(@) { LOG llevError, join "", @_ }
263     sub warn (@) { LOG llevWarn , join "", @_ }
264     sub info (@) { LOG llevInfo , join "", @_ }
265     sub debug(@) { LOG llevDebug, join "", @_ }
266     sub trace(@) { LOG llevTrace, join "", @_ }
267    
268 root 1.477 $Coro::State::WARNHOOK = sub {
269     my $msg = join "", @_;
270 root 1.103
271 root 1.477 $msg .= "\n"
272     unless $msg =~ /\n$/;
273 root 1.1
274 root 1.477 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
275 root 1.250
276 root 1.532 LOG llevWarn, $msg;
277 root 1.477 };
278 root 1.1
279 root 1.407 $Coro::State::DIEHOOK = sub {
280 root 1.410 return unless $^S eq 0; # "eq", not "=="
281    
282 root 1.532 error Carp::longmess $_[0];
283 root 1.477
284 root 1.522 if (in_main) {#d#
285 root 1.532 error "DIEHOOK called in main context, Coro bug?\n";#d#
286 root 1.410 return;#d#
287     }#d#
288    
289     # kill coroutine otherwise
290     Coro::terminate
291 root 1.407 };
292    
293 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
294     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
295     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
296     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
297     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
298 root 1.273 @safe::cf::arch::ISA = @cf::arch::ISA = 'cf::object';
299     @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; # not really true (yet)
300 root 1.25
301 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
302 root 1.25 # within the Safe compartment.
303 root 1.86 for my $pkg (qw(
304 root 1.100 cf::global cf::attachable
305 root 1.86 cf::object cf::object::player
306 root 1.89 cf::client cf::player
307 root 1.86 cf::arch cf::living
308 root 1.440 cf::map cf::mapspace
309     cf::party cf::region
310 root 1.86 )) {
311 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
312 root 1.25 }
313 root 1.1
314 root 1.396 $EV::DIED = sub {
315 root 1.493 Carp::cluck "error in event callback: @_";
316 root 1.18 };
317    
318 root 1.281 #############################################################################
319    
320 root 1.70 =head2 UTILITY FUNCTIONS
321    
322     =over 4
323    
324 root 1.154 =item dumpval $ref
325    
326 root 1.70 =cut
327 root 1.44
328 root 1.154 sub dumpval {
329     eval {
330     local $SIG{__DIE__};
331     my $d;
332     if (1) {
333     $d = new Data::Dumper([$_[0]], ["*var"]);
334     $d->Terse(1);
335     $d->Indent(2);
336     $d->Quotekeys(0);
337     $d->Useqq(1);
338     #$d->Bless(...);
339     $d->Seen($_[1]) if @_ > 1;
340     $d = $d->Dump();
341     }
342     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
343     $d
344     } || "[unable to dump $_[0]: '$@']";
345     }
346    
347 root 1.398 =item $ref = cf::decode_json $json
348 root 1.70
349     Converts a JSON string into the corresponding perl data structure.
350    
351 root 1.398 =item $json = cf::encode_json $ref
352 root 1.70
353     Converts a perl data structure into its JSON representation.
354    
355 root 1.287 =cut
356    
357 root 1.290 our $json_coder = JSON::XS->new->utf8->max_size (1e6); # accept ~1mb max
358 root 1.287
359 root 1.398 sub encode_json($) { $json_coder->encode ($_[0]) }
360     sub decode_json($) { $json_coder->decode ($_[0]) }
361 root 1.287
362 root 1.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.554 $rmp,
1869     $rmp->{wall_name}, $rmp->{monsterstyle},
1870     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1871     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{this_map},
1872 root 1.110 $rmp->{exit_on_final_map},
1873     $rmp->{xsize}, $rmp->{ysize},
1874     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1875     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1876     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1877     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1878     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1879 root 1.554 (cf::region::find $rmp->{region})
1880 root 1.162 )
1881 root 1.110 }
1882    
1883 root 1.187 =item cf::map->register ($regex, $prio)
1884    
1885     Register a handler for the map path matching the given regex at the
1886     givne priority (higher is better, built-in handlers have priority 0, the
1887     default).
1888    
1889     =cut
1890    
1891 root 1.166 sub register {
1892 root 1.187 my (undef, $regex, $prio) = @_;
1893 root 1.166 my $pkg = caller;
1894    
1895     push @{"$pkg\::ISA"}, __PACKAGE__;
1896    
1897 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1898 root 1.166 }
1899    
1900     # also paths starting with '/'
1901 root 1.524 $EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1902 root 1.166
1903 root 1.170 sub thawer_merge {
1904 root 1.172 my ($self, $merge) = @_;
1905    
1906 root 1.170 # we have to keep some variables in memory intact
1907 root 1.172 local $self->{path};
1908     local $self->{load_path};
1909 root 1.170
1910 root 1.172 $self->SUPER::thawer_merge ($merge);
1911 root 1.170 }
1912    
1913 root 1.166 sub normalise {
1914     my ($path, $base) = @_;
1915    
1916 root 1.543 $path = "$path"; # make sure it's a string
1917 root 1.192
1918 root 1.199 $path =~ s/\.map$//;
1919    
1920 root 1.166 # map plan:
1921     #
1922     # /! non-realised random map exit (special hack!)
1923     # {... are special paths that are not being touched
1924     # ?xxx/... are special absolute paths
1925     # ?random/... random maps
1926     # /... normal maps
1927     # ~user/... per-player map of a specific user
1928    
1929     $path =~ s/$PATH_SEP/\//go;
1930    
1931     # treat it as relative path if it starts with
1932     # something that looks reasonable
1933     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1934     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1935    
1936     $base =~ s{[^/]+/?$}{};
1937     $path = "$base/$path";
1938     }
1939    
1940     for ($path) {
1941     redo if s{/\.?/}{/};
1942     redo if s{/[^/]+/\.\./}{/};
1943     }
1944    
1945     $path
1946     }
1947    
1948     sub new_from_path {
1949     my (undef, $path, $base) = @_;
1950    
1951     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1952    
1953     $path = normalise $path, $base;
1954    
1955 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1956     if ($path =~ $EXT_MAP{$pkg}[1]) {
1957 root 1.166 my $self = bless cf::map::new, $pkg;
1958     $self->{path} = $path; $self->path ($path);
1959     $self->init; # pass $1 etc.
1960     return $self;
1961     }
1962     }
1963    
1964 root 1.543 Carp::cluck "unable to resolve path '$path' (base '$base')";
1965 root 1.166 ()
1966     }
1967    
1968     sub init {
1969     my ($self) = @_;
1970    
1971     $self
1972     }
1973    
1974     sub as_string {
1975     my ($self) = @_;
1976    
1977     "$self->{path}"
1978     }
1979    
1980     # the displayed name, this is a one way mapping
1981     sub visible_name {
1982     &as_string
1983     }
1984    
1985     # the original (read-only) location
1986     sub load_path {
1987     my ($self) = @_;
1988    
1989 root 1.254 "$MAPDIR/$self->{path}.map"
1990 root 1.166 }
1991    
1992     # the temporary/swap location
1993     sub save_path {
1994     my ($self) = @_;
1995    
1996 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1997 root 1.254 "$TMPDIR/$path.map"
1998 root 1.166 }
1999    
2000     # the unique path, undef == no special unique path
2001     sub uniq_path {
2002     my ($self) = @_;
2003    
2004 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
2005 root 1.253 "$UNIQUEDIR/$path"
2006 root 1.166 }
2007    
2008 root 1.275 sub decay_objects {
2009     my ($self) = @_;
2010    
2011     return if $self->{deny_reset};
2012    
2013     $self->do_decay_objects;
2014     }
2015    
2016 root 1.166 sub unlink_save {
2017     my ($self) = @_;
2018    
2019     utf8::encode (my $save = $self->save_path);
2020 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
2021     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
2022 root 1.166 }
2023    
2024     sub load_header_from($) {
2025     my ($self, $path) = @_;
2026 root 1.110
2027     utf8::encode $path;
2028 root 1.356 my $f = new_from_file cf::object::thawer $path
2029     or return;
2030 root 1.110
2031 root 1.356 $self->_load_header ($f)
2032 root 1.110 or return;
2033    
2034 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
2035     $f->resolve_delayed_derefs;
2036    
2037 root 1.166 $self->{load_path} = $path;
2038 root 1.135
2039 root 1.166 1
2040     }
2041 root 1.110
2042 root 1.188 sub load_header_orig {
2043 root 1.166 my ($self) = @_;
2044 root 1.110
2045 root 1.166 $self->load_header_from ($self->load_path)
2046 root 1.110 }
2047    
2048 root 1.188 sub load_header_temp {
2049 root 1.166 my ($self) = @_;
2050 root 1.110
2051 root 1.166 $self->load_header_from ($self->save_path)
2052     }
2053 root 1.110
2054 root 1.188 sub prepare_temp {
2055     my ($self) = @_;
2056    
2057     $self->last_access ((delete $self->{last_access})
2058     || $cf::RUNTIME); #d#
2059     # safety
2060     $self->{instantiate_time} = $cf::RUNTIME
2061     if $self->{instantiate_time} > $cf::RUNTIME;
2062     }
2063    
2064     sub prepare_orig {
2065     my ($self) = @_;
2066    
2067     $self->{load_original} = 1;
2068     $self->{instantiate_time} = $cf::RUNTIME;
2069     $self->last_access ($cf::RUNTIME);
2070     $self->instantiate;
2071     }
2072    
2073 root 1.166 sub load_header {
2074     my ($self) = @_;
2075 root 1.110
2076 root 1.188 if ($self->load_header_temp) {
2077     $self->prepare_temp;
2078 root 1.166 } else {
2079 root 1.188 $self->load_header_orig
2080 root 1.166 or return;
2081 root 1.188 $self->prepare_orig;
2082 root 1.166 }
2083 root 1.120
2084 root 1.275 $self->{deny_reset} = 1
2085     if $self->no_reset;
2086    
2087 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
2088     unless $self->default_region;
2089    
2090 root 1.166 1
2091     }
2092 root 1.110
2093 root 1.166 sub find;
2094     sub find {
2095     my ($path, $origin) = @_;
2096 root 1.134
2097 root 1.543 cf::cede_to_tick;
2098    
2099 root 1.166 $path = normalise $path, $origin && $origin->path;
2100 root 1.110
2101 root 1.459 my $guard1 = cf::lock_acquire "map_data:$path";#d#remove
2102     my $guard2 = cf::lock_acquire "map_find:$path";
2103 root 1.110
2104 root 1.166 $cf::MAP{$path} || do {
2105     my $map = new_from_path cf::map $path
2106     or return;
2107 root 1.110
2108 root 1.116 $map->{last_save} = $cf::RUNTIME;
2109 root 1.110
2110 root 1.166 $map->load_header
2111     or return;
2112 root 1.134
2113 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2114 root 1.185 # doing this can freeze the server in a sync job, obviously
2115     #$cf::WAIT_FOR_TICK->wait;
2116 root 1.429 undef $guard2;
2117 root 1.358 undef $guard1;
2118 root 1.112 $map->reset;
2119 root 1.192 return find $path;
2120 root 1.112 }
2121 root 1.110
2122 root 1.166 $cf::MAP{$path} = $map
2123 root 1.110 }
2124     }
2125    
2126 root 1.500 sub pre_load { }
2127     #sub post_load { } # XS
2128 root 1.188
2129 root 1.110 sub load {
2130     my ($self) = @_;
2131    
2132 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2133    
2134 root 1.120 my $path = $self->{path};
2135    
2136 root 1.256 {
2137 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2138 root 1.256
2139 root 1.357 return unless $self->valid;
2140 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2141 root 1.110
2142 root 1.256 $self->in_memory (cf::MAP_LOADING);
2143 root 1.110
2144 root 1.256 $self->alloc;
2145 root 1.188
2146 root 1.256 $self->pre_load;
2147 root 1.346 cf::cede_to_tick;
2148 root 1.188
2149 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2150     $f->skip_block;
2151     $self->_load_objects ($f)
2152 root 1.256 or return;
2153 root 1.110
2154 root 1.439 $self->post_load_original
2155 root 1.256 if delete $self->{load_original};
2156 root 1.111
2157 root 1.256 if (my $uniq = $self->uniq_path) {
2158     utf8::encode $uniq;
2159 root 1.356 unless (aio_stat $uniq) {
2160     if (my $f = new_from_file cf::object::thawer $uniq) {
2161     $self->clear_unique_items;
2162     $self->_load_objects ($f);
2163     $f->resolve_delayed_derefs;
2164     }
2165 root 1.256 }
2166 root 1.110 }
2167    
2168 root 1.356 $f->resolve_delayed_derefs;
2169    
2170 root 1.346 cf::cede_to_tick;
2171 root 1.256 # now do the right thing for maps
2172     $self->link_multipart_objects;
2173 root 1.110 $self->difficulty ($self->estimate_difficulty)
2174     unless $self->difficulty;
2175 root 1.346 cf::cede_to_tick;
2176 root 1.256
2177     unless ($self->{deny_activate}) {
2178     $self->decay_objects;
2179     $self->fix_auto_apply;
2180     $self->update_buttons;
2181 root 1.346 cf::cede_to_tick;
2182 root 1.256 $self->activate;
2183     }
2184    
2185 root 1.325 $self->{last_save} = $cf::RUNTIME;
2186     $self->last_access ($cf::RUNTIME);
2187 root 1.324
2188 root 1.420 $self->in_memory (cf::MAP_ACTIVE);
2189 root 1.110 }
2190    
2191 root 1.188 $self->post_load;
2192 root 1.553
2193     1
2194 root 1.166 }
2195    
2196 root 1.507 # customize the map for a given player, i.e.
2197     # return the _real_ map. used by e.g. per-player
2198     # maps to change the path to ~playername/mappath
2199 root 1.166 sub customise_for {
2200     my ($self, $ob) = @_;
2201    
2202     return find "~" . $ob->name . "/" . $self->{path}
2203     if $self->per_player;
2204 root 1.134
2205 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2206     # if $self->per_party;
2207    
2208 root 1.166 $self
2209 root 1.110 }
2210    
2211 root 1.157 # find and load all maps in the 3x3 area around a map
2212 root 1.333 sub load_neighbours {
2213 root 1.157 my ($map) = @_;
2214    
2215 root 1.333 my @neigh; # diagonal neighbours
2216 root 1.157
2217     for (0 .. 3) {
2218     my $neigh = $map->tile_path ($_)
2219     or next;
2220     $neigh = find $neigh, $map
2221     or next;
2222     $neigh->load;
2223    
2224 root 1.527 # now find the diagonal neighbours
2225 root 1.333 push @neigh,
2226     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2227     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2228 root 1.157 }
2229    
2230 root 1.333 for (grep defined $_->[0], @neigh) {
2231     my ($path, $origin) = @$_;
2232     my $neigh = find $path, $origin
2233 root 1.157 or next;
2234     $neigh->load;
2235     }
2236     }
2237    
2238 root 1.133 sub find_sync {
2239 root 1.110 my ($path, $origin) = @_;
2240    
2241 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_find_sync"
2242     if $Coro::current == $Coro::main;
2243    
2244     find $path, $origin
2245 root 1.133 }
2246    
2247     sub do_load_sync {
2248     my ($map) = @_;
2249 root 1.110
2250 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_load_sync"
2251 root 1.342 if $Coro::current == $Coro::main;
2252 root 1.339
2253 root 1.534 $map->load;
2254 root 1.110 }
2255    
2256 root 1.157 our %MAP_PREFETCH;
2257 root 1.183 our $MAP_PREFETCHER = undef;
2258 root 1.157
2259     sub find_async {
2260 root 1.339 my ($path, $origin, $load) = @_;
2261 root 1.157
2262 root 1.166 $path = normalise $path, $origin && $origin->{path};
2263 root 1.157
2264 root 1.166 if (my $map = $cf::MAP{$path}) {
2265 root 1.420 return $map if !$load || $map->in_memory == cf::MAP_ACTIVE;
2266 root 1.157 }
2267    
2268 root 1.339 $MAP_PREFETCH{$path} |= $load;
2269    
2270 root 1.183 $MAP_PREFETCHER ||= cf::async {
2271 root 1.374 $Coro::current->{desc} = "map prefetcher";
2272    
2273 root 1.183 while (%MAP_PREFETCH) {
2274 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2275     if (my $map = find $k) {
2276     $map->load if $v;
2277 root 1.308 }
2278 root 1.183
2279 root 1.339 delete $MAP_PREFETCH{$k};
2280 root 1.183 }
2281     }
2282     undef $MAP_PREFETCHER;
2283     };
2284 root 1.189 $MAP_PREFETCHER->prio (6);
2285 root 1.157
2286     ()
2287     }
2288    
2289 root 1.518 # common code, used by both ->save and ->swapout
2290     sub _save {
2291 root 1.110 my ($self) = @_;
2292    
2293     $self->{last_save} = $cf::RUNTIME;
2294    
2295     return unless $self->dirty;
2296    
2297 root 1.166 my $save = $self->save_path; utf8::encode $save;
2298     my $uniq = $self->uniq_path; utf8::encode $uniq;
2299 root 1.117
2300 root 1.110 $self->{load_path} = $save;
2301    
2302     return if $self->{deny_save};
2303    
2304 root 1.132 local $self->{last_access} = $self->last_access;#d#
2305    
2306 root 1.143 cf::async {
2307 root 1.374 $Coro::current->{desc} = "map player save";
2308 root 1.143 $_->contr->save for $self->players;
2309     };
2310    
2311 root 1.420 cf::get_slot 0.02;
2312    
2313 root 1.110 if ($uniq) {
2314 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2315     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2316 root 1.110 } else {
2317 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2318 root 1.110 }
2319     }
2320    
2321 root 1.518 sub save {
2322     my ($self) = @_;
2323    
2324     my $lock = cf::lock_acquire "map_data:$self->{path}";
2325    
2326     $self->_save;
2327     }
2328    
2329 root 1.110 sub swap_out {
2330     my ($self) = @_;
2331    
2332 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2333 root 1.137
2334 root 1.420 return if $self->in_memory != cf::MAP_ACTIVE;
2335 root 1.110 return if $self->{deny_save};
2336 root 1.518 return if $self->players;
2337 root 1.110
2338 root 1.518 # first deactivate the map and "unlink" it from the core
2339     $self->deactivate;
2340     $_->clear_links_to ($self) for values %cf::MAP;
2341 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2342    
2343 root 1.518 # then atomically save
2344     $self->_save;
2345    
2346     # then free the map
2347 root 1.110 $self->clear;
2348     }
2349    
2350 root 1.112 sub reset_at {
2351     my ($self) = @_;
2352 root 1.110
2353     # TODO: safety, remove and allow resettable per-player maps
2354 root 1.114 return 1e99 if $self->{deny_reset};
2355 root 1.110
2356 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2357 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2358 root 1.110
2359 root 1.112 $time + $to
2360     }
2361    
2362     sub should_reset {
2363     my ($self) = @_;
2364    
2365     $self->reset_at <= $cf::RUNTIME
2366 root 1.111 }
2367    
2368 root 1.110 sub reset {
2369     my ($self) = @_;
2370    
2371 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2372 root 1.137
2373 root 1.110 return if $self->players;
2374    
2375 root 1.532 cf::trace "resetting map ", $self->path, "\n";
2376 root 1.110
2377 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2378    
2379     # need to save uniques path
2380     unless ($self->{deny_save}) {
2381     my $uniq = $self->uniq_path; utf8::encode $uniq;
2382    
2383     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2384     if $uniq;
2385     }
2386    
2387 root 1.111 delete $cf::MAP{$self->path};
2388 root 1.110
2389 root 1.358 $self->deactivate;
2390 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2391 root 1.167 $self->clear;
2392    
2393 root 1.166 $self->unlink_save;
2394 root 1.111 $self->destroy;
2395 root 1.110 }
2396    
2397 root 1.114 my $nuke_counter = "aaaa";
2398    
2399     sub nuke {
2400     my ($self) = @_;
2401    
2402 root 1.349 {
2403     my $lock = cf::lock_acquire "map_data:$self->{path}";
2404    
2405     delete $cf::MAP{$self->path};
2406 root 1.174
2407 root 1.351 $self->unlink_save;
2408    
2409 root 1.524 bless $self, "cf::map::wrap";
2410 root 1.349 delete $self->{deny_reset};
2411     $self->{deny_save} = 1;
2412     $self->reset_timeout (1);
2413     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2414 root 1.174
2415 root 1.349 $cf::MAP{$self->path} = $self;
2416     }
2417 root 1.174
2418 root 1.114 $self->reset; # polite request, might not happen
2419     }
2420    
2421 root 1.276 =item $maps = cf::map::tmp_maps
2422    
2423     Returns an arrayref with all map paths of currently instantiated and saved
2424 root 1.277 maps. May block.
2425 root 1.276
2426     =cut
2427    
2428     sub tmp_maps() {
2429     [
2430     map {
2431     utf8::decode $_;
2432 root 1.277 /\.map$/
2433 root 1.276 ? normalise $_
2434     : ()
2435     } @{ aio_readdir $TMPDIR or [] }
2436     ]
2437     }
2438    
2439 root 1.277 =item $maps = cf::map::random_maps
2440    
2441     Returns an arrayref with all map paths of currently instantiated and saved
2442     random maps. May block.
2443    
2444     =cut
2445    
2446     sub random_maps() {
2447     [
2448     map {
2449     utf8::decode $_;
2450     /\.map$/
2451     ? normalise "?random/$_"
2452     : ()
2453     } @{ aio_readdir $RANDOMDIR or [] }
2454     ]
2455     }
2456    
2457 root 1.158 =item cf::map::unique_maps
2458    
2459 root 1.166 Returns an arrayref of paths of all shared maps that have
2460 root 1.158 instantiated unique items. May block.
2461    
2462     =cut
2463    
2464     sub unique_maps() {
2465 root 1.276 [
2466     map {
2467     utf8::decode $_;
2468 root 1.419 s/\.map$//; # TODO future compatibility hack
2469     /\.pst$/ || !/^$PATH_SEP/o # TODO unique maps apparebntly lack the .map suffix :/
2470     ? ()
2471     : normalise $_
2472 root 1.276 } @{ aio_readdir $UNIQUEDIR or [] }
2473     ]
2474 root 1.158 }
2475    
2476 root 1.489 =item cf::map::static_maps
2477    
2478     Returns an arrayref if paths of all static maps (all preinstalled F<.map>
2479 root 1.491 file in the shared directory excluding F</styles> and F</editor>). May
2480     block.
2481 root 1.489
2482     =cut
2483    
2484     sub static_maps() {
2485     my @dirs = "";
2486     my @maps;
2487    
2488     while (@dirs) {
2489     my $dir = shift @dirs;
2490    
2491 root 1.491 next if $dir eq "/styles" || $dir eq "/editor";
2492 root 1.490
2493 root 1.489 my ($dirs, $files) = Coro::AIO::aio_scandir "$MAPDIR$dir", 2
2494     or return;
2495    
2496     for (@$files) {
2497     s/\.map$// or next;
2498     utf8::decode $_;
2499     push @maps, "$dir/$_";
2500     }
2501    
2502     push @dirs, map "$dir/$_", @$dirs;
2503     }
2504    
2505     \@maps
2506     }
2507    
2508 root 1.155 =back
2509    
2510     =head3 cf::object
2511    
2512     =cut
2513    
2514     package cf::object;
2515    
2516     =over 4
2517    
2518     =item $ob->inv_recursive
2519 root 1.110
2520 root 1.419 Returns the inventory of the object I<and> their inventories, recursively,
2521     but I<not> the object itself.
2522 root 1.110
2523 root 1.155 =cut
2524 root 1.144
2525 root 1.155 sub inv_recursive_;
2526     sub inv_recursive_ {
2527     map { $_, inv_recursive_ $_->inv } @_
2528     }
2529 root 1.110
2530 root 1.155 sub inv_recursive {
2531     inv_recursive_ inv $_[0]
2532 root 1.110 }
2533    
2534 root 1.356 =item $ref = $ob->ref
2535    
2536 root 1.419 Creates and returns a persistent reference to an object that can be stored as a string.
2537 root 1.356
2538     =item $ob = cf::object::deref ($refstring)
2539    
2540     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2541     even if the object actually exists. May block.
2542    
2543     =cut
2544    
2545     sub deref {
2546     my ($ref) = @_;
2547    
2548 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2549 root 1.356 my ($uuid, $name) = ($1, $2);
2550     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2551     or return;
2552     $pl->ob->uuid eq $uuid
2553     or return;
2554    
2555     $pl->ob
2556     } else {
2557     warn "$ref: cannot resolve object reference\n";
2558     undef
2559     }
2560     }
2561    
2562 root 1.110 package cf;
2563    
2564     =back
2565    
2566 root 1.95 =head3 cf::object::player
2567    
2568     =over 4
2569    
2570 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2571 root 1.28
2572     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2573     can be C<undef>. Does the right thing when the player is currently in a
2574     dialogue with the given NPC character.
2575    
2576     =cut
2577    
2578 root 1.428 our $SAY_CHANNEL = {
2579     id => "say",
2580     title => "Map",
2581     reply => "say ",
2582 root 1.468 tooltip => "Things said to and replied from NPCs near you and other players on the same map only.",
2583 root 1.428 };
2584    
2585     our $CHAT_CHANNEL = {
2586     id => "chat",
2587     title => "Chat",
2588     reply => "chat ",
2589     tooltip => "Player chat and shouts, global to the server.",
2590     };
2591    
2592 root 1.22 # rough implementation of a future "reply" method that works
2593     # with dialog boxes.
2594 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2595 root 1.23 sub cf::object::player::reply($$$;$) {
2596     my ($self, $npc, $msg, $flags) = @_;
2597    
2598     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2599 root 1.22
2600 root 1.24 if ($self->{record_replies}) {
2601     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2602 elmex 1.282
2603 root 1.24 } else {
2604 elmex 1.282 my $pl = $self->contr;
2605    
2606     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2607 root 1.316 my $dialog = $pl->{npc_dialog};
2608     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2609 elmex 1.282
2610     } else {
2611     $msg = $npc->name . " says: $msg" if $npc;
2612 root 1.428 $self->send_msg ($SAY_CHANNEL => $msg, $flags);
2613 elmex 1.282 }
2614 root 1.24 }
2615 root 1.22 }
2616    
2617 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2618    
2619     =cut
2620    
2621     sub cf::object::send_msg {
2622     my $pl = shift->contr
2623     or return;
2624     $pl->send_msg (@_);
2625     }
2626    
2627 root 1.79 =item $player_object->may ("access")
2628    
2629     Returns wether the given player is authorized to access resource "access"
2630     (e.g. "command_wizcast").
2631    
2632     =cut
2633    
2634     sub cf::object::player::may {
2635     my ($self, $access) = @_;
2636    
2637     $self->flag (cf::FLAG_WIZ) ||
2638     (ref $cf::CFG{"may_$access"}
2639     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2640     : $cf::CFG{"may_$access"})
2641     }
2642 root 1.70
2643 root 1.115 =item $player_object->enter_link
2644    
2645     Freezes the player and moves him/her to a special map (C<{link}>).
2646    
2647 root 1.446 The player should be reasonably safe there for short amounts of time (e.g.
2648     for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2649 root 1.527 though, as the player cannot control the character while it is on the link
2650 root 1.446 map.
2651 root 1.115
2652 root 1.166 Will never block.
2653    
2654 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2655    
2656 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2657     map. If the map is not valid (or omitted), the player will be moved back
2658     to the location he/she was before the call to C<enter_link>, or, if that
2659     fails, to the emergency map position.
2660 root 1.115
2661     Might block.
2662    
2663     =cut
2664    
2665 root 1.166 sub link_map {
2666     unless ($LINK_MAP) {
2667     $LINK_MAP = cf::map::find "{link}"
2668 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2669 root 1.166 $LINK_MAP->load;
2670     }
2671    
2672     $LINK_MAP
2673     }
2674    
2675 root 1.110 sub cf::object::player::enter_link {
2676     my ($self) = @_;
2677    
2678 root 1.259 $self->deactivate_recursive;
2679 root 1.258
2680 root 1.527 ++$self->{_link_recursion};
2681    
2682 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2683 root 1.110
2684 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2685 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2686 root 1.110
2687 root 1.519 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2688 root 1.110 }
2689    
2690     sub cf::object::player::leave_link {
2691     my ($self, $map, $x, $y) = @_;
2692    
2693 root 1.270 return unless $self->contr->active;
2694    
2695 root 1.110 my $link_pos = delete $self->{_link_pos};
2696    
2697     unless ($map) {
2698     # restore original map position
2699     ($map, $x, $y) = @{ $link_pos || [] };
2700 root 1.133 $map = cf::map::find $map;
2701 root 1.110
2702     unless ($map) {
2703     ($map, $x, $y) = @$EMERGENCY_POSITION;
2704 root 1.133 $map = cf::map::find $map
2705 root 1.110 or die "FATAL: cannot load emergency map\n";
2706     }
2707     }
2708    
2709     ($x, $y) = (-1, -1)
2710     unless (defined $x) && (defined $y);
2711    
2712     # use -1 or undef as default coordinates, not 0, 0
2713     ($x, $y) = ($map->enter_x, $map->enter_y)
2714 root 1.492 if $x <= 0 && $y <= 0;
2715 root 1.110
2716     $map->load;
2717 root 1.333 $map->load_neighbours;
2718 root 1.110
2719 root 1.143 return unless $self->contr->active;
2720 root 1.215
2721     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2722 root 1.527 if ($self->enter_map ($map, $x, $y)) {
2723     # entering was successful
2724     delete $self->{_link_recursion};
2725     # only activate afterwards, to support waiting in hooks
2726     $self->activate_recursive;
2727     }
2728 root 1.476
2729 root 1.110 }
2730    
2731 root 1.527 =item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2732 root 1.268
2733     Moves the player to the given map-path and coordinates by first freezing
2734     her, loading and preparing them map, calling the provided $check callback
2735     that has to return the map if sucecssful, and then unfreezes the player on
2736 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2737     be called at the end of this process.
2738 root 1.110
2739 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2740     it needs a loaded map it has to call C<< ->load >>.
2741    
2742 root 1.110 =cut
2743    
2744 root 1.270 our $GOTOGEN;
2745    
2746 root 1.136 sub cf::object::player::goto {
2747 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2748 root 1.268
2749 root 1.527 if ($self->{_link_recursion} >= $MAX_LINKS) {
2750 root 1.532 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2751 root 1.527 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2752     ($path, $x, $y) = @$EMERGENCY_POSITION;
2753     }
2754    
2755 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2756     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2757    
2758 root 1.110 $self->enter_link;
2759    
2760 root 1.140 (async {
2761 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2762    
2763 root 1.365 # *tag paths override both path and x|y
2764     if ($path =~ /^\*(.*)$/) {
2765     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2766     my $ob = $obs[rand @obs];
2767 root 1.366
2768 root 1.367 # see if we actually can go there
2769 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2770     $ob = $obs[rand @obs];
2771 root 1.369 } else {
2772     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2773 root 1.368 }
2774 root 1.369 # else put us there anyways for now #d#
2775 root 1.366
2776 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2777 root 1.369 } else {
2778     ($path, $x, $y) = (undef, undef, undef);
2779 root 1.365 }
2780     }
2781    
2782 root 1.197 my $map = eval {
2783 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2784 root 1.268
2785     if ($map) {
2786     $map = $map->customise_for ($self);
2787 root 1.527 $map = $check->($map, $x, $y, $self) if $check && $map;
2788 root 1.268 } else {
2789 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2790 root 1.268 }
2791    
2792 root 1.197 $map
2793 root 1.268 };
2794    
2795     if ($@) {
2796     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2797     LOG llevError | logBacktrace, Carp::longmess $@;
2798     }
2799 root 1.115
2800 root 1.270 if ($gen == $self->{_goto_generation}) {
2801     delete $self->{_goto_generation};
2802     $self->leave_link ($map, $x, $y);
2803     }
2804 root 1.306
2805 root 1.527 $done->($self) if $done;
2806 root 1.110 })->prio (1);
2807     }
2808    
2809     =item $player_object->enter_exit ($exit_object)
2810    
2811     =cut
2812    
2813     sub parse_random_map_params {
2814     my ($spec) = @_;
2815    
2816     my $rmp = { # defaults
2817 root 1.181 xsize => (cf::rndm 15, 40),
2818     ysize => (cf::rndm 15, 40),
2819     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2820 root 1.182 #layout => string,
2821 root 1.110 };
2822    
2823     for (split /\n/, $spec) {
2824     my ($k, $v) = split /\s+/, $_, 2;
2825    
2826     $rmp->{lc $k} = $v if (length $k) && (length $v);
2827     }
2828    
2829     $rmp
2830     }
2831    
2832     sub prepare_random_map {
2833     my ($exit) = @_;
2834    
2835     # all this does is basically replace the /! path by
2836     # a new random map path (?random/...) with a seed
2837     # that depends on the exit object
2838    
2839     my $rmp = parse_random_map_params $exit->msg;
2840    
2841     if ($exit->map) {
2842 root 1.198 $rmp->{region} = $exit->region->name;
2843 root 1.110 $rmp->{origin_map} = $exit->map->path;
2844     $rmp->{origin_x} = $exit->x;
2845     $rmp->{origin_y} = $exit->y;
2846 root 1.430
2847     $exit->map->touch;
2848 root 1.110 }
2849    
2850     $rmp->{random_seed} ||= $exit->random_seed;
2851    
2852 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2853 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2854 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2855 root 1.110
2856 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2857 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2858 root 1.177 undef $fh;
2859     aio_rename "$meta~", $meta;
2860 root 1.110
2861 root 1.430 my $slaying = "?random/$md5";
2862    
2863     if ($exit->valid) {
2864     $exit->slaying ("?random/$md5");
2865     $exit->msg (undef);
2866     }
2867 root 1.110 }
2868     }
2869    
2870     sub cf::object::player::enter_exit {
2871     my ($self, $exit) = @_;
2872    
2873     return unless $self->type == cf::PLAYER;
2874    
2875 root 1.430 $self->enter_link;
2876    
2877     (async {
2878     $Coro::current->{desc} = "enter_exit";
2879    
2880     unless (eval {
2881     $self->deactivate_recursive; # just to be sure
2882 root 1.195
2883 root 1.430 # random map handling
2884     {
2885     my $guard = cf::lock_acquire "exit_prepare:$exit";
2886 root 1.195
2887 root 1.430 prepare_random_map $exit
2888     if $exit->slaying eq "/!";
2889     }
2890 root 1.110
2891 root 1.430 my $map = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2892     my $x = $exit->stats->hp;
2893     my $y = $exit->stats->sp;
2894 root 1.296
2895 root 1.430 $self->goto ($map, $x, $y);
2896 root 1.374
2897 root 1.430 # if exit is damned, update players death & WoR home-position
2898     $self->contr->savebed ($map, $x, $y)
2899     if $exit->flag (cf::FLAG_DAMNED);
2900 root 1.110
2901 root 1.430 1
2902 root 1.110 }) {
2903 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2904 root 1.233 . "I'll try to bring you back to the map you were before. "
2905     . "Please report this to the dungeon master!",
2906     cf::NDI_UNIQUE | cf::NDI_RED);
2907 root 1.110
2908 root 1.532 error "ERROR in enter_exit: $@";
2909 root 1.110 $self->leave_link;
2910     }
2911     })->prio (1);
2912     }
2913    
2914 root 1.95 =head3 cf::client
2915    
2916     =over 4
2917    
2918     =item $client->send_drawinfo ($text, $flags)
2919    
2920     Sends a drawinfo packet to the client. Circumvents output buffering so
2921     should not be used under normal circumstances.
2922    
2923 root 1.70 =cut
2924    
2925 root 1.95 sub cf::client::send_drawinfo {
2926     my ($self, $text, $flags) = @_;
2927    
2928     utf8::encode $text;
2929 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2930 root 1.95 }
2931    
2932 root 1.494 =item $client->send_big_packet ($pkt)
2933    
2934     Like C<send_packet>, but tries to compress large packets, and fragments
2935     them as required.
2936    
2937     =cut
2938    
2939     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2940    
2941     sub cf::client::send_big_packet {
2942     my ($self, $pkt) = @_;
2943    
2944     # try lzf for large packets
2945     $pkt = "lzf " . Compress::LZF::compress $pkt
2946     if 1024 <= length $pkt and $self->{can_lzf};
2947    
2948     # split very large packets
2949     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2950     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2951     $pkt = "frag";
2952     }
2953    
2954     $self->send_packet ($pkt);
2955     }
2956    
2957 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2958 root 1.283
2959     Send a drawinfo or msg packet to the client, formatting the msg for the
2960     client if neccessary. C<$type> should be a string identifying the type of
2961     the message, with C<log> being the default. If C<$color> is negative, suppress
2962     the message unless the client supports the msg packet.
2963    
2964     =cut
2965    
2966 root 1.391 # non-persistent channels (usually the info channel)
2967 root 1.350 our %CHANNEL = (
2968 root 1.486 "c/motd" => {
2969     id => "infobox",
2970     title => "MOTD",
2971     reply => undef,
2972     tooltip => "The message of the day",
2973     },
2974 root 1.350 "c/identify" => {
2975 root 1.375 id => "infobox",
2976 root 1.350 title => "Identify",
2977     reply => undef,
2978     tooltip => "Items recently identified",
2979     },
2980 root 1.352 "c/examine" => {
2981 root 1.375 id => "infobox",
2982 root 1.352 title => "Examine",
2983     reply => undef,
2984     tooltip => "Signs and other items you examined",
2985     },
2986 root 1.487 "c/shopinfo" => {
2987     id => "infobox",
2988     title => "Shop Info",
2989     reply => undef,
2990     tooltip => "What your bargaining skill tells you about the shop",
2991     },
2992 root 1.389 "c/book" => {
2993     id => "infobox",
2994     title => "Book",
2995     reply => undef,
2996     tooltip => "The contents of a note or book",
2997     },
2998 root 1.375 "c/lookat" => {
2999     id => "infobox",
3000     title => "Look",
3001     reply => undef,
3002     tooltip => "What you saw there",
3003     },
3004 root 1.390 "c/who" => {
3005     id => "infobox",
3006     title => "Players",
3007     reply => undef,
3008     tooltip => "Shows players who are currently online",
3009     },
3010     "c/body" => {
3011     id => "infobox",
3012     title => "Body Parts",
3013     reply => undef,
3014     tooltip => "Shows which body parts you posess and are available",
3015     },
3016 root 1.465 "c/statistics" => {
3017     id => "infobox",
3018     title => "Statistics",
3019     reply => undef,
3020     tooltip => "Shows your primary statistics",
3021     },
3022 root 1.450 "c/skills" => {
3023     id => "infobox",
3024     title => "Skills",
3025     reply => undef,
3026     tooltip => "Shows your experience per skill and item power",
3027     },
3028 root 1.470 "c/shopitems" => {
3029     id => "infobox",
3030     title => "Shop Items",
3031     reply => undef,
3032     tooltip => "Shows the items currently for sale in this shop",
3033     },
3034 root 1.465 "c/resistances" => {
3035     id => "infobox",
3036     title => "Resistances",
3037     reply => undef,
3038     tooltip => "Shows your resistances",
3039     },
3040     "c/pets" => {
3041     id => "infobox",
3042     title => "Pets",
3043     reply => undef,
3044     tooltip => "Shows information abotu your pets/a specific pet",
3045     },
3046 root 1.471 "c/perceiveself" => {
3047     id => "infobox",
3048     title => "Perceive Self",
3049     reply => undef,
3050     tooltip => "You gained detailed knowledge about yourself",
3051     },
3052 root 1.390 "c/uptime" => {
3053     id => "infobox",
3054     title => "Uptime",
3055     reply => undef,
3056 root 1.391 tooltip => "How long the server has been running since last restart",
3057 root 1.390 },
3058     "c/mapinfo" => {
3059     id => "infobox",
3060     title => "Map Info",
3061     reply => undef,
3062     tooltip => "Information related to the maps",
3063     },
3064 root 1.426 "c/party" => {
3065     id => "party",
3066     title => "Party",
3067     reply => "gsay ",
3068     tooltip => "Messages and chat related to your party",
3069     },
3070 root 1.464 "c/death" => {
3071     id => "death",
3072     title => "Death",
3073     reply => undef,
3074     tooltip => "Reason for and more info about your most recent death",
3075     },
3076 root 1.462 "c/say" => $SAY_CHANNEL,
3077     "c/chat" => $CHAT_CHANNEL,
3078 root 1.350 );
3079    
3080 root 1.283 sub cf::client::send_msg {
3081 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
3082 root 1.283
3083 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
3084     unless $color & cf::NDI_VERBATIM;
3085 root 1.283
3086 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
3087 root 1.311
3088 root 1.350 # check predefined channels, for the benefit of C
3089 root 1.375 if ($CHANNEL{$channel}) {
3090     $channel = $CHANNEL{$channel};
3091    
3092 root 1.463 $self->ext_msg (channel_info => $channel);
3093 root 1.375 $channel = $channel->{id};
3094 root 1.350
3095 root 1.375 } elsif (ref $channel) {
3096 root 1.311 # send meta info to client, if not yet sent
3097     unless (exists $self->{channel}{$channel->{id}}) {
3098     $self->{channel}{$channel->{id}} = $channel;
3099 root 1.463 $self->ext_msg (channel_info => $channel);
3100 root 1.311 }
3101    
3102     $channel = $channel->{id};
3103     }
3104    
3105 root 1.313 return unless @extra || length $msg;
3106    
3107 root 1.463 # default colour, mask it out
3108     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3109     if $color & cf::NDI_DEF;
3110    
3111     my $pkt = "msg "
3112     . $self->{json_coder}->encode (
3113     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3114     );
3115    
3116 root 1.494 $self->send_big_packet ($pkt);
3117 root 1.283 }
3118    
3119 root 1.316 =item $client->ext_msg ($type, @msg)
3120 root 1.232
3121 root 1.287 Sends an ext event to the client.
3122 root 1.232
3123     =cut
3124    
3125 root 1.316 sub cf::client::ext_msg($$@) {
3126     my ($self, $type, @msg) = @_;
3127 root 1.232
3128 root 1.343 if ($self->extcmd == 2) {
3129 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3130 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3131 root 1.316 push @msg, msgtype => "event_$type";
3132 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3133 root 1.316 }
3134 root 1.232 }
3135 root 1.95
3136 root 1.336 =item $client->ext_reply ($msgid, @msg)
3137    
3138     Sends an ext reply to the client.
3139    
3140     =cut
3141    
3142     sub cf::client::ext_reply($$@) {
3143     my ($self, $id, @msg) = @_;
3144    
3145     if ($self->extcmd == 2) {
3146 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3147 root 1.343 } elsif ($self->extcmd == 1) {
3148 root 1.336 #TODO: version 1, remove
3149     unshift @msg, msgtype => "reply", msgid => $id;
3150 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3151 root 1.336 }
3152     }
3153    
3154 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3155    
3156     Queues a query to the client, calling the given callback with
3157     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3158     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3159    
3160 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3161     become reliable at some point in the future.
3162 root 1.95
3163     =cut
3164    
3165     sub cf::client::query {
3166     my ($self, $flags, $text, $cb) = @_;
3167    
3168     return unless $self->state == ST_PLAYING
3169     || $self->state == ST_SETUP
3170     || $self->state == ST_CUSTOM;
3171    
3172     $self->state (ST_CUSTOM);
3173    
3174     utf8::encode $text;
3175     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3176    
3177     $self->send_packet ($self->{query_queue}[0][0])
3178     if @{ $self->{query_queue} } == 1;
3179 root 1.287
3180     1
3181 root 1.95 }
3182    
3183     cf::client->attach (
3184 root 1.290 on_connect => sub {
3185     my ($ns) = @_;
3186    
3187     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3188     },
3189 root 1.95 on_reply => sub {
3190     my ($ns, $msg) = @_;
3191    
3192     # this weird shuffling is so that direct followup queries
3193     # get handled first
3194 root 1.128 my $queue = delete $ns->{query_queue}
3195 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3196 root 1.95
3197     (shift @$queue)->[1]->($msg);
3198 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3199 root 1.95
3200     push @{ $ns->{query_queue} }, @$queue;
3201    
3202     if (@{ $ns->{query_queue} } == @$queue) {
3203     if (@$queue) {
3204     $ns->send_packet ($ns->{query_queue}[0][0]);
3205     } else {
3206 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3207 root 1.95 }
3208     }
3209     },
3210 root 1.287 on_exticmd => sub {
3211     my ($ns, $buf) = @_;
3212    
3213 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3214 root 1.287
3215     if (ref $msg) {
3216 root 1.316 my ($type, $reply, @payload) =
3217     "ARRAY" eq ref $msg
3218     ? @$msg
3219     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3220    
3221 root 1.338 my @reply;
3222    
3223 root 1.316 if (my $cb = $EXTICMD{$type}) {
3224 root 1.338 @reply = $cb->($ns, @payload);
3225     }
3226    
3227     $ns->ext_reply ($reply, @reply)
3228     if $reply;
3229 root 1.316
3230 root 1.287 } else {
3231 root 1.532 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3232 root 1.287 }
3233    
3234     cf::override;
3235     },
3236 root 1.95 );
3237    
3238 root 1.140 =item $client->async (\&cb)
3239 root 1.96
3240     Create a new coroutine, running the specified callback. The coroutine will
3241     be automatically cancelled when the client gets destroyed (e.g. on logout,
3242     or loss of connection).
3243    
3244     =cut
3245    
3246 root 1.140 sub cf::client::async {
3247 root 1.96 my ($self, $cb) = @_;
3248    
3249 root 1.140 my $coro = &Coro::async ($cb);
3250 root 1.103
3251     $coro->on_destroy (sub {
3252 root 1.96 delete $self->{_coro}{$coro+0};
3253 root 1.103 });
3254 root 1.96
3255     $self->{_coro}{$coro+0} = $coro;
3256 root 1.103
3257     $coro
3258 root 1.96 }
3259    
3260     cf::client->attach (
3261 root 1.509 on_client_destroy => sub {
3262 root 1.96 my ($ns) = @_;
3263    
3264 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3265 root 1.96 },
3266     );
3267    
3268 root 1.95 =back
3269    
3270 root 1.70
3271     =head2 SAFE SCRIPTING
3272    
3273     Functions that provide a safe environment to compile and execute
3274     snippets of perl code without them endangering the safety of the server
3275     itself. Looping constructs, I/O operators and other built-in functionality
3276     is not available in the safe scripting environment, and the number of
3277 root 1.79 functions and methods that can be called is greatly reduced.
3278 root 1.70
3279     =cut
3280 root 1.23
3281 root 1.42 our $safe = new Safe "safe";
3282 root 1.23 our $safe_hole = new Safe::Hole;
3283    
3284     $SIG{FPE} = 'IGNORE';
3285    
3286 root 1.328 $safe->permit_only (Opcode::opset qw(
3287 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3288 root 1.328 grepstart grepwhile mapstart mapwhile
3289     sort time
3290     ));
3291 root 1.23
3292 root 1.25 # here we export the classes and methods available to script code
3293    
3294 root 1.70 =pod
3295    
3296 root 1.228 The following functions and methods are available within a safe environment:
3297 root 1.70
3298 root 1.297 cf::object
3299 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3300 root 1.425 insert remove name archname title slaying race decrease split
3301 root 1.466 value
3302 root 1.297
3303     cf::object::player
3304     player
3305    
3306     cf::player
3307     peaceful
3308    
3309     cf::map
3310     trigger
3311 root 1.70
3312     =cut
3313    
3314 root 1.25 for (
3315 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3316 elmex 1.431 insert remove inv nrof name archname title slaying race
3317 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3318 root 1.25 ["cf::object::player" => qw(player)],
3319 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3320 elmex 1.91 ["cf::map" => qw(trigger)],
3321 root 1.25 ) {
3322     my ($pkg, @funs) = @$_;
3323 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3324 root 1.25 for @funs;
3325     }
3326 root 1.23
3327 root 1.70 =over 4
3328    
3329     =item @retval = safe_eval $code, [var => value, ...]
3330    
3331     Compiled and executes the given perl code snippet. additional var/value
3332     pairs result in temporary local (my) scalar variables of the given name
3333     that are available in the code snippet. Example:
3334    
3335     my $five = safe_eval '$first + $second', first => 1, second => 4;
3336    
3337     =cut
3338    
3339 root 1.23 sub safe_eval($;@) {
3340     my ($code, %vars) = @_;
3341    
3342     my $qcode = $code;
3343     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3344     $qcode =~ s/\n/\\n/g;
3345    
3346 root 1.466 %vars = (_dummy => 0) unless %vars;
3347    
3348 root 1.499 my @res;
3349 root 1.23 local $_;
3350    
3351 root 1.42 my $eval =
3352 root 1.23 "do {\n"
3353     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3354     . "#line 0 \"{$qcode}\"\n"
3355     . $code
3356     . "\n}"
3357 root 1.25 ;
3358    
3359 root 1.499 if ($CFG{safe_eval}) {
3360     sub_generation_inc;
3361     local @safe::cf::_safe_eval_args = values %vars;
3362     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3363     sub_generation_inc;
3364     } else {
3365     local @cf::_safe_eval_args = values %vars;
3366     @res = wantarray ? eval eval : scalar eval $eval;
3367     }
3368 root 1.25
3369 root 1.42 if ($@) {
3370 root 1.532 warn "$@",
3371     "while executing safe code '$code'\n",
3372     "with arguments " . (join " ", %vars) . "\n";
3373 root 1.42 }
3374    
3375 root 1.25 wantarray ? @res : $res[0]
3376 root 1.23 }
3377    
3378 root 1.69 =item cf::register_script_function $function => $cb
3379    
3380     Register a function that can be called from within map/npc scripts. The
3381     function should be reasonably secure and should be put into a package name
3382     like the extension.
3383    
3384     Example: register a function that gets called whenever a map script calls
3385     C<rent::overview>, as used by the C<rent> extension.
3386    
3387     cf::register_script_function "rent::overview" => sub {
3388     ...
3389     };
3390    
3391     =cut
3392    
3393 root 1.23 sub register_script_function {
3394     my ($fun, $cb) = @_;
3395    
3396 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3397     *$fun = $safe_hole->wrap ($cb);
3398 root 1.23 }
3399    
3400 root 1.70 =back
3401    
3402 root 1.71 =cut
3403    
3404 root 1.23 #############################################################################
3405 root 1.203 # the server's init and main functions
3406    
3407 root 1.246 sub load_facedata($) {
3408     my ($path) = @_;
3409 root 1.223
3410 root 1.348 # HACK to clear player env face cache, we need some signal framework
3411     # for this (global event?)
3412     %ext::player_env::MUSIC_FACE_CACHE = ();
3413    
3414 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3415 root 1.334
3416 root 1.532 trace "loading facedata from $path\n";
3417 root 1.223
3418 root 1.546 0 < aio_load $path, my $facedata
3419 root 1.223 or die "$path: $!";
3420    
3421 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3422 root 1.223
3423 root 1.236 $facedata->{version} == 2
3424 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3425    
3426 root 1.334 # patch in the exptable
3427 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3428 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3429     type => FT_RSRC,
3430 root 1.500 data => $exp_table,
3431     hash => (Digest::MD5::md5 $exp_table),
3432 root 1.334 };
3433     cf::cede_to_tick;
3434    
3435 root 1.236 {
3436     my $faces = $facedata->{faceinfo};
3437    
3438     while (my ($face, $info) = each %$faces) {
3439     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3440 root 1.405
3441 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3442     cf::face::set_magicmap $idx, $info->{magicmap};
3443 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3444     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3445 root 1.302
3446     cf::cede_to_tick;
3447 root 1.236 }
3448    
3449     while (my ($face, $info) = each %$faces) {
3450     next unless $info->{smooth};
3451 root 1.405
3452 root 1.236 my $idx = cf::face::find $face
3453     or next;
3454 root 1.405
3455 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3456 root 1.302 cf::face::set_smooth $idx, $smooth;
3457     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3458 root 1.236 } else {
3459 root 1.532 error "smooth face '$info->{smooth}' not found for face '$face'";
3460 root 1.236 }
3461 root 1.302
3462     cf::cede_to_tick;
3463 root 1.236 }
3464 root 1.223 }
3465    
3466 root 1.236 {
3467     my $anims = $facedata->{animinfo};
3468    
3469     while (my ($anim, $info) = each %$anims) {
3470     cf::anim::set $anim, $info->{frames}, $info->{facings};
3471 root 1.302 cf::cede_to_tick;
3472 root 1.225 }
3473 root 1.236
3474     cf::anim::invalidate_all; # d'oh
3475 root 1.225 }
3476    
3477 root 1.302 {
3478     my $res = $facedata->{resource};
3479    
3480     while (my ($name, $info) = each %$res) {
3481 root 1.405 if (defined $info->{type}) {
3482     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3483    
3484 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3485 root 1.405 cf::face::set_type $idx, $info->{type};
3486 root 1.337 } else {
3487 root 1.530 $RESOURCE{$name} = $info; # unused
3488 root 1.307 }
3489 root 1.302
3490     cf::cede_to_tick;
3491     }
3492 root 1.406 }
3493    
3494     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3495 root 1.321
3496 root 1.406 1
3497     }
3498    
3499 root 1.318 register_exticmd fx_want => sub {
3500     my ($ns, $want) = @_;
3501    
3502     while (my ($k, $v) = each %$want) {
3503     $ns->fx_want ($k, $v);
3504     }
3505     };
3506    
3507 root 1.423 sub load_resource_file($) {
3508 root 1.424 my $guard = lock_acquire "load_resource_file";
3509    
3510 root 1.423 my $status = load_resource_file_ $_[0];
3511     get_slot 0.1, 100;
3512     cf::arch::commit_load;
3513 root 1.424
3514 root 1.423 $status
3515     }
3516    
3517 root 1.253 sub reload_regions {
3518 root 1.348 # HACK to clear player env face cache, we need some signal framework
3519     # for this (global event?)
3520     %ext::player_env::MUSIC_FACE_CACHE = ();
3521    
3522 root 1.253 load_resource_file "$MAPDIR/regions"
3523     or die "unable to load regions file\n";
3524 root 1.304
3525     for (cf::region::list) {
3526     $_->{match} = qr/$_->{match}/
3527     if exists $_->{match};
3528     }
3529 root 1.253 }
3530    
3531 root 1.246 sub reload_facedata {
3532 root 1.253 load_facedata "$DATADIR/facedata"
3533 root 1.246 or die "unable to load facedata\n";
3534     }
3535    
3536     sub reload_archetypes {
3537 root 1.253 load_resource_file "$DATADIR/archetypes"
3538 root 1.246 or die "unable to load archetypes\n";
3539 root 1.241 }
3540    
3541 root 1.246 sub reload_treasures {
3542 root 1.253 load_resource_file "$DATADIR/treasures"
3543 root 1.246 or die "unable to load treasurelists\n";
3544 root 1.241 }
3545    
3546 root 1.530 sub reload_sound {
3547 root 1.532 trace "loading sound config from $DATADIR/sound\n";
3548 root 1.531
3549 root 1.530 0 < Coro::AIO::aio_load "$DATADIR/sound", my $data
3550     or die "$DATADIR/sound $!";
3551    
3552     my $soundconf = JSON::XS->new->utf8->relaxed->decode ($data);
3553    
3554     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3555     my $sound = $soundconf->{compat}[$_]
3556     or next;
3557    
3558     my $face = cf::face::find "sound/$sound->[1]";
3559     cf::sound::set $sound->[0] => $face;
3560     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3561     }
3562    
3563     while (my ($k, $v) = each %{$soundconf->{event}}) {
3564     my $face = cf::face::find "sound/$v";
3565     cf::sound::set $k => $face;
3566     }
3567     }
3568    
3569 root 1.223 sub reload_resources {
3570 root 1.532 trace "reloading resource files...\n";
3571 root 1.245
3572 root 1.545 reload_exp_table;
3573     reload_materials;
3574 root 1.246 reload_facedata;
3575 root 1.530 reload_sound;
3576 root 1.246 reload_archetypes;
3577 root 1.423 reload_regions;
3578 root 1.246 reload_treasures;
3579 root 1.245
3580 root 1.532 trace "finished reloading resource files\n";
3581 root 1.223 }
3582    
3583 root 1.345 sub reload_config {
3584 root 1.532 trace "reloading config file...\n";
3585 root 1.485
3586 root 1.546 0 < aio_load "$CONFDIR/config", my $config
3587     or die "$CONFDIR/config: $!";
3588    
3589     utf8::decode $config;
3590 root 1.548 *CFG = yaml_load $config;
3591 root 1.131
3592 root 1.527 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3593 root 1.131
3594 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3595     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3596    
3597 root 1.131 if (exists $CFG{mlockall}) {
3598     eval {
3599 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3600 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3601     };
3602     warn $@ if $@;
3603     }
3604 root 1.72 }
3605    
3606 root 1.445 sub pidfile() {
3607     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3608     or die "$PIDFILE: $!";
3609     flock $fh, &Fcntl::LOCK_EX
3610     or die "$PIDFILE: flock: $!";
3611     $fh
3612     }
3613    
3614     # make sure only one server instance is running at any one time
3615     sub atomic {
3616     my $fh = pidfile;
3617    
3618     my $pid = <$fh>;
3619     kill 9, $pid if $pid > 0;
3620    
3621     seek $fh, 0, 0;
3622     print $fh $$;
3623     }
3624    
3625 root 1.474 sub main_loop {
3626 root 1.532 trace "EV::loop starting\n";
3627 root 1.474 if (1) {
3628     EV::loop;
3629     }
3630 root 1.532 trace "EV::loop returned\n";
3631 root 1.474 goto &main_loop unless $REALLY_UNLOOP;
3632     }
3633    
3634 root 1.39 sub main {
3635 root 1.453 cf::init_globals; # initialise logging
3636    
3637     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3638 root 1.540 LOG llevInfo, "Copyright (C) 2005-2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3639 root 1.453 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3640     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3641    
3642     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3643 root 1.445
3644 root 1.108 # we must not ever block the main coroutine
3645     local $Coro::idle = sub {
3646 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3647 root 1.175 (async {
3648 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3649 root 1.396 EV::loop EV::LOOP_ONESHOT;
3650 root 1.175 })->prio (Coro::PRIO_MAX);
3651 root 1.108 };
3652    
3653 root 1.453 evthread_start IO::AIO::poll_fileno;
3654    
3655     cf::sync_job {
3656 root 1.543 cf::incloader::init ();
3657 root 1.540
3658 root 1.515 cf::init_anim;
3659     cf::init_attackmess;
3660     cf::init_dynamic;
3661    
3662 root 1.495 cf::load_settings;
3663    
3664 root 1.453 reload_resources;
3665 root 1.423 reload_config;
3666     db_init;
3667 root 1.453
3668     cf::init_uuid;
3669     cf::init_signals;
3670     cf::init_skills;
3671    
3672     cf::init_beforeplay;
3673    
3674     atomic;
3675    
3676 root 1.423 load_extensions;
3677    
3678 root 1.453 utime time, time, $RUNTIMEFILE;
3679 root 1.183
3680 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3681 root 1.475 use POSIX ();
3682 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3683 root 1.445
3684 root 1.550 cf::_post_init 0;
3685 root 1.453 };
3686 root 1.445
3687 root 1.516 cf::object::thawer::errors_are_fatal 0;
3688 root 1.532 info "parse errors in files are no longer fatal from this point on.\n";
3689 root 1.516
3690 root 1.540 my $free_main; $free_main = EV::idle sub {
3691     undef $free_main;
3692     undef &main; # free gobs of memory :)
3693     };
3694    
3695     goto &main_loop;
3696 root 1.34 }
3697    
3698     #############################################################################
3699 root 1.155 # initialisation and cleanup
3700    
3701     # install some emergency cleanup handlers
3702     BEGIN {
3703 root 1.396 our %SIGWATCHER = ();
3704 root 1.155 for my $signal (qw(INT HUP TERM)) {
3705 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3706 root 1.396 cf::cleanup "SIG$signal";
3707     };
3708 root 1.155 }
3709     }
3710    
3711 root 1.417 sub write_runtime_sync {
3712 root 1.512 my $t0 = AE::time;
3713 root 1.506
3714 root 1.281 # first touch the runtime file to show we are still running:
3715     # the fsync below can take a very very long time.
3716    
3717 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3718 root 1.281
3719     my $guard = cf::lock_acquire "write_runtime";
3720    
3721 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3722 root 1.281 or return;
3723    
3724     my $value = $cf::RUNTIME + 90 + 10;
3725     # 10 is the runtime save interval, for a monotonic clock
3726     # 60 allows for the watchdog to kill the server.
3727    
3728     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3729     and return;
3730    
3731     # always fsync - this file is important
3732     aio_fsync $fh
3733     and return;
3734    
3735     # touch it again to show we are up-to-date
3736     aio_utime $fh, undef, undef;
3737    
3738     close $fh
3739     or return;
3740    
3741 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3742 root 1.281 and return;
3743    
3744 root 1.532 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3745 root 1.281
3746     1
3747     }
3748    
3749 root 1.416 our $uuid_lock;
3750     our $uuid_skip;
3751    
3752     sub write_uuid_sync($) {
3753     $uuid_skip ||= $_[0];
3754    
3755     return if $uuid_lock;
3756     local $uuid_lock = 1;
3757    
3758     my $uuid = "$LOCALDIR/uuid";
3759    
3760     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3761     or return;
3762    
3763 root 1.454 my $value = uuid_seq uuid_cur;
3764    
3765 root 1.455 unless ($value) {
3766 root 1.532 info "cowardly refusing to write zero uuid value!\n";
3767 root 1.454 return;
3768     }
3769    
3770     my $value = uuid_str $value + $uuid_skip;
3771 root 1.416 $uuid_skip = 0;
3772    
3773     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3774     and return;
3775    
3776     # always fsync - this file is important
3777     aio_fsync $fh
3778     and return;
3779    
3780     close $fh
3781     or return;
3782    
3783     aio_rename "$uuid~", $uuid
3784     and return;
3785    
3786 root 1.532 trace "uuid file written ($value).\n";
3787 root 1.416
3788     1
3789    
3790     }
3791    
3792     sub write_uuid($$) {
3793     my ($skip, $sync) = @_;
3794    
3795     $sync ? write_uuid_sync $skip
3796     : async { write_uuid_sync $skip };
3797     }
3798    
3799 root 1.156 sub emergency_save() {
3800 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3801    
3802 root 1.532 info "emergency_perl_save: enter\n";
3803 root 1.155
3804 root 1.534 # this is a trade-off: we want to be very quick here, so
3805     # save all maps without fsync, and later call a global sync
3806     # (which in turn might be very very slow)
3807     local $USE_FSYNC = 0;
3808    
3809 root 1.155 cf::sync_job {
3810 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3811    
3812 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3813     # refcount bugs in for. also avoids problems with players
3814 root 1.167 # and maps saved/destroyed asynchronously.
3815 root 1.532 info "emergency_perl_save: begin player save\n";
3816 root 1.155 for my $login (keys %cf::PLAYER) {
3817     my $pl = $cf::PLAYER{$login} or next;
3818     $pl->valid or next;
3819 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3820 root 1.155 $pl->save;
3821     }
3822 root 1.532 info "emergency_perl_save: end player save\n";
3823 root 1.155
3824 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3825    
3826 root 1.532 info "emergency_perl_save: begin map save\n";
3827 root 1.155 for my $path (keys %cf::MAP) {
3828     my $map = $cf::MAP{$path} or next;
3829     $map->valid or next;
3830     $map->save;
3831     }
3832 root 1.532 info "emergency_perl_save: end map save\n";
3833 root 1.208
3834 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3835    
3836 root 1.532 info "emergency_perl_save: begin database checkpoint\n";
3837 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3838 root 1.532 info "emergency_perl_save: end database checkpoint\n";
3839 root 1.416
3840 root 1.532 info "emergency_perl_save: begin write uuid\n";
3841 root 1.416 write_uuid_sync 1;
3842 root 1.532 info "emergency_perl_save: end write uuid\n";
3843 root 1.155
3844 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3845    
3846     trace "emergency_perl_save: syncing database to disk";
3847     BDB::db_env_txn_checkpoint $DB_ENV;
3848    
3849 root 1.536 info "emergency_perl_save: starting sync\n";
3850 root 1.535 IO::AIO::aio_sync sub {
3851 root 1.536 info "emergency_perl_save: finished sync\n";
3852 root 1.535 };
3853    
3854     cf::write_runtime_sync; # external watchdog should not bark
3855    
3856     trace "emergency_perl_save: flushing outstanding aio requests";
3857     while (IO::AIO::nreqs || BDB::nreqs) {
3858     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3859     }
3860    
3861     cf::write_runtime_sync; # external watchdog should not bark
3862 root 1.457 };
3863    
3864 root 1.532 info "emergency_perl_save: leave\n";
3865 root 1.155 }
3866 root 1.22
3867 root 1.211 sub post_cleanup {
3868     my ($make_core) = @_;
3869    
3870 root 1.535 IO::AIO::flush;
3871    
3872 root 1.532 error Carp::longmess "post_cleanup backtrace"
3873 root 1.211 if $make_core;
3874 root 1.445
3875     my $fh = pidfile;
3876     unlink $PIDFILE if <$fh> == $$;
3877 root 1.211 }
3878    
3879 root 1.441 # a safer delete_package, copied from Symbol
3880     sub clear_package($) {
3881     my $pkg = shift;
3882    
3883     # expand to full symbol table name if needed
3884     unless ($pkg =~ /^main::.*::$/) {
3885     $pkg = "main$pkg" if $pkg =~ /^::/;
3886     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3887     $pkg .= '::' unless $pkg =~ /::$/;
3888     }
3889    
3890     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3891     my $stem_symtab = *{$stem}{HASH};
3892    
3893     defined $stem_symtab and exists $stem_symtab->{$leaf}
3894     or return;
3895    
3896     # clear all symbols
3897     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3898     for my $name (keys %$leaf_symtab) {
3899     _gv_clear *{"$pkg$name"};
3900     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3901     }
3902     }
3903    
3904 root 1.246 sub do_reload_perl() {
3905 root 1.106 # can/must only be called in main
3906 root 1.543 unless (in_main) {
3907 root 1.532 error "can only reload from main coroutine";
3908 root 1.106 return;
3909     }
3910    
3911 root 1.441 return if $RELOAD++;
3912    
3913 root 1.512 my $t1 = AE::time;
3914 root 1.457
3915 root 1.441 while ($RELOAD) {
3916 root 1.543 cf::get_slot 0.1, -1, "reload_perl";
3917 root 1.550 info "perl_reload: reloading...";
3918 root 1.103
3919 root 1.550 trace "perl_reload: entering sync_job";
3920 root 1.212
3921 root 1.441 cf::sync_job {
3922 root 1.543 #cf::emergency_save;
3923 root 1.183
3924 root 1.550 trace "perl_reload: cancelling all extension coros";
3925 root 1.441 $_->cancel for values %EXT_CORO;
3926     %EXT_CORO = ();
3927 root 1.223
3928 root 1.550 trace "perl_reload: removing commands";
3929 root 1.441 %COMMAND = ();
3930 root 1.103
3931 root 1.550 trace "perl_reload: removing ext/exti commands";
3932 root 1.441 %EXTCMD = ();
3933     %EXTICMD = ();
3934 root 1.159
3935 root 1.550 trace "perl_reload: unloading/nuking all extensions";
3936 root 1.441 for my $pkg (@EXTS) {
3937 root 1.532 trace "... unloading $pkg";
3938 root 1.159
3939 root 1.441 if (my $cb = $pkg->can ("unload")) {
3940     eval {
3941     $cb->($pkg);
3942     1
3943 root 1.532 } or error "$pkg unloaded, but with errors: $@";
3944 root 1.441 }
3945 root 1.159
3946 root 1.532 trace "... clearing $pkg";
3947 root 1.441 clear_package $pkg;
3948 root 1.159 }
3949    
3950 root 1.550 trace "perl_reload: unloading all perl modules loaded from $LIBDIR";
3951 root 1.441 while (my ($k, $v) = each %INC) {
3952     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3953 root 1.65
3954 root 1.532 trace "... unloading $k";
3955 root 1.441 delete $INC{$k};
3956 root 1.65
3957 root 1.441 $k =~ s/\.pm$//;
3958     $k =~ s/\//::/g;
3959 root 1.65
3960 root 1.441 if (my $cb = $k->can ("unload_module")) {
3961     $cb->();
3962     }
3963 root 1.65
3964 root 1.441 clear_package $k;
3965 root 1.65 }
3966    
3967 root 1.550 trace "perl_reload: getting rid of safe::, as good as possible";
3968 root 1.441 clear_package "safe::$_"
3969     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3970 root 1.65
3971 root 1.550 trace "perl_reload: unloading cf.pm \"a bit\"";
3972 root 1.441 delete $INC{"cf.pm"};
3973 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3974 root 1.65
3975 root 1.441 # don't, removes xs symbols, too,
3976     # and global variables created in xs
3977     #clear_package __PACKAGE__;
3978 root 1.65
3979 root 1.550 info "perl_reload: unload completed, starting to reload now";
3980 root 1.65
3981 root 1.550 trace "perl_reload: reloading cf.pm";
3982 root 1.441 require cf;
3983 root 1.483 cf::_connect_to_perl_1;
3984 root 1.183
3985 root 1.550 trace "perl_reload: loading config and database again";
3986 root 1.441 cf::reload_config;
3987 root 1.100
3988 root 1.550 trace "perl_reload: loading extensions";
3989 root 1.441 cf::load_extensions;
3990 root 1.65
3991 root 1.457 if ($REATTACH_ON_RELOAD) {
3992 root 1.550 trace "perl_reload: reattaching attachments to objects/players";
3993 root 1.457 _global_reattach; # objects, sockets
3994 root 1.550 trace "perl_reload: reattaching attachments to maps";
3995 root 1.457 reattach $_ for values %MAP;
3996 root 1.550 trace "perl_reload: reattaching attachments to players";
3997 root 1.457 reattach $_ for values %PLAYER;
3998     }
3999 root 1.65
4000 root 1.550 cf::_post_init 1;
4001 root 1.453
4002 root 1.550 trace "perl_reload: leaving sync_job";
4003 root 1.183
4004 root 1.441 1
4005     } or do {
4006 root 1.532 error $@;
4007 root 1.550 cf::cleanup "perl_reload: error, exiting.";
4008 root 1.441 };
4009 root 1.183
4010 root 1.441 --$RELOAD;
4011     }
4012 root 1.457
4013 root 1.512 $t1 = AE::time - $t1;
4014 root 1.550 info "perl_reload: completed in ${t1}s\n";
4015 root 1.65 };
4016    
4017 root 1.175 our $RELOAD_WATCHER; # used only during reload
4018    
4019 root 1.246 sub reload_perl() {
4020     # doing reload synchronously and two reloads happen back-to-back,
4021     # coro crashes during coro_state_free->destroy here.
4022    
4023 root 1.457 $RELOAD_WATCHER ||= cf::async {
4024     Coro::AIO::aio_wait cache_extensions;
4025    
4026 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
4027 root 1.457 do_reload_perl;
4028     undef $RELOAD_WATCHER;
4029     };
4030 root 1.396 };
4031 root 1.246 }
4032    
4033 root 1.111 register_command "reload" => sub {
4034 root 1.65 my ($who, $arg) = @_;
4035    
4036     if ($who->flag (FLAG_WIZ)) {
4037 root 1.175 $who->message ("reloading server.");
4038 root 1.374 async {
4039     $Coro::current->{desc} = "perl_reload";
4040     reload_perl;
4041     };
4042 root 1.65 }
4043     };
4044    
4045 root 1.540 #############################################################################
4046 root 1.17
4047 root 1.183 my $bug_warning = 0;
4048    
4049 root 1.239 our @WAIT_FOR_TICK;
4050     our @WAIT_FOR_TICK_BEGIN;
4051    
4052 root 1.546 sub wait_for_tick() {
4053 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4054 root 1.241
4055 root 1.239 my $signal = new Coro::Signal;
4056     push @WAIT_FOR_TICK, $signal;
4057     $signal->wait;
4058     }
4059    
4060 root 1.546 sub wait_for_tick_begin() {
4061 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4062 root 1.241
4063 root 1.239 my $signal = new Coro::Signal;
4064     push @WAIT_FOR_TICK_BEGIN, $signal;
4065     $signal->wait;
4066     }
4067    
4068 root 1.412 sub tick {
4069 root 1.396 if ($Coro::current != $Coro::main) {
4070     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
4071     unless ++$bug_warning > 10;
4072     return;
4073     }
4074    
4075     cf::server_tick; # one server iteration
4076 root 1.245
4077 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
4078 root 1.502
4079 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
4080 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
4081 root 1.396 Coro::async_pool {
4082     $Coro::current->{desc} = "runtime saver";
4083 root 1.417 write_runtime_sync
4084 root 1.532 or error "ERROR: unable to write runtime file: $!";
4085 root 1.396 };
4086     }
4087 root 1.265
4088 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
4089     $sig->send;
4090     }
4091     while (my $sig = shift @WAIT_FOR_TICK) {
4092     $sig->send;
4093     }
4094 root 1.265
4095 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
4096 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
4097 root 1.265
4098 root 1.412 if (0) {
4099     if ($NEXT_TICK) {
4100     my $jitter = $TICK_START - $NEXT_TICK;
4101     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
4102 root 1.532 debug "jitter $JITTER\n";#d#
4103 root 1.412 }
4104     }
4105     }
4106 root 1.35
4107 root 1.206 {
4108 root 1.401 # configure BDB
4109    
4110 root 1.503 BDB::min_parallel 16;
4111 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
4112 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
4113 root 1.77
4114 root 1.206 unless ($DB_ENV) {
4115     $DB_ENV = BDB::db_env_create;
4116 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
4117     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
4118     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
4119 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
4120     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
4121 root 1.206
4122 root 1.534 cf::sync_job {
4123     eval {
4124     BDB::db_env_open
4125     $DB_ENV,
4126     $BDBDIR,
4127     BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4128     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4129     0666;
4130 root 1.208
4131 root 1.534 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4132     };
4133 root 1.533
4134 root 1.534 cf::cleanup "db_env_open(db): $@" if $@;
4135     };
4136 root 1.206 }
4137 root 1.363
4138 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4139     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4140     };
4141     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4142     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4143     };
4144     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4145     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4146     };
4147 root 1.206 }
4148    
4149     {
4150 root 1.401 # configure IO::AIO
4151    
4152 root 1.206 IO::AIO::min_parallel 8;
4153     IO::AIO::max_poll_time $TICK * 0.1;
4154 root 1.435 undef $AnyEvent::AIO::WATCHER;
4155 root 1.206 }
4156 root 1.108
4157 root 1.552 our $_log_backtrace;
4158     our $_log_backtrace_last;
4159 root 1.262
4160 root 1.260 sub _log_backtrace {
4161     my ($msg, @addr) = @_;
4162    
4163 root 1.552 $msg =~ s/\n$//;
4164 root 1.260
4165 root 1.552 if ($_log_backtrace_last eq $msg) {
4166     LOG llevInfo, "[ABT] $msg\n";
4167     LOG llevInfo, "[ABT] [duplicate, suppressed]\n";
4168 root 1.262 # limit the # of concurrent backtraces
4169 root 1.552 } elsif ($_log_backtrace < 2) {
4170     $_log_backtrace_last = $msg;
4171 root 1.262 ++$_log_backtrace;
4172 root 1.446 my $perl_bt = Carp::longmess $msg;
4173 root 1.262 async {
4174 root 1.374 $Coro::current->{desc} = "abt $msg";
4175    
4176 root 1.262 my @bt = fork_call {
4177     @addr = map { sprintf "%x", $_ } @addr;
4178     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4179     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4180     or die "addr2line: $!";
4181    
4182     my @funcs;
4183     my @res = <$fh>;
4184     chomp for @res;
4185     while (@res) {
4186     my ($func, $line) = splice @res, 0, 2, ();
4187     push @funcs, "[$func] $line";
4188     }
4189 root 1.260
4190 root 1.262 @funcs
4191     };
4192 root 1.260
4193 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4194     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4195 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4196     --$_log_backtrace;
4197     };
4198     } else {
4199 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4200 root 1.552 LOG llevInfo, "[ABT] [overload, suppressed]\n";
4201 root 1.262 }
4202 root 1.260 }
4203    
4204 root 1.249 # load additional modules
4205 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4206 root 1.483 cf::_connect_to_perl_2;
4207 root 1.249
4208 root 1.125 END { cf::emergency_save }
4209    
4210 root 1.1 1
4211