ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.430
Committed: Sun May 4 14:12:37 2008 UTC (16 years ago) by root
Branch: MAIN
CVS Tags: rel-2_53
Changes since 1.429: +29 -23 lines
Log Message:
lotsa

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