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