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