ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.461
Committed: Fri Dec 19 22:47:29 2008 UTC (15 years, 5 months ago) by root
Branch: MAIN
Changes since 1.460: +0 -1 lines
Log Message:
new los code

File Contents

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