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