ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.58
Committed: Wed Aug 30 12:08:15 2006 UTC (17 years, 8 months ago) by root
Branch: MAIN
Changes since 1.57: +0 -20 lines
Log Message:
remove compatibility code

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