ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.426
Committed: Thu Apr 24 04:40:31 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
CVS Tags: rel-2_52
Changes since 1.425: +6 -0 lines
Log Message:
rewrote party commands

File Contents

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