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