ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.450
Committed: Mon Sep 22 01:33:09 2008 UTC (15 years, 8 months ago) by root
Branch: MAIN
Changes since 1.449: +6 -0 lines
Log Message:
many minor text layout fixes

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 root 1.450 "c/skills" => {
2777     id => "infobox",
2778     title => "Skills",
2779     reply => undef,
2780     tooltip => "Shows your experience per skill and item power",
2781     },
2782 root 1.390 "c/uptime" => {
2783     id => "infobox",
2784     title => "Uptime",
2785     reply => undef,
2786 root 1.391 tooltip => "How long the server has been running since last restart",
2787 root 1.390 },
2788     "c/mapinfo" => {
2789     id => "infobox",
2790     title => "Map Info",
2791     reply => undef,
2792     tooltip => "Information related to the maps",
2793     },
2794 root 1.426 "c/party" => {
2795     id => "party",
2796     title => "Party",
2797     reply => "gsay ",
2798     tooltip => "Messages and chat related to your party",
2799     },
2800 root 1.350 );
2801    
2802 root 1.283 sub cf::client::send_msg {
2803 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2804 root 1.283
2805 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
2806     unless $color & cf::NDI_VERBATIM;
2807 root 1.283
2808 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2809 root 1.311
2810 root 1.350 # check predefined channels, for the benefit of C
2811 root 1.375 if ($CHANNEL{$channel}) {
2812     $channel = $CHANNEL{$channel};
2813    
2814     $self->ext_msg (channel_info => $channel)
2815     if $self->can_msg;
2816    
2817     $channel = $channel->{id};
2818 root 1.350
2819 root 1.375 } elsif (ref $channel) {
2820 root 1.311 # send meta info to client, if not yet sent
2821     unless (exists $self->{channel}{$channel->{id}}) {
2822     $self->{channel}{$channel->{id}} = $channel;
2823 root 1.353 $self->ext_msg (channel_info => $channel)
2824     if $self->can_msg;
2825 root 1.311 }
2826    
2827     $channel = $channel->{id};
2828     }
2829    
2830 root 1.313 return unless @extra || length $msg;
2831    
2832 root 1.283 if ($self->can_msg) {
2833 root 1.323 # default colour, mask it out
2834     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2835     if $color & cf::NDI_DEF;
2836    
2837 root 1.443 my $pkt = "msg "
2838     . $self->{json_coder}->encode (
2839     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
2840     );
2841    
2842     # try lzf for large packets
2843     $pkt = "lzf " . Compress::LZF::compress $pkt
2844     if 1024 <= length $pkt and $self->{can_lzf};
2845    
2846     # split very large packets
2847     if (8192 < length $pkt and $self->{can_lzf}) {
2848     $self->send_packet ("frag $_") for unpack "(a8192)*", $pkt;
2849     $pkt = "frag";
2850     }
2851    
2852     $self->send_packet ($pkt);
2853 root 1.283 } else {
2854 root 1.323 if ($color >= 0) {
2855     # replace some tags by gcfclient-compatible ones
2856     for ($msg) {
2857     1 while
2858     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2859     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2860     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2861     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2862     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2863     }
2864    
2865     $color &= cf::NDI_COLOR_MASK;
2866 root 1.283
2867 root 1.327 utf8::encode $msg;
2868    
2869 root 1.284 if (0 && $msg =~ /\[/) {
2870 root 1.331 # COMMAND/INFO
2871     $self->send_packet ("drawextinfo $color 10 8 $msg")
2872 root 1.283 } else {
2873 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2874 root 1.283 $self->send_packet ("drawinfo $color $msg")
2875     }
2876     }
2877     }
2878     }
2879    
2880 root 1.316 =item $client->ext_msg ($type, @msg)
2881 root 1.232
2882 root 1.287 Sends an ext event to the client.
2883 root 1.232
2884     =cut
2885    
2886 root 1.316 sub cf::client::ext_msg($$@) {
2887     my ($self, $type, @msg) = @_;
2888 root 1.232
2889 root 1.343 if ($self->extcmd == 2) {
2890 root 1.316 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2891 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
2892 root 1.316 push @msg, msgtype => "event_$type";
2893     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2894     }
2895 root 1.232 }
2896 root 1.95
2897 root 1.336 =item $client->ext_reply ($msgid, @msg)
2898    
2899     Sends an ext reply to the client.
2900    
2901     =cut
2902    
2903     sub cf::client::ext_reply($$@) {
2904     my ($self, $id, @msg) = @_;
2905    
2906     if ($self->extcmd == 2) {
2907     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2908 root 1.343 } elsif ($self->extcmd == 1) {
2909 root 1.336 #TODO: version 1, remove
2910     unshift @msg, msgtype => "reply", msgid => $id;
2911     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2912     }
2913     }
2914    
2915 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2916    
2917     Queues a query to the client, calling the given callback with
2918     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2919     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2920    
2921 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2922     become reliable at some point in the future.
2923 root 1.95
2924     =cut
2925    
2926     sub cf::client::query {
2927     my ($self, $flags, $text, $cb) = @_;
2928    
2929     return unless $self->state == ST_PLAYING
2930     || $self->state == ST_SETUP
2931     || $self->state == ST_CUSTOM;
2932    
2933     $self->state (ST_CUSTOM);
2934    
2935     utf8::encode $text;
2936     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2937    
2938     $self->send_packet ($self->{query_queue}[0][0])
2939     if @{ $self->{query_queue} } == 1;
2940 root 1.287
2941     1
2942 root 1.95 }
2943    
2944     cf::client->attach (
2945 root 1.290 on_connect => sub {
2946     my ($ns) = @_;
2947    
2948     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2949     },
2950 root 1.95 on_reply => sub {
2951     my ($ns, $msg) = @_;
2952    
2953     # this weird shuffling is so that direct followup queries
2954     # get handled first
2955 root 1.128 my $queue = delete $ns->{query_queue}
2956 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2957 root 1.95
2958     (shift @$queue)->[1]->($msg);
2959 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2960 root 1.95
2961     push @{ $ns->{query_queue} }, @$queue;
2962    
2963     if (@{ $ns->{query_queue} } == @$queue) {
2964     if (@$queue) {
2965     $ns->send_packet ($ns->{query_queue}[0][0]);
2966     } else {
2967 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2968 root 1.95 }
2969     }
2970     },
2971 root 1.287 on_exticmd => sub {
2972     my ($ns, $buf) = @_;
2973    
2974 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2975 root 1.287
2976     if (ref $msg) {
2977 root 1.316 my ($type, $reply, @payload) =
2978     "ARRAY" eq ref $msg
2979     ? @$msg
2980     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2981    
2982 root 1.338 my @reply;
2983    
2984 root 1.316 if (my $cb = $EXTICMD{$type}) {
2985 root 1.338 @reply = $cb->($ns, @payload);
2986     }
2987    
2988     $ns->ext_reply ($reply, @reply)
2989     if $reply;
2990 root 1.316
2991 root 1.287 } else {
2992     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2993     }
2994    
2995     cf::override;
2996     },
2997 root 1.95 );
2998    
2999 root 1.140 =item $client->async (\&cb)
3000 root 1.96
3001     Create a new coroutine, running the specified callback. The coroutine will
3002     be automatically cancelled when the client gets destroyed (e.g. on logout,
3003     or loss of connection).
3004    
3005     =cut
3006    
3007 root 1.140 sub cf::client::async {
3008 root 1.96 my ($self, $cb) = @_;
3009    
3010 root 1.140 my $coro = &Coro::async ($cb);
3011 root 1.103
3012     $coro->on_destroy (sub {
3013 root 1.96 delete $self->{_coro}{$coro+0};
3014 root 1.103 });
3015 root 1.96
3016     $self->{_coro}{$coro+0} = $coro;
3017 root 1.103
3018     $coro
3019 root 1.96 }
3020    
3021     cf::client->attach (
3022     on_destroy => sub {
3023     my ($ns) = @_;
3024    
3025 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3026 root 1.96 },
3027     );
3028    
3029 root 1.95 =back
3030    
3031 root 1.70
3032     =head2 SAFE SCRIPTING
3033    
3034     Functions that provide a safe environment to compile and execute
3035     snippets of perl code without them endangering the safety of the server
3036     itself. Looping constructs, I/O operators and other built-in functionality
3037     is not available in the safe scripting environment, and the number of
3038 root 1.79 functions and methods that can be called is greatly reduced.
3039 root 1.70
3040     =cut
3041 root 1.23
3042 root 1.42 our $safe = new Safe "safe";
3043 root 1.23 our $safe_hole = new Safe::Hole;
3044    
3045     $SIG{FPE} = 'IGNORE';
3046    
3047 root 1.328 $safe->permit_only (Opcode::opset qw(
3048     :base_core :base_mem :base_orig :base_math
3049     grepstart grepwhile mapstart mapwhile
3050     sort time
3051     ));
3052 root 1.23
3053 root 1.25 # here we export the classes and methods available to script code
3054    
3055 root 1.70 =pod
3056    
3057 root 1.228 The following functions and methods are available within a safe environment:
3058 root 1.70
3059 root 1.297 cf::object
3060 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3061 root 1.425 insert remove name archname title slaying race decrease split
3062 root 1.297
3063     cf::object::player
3064     player
3065    
3066     cf::player
3067     peaceful
3068    
3069     cf::map
3070     trigger
3071 root 1.70
3072     =cut
3073    
3074 root 1.25 for (
3075 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3076 elmex 1.431 insert remove inv nrof name archname title slaying race
3077 elmex 1.438 decrease split destroy change_exp)],
3078 root 1.25 ["cf::object::player" => qw(player)],
3079     ["cf::player" => qw(peaceful)],
3080 elmex 1.91 ["cf::map" => qw(trigger)],
3081 root 1.25 ) {
3082     no strict 'refs';
3083     my ($pkg, @funs) = @$_;
3084 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3085 root 1.25 for @funs;
3086     }
3087 root 1.23
3088 root 1.70 =over 4
3089    
3090     =item @retval = safe_eval $code, [var => value, ...]
3091    
3092     Compiled and executes the given perl code snippet. additional var/value
3093     pairs result in temporary local (my) scalar variables of the given name
3094     that are available in the code snippet. Example:
3095    
3096     my $five = safe_eval '$first + $second', first => 1, second => 4;
3097    
3098     =cut
3099    
3100 root 1.23 sub safe_eval($;@) {
3101     my ($code, %vars) = @_;
3102    
3103     my $qcode = $code;
3104     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3105     $qcode =~ s/\n/\\n/g;
3106    
3107     local $_;
3108 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
3109 root 1.23
3110 root 1.42 my $eval =
3111 root 1.23 "do {\n"
3112     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3113     . "#line 0 \"{$qcode}\"\n"
3114     . $code
3115     . "\n}"
3116 root 1.25 ;
3117    
3118     sub_generation_inc;
3119 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3120 root 1.25 sub_generation_inc;
3121    
3122 root 1.42 if ($@) {
3123     warn "$@";
3124     warn "while executing safe code '$code'\n";
3125     warn "with arguments " . (join " ", %vars) . "\n";
3126     }
3127    
3128 root 1.25 wantarray ? @res : $res[0]
3129 root 1.23 }
3130    
3131 root 1.69 =item cf::register_script_function $function => $cb
3132    
3133     Register a function that can be called from within map/npc scripts. The
3134     function should be reasonably secure and should be put into a package name
3135     like the extension.
3136    
3137     Example: register a function that gets called whenever a map script calls
3138     C<rent::overview>, as used by the C<rent> extension.
3139    
3140     cf::register_script_function "rent::overview" => sub {
3141     ...
3142     };
3143    
3144     =cut
3145    
3146 root 1.23 sub register_script_function {
3147     my ($fun, $cb) = @_;
3148    
3149     no strict 'refs';
3150 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
3151 root 1.23 }
3152    
3153 root 1.70 =back
3154    
3155 root 1.71 =cut
3156    
3157 root 1.23 #############################################################################
3158 root 1.203 # the server's init and main functions
3159    
3160 root 1.246 sub load_facedata($) {
3161     my ($path) = @_;
3162 root 1.223
3163 root 1.348 # HACK to clear player env face cache, we need some signal framework
3164     # for this (global event?)
3165     %ext::player_env::MUSIC_FACE_CACHE = ();
3166    
3167 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3168 root 1.334
3169 root 1.229 warn "loading facedata from $path\n";
3170 root 1.223
3171 root 1.236 my $facedata;
3172     0 < aio_load $path, $facedata
3173 root 1.223 or die "$path: $!";
3174    
3175 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3176 root 1.223
3177 root 1.236 $facedata->{version} == 2
3178 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3179    
3180 root 1.334 # patch in the exptable
3181     $facedata->{resource}{"res/exp_table"} = {
3182     type => FT_RSRC,
3183 root 1.337 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
3184 root 1.334 };
3185     cf::cede_to_tick;
3186    
3187 root 1.236 {
3188     my $faces = $facedata->{faceinfo};
3189    
3190     while (my ($face, $info) = each %$faces) {
3191     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3192 root 1.405
3193 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3194     cf::face::set_magicmap $idx, $info->{magicmap};
3195 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
3196     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
3197 root 1.302
3198     cf::cede_to_tick;
3199 root 1.236 }
3200    
3201     while (my ($face, $info) = each %$faces) {
3202     next unless $info->{smooth};
3203 root 1.405
3204 root 1.236 my $idx = cf::face::find $face
3205     or next;
3206 root 1.405
3207 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3208 root 1.302 cf::face::set_smooth $idx, $smooth;
3209     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3210 root 1.236 } else {
3211     warn "smooth face '$info->{smooth}' not found for face '$face'";
3212     }
3213 root 1.302
3214     cf::cede_to_tick;
3215 root 1.236 }
3216 root 1.223 }
3217    
3218 root 1.236 {
3219     my $anims = $facedata->{animinfo};
3220    
3221     while (my ($anim, $info) = each %$anims) {
3222     cf::anim::set $anim, $info->{frames}, $info->{facings};
3223 root 1.302 cf::cede_to_tick;
3224 root 1.225 }
3225 root 1.236
3226     cf::anim::invalidate_all; # d'oh
3227 root 1.225 }
3228    
3229 root 1.302 {
3230     # TODO: for gcfclient pleasure, we should give resources
3231     # that gcfclient doesn't grok a >10000 face index.
3232     my $res = $facedata->{resource};
3233    
3234     while (my ($name, $info) = each %$res) {
3235 root 1.405 if (defined $info->{type}) {
3236     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3237     my $data;
3238    
3239     if ($info->{type} & 1) {
3240     # prepend meta info
3241    
3242     my $meta = $enc->encode ({
3243     name => $name,
3244     %{ $info->{meta} || {} },
3245     });
3246 root 1.307
3247 root 1.405 $data = pack "(w/a*)*", $meta, $info->{data};
3248     } else {
3249     $data = $info->{data};
3250     }
3251 root 1.318
3252 root 1.405 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
3253     cf::face::set_type $idx, $info->{type};
3254 root 1.337 } else {
3255 root 1.405 $RESOURCE{$name} = $info;
3256 root 1.307 }
3257 root 1.302
3258     cf::cede_to_tick;
3259     }
3260 root 1.406 }
3261    
3262     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3263 root 1.321
3264 root 1.406 1
3265     }
3266    
3267     cf::global->attach (on_resource_update => sub {
3268     if (my $soundconf = $RESOURCE{"res/sound.conf"}) {
3269     $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data});
3270    
3271     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3272     my $sound = $soundconf->{compat}[$_]
3273     or next;
3274 root 1.321
3275 root 1.406 my $face = cf::face::find "sound/$sound->[1]";
3276     cf::sound::set $sound->[0] => $face;
3277     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3278     }
3279 root 1.321
3280 root 1.406 while (my ($k, $v) = each %{$soundconf->{event}}) {
3281     my $face = cf::face::find "sound/$v";
3282     cf::sound::set $k => $face;
3283 root 1.321 }
3284 root 1.302 }
3285 root 1.406 });
3286 root 1.223
3287 root 1.318 register_exticmd fx_want => sub {
3288     my ($ns, $want) = @_;
3289    
3290     while (my ($k, $v) = each %$want) {
3291     $ns->fx_want ($k, $v);
3292     }
3293     };
3294    
3295 root 1.423 sub load_resource_file($) {
3296 root 1.424 my $guard = lock_acquire "load_resource_file";
3297    
3298 root 1.423 my $status = load_resource_file_ $_[0];
3299     get_slot 0.1, 100;
3300     cf::arch::commit_load;
3301 root 1.424
3302 root 1.423 $status
3303     }
3304    
3305 root 1.253 sub reload_regions {
3306 root 1.348 # HACK to clear player env face cache, we need some signal framework
3307     # for this (global event?)
3308     %ext::player_env::MUSIC_FACE_CACHE = ();
3309    
3310 root 1.253 load_resource_file "$MAPDIR/regions"
3311     or die "unable to load regions file\n";
3312 root 1.304
3313     for (cf::region::list) {
3314     $_->{match} = qr/$_->{match}/
3315     if exists $_->{match};
3316     }
3317 root 1.253 }
3318    
3319 root 1.246 sub reload_facedata {
3320 root 1.253 load_facedata "$DATADIR/facedata"
3321 root 1.246 or die "unable to load facedata\n";
3322     }
3323    
3324     sub reload_archetypes {
3325 root 1.253 load_resource_file "$DATADIR/archetypes"
3326 root 1.246 or die "unable to load archetypes\n";
3327 root 1.241 }
3328    
3329 root 1.246 sub reload_treasures {
3330 root 1.253 load_resource_file "$DATADIR/treasures"
3331 root 1.246 or die "unable to load treasurelists\n";
3332 root 1.241 }
3333    
3334 root 1.223 sub reload_resources {
3335 root 1.245 warn "reloading resource files...\n";
3336    
3337 root 1.246 reload_facedata;
3338     reload_archetypes;
3339 root 1.423 reload_regions;
3340 root 1.246 reload_treasures;
3341 root 1.245
3342     warn "finished reloading resource files\n";
3343 root 1.223 }
3344    
3345     sub init {
3346 root 1.423 my $guard = freeze_mainloop;
3347    
3348 root 1.435 evthread_start IO::AIO::poll_fileno;
3349    
3350 root 1.223 reload_resources;
3351 root 1.203 }
3352 root 1.34
3353 root 1.345 sub reload_config {
3354 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3355 root 1.72 or return;
3356    
3357     local $/;
3358 root 1.408 *CFG = YAML::Load <$fh>;
3359 root 1.131
3360     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3361    
3362 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3363     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3364    
3365 root 1.131 if (exists $CFG{mlockall}) {
3366     eval {
3367 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3368 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3369     };
3370     warn $@ if $@;
3371     }
3372 root 1.72 }
3373    
3374 root 1.445 sub pidfile() {
3375     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3376     or die "$PIDFILE: $!";
3377     flock $fh, &Fcntl::LOCK_EX
3378     or die "$PIDFILE: flock: $!";
3379     $fh
3380     }
3381    
3382     # make sure only one server instance is running at any one time
3383     sub atomic {
3384     my $fh = pidfile;
3385    
3386     my $pid = <$fh>;
3387     kill 9, $pid if $pid > 0;
3388    
3389     seek $fh, 0, 0;
3390     print $fh $$;
3391     }
3392    
3393 root 1.39 sub main {
3394 root 1.445 atomic;
3395    
3396 root 1.108 # we must not ever block the main coroutine
3397     local $Coro::idle = sub {
3398 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3399 root 1.175 (async {
3400 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3401 root 1.396 EV::loop EV::LOOP_ONESHOT;
3402 root 1.175 })->prio (Coro::PRIO_MAX);
3403 root 1.108 };
3404    
3405 root 1.423 {
3406     my $guard = freeze_mainloop;
3407     reload_config;
3408     db_init;
3409     load_extensions;
3410    
3411     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3412     }
3413 root 1.183
3414 root 1.445 utime time, time, $RUNTIMEFILE;
3415    
3416     # no (long-running) fork's whatsoever before this point(!)
3417     POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3418    
3419 root 1.396 EV::loop;
3420 root 1.34 }
3421    
3422     #############################################################################
3423 root 1.155 # initialisation and cleanup
3424    
3425     # install some emergency cleanup handlers
3426     BEGIN {
3427 root 1.396 our %SIGWATCHER = ();
3428 root 1.155 for my $signal (qw(INT HUP TERM)) {
3429 root 1.396 $SIGWATCHER{$signal} = EV::signal $signal, sub {
3430     cf::cleanup "SIG$signal";
3431     };
3432 root 1.155 }
3433     }
3434    
3435 root 1.417 sub write_runtime_sync {
3436 root 1.281 # first touch the runtime file to show we are still running:
3437     # the fsync below can take a very very long time.
3438    
3439 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3440 root 1.281
3441     my $guard = cf::lock_acquire "write_runtime";
3442    
3443 root 1.445 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT, 0644
3444 root 1.281 or return;
3445    
3446     my $value = $cf::RUNTIME + 90 + 10;
3447     # 10 is the runtime save interval, for a monotonic clock
3448     # 60 allows for the watchdog to kill the server.
3449    
3450     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3451     and return;
3452    
3453     # always fsync - this file is important
3454     aio_fsync $fh
3455     and return;
3456    
3457     # touch it again to show we are up-to-date
3458     aio_utime $fh, undef, undef;
3459    
3460     close $fh
3461     or return;
3462    
3463 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3464 root 1.281 and return;
3465    
3466     warn "runtime file written.\n";
3467    
3468     1
3469     }
3470    
3471 root 1.416 our $uuid_lock;
3472     our $uuid_skip;
3473    
3474     sub write_uuid_sync($) {
3475     $uuid_skip ||= $_[0];
3476    
3477     return if $uuid_lock;
3478     local $uuid_lock = 1;
3479    
3480     my $uuid = "$LOCALDIR/uuid";
3481    
3482     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3483     or return;
3484    
3485     my $value = uuid_str $uuid_skip + uuid_seq uuid_cur;
3486     $uuid_skip = 0;
3487    
3488     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3489     and return;
3490    
3491     # always fsync - this file is important
3492     aio_fsync $fh
3493     and return;
3494    
3495     close $fh
3496     or return;
3497    
3498     aio_rename "$uuid~", $uuid
3499     and return;
3500    
3501     warn "uuid file written ($value).\n";
3502    
3503     1
3504    
3505     }
3506    
3507     sub write_uuid($$) {
3508     my ($skip, $sync) = @_;
3509    
3510     $sync ? write_uuid_sync $skip
3511     : async { write_uuid_sync $skip };
3512     }
3513    
3514 root 1.156 sub emergency_save() {
3515 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3516    
3517     warn "enter emergency perl save\n";
3518    
3519     cf::sync_job {
3520     # use a peculiar iteration method to avoid tripping on perl
3521     # refcount bugs in for. also avoids problems with players
3522 root 1.167 # and maps saved/destroyed asynchronously.
3523 root 1.155 warn "begin emergency player save\n";
3524     for my $login (keys %cf::PLAYER) {
3525     my $pl = $cf::PLAYER{$login} or next;
3526     $pl->valid or next;
3527 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3528 root 1.155 $pl->save;
3529     }
3530     warn "end emergency player save\n";
3531    
3532     warn "begin emergency map save\n";
3533     for my $path (keys %cf::MAP) {
3534     my $map = $cf::MAP{$path} or next;
3535     $map->valid or next;
3536     $map->save;
3537     }
3538     warn "end emergency map save\n";
3539 root 1.208
3540     warn "begin emergency database checkpoint\n";
3541     BDB::db_env_txn_checkpoint $DB_ENV;
3542     warn "end emergency database checkpoint\n";
3543 root 1.416
3544     warn "begin write uuid\n";
3545     write_uuid_sync 1;
3546     warn "end write uuid\n";
3547 root 1.155 };
3548    
3549     warn "leave emergency perl save\n";
3550     }
3551 root 1.22
3552 root 1.211 sub post_cleanup {
3553     my ($make_core) = @_;
3554    
3555     warn Carp::longmess "post_cleanup backtrace"
3556     if $make_core;
3557 root 1.445
3558     my $fh = pidfile;
3559     unlink $PIDFILE if <$fh> == $$;
3560 root 1.211 }
3561    
3562 root 1.441 # a safer delete_package, copied from Symbol
3563     sub clear_package($) {
3564     my $pkg = shift;
3565    
3566     # expand to full symbol table name if needed
3567     unless ($pkg =~ /^main::.*::$/) {
3568     $pkg = "main$pkg" if $pkg =~ /^::/;
3569     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3570     $pkg .= '::' unless $pkg =~ /::$/;
3571     }
3572    
3573     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3574     my $stem_symtab = *{$stem}{HASH};
3575    
3576     defined $stem_symtab and exists $stem_symtab->{$leaf}
3577     or return;
3578    
3579     # clear all symbols
3580     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3581     for my $name (keys %$leaf_symtab) {
3582     _gv_clear *{"$pkg$name"};
3583     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3584     }
3585     warn "cleared package #$pkg\n";#d#
3586     }
3587    
3588     our $RELOAD; # how many times to reload
3589    
3590 root 1.246 sub do_reload_perl() {
3591 root 1.106 # can/must only be called in main
3592     if ($Coro::current != $Coro::main) {
3593 root 1.183 warn "can only reload from main coroutine";
3594 root 1.106 return;
3595     }
3596    
3597 root 1.441 return if $RELOAD++;
3598    
3599     while ($RELOAD) {
3600     warn "reloading...";
3601 root 1.103
3602 root 1.441 warn "entering sync_job";
3603 root 1.212
3604 root 1.441 cf::sync_job {
3605     cf::write_runtime_sync; # external watchdog should not bark
3606     cf::emergency_save;
3607     cf::write_runtime_sync; # external watchdog should not bark
3608 root 1.183
3609 root 1.441 warn "syncing database to disk";
3610     BDB::db_env_txn_checkpoint $DB_ENV;
3611 root 1.106
3612 root 1.441 # if anything goes wrong in here, we should simply crash as we already saved
3613 root 1.65
3614 root 1.441 warn "flushing outstanding aio requests";
3615     while (IO::AIO::nreqs || BDB::nreqs) {
3616     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3617     }
3618 root 1.183
3619 root 1.441 warn "cancelling all extension coros";
3620     $_->cancel for values %EXT_CORO;
3621     %EXT_CORO = ();
3622 root 1.223
3623 root 1.441 warn "removing commands";
3624     %COMMAND = ();
3625 root 1.103
3626 root 1.441 warn "removing ext/exti commands";
3627     %EXTCMD = ();
3628     %EXTICMD = ();
3629 root 1.159
3630 root 1.441 warn "unloading/nuking all extensions";
3631     for my $pkg (@EXTS) {
3632     warn "... unloading $pkg";
3633 root 1.159
3634 root 1.441 if (my $cb = $pkg->can ("unload")) {
3635     eval {
3636     $cb->($pkg);
3637     1
3638     } or warn "$pkg unloaded, but with errors: $@";
3639     }
3640 root 1.159
3641 root 1.441 warn "... clearing $pkg";
3642     clear_package $pkg;
3643 root 1.159 }
3644    
3645 root 1.441 warn "unloading all perl modules loaded from $LIBDIR";
3646     while (my ($k, $v) = each %INC) {
3647     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3648 root 1.65
3649 root 1.441 warn "... unloading $k";
3650     delete $INC{$k};
3651 root 1.65
3652 root 1.441 $k =~ s/\.pm$//;
3653     $k =~ s/\//::/g;
3654 root 1.65
3655 root 1.441 if (my $cb = $k->can ("unload_module")) {
3656     $cb->();
3657     }
3658 root 1.65
3659 root 1.441 clear_package $k;
3660 root 1.65 }
3661    
3662 root 1.441 warn "getting rid of safe::, as good as possible";
3663     clear_package "safe::$_"
3664     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3665 root 1.65
3666 root 1.441 warn "unloading cf.pm \"a bit\"";
3667     delete $INC{"cf.pm"};
3668     delete $INC{"cf/pod.pm"};
3669 root 1.65
3670 root 1.441 # don't, removes xs symbols, too,
3671     # and global variables created in xs
3672     #clear_package __PACKAGE__;
3673 root 1.65
3674 root 1.441 warn "unload completed, starting to reload now";
3675 root 1.65
3676 root 1.441 warn "reloading cf.pm";
3677     require cf;
3678     cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3679 root 1.183
3680 root 1.441 warn "loading config and database again";
3681     cf::reload_config;
3682 root 1.100
3683 root 1.441 warn "loading extensions";
3684     cf::load_extensions;
3685 root 1.65
3686 root 1.441 warn "reattaching attachments to objects/players";
3687     _global_reattach; # objects, sockets
3688     warn "reattaching attachments to maps";
3689     reattach $_ for values %MAP;
3690     warn "reattaching attachments to players";
3691     reattach $_ for values %PLAYER;
3692 root 1.65
3693 root 1.441 warn "leaving sync_job";
3694 root 1.183
3695 root 1.441 1
3696     } or do {
3697     warn $@;
3698     cf::cleanup "error while reloading, exiting.";
3699     };
3700 root 1.183
3701 root 1.441 warn "reloaded";
3702     --$RELOAD;
3703     }
3704 root 1.65 };
3705    
3706 root 1.175 our $RELOAD_WATCHER; # used only during reload
3707    
3708 root 1.246 sub reload_perl() {
3709     # doing reload synchronously and two reloads happen back-to-back,
3710     # coro crashes during coro_state_free->destroy here.
3711    
3712 root 1.396 $RELOAD_WATCHER ||= EV::timer 0, 0, sub {
3713 root 1.409 do_reload_perl;
3714 root 1.396 undef $RELOAD_WATCHER;
3715     };
3716 root 1.246 }
3717    
3718 root 1.111 register_command "reload" => sub {
3719 root 1.65 my ($who, $arg) = @_;
3720    
3721     if ($who->flag (FLAG_WIZ)) {
3722 root 1.175 $who->message ("reloading server.");
3723 root 1.374 async {
3724     $Coro::current->{desc} = "perl_reload";
3725     reload_perl;
3726     };
3727 root 1.65 }
3728     };
3729    
3730 root 1.27 unshift @INC, $LIBDIR;
3731 root 1.17
3732 root 1.183 my $bug_warning = 0;
3733    
3734 root 1.239 our @WAIT_FOR_TICK;
3735     our @WAIT_FOR_TICK_BEGIN;
3736    
3737     sub wait_for_tick {
3738 root 1.423 return if tick_inhibit || $Coro::current == $Coro::main;
3739 root 1.241
3740 root 1.239 my $signal = new Coro::Signal;
3741     push @WAIT_FOR_TICK, $signal;
3742     $signal->wait;
3743     }
3744    
3745     sub wait_for_tick_begin {
3746 root 1.423 return if tick_inhibit || $Coro::current == $Coro::main;
3747 root 1.241
3748 root 1.239 my $signal = new Coro::Signal;
3749     push @WAIT_FOR_TICK_BEGIN, $signal;
3750     $signal->wait;
3751     }
3752    
3753 root 1.412 sub tick {
3754 root 1.396 if ($Coro::current != $Coro::main) {
3755     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3756     unless ++$bug_warning > 10;
3757     return;
3758     }
3759    
3760     cf::server_tick; # one server iteration
3761 root 1.245
3762 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3763 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3764 root 1.396 Coro::async_pool {
3765     $Coro::current->{desc} = "runtime saver";
3766 root 1.417 write_runtime_sync
3767 root 1.396 or warn "ERROR: unable to write runtime file: $!";
3768     };
3769     }
3770 root 1.265
3771 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3772     $sig->send;
3773     }
3774     while (my $sig = shift @WAIT_FOR_TICK) {
3775     $sig->send;
3776     }
3777 root 1.265
3778 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
3779 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3780 root 1.265
3781 root 1.412 if (0) {
3782     if ($NEXT_TICK) {
3783     my $jitter = $TICK_START - $NEXT_TICK;
3784     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
3785     warn "jitter $JITTER\n";#d#
3786     }
3787     }
3788     }
3789 root 1.35
3790 root 1.206 {
3791 root 1.401 # configure BDB
3792    
3793 root 1.363 BDB::min_parallel 8;
3794 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
3795 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
3796 root 1.77
3797 root 1.206 unless ($DB_ENV) {
3798     $DB_ENV = BDB::db_env_create;
3799 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
3800     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
3801     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
3802 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
3803     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
3804 root 1.206
3805     cf::sync_job {
3806 root 1.208 eval {
3807     BDB::db_env_open
3808     $DB_ENV,
3809 root 1.253 $BDBDIR,
3810 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3811     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3812     0666;
3813    
3814 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3815 root 1.208 };
3816    
3817     cf::cleanup "db_env_open(db): $@" if $@;
3818 root 1.206 };
3819     }
3820 root 1.363
3821 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
3822     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
3823     };
3824     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
3825     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
3826     };
3827     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
3828     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
3829     };
3830 root 1.206 }
3831    
3832     {
3833 root 1.401 # configure IO::AIO
3834    
3835 root 1.206 IO::AIO::min_parallel 8;
3836     IO::AIO::max_poll_time $TICK * 0.1;
3837 root 1.435 undef $AnyEvent::AIO::WATCHER;
3838 root 1.206 }
3839 root 1.108
3840 root 1.262 my $_log_backtrace;
3841    
3842 root 1.260 sub _log_backtrace {
3843     my ($msg, @addr) = @_;
3844    
3845 root 1.262 $msg =~ s/\n//;
3846 root 1.260
3847 root 1.262 # limit the # of concurrent backtraces
3848     if ($_log_backtrace < 2) {
3849     ++$_log_backtrace;
3850 root 1.446 my $perl_bt = Carp::longmess $msg;
3851 root 1.262 async {
3852 root 1.374 $Coro::current->{desc} = "abt $msg";
3853    
3854 root 1.262 my @bt = fork_call {
3855     @addr = map { sprintf "%x", $_ } @addr;
3856     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3857     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3858     or die "addr2line: $!";
3859    
3860     my @funcs;
3861     my @res = <$fh>;
3862     chomp for @res;
3863     while (@res) {
3864     my ($func, $line) = splice @res, 0, 2, ();
3865     push @funcs, "[$func] $line";
3866     }
3867 root 1.260
3868 root 1.262 @funcs
3869     };
3870 root 1.260
3871 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
3872     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
3873 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
3874     --$_log_backtrace;
3875     };
3876     } else {
3877 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3878 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3879     }
3880 root 1.260 }
3881    
3882 root 1.249 # load additional modules
3883     use cf::pod;
3884    
3885 root 1.125 END { cf::emergency_save }
3886    
3887 root 1.1 1
3888