ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.361
Committed: Sun Sep 9 12:52:48 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.360: +14 -0 lines
Log Message:
*** empty log message ***

File Contents

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