ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.533
Committed: Thu Apr 29 07:59:17 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.532: +14 -12 lines
Log Message:
no sync_job error at startup

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