ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.444
Committed: Mon Sep 8 11:27:25 2008 UTC (15 years, 8 months ago) by root
Branch: MAIN
Changes since 1.443: +0 -91 lines
Log Message:
*** empty log message ***

File Contents

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