ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.60
Committed: Thu Aug 31 06:23:19 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.59: +20 -11 lines
Log Message:
*** empty log message ***

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.60 if (length $$rdata) {
484     warn sprintf "saving %s (%d,%d)\n",
485     $filename, length $$rdata, scalar @$objs;
486 root 1.59
487 root 1.60 if (open my $fh, ">:raw", "$filename~") {
488 root 1.59 chmod SAVE_MODE, $fh;
489 root 1.60 syswrite $fh, $$rdata;
490 root 1.59 close $fh;
491 root 1.60
492     if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
493     chmod SAVE_MODE, $fh;
494     syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
495     close $fh;
496     rename "$filename.pst~", "$filename.pst";
497     } else {
498     unlink "$filename.pst";
499     }
500    
501     rename "$filename~", $filename;
502 root 1.59 } else {
503 root 1.60 warn "FATAL: $filename~: $!\n";
504 root 1.59 }
505 root 1.46 } else {
506 root 1.60 unlink $filename;
507     unlink "$filename.pst";
508 root 1.45 }
509     }
510    
511 root 1.46 sub object_thawer_load {
512     my ($filename) = @_;
513    
514     open my $fh, "<:raw:perlio", "$filename.pst"
515     or return;
516 root 1.45
517 root 1.46 eval { local $/; (Storable::thaw <$fh>)->{objs} }
518 root 1.45 }
519    
520     attach_to_objects
521     prio => -1000000,
522     on_clone => sub {
523     my ($src, $dst) = @_;
524    
525     @{$dst->registry} = @{$src->registry};
526    
527     %$dst = %$src;
528    
529 root 1.50 %{$dst->{_attachment}} = %{$src->{_attachment}}
530 root 1.45 if exists $src->{_attachment};
531     },
532     ;
533    
534     #############################################################################
535 root 1.39 # old plug-in events
536    
537 root 1.1 sub inject_event {
538 root 1.14 my $extension = shift;
539     my $event_code = shift;
540 root 1.1
541 root 1.14 my $cb = $hook[$event_code]{$extension}
542 root 1.5 or return;
543    
544 root 1.14 &$cb
545 root 1.5 }
546    
547     sub inject_global_event {
548 root 1.12 my $event = shift;
549 root 1.5
550 root 1.12 my $cb = $hook[$event]
551 root 1.1 or return;
552    
553 root 1.12 List::Util::max map &$_, values %$cb
554 root 1.1 }
555    
556     sub inject_command {
557     my ($name, $obj, $params) = @_;
558    
559     for my $cmd (@{ $command{$name} }) {
560     $cmd->[1]->($obj, $params);
561     }
562    
563     -1
564     }
565    
566     sub register_command {
567     my ($name, $time, $cb) = @_;
568    
569     my $caller = caller;
570 root 1.16 #warn "registering command '$name/$time' to '$caller'";
571 root 1.4
572 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
573     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
574     }
575    
576 root 1.16 sub register_extcmd {
577     my ($name, $cb) = @_;
578    
579     my $caller = caller;
580     #warn "registering extcmd '$name' to '$caller'";
581    
582     $extcmd{$name} = [$cb, $caller];
583     }
584    
585 root 1.6 sub register {
586     my ($base, $pkg) = @_;
587    
588 root 1.45 #TODO
589 root 1.6 }
590    
591 root 1.1 sub load_extension {
592     my ($path) = @_;
593    
594     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
595 root 1.5 my $base = $1;
596 root 1.1 my $pkg = $1;
597     $pkg =~ s/[^[:word:]]/_/g;
598 root 1.41 $pkg = "ext::$pkg";
599 root 1.1
600     warn "loading '$path' into '$pkg'\n";
601    
602     open my $fh, "<:utf8", $path
603     or die "$path: $!";
604    
605     my $source =
606     "package $pkg; use strict; use utf8;\n"
607     . "#line 1 \"$path\"\n{\n"
608     . (do { local $/; <$fh> })
609     . "\n};\n1";
610    
611     eval $source
612     or die "$path: $@";
613    
614     push @exts, $pkg;
615 root 1.5 $ext_pkg{$base} = $pkg;
616 root 1.1
617 root 1.6 # no strict 'refs';
618 root 1.23 # @{"$pkg\::ISA"} = ext::;
619 root 1.1
620 root 1.6 register $base, $pkg;
621 root 1.1 }
622    
623     sub unload_extension {
624     my ($pkg) = @_;
625    
626     warn "removing extension $pkg\n";
627    
628     # remove hooks
629 root 1.45 #TODO
630     # for my $idx (0 .. $#PLUGIN_EVENT) {
631     # delete $hook[$idx]{$pkg};
632     # }
633 root 1.1
634     # remove commands
635     for my $name (keys %command) {
636     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
637    
638     if (@cb) {
639     $command{$name} = \@cb;
640     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
641     } else {
642     delete $command{$name};
643     delete $COMMAND{"$name\000"};
644     }
645     }
646    
647 root 1.15 # remove extcmds
648 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
649     delete $extcmd{$name};
650 root 1.15 }
651    
652 root 1.43 if (my $cb = $pkg->can ("unload")) {
653 elmex 1.31 eval {
654     $cb->($pkg);
655     1
656     } or warn "$pkg unloaded, but with errors: $@";
657     }
658    
659 root 1.1 Symbol::delete_package $pkg;
660     }
661    
662     sub load_extensions {
663     my $LIBDIR = maps_directory "perl";
664    
665     for my $ext (<$LIBDIR/*.ext>) {
666 root 1.3 next unless -r $ext;
667 root 1.2 eval {
668     load_extension $ext;
669     1
670     } or warn "$ext not loaded: $@";
671 root 1.1 }
672     }
673    
674 root 1.36 sub _perl_reload(&) {
675     my ($msg) = @_;
676    
677     $msg->("reloading...");
678    
679     eval {
680     # 1. cancel all watchers
681     $_->cancel for Event::all_watchers;
682    
683     # 2. unload all extensions
684     for (@exts) {
685     $msg->("unloading <$_>");
686     unload_extension $_;
687     }
688    
689     # 3. unload all modules loaded from $LIBDIR
690     while (my ($k, $v) = each %INC) {
691     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
692    
693     $msg->("removing <$k>");
694     delete $INC{$k};
695 root 1.1
696 root 1.36 $k =~ s/\.pm$//;
697     $k =~ s/\//::/g;
698 root 1.3
699 root 1.36 if (my $cb = $k->can ("unload_module")) {
700     $cb->();
701 root 1.27 }
702    
703 root 1.36 Symbol::delete_package $k;
704     }
705 root 1.27
706 root 1.41 # 4. get rid of safe::, as good as possible
707     Symbol::delete_package "safe::$_"
708 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
709 root 1.36
710     # 5. remove register_script_function callbacks
711     # TODO
712    
713     # 6. unload cf.pm "a bit"
714     delete $INC{"cf.pm"};
715    
716 root 1.41 # don't, removes xs symbols, too,
717     # and global variables created in xs
718 root 1.36 #Symbol::delete_package __PACKAGE__;
719    
720     # 7. reload cf.pm
721     $msg->("reloading cf.pm");
722     require cf;
723     };
724     $msg->($@) if $@;
725 root 1.27
726 root 1.36 $msg->("reloaded");
727     };
728 root 1.27
729 root 1.36 sub perl_reload() {
730     _perl_reload {
731     warn $_[0];
732     print "$_[0]\n";
733     };
734     }
735 root 1.27
736 root 1.36 register_command "perl-reload", 0, sub {
737     my ($who, $arg) = @_;
738 root 1.27
739 root 1.36 if ($who->flag (FLAG_WIZ)) {
740     _perl_reload {
741     warn $_[0];
742     $who->message ($_[0]);
743 root 1.4 };
744 root 1.1 }
745     };
746    
747 root 1.8 #############################################################################
748 root 1.28 # extcmd framework, basically convert ext <msg>
749 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
750    
751 root 1.44 attach_to_players
752 root 1.43 on_extcmd => sub {
753     my ($pl, $buf) = @_;
754    
755     my $msg = eval { from_json $buf };
756    
757     if (ref $msg) {
758     if (my $cb = $extcmd{$msg->{msgtype}}) {
759     if (my %reply = $cb->[0]->($pl, $msg)) {
760     $pl->ext_reply ($msg->{msgid}, %reply);
761     }
762 root 1.28 }
763 root 1.43 } else {
764     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
765 root 1.28 }
766 root 1.15
767 root 1.43 cf::override;
768     },
769     ;
770 root 1.15
771     #############################################################################
772 root 1.8 # load/save/clean perl data associated with a map
773    
774 root 1.39 *cf::mapsupport::on_clean = sub {
775 root 1.13 my ($map) = @_;
776 root 1.7
777     my $path = $map->tmpname;
778     defined $path or return;
779    
780 root 1.46 unlink "$path.pst";
781 root 1.7 };
782    
783 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
784    
785 root 1.8 #############################################################################
786     # load/save perl data associated with player->ob objects
787    
788 root 1.33 sub all_objects(@) {
789     @_, map all_objects ($_->inv), @_
790     }
791    
792 root 1.60 # TODO: compatibility cruft, remove when no longer needed
793 root 1.39 attach_to_players
794     on_load => sub {
795     my ($pl, $path) = @_;
796    
797     for my $o (all_objects $pl->ob) {
798     if (my $value = $o->get_ob_key_value ("_perl_data")) {
799     $o->set_ob_key_value ("_perl_data");
800 root 1.8
801 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
802     }
803 root 1.11 }
804 root 1.39 },
805     ;
806 root 1.6
807 root 1.22 #############################################################################
808     # core extensions - in perl
809    
810 root 1.23 =item cf::player::exists $login
811    
812     Returns true when the given account exists.
813    
814     =cut
815    
816     sub cf::player::exists($) {
817     cf::player::find $_[0]
818     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
819     }
820    
821 root 1.28 =item $player->reply ($npc, $msg[, $flags])
822    
823     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
824     can be C<undef>. Does the right thing when the player is currently in a
825     dialogue with the given NPC character.
826    
827     =cut
828    
829 root 1.22 # rough implementation of a future "reply" method that works
830     # with dialog boxes.
831 root 1.23 sub cf::object::player::reply($$$;$) {
832     my ($self, $npc, $msg, $flags) = @_;
833    
834     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
835 root 1.22
836 root 1.24 if ($self->{record_replies}) {
837     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
838     } else {
839     $msg = $npc->name . " says: $msg" if $npc;
840     $self->message ($msg, $flags);
841     }
842 root 1.22 }
843    
844 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
845    
846     Sends an ext reply to the player.
847    
848     =cut
849    
850     sub cf::player::ext_reply($$$%) {
851     my ($self, $id, %msg) = @_;
852    
853     $msg{msgid} = $id;
854    
855     $self->send ("ext " . to_json \%msg);
856     }
857    
858 root 1.22 #############################################################################
859 root 1.23 # map scripting support
860    
861 root 1.42 our $safe = new Safe "safe";
862 root 1.23 our $safe_hole = new Safe::Hole;
863    
864     $SIG{FPE} = 'IGNORE';
865    
866     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
867    
868 root 1.25 # here we export the classes and methods available to script code
869    
870     for (
871 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
872 root 1.25 ["cf::object::player" => qw(player)],
873     ["cf::player" => qw(peaceful)],
874     ) {
875     no strict 'refs';
876     my ($pkg, @funs) = @$_;
877 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
878 root 1.25 for @funs;
879     }
880 root 1.23
881     sub safe_eval($;@) {
882     my ($code, %vars) = @_;
883    
884     my $qcode = $code;
885     $qcode =~ s/"/‟/g; # not allowed in #line filenames
886     $qcode =~ s/\n/\\n/g;
887    
888     local $_;
889 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
890 root 1.23
891 root 1.42 my $eval =
892 root 1.23 "do {\n"
893     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
894     . "#line 0 \"{$qcode}\"\n"
895     . $code
896     . "\n}"
897 root 1.25 ;
898    
899     sub_generation_inc;
900 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
901 root 1.25 sub_generation_inc;
902    
903 root 1.42 if ($@) {
904     warn "$@";
905     warn "while executing safe code '$code'\n";
906     warn "with arguments " . (join " ", %vars) . "\n";
907     }
908    
909 root 1.25 wantarray ? @res : $res[0]
910 root 1.23 }
911    
912     sub register_script_function {
913     my ($fun, $cb) = @_;
914    
915     no strict 'refs';
916 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
917 root 1.23 }
918    
919     #############################################################################
920 root 1.34 # the server's main()
921    
922 root 1.39 sub main {
923 root 1.34 Event::loop;
924     }
925    
926     #############################################################################
927 root 1.22 # initialisation
928    
929 root 1.6 register "<global>", __PACKAGE__;
930    
931 root 1.27 unshift @INC, $LIBDIR;
932 root 1.17
933 root 1.1 load_extensions;
934    
935 root 1.35 $TICK_WATCHER = Event->timer (
936     prio => 1,
937     at => $NEXT_TICK || 1,
938     cb => sub {
939     cf::server_tick; # one server iteration
940    
941     my $NOW = Event::time;
942     $NEXT_TICK += $TICK;
943    
944 root 1.37 # if we are delayed by four ticks, skip them all
945     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
946 root 1.35
947     $TICK_WATCHER->at ($NEXT_TICK);
948     $TICK_WATCHER->start;
949     },
950     );
951    
952 root 1.47 _reload_2;
953    
954 root 1.1 1
955