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