ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.448
Committed: Fri Sep 19 05:30:23 2008 UTC (15 years, 8 months ago) by root
Branch: MAIN
Changes since 1.447: +17 -1 lines
Log Message:
*** empty log message ***

File Contents

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