ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.623
Committed: Wed Aug 30 14:18:51 2017 UTC (6 years, 8 months ago) by root
Branch: MAIN
Changes since 1.622: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

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