ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.440
Committed: Mon Aug 11 23:23:41 2008 UTC (15 years, 9 months ago) by root
Branch: MAIN
Changes since 1.439: +2 -1 lines
Log Message:
*** empty log message ***

File Contents

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