ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.415
Committed: Thu Apr 10 15:35:16 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.414: +27 -2 lines
Log Message:
*** empty log message ***

File Contents

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