ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.553
Committed: Tue May 18 21:30:16 2010 UTC (14 years ago) by root
Branch: MAIN
CVS Tags: rel-3_0
Changes since 1.552: +2 -0 lines
Log Message:
shield follow against yuna resetting the map at the right time

File Contents

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