ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.54
Committed: Tue Aug 29 14:49:28 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.53: +11 -24 lines
Log Message:
implement detach

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.54 *cf::object::attach =
288     *cf::player::attach =
289     *cf::map::attach = sub {
290 root 1.48 my ($obj, $name, %arg) = @_;
291 root 1.46
292 root 1.48 _attach_attachment $obj, $name, %arg;
293 root 1.46 }
294    
295 root 1.54 # all those should be optimised
296     *cf::object::detach =
297     *cf::player::detach =
298     *cf::map::detach = sub {
299     my ($obj, $name) = @_;
300 root 1.46
301 root 1.54 delete $obj->{_attachment}{$name};
302     reattach $obj;
303     };
304 root 1.53
305 root 1.39 sub attach_global {
306     _attach @CB_GLOBAL, KLASS_GLOBAL, @_
307     }
308    
309 root 1.40 sub attach_to_type {
310 root 1.39 my $type = shift;
311 root 1.47 my $subtype = shift;
312 root 1.45
313 root 1.47 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_
314 root 1.39 }
315    
316     sub attach_to_objects {
317 root 1.40 _attach @CB_OBJECT, KLASS_OBJECT, @_
318 root 1.39 }
319    
320     sub attach_to_players {
321 root 1.40 _attach @CB_PLAYER, KLASS_PLAYER, @_
322 root 1.39 }
323    
324     sub attach_to_maps {
325 root 1.40 _attach @CB_MAP, KLASS_MAP, @_
326 root 1.39 }
327    
328 root 1.45 sub register_attachment {
329     my $name = shift;
330    
331 root 1.47 $attachment{$name} = [[KLASS_OBJECT, @_]];
332 root 1.45 }
333    
334 root 1.52 sub register_map_attachment {
335     my $name = shift;
336    
337     $attachment{$name} = [[KLASS_MAP, @_]];
338     }
339    
340 root 1.39 our $override;
341 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
342 root 1.39
343 root 1.45 sub override {
344     $override = 1;
345     @invoke_results = ();
346 root 1.39 }
347    
348 root 1.45 sub do_invoke {
349 root 1.39 my $event = shift;
350 root 1.40 my $callbacks = shift;
351 root 1.39
352 root 1.45 @invoke_results = ();
353    
354 root 1.39 local $override;
355    
356 root 1.40 for (@$callbacks) {
357 root 1.39 eval { &{$_->[1]} };
358    
359     if ($@) {
360     warn "$@";
361     warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n";
362     override;
363     }
364    
365     return 1 if $override;
366     }
367    
368     0
369     }
370    
371     #############################################################################
372 root 1.45 # object support
373    
374     sub instantiate {
375     my ($obj, $data) = @_;
376    
377     $data = from_json $data;
378    
379     for (@$data) {
380 root 1.46 my ($name, $args) = @$_;
381 root 1.49
382     $obj->attach ($name, %{$args || {} });
383 root 1.46 }
384     }
385    
386     # basically do the same as instantiate, without calling instantiate
387     sub reattach {
388     my ($obj) = @_;
389     my $registry = $obj->registry;
390 root 1.45
391 root 1.47 @$registry = ();
392    
393 root 1.50 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
394 root 1.49
395 root 1.50 for my $name (keys %{ $obj->{_attachment} || {} }) {
396 root 1.45 if (my $attach = $attachment{$name}) {
397 root 1.47 for (@$attach) {
398     my ($klass, @attach) = @$_;
399     _attach @$registry, $klass, @attach;
400     }
401 root 1.45 } else {
402 root 1.46 warn "object uses attachment '$name' that is not available, postponing.\n";
403 root 1.45 }
404 root 1.46 }
405     }
406 root 1.45
407 root 1.46 sub object_freezer_save {
408     my ($filename, $objs) = @_;
409    
410     if (@$objs) {
411 root 1.51 open my $fh, ">:raw", "$filename.pst~";
412 root 1.46 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
413     close $fh;
414 root 1.51 chmod SAVE_MODE, "$filename.pst~";
415     rename "$filename.pst~", "$filename.pst";
416 root 1.46 } else {
417 root 1.51 unlink "$filename.pst";
418 root 1.45 }
419 root 1.51
420     chmod SAVE_MODE, "$filename~";
421     rename "$filename~", $filename;
422 root 1.45 }
423    
424 root 1.46 sub object_thawer_load {
425     my ($filename) = @_;
426    
427     open my $fh, "<:raw:perlio", "$filename.pst"
428     or return;
429 root 1.45
430 root 1.46 eval { local $/; (Storable::thaw <$fh>)->{objs} }
431 root 1.45 }
432    
433     attach_to_objects
434     prio => -1000000,
435     on_clone => sub {
436     my ($src, $dst) = @_;
437    
438     @{$dst->registry} = @{$src->registry};
439    
440     %$dst = %$src;
441    
442 root 1.50 %{$dst->{_attachment}} = %{$src->{_attachment}}
443 root 1.45 if exists $src->{_attachment};
444     },
445     ;
446    
447     #############################################################################
448 root 1.39 # old plug-in events
449    
450 root 1.1 sub inject_event {
451 root 1.14 my $extension = shift;
452     my $event_code = shift;
453 root 1.1
454 root 1.14 my $cb = $hook[$event_code]{$extension}
455 root 1.5 or return;
456    
457 root 1.14 &$cb
458 root 1.5 }
459    
460     sub inject_global_event {
461 root 1.12 my $event = shift;
462 root 1.5
463 root 1.12 my $cb = $hook[$event]
464 root 1.1 or return;
465    
466 root 1.12 List::Util::max map &$_, values %$cb
467 root 1.1 }
468    
469     sub inject_command {
470     my ($name, $obj, $params) = @_;
471    
472     for my $cmd (@{ $command{$name} }) {
473     $cmd->[1]->($obj, $params);
474     }
475    
476     -1
477     }
478    
479     sub register_command {
480     my ($name, $time, $cb) = @_;
481    
482     my $caller = caller;
483 root 1.16 #warn "registering command '$name/$time' to '$caller'";
484 root 1.4
485 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
486     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
487     }
488    
489 root 1.16 sub register_extcmd {
490     my ($name, $cb) = @_;
491    
492     my $caller = caller;
493     #warn "registering extcmd '$name' to '$caller'";
494    
495     $extcmd{$name} = [$cb, $caller];
496     }
497    
498 root 1.6 sub register {
499     my ($base, $pkg) = @_;
500    
501 root 1.45 #TODO
502 root 1.6 }
503    
504 root 1.1 sub load_extension {
505     my ($path) = @_;
506    
507     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
508 root 1.5 my $base = $1;
509 root 1.1 my $pkg = $1;
510     $pkg =~ s/[^[:word:]]/_/g;
511 root 1.41 $pkg = "ext::$pkg";
512 root 1.1
513     warn "loading '$path' into '$pkg'\n";
514    
515     open my $fh, "<:utf8", $path
516     or die "$path: $!";
517    
518     my $source =
519     "package $pkg; use strict; use utf8;\n"
520     . "#line 1 \"$path\"\n{\n"
521     . (do { local $/; <$fh> })
522     . "\n};\n1";
523    
524     eval $source
525     or die "$path: $@";
526    
527     push @exts, $pkg;
528 root 1.5 $ext_pkg{$base} = $pkg;
529 root 1.1
530 root 1.6 # no strict 'refs';
531 root 1.23 # @{"$pkg\::ISA"} = ext::;
532 root 1.1
533 root 1.6 register $base, $pkg;
534 root 1.1 }
535    
536     sub unload_extension {
537     my ($pkg) = @_;
538    
539     warn "removing extension $pkg\n";
540    
541     # remove hooks
542 root 1.45 #TODO
543     # for my $idx (0 .. $#PLUGIN_EVENT) {
544     # delete $hook[$idx]{$pkg};
545     # }
546 root 1.1
547     # remove commands
548     for my $name (keys %command) {
549     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
550    
551     if (@cb) {
552     $command{$name} = \@cb;
553     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
554     } else {
555     delete $command{$name};
556     delete $COMMAND{"$name\000"};
557     }
558     }
559    
560 root 1.15 # remove extcmds
561 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
562     delete $extcmd{$name};
563 root 1.15 }
564    
565 root 1.43 if (my $cb = $pkg->can ("unload")) {
566 elmex 1.31 eval {
567     $cb->($pkg);
568     1
569     } or warn "$pkg unloaded, but with errors: $@";
570     }
571    
572 root 1.1 Symbol::delete_package $pkg;
573     }
574    
575     sub load_extensions {
576     my $LIBDIR = maps_directory "perl";
577    
578     for my $ext (<$LIBDIR/*.ext>) {
579 root 1.3 next unless -r $ext;
580 root 1.2 eval {
581     load_extension $ext;
582     1
583     } or warn "$ext not loaded: $@";
584 root 1.1 }
585     }
586    
587 root 1.36 sub _perl_reload(&) {
588     my ($msg) = @_;
589    
590     $msg->("reloading...");
591    
592     eval {
593     # 1. cancel all watchers
594     $_->cancel for Event::all_watchers;
595    
596     # 2. unload all extensions
597     for (@exts) {
598     $msg->("unloading <$_>");
599     unload_extension $_;
600     }
601    
602     # 3. unload all modules loaded from $LIBDIR
603     while (my ($k, $v) = each %INC) {
604     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
605    
606     $msg->("removing <$k>");
607     delete $INC{$k};
608 root 1.1
609 root 1.36 $k =~ s/\.pm$//;
610     $k =~ s/\//::/g;
611 root 1.3
612 root 1.36 if (my $cb = $k->can ("unload_module")) {
613     $cb->();
614 root 1.27 }
615    
616 root 1.36 Symbol::delete_package $k;
617     }
618 root 1.27
619 root 1.41 # 4. get rid of safe::, as good as possible
620     Symbol::delete_package "safe::$_"
621 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
622 root 1.36
623     # 5. remove register_script_function callbacks
624     # TODO
625    
626     # 6. unload cf.pm "a bit"
627     delete $INC{"cf.pm"};
628    
629 root 1.41 # don't, removes xs symbols, too,
630     # and global variables created in xs
631 root 1.36 #Symbol::delete_package __PACKAGE__;
632    
633     # 7. reload cf.pm
634     $msg->("reloading cf.pm");
635     require cf;
636     };
637     $msg->($@) if $@;
638 root 1.27
639 root 1.36 $msg->("reloaded");
640     };
641 root 1.27
642 root 1.36 sub perl_reload() {
643     _perl_reload {
644     warn $_[0];
645     print "$_[0]\n";
646     };
647     }
648 root 1.27
649 root 1.36 register_command "perl-reload", 0, sub {
650     my ($who, $arg) = @_;
651 root 1.27
652 root 1.36 if ($who->flag (FLAG_WIZ)) {
653     _perl_reload {
654     warn $_[0];
655     $who->message ($_[0]);
656 root 1.4 };
657 root 1.1 }
658     };
659    
660 root 1.8 #############################################################################
661 root 1.28 # extcmd framework, basically convert ext <msg>
662 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
663    
664 root 1.44 attach_to_players
665 root 1.43 on_extcmd => sub {
666     my ($pl, $buf) = @_;
667    
668     my $msg = eval { from_json $buf };
669    
670     if (ref $msg) {
671     if (my $cb = $extcmd{$msg->{msgtype}}) {
672     if (my %reply = $cb->[0]->($pl, $msg)) {
673     $pl->ext_reply ($msg->{msgid}, %reply);
674     }
675 root 1.28 }
676 root 1.43 } else {
677     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
678 root 1.28 }
679 root 1.15
680 root 1.43 cf::override;
681     },
682     ;
683 root 1.15
684     #############################################################################
685 root 1.8 # load/save/clean perl data associated with a map
686    
687 root 1.39 *cf::mapsupport::on_clean = sub {
688 root 1.13 my ($map) = @_;
689 root 1.7
690     my $path = $map->tmpname;
691     defined $path or return;
692    
693 root 1.46 unlink "$path.pst";
694 root 1.7 };
695    
696 root 1.51 # old style persistent data, TODO: remove #d#
697 root 1.39 *cf::mapsupport::on_swapin =
698     *cf::mapsupport::on_load = sub {
699 root 1.13 my ($map) = @_;
700 root 1.6
701     my $path = $map->tmpname;
702     $path = $map->path unless defined $path;
703    
704     open my $fh, "<:raw", "$path.cfperl"
705     or return; # no perl data
706    
707     my $data = Storable::thaw do { local $/; <$fh> };
708    
709     $data->{version} <= 1
710     or return; # too new
711    
712     $map->_set_obs ($data->{obs});
713 root 1.50 $map->invoke (EVENT_MAP_UPGRADE);
714 root 1.6 };
715    
716 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
717    
718 root 1.8 #############################################################################
719     # load/save perl data associated with player->ob objects
720    
721 root 1.33 sub all_objects(@) {
722     @_, map all_objects ($_->inv), @_
723     }
724    
725 root 1.39 attach_to_players
726     on_load => sub {
727     my ($pl, $path) = @_;
728    
729     for my $o (all_objects $pl->ob) {
730     if (my $value = $o->get_ob_key_value ("_perl_data")) {
731     $o->set_ob_key_value ("_perl_data");
732 root 1.8
733 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
734     }
735 root 1.11 }
736 root 1.39 },
737     ;
738 root 1.6
739 root 1.22 #############################################################################
740     # core extensions - in perl
741    
742 root 1.23 =item cf::player::exists $login
743    
744     Returns true when the given account exists.
745    
746     =cut
747    
748     sub cf::player::exists($) {
749     cf::player::find $_[0]
750     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
751     }
752    
753 root 1.28 =item $player->reply ($npc, $msg[, $flags])
754    
755     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
756     can be C<undef>. Does the right thing when the player is currently in a
757     dialogue with the given NPC character.
758    
759     =cut
760    
761 root 1.22 # rough implementation of a future "reply" method that works
762     # with dialog boxes.
763 root 1.23 sub cf::object::player::reply($$$;$) {
764     my ($self, $npc, $msg, $flags) = @_;
765    
766     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
767 root 1.22
768 root 1.24 if ($self->{record_replies}) {
769     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
770     } else {
771     $msg = $npc->name . " says: $msg" if $npc;
772     $self->message ($msg, $flags);
773     }
774 root 1.22 }
775    
776 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
777    
778     Sends an ext reply to the player.
779    
780     =cut
781    
782     sub cf::player::ext_reply($$$%) {
783     my ($self, $id, %msg) = @_;
784    
785     $msg{msgid} = $id;
786    
787     $self->send ("ext " . to_json \%msg);
788     }
789    
790 root 1.22 #############################################################################
791 root 1.23 # map scripting support
792    
793 root 1.42 our $safe = new Safe "safe";
794 root 1.23 our $safe_hole = new Safe::Hole;
795    
796     $SIG{FPE} = 'IGNORE';
797    
798     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
799    
800 root 1.25 # here we export the classes and methods available to script code
801    
802     for (
803 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
804 root 1.25 ["cf::object::player" => qw(player)],
805     ["cf::player" => qw(peaceful)],
806     ) {
807     no strict 'refs';
808     my ($pkg, @funs) = @$_;
809 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
810 root 1.25 for @funs;
811     }
812 root 1.23
813     sub safe_eval($;@) {
814     my ($code, %vars) = @_;
815    
816     my $qcode = $code;
817     $qcode =~ s/"/‟/g; # not allowed in #line filenames
818     $qcode =~ s/\n/\\n/g;
819    
820     local $_;
821 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
822 root 1.23
823 root 1.42 my $eval =
824 root 1.23 "do {\n"
825     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
826     . "#line 0 \"{$qcode}\"\n"
827     . $code
828     . "\n}"
829 root 1.25 ;
830    
831     sub_generation_inc;
832 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
833 root 1.25 sub_generation_inc;
834    
835 root 1.42 if ($@) {
836     warn "$@";
837     warn "while executing safe code '$code'\n";
838     warn "with arguments " . (join " ", %vars) . "\n";
839     }
840    
841 root 1.25 wantarray ? @res : $res[0]
842 root 1.23 }
843    
844     sub register_script_function {
845     my ($fun, $cb) = @_;
846    
847     no strict 'refs';
848 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
849 root 1.23 }
850    
851     #############################################################################
852 root 1.34 # the server's main()
853    
854 root 1.39 sub main {
855 root 1.34 Event::loop;
856     }
857    
858     #############################################################################
859 root 1.22 # initialisation
860    
861 root 1.6 register "<global>", __PACKAGE__;
862    
863 root 1.27 unshift @INC, $LIBDIR;
864 root 1.17
865 root 1.1 load_extensions;
866    
867 root 1.35 $TICK_WATCHER = Event->timer (
868     prio => 1,
869     at => $NEXT_TICK || 1,
870     cb => sub {
871     cf::server_tick; # one server iteration
872    
873     my $NOW = Event::time;
874     $NEXT_TICK += $TICK;
875    
876 root 1.37 # if we are delayed by four ticks, skip them all
877     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
878 root 1.35
879     $TICK_WATCHER->at ($NEXT_TICK);
880     $TICK_WATCHER->start;
881     },
882     );
883    
884 root 1.47 _reload_2;
885    
886 root 1.1 1
887