ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.443
Committed: Sun Aug 31 10:05:26 2008 UTC (15 years, 9 months ago) by root
Branch: MAIN
CVS Tags: rel-2_7
Changes since 1.442: +17 -3 lines
Log Message:
*** empty log message ***

File Contents

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