ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.438
Committed: Sun Jul 13 20:15:51 2008 UTC (15 years, 10 months ago) by elmex
Branch: MAIN
Changes since 1.437: +1 -1 lines
Log Message:
minor rebalancement of the jeweler skill. and added function to safe environment

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