ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.64
Committed: Sun Sep 10 00:51:24 2006 UTC (17 years, 8 months ago) by root
Branch: MAIN
Changes since 1.63: +1 -1 lines
Log Message:
likely fix another crash bug

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.63 _init_vars;
17 root 1.47
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 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
359 root 1.39 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.63
701     $msg->("reattach");
702     _global_reattach;
703 root 1.36 };
704     $msg->($@) if $@;
705 root 1.27
706 root 1.36 $msg->("reloaded");
707     };
708 root 1.27
709 root 1.36 sub perl_reload() {
710     _perl_reload {
711     warn $_[0];
712     print "$_[0]\n";
713     };
714     }
715 root 1.27
716 root 1.36 register_command "perl-reload", 0, sub {
717     my ($who, $arg) = @_;
718 root 1.27
719 root 1.36 if ($who->flag (FLAG_WIZ)) {
720     _perl_reload {
721     warn $_[0];
722     $who->message ($_[0]);
723 root 1.4 };
724 root 1.1 }
725     };
726    
727 root 1.8 #############################################################################
728 root 1.28 # extcmd framework, basically convert ext <msg>
729 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
730    
731 root 1.44 attach_to_players
732 root 1.43 on_extcmd => sub {
733     my ($pl, $buf) = @_;
734    
735     my $msg = eval { from_json $buf };
736    
737     if (ref $msg) {
738     if (my $cb = $extcmd{$msg->{msgtype}}) {
739     if (my %reply = $cb->[0]->($pl, $msg)) {
740     $pl->ext_reply ($msg->{msgid}, %reply);
741     }
742 root 1.28 }
743 root 1.43 } else {
744     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
745 root 1.28 }
746 root 1.15
747 root 1.43 cf::override;
748     },
749     ;
750 root 1.15
751     #############################################################################
752 root 1.8 # load/save/clean perl data associated with a map
753    
754 root 1.39 *cf::mapsupport::on_clean = sub {
755 root 1.13 my ($map) = @_;
756 root 1.7
757     my $path = $map->tmpname;
758     defined $path or return;
759    
760 root 1.46 unlink "$path.pst";
761 root 1.7 };
762    
763 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
764    
765 root 1.8 #############################################################################
766     # load/save perl data associated with player->ob objects
767    
768 root 1.33 sub all_objects(@) {
769     @_, map all_objects ($_->inv), @_
770     }
771    
772 root 1.60 # TODO: compatibility cruft, remove when no longer needed
773 root 1.39 attach_to_players
774     on_load => sub {
775     my ($pl, $path) = @_;
776    
777     for my $o (all_objects $pl->ob) {
778     if (my $value = $o->get_ob_key_value ("_perl_data")) {
779     $o->set_ob_key_value ("_perl_data");
780 root 1.8
781 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
782     }
783 root 1.11 }
784 root 1.39 },
785     ;
786 root 1.6
787 root 1.22 #############################################################################
788     # core extensions - in perl
789    
790 root 1.23 =item cf::player::exists $login
791    
792     Returns true when the given account exists.
793    
794     =cut
795    
796     sub cf::player::exists($) {
797     cf::player::find $_[0]
798     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
799     }
800    
801 root 1.28 =item $player->reply ($npc, $msg[, $flags])
802    
803     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
804     can be C<undef>. Does the right thing when the player is currently in a
805     dialogue with the given NPC character.
806    
807     =cut
808    
809 root 1.22 # rough implementation of a future "reply" method that works
810     # with dialog boxes.
811 root 1.23 sub cf::object::player::reply($$$;$) {
812     my ($self, $npc, $msg, $flags) = @_;
813    
814     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
815 root 1.22
816 root 1.24 if ($self->{record_replies}) {
817     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
818     } else {
819     $msg = $npc->name . " says: $msg" if $npc;
820     $self->message ($msg, $flags);
821     }
822 root 1.22 }
823    
824 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
825    
826     Sends an ext reply to the player.
827    
828     =cut
829    
830     sub cf::player::ext_reply($$$%) {
831     my ($self, $id, %msg) = @_;
832    
833     $msg{msgid} = $id;
834    
835     $self->send ("ext " . to_json \%msg);
836     }
837    
838 root 1.22 #############################################################################
839 root 1.23 # map scripting support
840    
841 root 1.42 our $safe = new Safe "safe";
842 root 1.23 our $safe_hole = new Safe::Hole;
843    
844     $SIG{FPE} = 'IGNORE';
845    
846     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
847    
848 root 1.25 # here we export the classes and methods available to script code
849    
850     for (
851 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
852 root 1.25 ["cf::object::player" => qw(player)],
853     ["cf::player" => qw(peaceful)],
854     ) {
855     no strict 'refs';
856     my ($pkg, @funs) = @$_;
857 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
858 root 1.25 for @funs;
859     }
860 root 1.23
861     sub safe_eval($;@) {
862     my ($code, %vars) = @_;
863    
864     my $qcode = $code;
865     $qcode =~ s/"/‟/g; # not allowed in #line filenames
866     $qcode =~ s/\n/\\n/g;
867    
868     local $_;
869 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
870 root 1.23
871 root 1.42 my $eval =
872 root 1.23 "do {\n"
873     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
874     . "#line 0 \"{$qcode}\"\n"
875     . $code
876     . "\n}"
877 root 1.25 ;
878    
879     sub_generation_inc;
880 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
881 root 1.25 sub_generation_inc;
882    
883 root 1.42 if ($@) {
884     warn "$@";
885     warn "while executing safe code '$code'\n";
886     warn "with arguments " . (join " ", %vars) . "\n";
887     }
888    
889 root 1.25 wantarray ? @res : $res[0]
890 root 1.23 }
891    
892     sub register_script_function {
893     my ($fun, $cb) = @_;
894    
895     no strict 'refs';
896 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
897 root 1.23 }
898    
899     #############################################################################
900 root 1.34 # the server's main()
901    
902 root 1.39 sub main {
903 root 1.61 load_extensions;
904 root 1.34 Event::loop;
905     }
906    
907     #############################################################################
908 root 1.22 # initialisation
909    
910 root 1.6 register "<global>", __PACKAGE__;
911    
912 root 1.27 unshift @INC, $LIBDIR;
913 root 1.17
914 root 1.35 $TICK_WATCHER = Event->timer (
915     prio => 1,
916     at => $NEXT_TICK || 1,
917     cb => sub {
918     cf::server_tick; # one server iteration
919    
920     my $NOW = Event::time;
921     $NEXT_TICK += $TICK;
922    
923 root 1.37 # if we are delayed by four ticks, skip them all
924     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
925 root 1.35
926     $TICK_WATCHER->at ($NEXT_TICK);
927     $TICK_WATCHER->start;
928     },
929     );
930    
931 root 1.1 1
932