ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.434
Committed: Thu May 29 03:27:37 2008 UTC (16 years ago) by root
Branch: MAIN
Changes since 1.433: +6 -3 lines
Log Message:
*** empty log message ***

File Contents

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