ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.581
Committed: Fri Feb 3 02:04:11 2012 UTC (12 years, 3 months ago) by root
Branch: MAIN
Changes since 1.580: +39 -1 lines
Log Message:
only load the highscore file once

File Contents

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