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

File Contents

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