ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.449
Committed: Sat Sep 20 00:09:27 2008 UTC (15 years, 8 months ago) by root
Branch: MAIN
Changes since 1.448: +3 -16 lines
Log Message:
*** empty log message ***

File Contents

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