ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.62
Committed: Fri Sep 8 16:51:44 2006 UTC (17 years, 8 months ago) by root
Branch: MAIN
Changes since 1.61: +0 -36 lines
Log Message:
generic accessors, take one

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3     use Symbol;
4     use List::Util;
5 root 1.6 use Storable;
6 root 1.23 use Opcode;
7     use Safe;
8     use Safe::Hole;
9 root 1.19
10 root 1.32 use Time::HiRes;
11 root 1.18 use Event;
12 root 1.19 $Event::Eval = 1; # no idea why this is required, but it is
13 root 1.1
14     use strict;
15    
16 root 1.47 _reload_1;
17    
18 root 1.39 our %COMMAND = ();
19 root 1.1 our @EVENT;
20 root 1.27 our $LIBDIR = maps_directory "perl";
21 root 1.1
22 root 1.35 our $TICK = MAX_TIME * 1e-6;
23     our $TICK_WATCHER;
24     our $NEXT_TICK;
25    
26 root 1.1 BEGIN {
27     *CORE::GLOBAL::warn = sub {
28     my $msg = join "", @_;
29     $msg .= "\n"
30     unless $msg =~ /\n$/;
31    
32     print STDERR "cfperl: $msg";
33     LOG llevError, "cfperl: $msg";
34     };
35     }
36    
37 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
38 root 1.25
39 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
40 root 1.25 # within the Safe compartment.
41 root 1.50 for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
42 root 1.25 no strict 'refs';
43 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
44 root 1.25 }
45 root 1.1
46 root 1.18 $Event::DIED = sub {
47     warn "error in event callback: @_";
48     };
49    
50 root 1.5 my %ext_pkg;
51 root 1.1 my @exts;
52     my @hook;
53     my %command;
54 root 1.15 my %extcmd;
55 root 1.1
56 root 1.39 #############################################################################
57 root 1.45 # utility functions
58 root 1.44
59 root 1.45 use JSON::Syck (); # TODO# replace by JSON::PC once working
60 root 1.44
61 root 1.45 sub from_json($) {
62     $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
63     JSON::Syck::Load $_[0]
64 root 1.44 }
65    
66 root 1.45 sub to_json($) {
67     $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
68     JSON::Syck::Dump $_[0]
69 root 1.44 }
70    
71     #############################################################################
72 root 1.39 # "new" plug-in system
73    
74 root 1.55 =head3 EVENTS AND OBJECT ATTACHMENTS
75    
76     =over 4
77    
78 root 1.48 =item $object->attach ($attachment, key => $value...)
79 root 1.46
80 root 1.53 =item $object->detach ($attachment)
81    
82     Attach/detach a pre-registered attachment to an object.
83 root 1.46
84 root 1.48 =item $player->attach ($attachment, key => $value...)
85 root 1.46
86 root 1.53 =item $player->detach ($attachment)
87    
88     Attach/detach a pre-registered attachment to a player.
89    
90     =item $map->attach ($attachment, key => $value...)
91 root 1.46
92 root 1.53 =item $map->detach ($attachment)
93 root 1.46
94 root 1.53 Attach/detach a pre-registered attachment to a map.
95 root 1.39
96 root 1.55 =item $bool = $object->attached ($name)
97    
98     =item $bool = $player->attached ($name)
99    
100     =item $bool = $map->attached ($name)
101    
102     Checks wether the named attachment is currently attached to the object.
103    
104 root 1.40 =item cf::attach_global ...
105 root 1.39
106 root 1.46 Attach handlers for global events.
107    
108     This and all following C<attach_*>-functions expect any number of the
109     following handler/hook descriptions:
110    
111     =over 4
112    
113     =item prio => $number
114    
115     Set the priority for all following handlers/hooks (unless overwritten
116     by another C<prio> setting). Lower priority handlers get executed
117     earlier. The default priority is C<0>, and many built-in handlers are
118     registered at priority C<-1000>, so lower priorities should not be used
119     unless you know what you are doing.
120    
121     =item on_I<event> => \&cb
122    
123     Call the given code reference whenever the named event happens (event is
124     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
125     handlers are recognised generally depends on the type of object these
126     handlers attach to).
127    
128     See F<include/eventinc.h> for the full list of events supported, and their
129     class.
130    
131     =item package => package::
132    
133     Look for sub functions of the name C<< on_I<event> >> in the given
134     package and register them. Only handlers for eevents supported by the
135     object/class are recognised.
136    
137     =back
138    
139 root 1.47 =item cf::attach_to_type $object_type, $subtype, ...
140 root 1.39
141 root 1.47 Attach handlers for a specific object type (e.g. TRANSPORT) and
142     subtype. If C<$subtype> is zero or undef, matches all objects of the given
143     type.
144 root 1.46
145 root 1.40 =item cf::attach_to_objects ...
146 root 1.39
147 root 1.46 Attach handlers to all objects. Do not use this except for debugging or
148     very rare events, as handlers are (obviously) called for I<all> objects in
149     the game.
150    
151 root 1.40 =item cf::attach_to_players ...
152 root 1.39
153 root 1.46 Attach handlers to all players.
154    
155 root 1.40 =item cf::attach_to_maps ...
156 root 1.39
157 root 1.46 Attach handlers to all maps.
158    
159 root 1.45 =item cf:register_attachment $name, ...
160    
161 root 1.52 Register an attachment by name through which objects can refer to this
162     attachment.
163    
164 root 1.55 =item cf:register_player_attachment $name, ...
165    
166     Register an attachment by name through which players can refer to this
167     attachment.
168    
169 root 1.52 =item cf:register_map_attachment $name, ...
170    
171     Register an attachment by name through which maps can refer to this
172     attachment.
173    
174 root 1.39 =cut
175    
176 root 1.40 # the following variables are defined in .xs and must not be re-created
177 root 1.39 our @CB_GLOBAL = (); # registry for all global events
178 root 1.45 our @CB_OBJECT = (); # all objects (should not be used except in emergency)
179 root 1.40 our @CB_PLAYER = ();
180 root 1.39 our @CB_TYPE = (); # registry for type (cf-object class) based events
181 root 1.40 our @CB_MAP = ();
182 root 1.39
183 root 1.45 my %attachment;
184    
185 root 1.39 sub _attach_cb($\%$$$) {
186     my ($registry, $undo, $event, $prio, $cb) = @_;
187    
188     use sort 'stable';
189    
190     $cb = [$prio, $cb];
191    
192     @{$registry->[$event]} = sort
193     { $a->[0] cmp $b->[0] }
194     @{$registry->[$event] || []}, $cb;
195    
196     push @{$undo->{cb}}, [$event, $cb];
197     }
198    
199     # attach handles attaching event callbacks
200     # the only thing the caller has to do is pass the correct
201     # registry (== where the callback attaches to).
202 root 1.45 sub _attach(\@$@) {
203     my ($registry, $klass, @arg) = @_;
204 root 1.39
205     my $prio = 0;
206    
207     my %undo = (
208     registry => $registry,
209     cb => [],
210     );
211    
212     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
213    
214 root 1.45 while (@arg) {
215     my $type = shift @arg;
216 root 1.39
217     if ($type eq "prio") {
218 root 1.45 $prio = shift @arg;
219 root 1.39
220     } elsif ($type eq "package") {
221 root 1.45 my $pkg = shift @arg;
222 root 1.39
223     while (my ($name, $id) = each %cb_id) {
224     if (my $cb = $pkg->can ($name)) {
225     _attach_cb $registry, %undo, $id, $prio, $cb;
226     }
227     }
228    
229     } elsif (exists $cb_id{$type}) {
230 root 1.45 _attach_cb $registry, %undo, $cb_id{$type}, $prio, shift @arg;
231 root 1.39
232     } elsif (ref $type) {
233     warn "attaching objects not supported, ignoring.\n";
234    
235     } else {
236 root 1.45 shift @arg;
237 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
238     }
239     }
240    
241     \%undo
242     }
243    
244 root 1.46 sub _attach_attachment {
245 root 1.48 my ($obj, $name, %arg) = @_;
246 root 1.46
247 root 1.55 return if exists $obj->{_attachment}{$name};
248    
249 root 1.46 my $res;
250    
251     if (my $attach = $attachment{$name}) {
252     my $registry = $obj->registry;
253    
254 root 1.47 for (@$attach) {
255     my ($klass, @attach) = @$_;
256     $res = _attach @$registry, $klass, @attach;
257     }
258 root 1.46
259 root 1.48 $obj->{$name} = \%arg;
260 root 1.46 } else {
261     warn "object uses attachment '$name' that is not available, postponing.\n";
262     }
263    
264 root 1.50 $obj->{_attachment}{$name} = undef;
265 root 1.46
266     $res->{attachment} = $name;
267     $res
268     }
269    
270 root 1.54 *cf::object::attach =
271     *cf::player::attach =
272     *cf::map::attach = sub {
273 root 1.48 my ($obj, $name, %arg) = @_;
274 root 1.46
275 root 1.48 _attach_attachment $obj, $name, %arg;
276 root 1.55 };
277 root 1.46
278 root 1.54 # all those should be optimised
279     *cf::object::detach =
280     *cf::player::detach =
281     *cf::map::detach = sub {
282     my ($obj, $name) = @_;
283 root 1.46
284 root 1.54 delete $obj->{_attachment}{$name};
285 root 1.55 reattach ($obj);
286     };
287    
288     *cf::object::attached =
289     *cf::player::attached =
290     *cf::map::attached = sub {
291     my ($obj, $name) = @_;
292    
293     exists $obj->{_attachment}{$name}
294 root 1.54 };
295 root 1.53
296 root 1.39 sub attach_global {
297     _attach @CB_GLOBAL, KLASS_GLOBAL, @_
298     }
299    
300 root 1.40 sub attach_to_type {
301 root 1.39 my $type = shift;
302 root 1.47 my $subtype = shift;
303 root 1.45
304 root 1.47 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_
305 root 1.39 }
306    
307     sub attach_to_objects {
308 root 1.40 _attach @CB_OBJECT, KLASS_OBJECT, @_
309 root 1.39 }
310    
311     sub attach_to_players {
312 root 1.40 _attach @CB_PLAYER, KLASS_PLAYER, @_
313 root 1.39 }
314    
315     sub attach_to_maps {
316 root 1.40 _attach @CB_MAP, KLASS_MAP, @_
317 root 1.39 }
318    
319 root 1.45 sub register_attachment {
320     my $name = shift;
321    
322 root 1.47 $attachment{$name} = [[KLASS_OBJECT, @_]];
323 root 1.45 }
324    
325 root 1.55 sub register_player_attachment {
326     my $name = shift;
327    
328     $attachment{$name} = [[KLASS_PLAYER, @_]];
329     }
330    
331 root 1.52 sub register_map_attachment {
332     my $name = shift;
333    
334     $attachment{$name} = [[KLASS_MAP, @_]];
335     }
336    
337 root 1.39 our $override;
338 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
339 root 1.39
340 root 1.45 sub override {
341     $override = 1;
342     @invoke_results = ();
343 root 1.39 }
344    
345 root 1.45 sub do_invoke {
346 root 1.39 my $event = shift;
347 root 1.40 my $callbacks = shift;
348 root 1.39
349 root 1.45 @invoke_results = ();
350    
351 root 1.39 local $override;
352    
353 root 1.40 for (@$callbacks) {
354 root 1.39 eval { &{$_->[1]} };
355    
356     if ($@) {
357     warn "$@";
358     warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n";
359     override;
360     }
361    
362     return 1 if $override;
363     }
364    
365     0
366     }
367    
368 root 1.55 =item $bool = cf::invoke EVENT_GLOBAL_XXX, ...
369    
370     =item $bool = $object->invoke (EVENT_OBJECT_XXX, ...)
371    
372     =item $bool = $player->invoke (EVENT_PLAYER_XXX, ...)
373    
374     =item $bool = $map->invoke (EVENT_MAP_XXX, ...)
375    
376     Generate a global/object/player/map-specific event with the given arguments.
377    
378     This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be
379     removed in future versions), and there is no public API to access override
380     results (if you must, access C<@cf::invoke_results> directly).
381    
382     =back
383    
384     =head2 methods valid for all pointers
385    
386     =over 4
387    
388     =item $object->valid
389    
390     =item $player->valid
391    
392     =item $map->valid
393    
394     Just because you have a perl object does not mean that the corresponding
395     C-level object still exists. If you try to access an object that has no
396     valid C counterpart anymore you get an exception at runtime. This method
397     can be used to test for existence of the C object part without causing an
398     exception.
399    
400     =back
401    
402     =cut
403    
404     *cf::object::valid =
405     *cf::player::valid =
406     *cf::map::valid = \&cf::_valid;
407    
408 root 1.39 #############################################################################
409 root 1.45 # object support
410    
411     sub instantiate {
412     my ($obj, $data) = @_;
413    
414     $data = from_json $data;
415    
416     for (@$data) {
417 root 1.46 my ($name, $args) = @$_;
418 root 1.49
419     $obj->attach ($name, %{$args || {} });
420 root 1.46 }
421     }
422    
423     # basically do the same as instantiate, without calling instantiate
424     sub reattach {
425     my ($obj) = @_;
426     my $registry = $obj->registry;
427 root 1.45
428 root 1.47 @$registry = ();
429    
430 root 1.50 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
431 root 1.49
432 root 1.50 for my $name (keys %{ $obj->{_attachment} || {} }) {
433 root 1.45 if (my $attach = $attachment{$name}) {
434 root 1.47 for (@$attach) {
435     my ($klass, @attach) = @$_;
436     _attach @$registry, $klass, @attach;
437     }
438 root 1.45 } else {
439 root 1.46 warn "object uses attachment '$name' that is not available, postponing.\n";
440 root 1.45 }
441 root 1.46 }
442     }
443 root 1.45
444 root 1.46 sub object_freezer_save {
445 root 1.59 my ($filename, $rdata, $objs) = @_;
446 root 1.46
447 root 1.60 if (length $$rdata) {
448     warn sprintf "saving %s (%d,%d)\n",
449     $filename, length $$rdata, scalar @$objs;
450 root 1.59
451 root 1.60 if (open my $fh, ">:raw", "$filename~") {
452 root 1.59 chmod SAVE_MODE, $fh;
453 root 1.60 syswrite $fh, $$rdata;
454 root 1.59 close $fh;
455 root 1.60
456     if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
457     chmod SAVE_MODE, $fh;
458     syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
459     close $fh;
460     rename "$filename.pst~", "$filename.pst";
461     } else {
462     unlink "$filename.pst";
463     }
464    
465     rename "$filename~", $filename;
466 root 1.59 } else {
467 root 1.60 warn "FATAL: $filename~: $!\n";
468 root 1.59 }
469 root 1.46 } else {
470 root 1.60 unlink $filename;
471     unlink "$filename.pst";
472 root 1.45 }
473     }
474    
475 root 1.46 sub object_thawer_load {
476     my ($filename) = @_;
477    
478 root 1.61 local $/;
479    
480     my $av;
481    
482     #TODO: use sysread etc.
483     if (open my $data, "<:raw:perlio", $filename) {
484     $data = <$data>;
485     if (open my $pst, "<:raw:perlio", "$filename.pst") {
486     $av = eval { (Storable::thaw <$pst>)->{objs} };
487     }
488     return ($data, $av);
489     }
490 root 1.45
491 root 1.61 ()
492 root 1.45 }
493    
494     attach_to_objects
495     prio => -1000000,
496     on_clone => sub {
497     my ($src, $dst) = @_;
498    
499     @{$dst->registry} = @{$src->registry};
500    
501     %$dst = %$src;
502    
503 root 1.50 %{$dst->{_attachment}} = %{$src->{_attachment}}
504 root 1.45 if exists $src->{_attachment};
505     },
506     ;
507    
508     #############################################################################
509 root 1.39 # old plug-in events
510    
511 root 1.1 sub inject_event {
512 root 1.14 my $extension = shift;
513     my $event_code = shift;
514 root 1.1
515 root 1.14 my $cb = $hook[$event_code]{$extension}
516 root 1.5 or return;
517    
518 root 1.14 &$cb
519 root 1.5 }
520    
521     sub inject_global_event {
522 root 1.12 my $event = shift;
523 root 1.5
524 root 1.12 my $cb = $hook[$event]
525 root 1.1 or return;
526    
527 root 1.12 List::Util::max map &$_, values %$cb
528 root 1.1 }
529    
530     sub inject_command {
531     my ($name, $obj, $params) = @_;
532    
533     for my $cmd (@{ $command{$name} }) {
534     $cmd->[1]->($obj, $params);
535     }
536    
537     -1
538     }
539    
540     sub register_command {
541     my ($name, $time, $cb) = @_;
542    
543     my $caller = caller;
544 root 1.16 #warn "registering command '$name/$time' to '$caller'";
545 root 1.4
546 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
547     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
548     }
549    
550 root 1.16 sub register_extcmd {
551     my ($name, $cb) = @_;
552    
553     my $caller = caller;
554     #warn "registering extcmd '$name' to '$caller'";
555    
556     $extcmd{$name} = [$cb, $caller];
557     }
558    
559 root 1.6 sub register {
560     my ($base, $pkg) = @_;
561    
562 root 1.45 #TODO
563 root 1.6 }
564    
565 root 1.1 sub load_extension {
566     my ($path) = @_;
567    
568     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
569 root 1.5 my $base = $1;
570 root 1.1 my $pkg = $1;
571     $pkg =~ s/[^[:word:]]/_/g;
572 root 1.41 $pkg = "ext::$pkg";
573 root 1.1
574     warn "loading '$path' into '$pkg'\n";
575    
576     open my $fh, "<:utf8", $path
577     or die "$path: $!";
578    
579     my $source =
580     "package $pkg; use strict; use utf8;\n"
581     . "#line 1 \"$path\"\n{\n"
582     . (do { local $/; <$fh> })
583     . "\n};\n1";
584    
585     eval $source
586     or die "$path: $@";
587    
588     push @exts, $pkg;
589 root 1.5 $ext_pkg{$base} = $pkg;
590 root 1.1
591 root 1.6 # no strict 'refs';
592 root 1.23 # @{"$pkg\::ISA"} = ext::;
593 root 1.1
594 root 1.6 register $base, $pkg;
595 root 1.1 }
596    
597     sub unload_extension {
598     my ($pkg) = @_;
599    
600     warn "removing extension $pkg\n";
601    
602     # remove hooks
603 root 1.45 #TODO
604     # for my $idx (0 .. $#PLUGIN_EVENT) {
605     # delete $hook[$idx]{$pkg};
606     # }
607 root 1.1
608     # remove commands
609     for my $name (keys %command) {
610     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
611    
612     if (@cb) {
613     $command{$name} = \@cb;
614     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
615     } else {
616     delete $command{$name};
617     delete $COMMAND{"$name\000"};
618     }
619     }
620    
621 root 1.15 # remove extcmds
622 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
623     delete $extcmd{$name};
624 root 1.15 }
625    
626 root 1.43 if (my $cb = $pkg->can ("unload")) {
627 elmex 1.31 eval {
628     $cb->($pkg);
629     1
630     } or warn "$pkg unloaded, but with errors: $@";
631     }
632    
633 root 1.1 Symbol::delete_package $pkg;
634     }
635    
636     sub load_extensions {
637     my $LIBDIR = maps_directory "perl";
638    
639     for my $ext (<$LIBDIR/*.ext>) {
640 root 1.3 next unless -r $ext;
641 root 1.2 eval {
642     load_extension $ext;
643     1
644     } or warn "$ext not loaded: $@";
645 root 1.1 }
646     }
647    
648 root 1.36 sub _perl_reload(&) {
649     my ($msg) = @_;
650    
651     $msg->("reloading...");
652    
653     eval {
654     # 1. cancel all watchers
655     $_->cancel for Event::all_watchers;
656    
657     # 2. unload all extensions
658     for (@exts) {
659     $msg->("unloading <$_>");
660     unload_extension $_;
661     }
662    
663     # 3. unload all modules loaded from $LIBDIR
664     while (my ($k, $v) = each %INC) {
665     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
666    
667     $msg->("removing <$k>");
668     delete $INC{$k};
669 root 1.1
670 root 1.36 $k =~ s/\.pm$//;
671     $k =~ s/\//::/g;
672 root 1.3
673 root 1.36 if (my $cb = $k->can ("unload_module")) {
674     $cb->();
675 root 1.27 }
676    
677 root 1.36 Symbol::delete_package $k;
678     }
679 root 1.27
680 root 1.41 # 4. get rid of safe::, as good as possible
681     Symbol::delete_package "safe::$_"
682 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
683 root 1.36
684     # 5. remove register_script_function callbacks
685     # TODO
686    
687     # 6. unload cf.pm "a bit"
688     delete $INC{"cf.pm"};
689    
690 root 1.41 # don't, removes xs symbols, too,
691     # and global variables created in xs
692 root 1.36 #Symbol::delete_package __PACKAGE__;
693    
694     # 7. reload cf.pm
695     $msg->("reloading cf.pm");
696     require cf;
697 root 1.61
698     $msg->("load extensions");
699     cf::load_extensions;
700 root 1.36 };
701     $msg->($@) if $@;
702 root 1.27
703 root 1.36 $msg->("reloaded");
704     };
705 root 1.27
706 root 1.36 sub perl_reload() {
707     _perl_reload {
708     warn $_[0];
709     print "$_[0]\n";
710     };
711     }
712 root 1.27
713 root 1.36 register_command "perl-reload", 0, sub {
714     my ($who, $arg) = @_;
715 root 1.27
716 root 1.36 if ($who->flag (FLAG_WIZ)) {
717     _perl_reload {
718     warn $_[0];
719     $who->message ($_[0]);
720 root 1.4 };
721 root 1.1 }
722     };
723    
724 root 1.8 #############################################################################
725 root 1.28 # extcmd framework, basically convert ext <msg>
726 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
727    
728 root 1.44 attach_to_players
729 root 1.43 on_extcmd => sub {
730     my ($pl, $buf) = @_;
731    
732     my $msg = eval { from_json $buf };
733    
734     if (ref $msg) {
735     if (my $cb = $extcmd{$msg->{msgtype}}) {
736     if (my %reply = $cb->[0]->($pl, $msg)) {
737     $pl->ext_reply ($msg->{msgid}, %reply);
738     }
739 root 1.28 }
740 root 1.43 } else {
741     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
742 root 1.28 }
743 root 1.15
744 root 1.43 cf::override;
745     },
746     ;
747 root 1.15
748     #############################################################################
749 root 1.8 # load/save/clean perl data associated with a map
750    
751 root 1.39 *cf::mapsupport::on_clean = sub {
752 root 1.13 my ($map) = @_;
753 root 1.7
754     my $path = $map->tmpname;
755     defined $path or return;
756    
757 root 1.46 unlink "$path.pst";
758 root 1.7 };
759    
760 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
761    
762 root 1.8 #############################################################################
763     # load/save perl data associated with player->ob objects
764    
765 root 1.33 sub all_objects(@) {
766     @_, map all_objects ($_->inv), @_
767     }
768    
769 root 1.60 # TODO: compatibility cruft, remove when no longer needed
770 root 1.39 attach_to_players
771     on_load => sub {
772     my ($pl, $path) = @_;
773    
774     for my $o (all_objects $pl->ob) {
775     if (my $value = $o->get_ob_key_value ("_perl_data")) {
776     $o->set_ob_key_value ("_perl_data");
777 root 1.8
778 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
779     }
780 root 1.11 }
781 root 1.39 },
782     ;
783 root 1.6
784 root 1.22 #############################################################################
785     # core extensions - in perl
786    
787 root 1.23 =item cf::player::exists $login
788    
789     Returns true when the given account exists.
790    
791     =cut
792    
793     sub cf::player::exists($) {
794     cf::player::find $_[0]
795     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
796     }
797    
798 root 1.28 =item $player->reply ($npc, $msg[, $flags])
799    
800     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
801     can be C<undef>. Does the right thing when the player is currently in a
802     dialogue with the given NPC character.
803    
804     =cut
805    
806 root 1.22 # rough implementation of a future "reply" method that works
807     # with dialog boxes.
808 root 1.23 sub cf::object::player::reply($$$;$) {
809     my ($self, $npc, $msg, $flags) = @_;
810    
811     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
812 root 1.22
813 root 1.24 if ($self->{record_replies}) {
814     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
815     } else {
816     $msg = $npc->name . " says: $msg" if $npc;
817     $self->message ($msg, $flags);
818     }
819 root 1.22 }
820    
821 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
822    
823     Sends an ext reply to the player.
824    
825     =cut
826    
827     sub cf::player::ext_reply($$$%) {
828     my ($self, $id, %msg) = @_;
829    
830     $msg{msgid} = $id;
831    
832     $self->send ("ext " . to_json \%msg);
833     }
834    
835 root 1.22 #############################################################################
836 root 1.23 # map scripting support
837    
838 root 1.42 our $safe = new Safe "safe";
839 root 1.23 our $safe_hole = new Safe::Hole;
840    
841     $SIG{FPE} = 'IGNORE';
842    
843     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
844    
845 root 1.25 # here we export the classes and methods available to script code
846    
847     for (
848 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
849 root 1.25 ["cf::object::player" => qw(player)],
850     ["cf::player" => qw(peaceful)],
851     ) {
852     no strict 'refs';
853     my ($pkg, @funs) = @$_;
854 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
855 root 1.25 for @funs;
856     }
857 root 1.23
858     sub safe_eval($;@) {
859     my ($code, %vars) = @_;
860    
861     my $qcode = $code;
862     $qcode =~ s/"/‟/g; # not allowed in #line filenames
863     $qcode =~ s/\n/\\n/g;
864    
865     local $_;
866 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
867 root 1.23
868 root 1.42 my $eval =
869 root 1.23 "do {\n"
870     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
871     . "#line 0 \"{$qcode}\"\n"
872     . $code
873     . "\n}"
874 root 1.25 ;
875    
876     sub_generation_inc;
877 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
878 root 1.25 sub_generation_inc;
879    
880 root 1.42 if ($@) {
881     warn "$@";
882     warn "while executing safe code '$code'\n";
883     warn "with arguments " . (join " ", %vars) . "\n";
884     }
885    
886 root 1.25 wantarray ? @res : $res[0]
887 root 1.23 }
888    
889     sub register_script_function {
890     my ($fun, $cb) = @_;
891    
892     no strict 'refs';
893 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
894 root 1.23 }
895    
896     #############################################################################
897 root 1.34 # the server's main()
898    
899 root 1.39 sub main {
900 root 1.61 load_extensions;
901 root 1.34 Event::loop;
902     }
903    
904     #############################################################################
905 root 1.22 # initialisation
906    
907 root 1.6 register "<global>", __PACKAGE__;
908    
909 root 1.27 unshift @INC, $LIBDIR;
910 root 1.17
911 root 1.35 $TICK_WATCHER = Event->timer (
912     prio => 1,
913     at => $NEXT_TICK || 1,
914     cb => sub {
915     cf::server_tick; # one server iteration
916    
917     my $NOW = Event::time;
918     $NEXT_TICK += $TICK;
919    
920 root 1.37 # if we are delayed by four ticks, skip them all
921     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
922 root 1.35
923     $TICK_WATCHER->at ($NEXT_TICK);
924     $TICK_WATCHER->start;
925     },
926     );
927    
928 root 1.47 _reload_2;
929    
930 root 1.1 1
931