ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.429
Committed: Sat May 3 15:17:13 2008 UTC (16 years ago) by root
Branch: MAIN
Changes since 1.428: +3 -3 lines
Log Message:
fix locking issue

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