ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.466
Committed: Thu Jan 8 03:03:24 2009 UTC (15 years, 4 months ago) by root
Branch: MAIN
Changes since 1.465: +10 -5 lines
Log Message:
connected => shstr, beginning of mapscript

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