ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.424
Committed: Sun Apr 20 05:24:55 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.423: +3 -0 lines
Log Message:
- implement archetype gc
- implement "proper" refcounting for arches
- serialise resource file loads
- implement memory poisoning
- minor cleanups

File Contents

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