ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.59
Committed: Wed Aug 30 16:30:37 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.58: +16 -10 lines
Log Message:
remove compression support, intiialise perl earlier etc. etc.

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