ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
(Generate patch)

Comparing deliantra/server/lib/cf.pm (file contents):
Revision 1.46 by root, Sun Aug 27 16:15:12 2006 UTC vs.
Revision 1.76 by root, Mon Oct 2 15:28:36 2006 UTC

5use Storable; 5use Storable;
6use Opcode; 6use Opcode;
7use Safe; 7use Safe;
8use Safe::Hole; 8use Safe::Hole;
9 9
10use YAML::Syck ();
10use Time::HiRes; 11use Time::HiRes;
11use Event; 12use Event;
12$Event::Eval = 1; # no idea why this is required, but it is 13$Event::Eval = 1; # no idea why this is required, but it is
13 14
15# work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
16$YAML::Syck::ImplicitUnicode = 1;
17
14use strict; 18use strict;
19
20_init_vars;
15 21
16our %COMMAND = (); 22our %COMMAND = ();
17our @EVENT; 23our @EVENT;
18our %PROP_TYPE;
19our %PROP_IDX;
20our $LIBDIR = maps_directory "perl"; 24our $LIBDIR = maps_directory "perl";
21 25
22our $TICK = MAX_TIME * 1e-6; 26our $TICK = MAX_TIME * 1e-6;
23our $TICK_WATCHER; 27our $TICK_WATCHER;
24our $NEXT_TICK; 28our $NEXT_TICK;
29
30our %CFG;
31
32our $uptime;
33
34$uptime ||= time;
35
36#############################################################################
37
38=head2 GLOBAL VARIABLES
39
40=over 4
41
42=item $cf::LIBDIR
43
44The perl library directory, where extensions and cf-specific modules can
45be found. It will be added to C<@INC> automatically.
46
47=item $cf::TICK
48
49The interval between server ticks, in seconds.
50
51=item %cf::CFG
52
53Configuration for the server, loaded from C</etc/crossfire/config>, or
54from wherever your confdir points to.
55
56=back
57
58=cut
25 59
26BEGIN { 60BEGIN {
27 *CORE::GLOBAL::warn = sub { 61 *CORE::GLOBAL::warn = sub {
28 my $msg = join "", @_; 62 my $msg = join "", @_;
29 $msg .= "\n" 63 $msg .= "\n"
32 print STDERR "cfperl: $msg"; 66 print STDERR "cfperl: $msg";
33 LOG llevError, "cfperl: $msg"; 67 LOG llevError, "cfperl: $msg";
34 }; 68 };
35} 69}
36 70
37my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
38
39# generate property mutators
40sub prop_gen {
41 my ($prefix, $class) = @_;
42
43 no strict 'refs';
44
45 for my $prop (keys %PROP_TYPE) {
46 $prop =~ /^\Q$prefix\E_(.*$)/ or next;
47 my $sub = lc $1;
48
49 my $type = $PROP_TYPE{$prop};
50 my $idx = $PROP_IDX {$prop};
51
52 *{"$class\::get_$sub"} = *{"$class\::$sub"} = sub {
53 $_[0]->get_property ($type, $idx)
54 };
55
56 *{"$class\::set_$sub"} = sub {
57 $_[0]->set_property ($type, $idx, $_[1]);
58 } unless $ignore_set{$prop};
59 }
60}
61
62# auto-generate most of the API
63
64prop_gen OBJECT_PROP => "cf::object";
65# CFAPI_OBJECT_ANIMATION?
66prop_gen PLAYER_PROP => "cf::object::player";
67
68prop_gen MAP_PROP => "cf::map";
69prop_gen ARCH_PROP => "cf::arch";
70
71@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; 71@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
72 72
73# we bless all objects into (empty) derived classes to force a method lookup 73# we bless all objects into (empty) derived classes to force a method lookup
74# within the Safe compartment. 74# within the Safe compartment.
75for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch)) { 75for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
76 no strict 'refs'; 76 no strict 'refs';
77 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg; 77 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
78} 78}
79 79
80$Event::DIED = sub { 80$Event::DIED = sub {
85my @exts; 85my @exts;
86my @hook; 86my @hook;
87my %command; 87my %command;
88my %extcmd; 88my %extcmd;
89 89
90############################################################################# 90=head2 UTILITY FUNCTIONS
91# utility functions 91
92=over 4
93
94=cut
92 95
93use JSON::Syck (); # TODO# replace by JSON::PC once working 96use JSON::Syck (); # TODO# replace by JSON::PC once working
97
98=item $ref = cf::from_json $json
99
100Converts a JSON string into the corresponding perl data structure.
101
102=cut
94 103
95sub from_json($) { 104sub from_json($) {
96 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs 105 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
97 JSON::Syck::Load $_[0] 106 JSON::Syck::Load $_[0]
98} 107}
99 108
109=item $json = cf::to_json $ref
110
111Converts a perl data structure into its JSON representation.
112
113=cut
114
100sub to_json($) { 115sub to_json($) {
101 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 116 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
102 JSON::Syck::Dump $_[0] 117 JSON::Syck::Dump $_[0]
103} 118}
104 119
105############################################################################# 120=back
106# "new" plug-in system
107 121
122=cut
123
124#############################################################################
125
126=head2 EVENTS AND OBJECT ATTACHMENTS
127
128=over 4
129
130=item $object->attach ($attachment, key => $value...)
131
108=item $object->attach ($attachment, ...) 132=item $object->detach ($attachment)
109 133
110Attach a pre-registered attachment to an object. 134Attach/detach a pre-registered attachment to an object.
111 135
136=item $player->attach ($attachment, key => $value...)
137
112=item $player->attach ($attachment, ...) 138=item $player->detach ($attachment)
113 139
114Attach a pre-registered attachment to a player. 140Attach/detach a pre-registered attachment to a player.
115 141
116=item $map->attach ($attachment, ...) # not yet persistent 142=item $map->attach ($attachment, key => $value...)
117 143
144=item $map->detach ($attachment)
145
118Attach a pre-registered attachment to a map. 146Attach/detach a pre-registered attachment to a map.
147
148=item $bool = $object->attached ($name)
149
150=item $bool = $player->attached ($name)
151
152=item $bool = $map->attached ($name)
153
154Checks wether the named attachment is currently attached to the object.
119 155
120=item cf::attach_global ... 156=item cf::attach_global ...
121 157
122Attach handlers for global events. 158Attach handlers for global events.
123 159
150package and register them. Only handlers for eevents supported by the 186package and register them. Only handlers for eevents supported by the
151object/class are recognised. 187object/class are recognised.
152 188
153=back 189=back
154 190
155=item cf::attach_to_type $object_type, ... 191=item cf::attach_to_type $object_type, $subtype, ...
156 192
157Attach handlers for a specific object type (e.g. TRANSPORT). 193Attach handlers for a specific object type (e.g. TRANSPORT) and
194subtype. If C<$subtype> is zero or undef, matches all objects of the given
195type.
158 196
159=item cf::attach_to_objects ... 197=item cf::attach_to_objects ...
160 198
161Attach handlers to all objects. Do not use this except for debugging or 199Attach handlers to all objects. Do not use this except for debugging or
162very rare events, as handlers are (obviously) called for I<all> objects in 200very rare events, as handlers are (obviously) called for I<all> objects in
169=item cf::attach_to_maps ... 207=item cf::attach_to_maps ...
170 208
171Attach handlers to all maps. 209Attach handlers to all maps.
172 210
173=item cf:register_attachment $name, ... 211=item cf:register_attachment $name, ...
212
213Register an attachment by name through which objects can refer to this
214attachment.
215
216=item cf:register_player_attachment $name, ...
217
218Register an attachment by name through which players can refer to this
219attachment.
220
221=item cf:register_map_attachment $name, ...
222
223Register an attachment by name through which maps can refer to this
224attachment.
174 225
175=cut 226=cut
176 227
177# the following variables are defined in .xs and must not be re-created 228# the following variables are defined in .xs and must not be re-created
178our @CB_GLOBAL = (); # registry for all global events 229our @CB_GLOBAL = (); # registry for all global events
241 292
242 \%undo 293 \%undo
243} 294}
244 295
245sub _attach_attachment { 296sub _attach_attachment {
246 my ($klass, $obj, $name, @args) = q_; 297 my ($obj, $name, %arg) = @_;
298
299 return if exists $obj->{_attachment}{$name};
247 300
248 my $res; 301 my $res;
249 302
250 if (my $attach = $attachment{$name}) { 303 if (my $attach = $attachment{$name}) {
251 my $registry = $obj->registry; 304 my $registry = $obj->registry;
252 305
306 for (@$attach) {
307 my ($klass, @attach) = @$_;
253 $res = _attach @$registry, $klass, @$attach; 308 $res = _attach @$registry, $klass, @attach;
254
255 if (my $cb = delete $registry->[EVENT_OBJECT_INSTANTIATE]) {
256 for (@$cb) {
257 eval { $_->[1]->($obj, @args); };
258 if ($@) {
259 warn "$@";
260 warn "... while processing '$name' instantiate with args <@args>.\n";
261 }
262 } 309 }
263 } 310
311 $obj->{$name} = \%arg;
264 } else { 312 } else {
265 warn "object uses attachment '$name' that is not available, postponing.\n"; 313 warn "object uses attachment '$name' that is not available, postponing.\n";
266 } 314 }
267 315
268 push @{$obj->{_attachment}}, $name; 316 $obj->{_attachment}{$name} = undef;
269 317
270 $res->{attachment} = $name; 318 $res->{attachment} = $name;
271 $res 319 $res
272} 320}
273 321
274sub cf::object::attach { 322*cf::object::attach =
323*cf::player::attach =
324*cf::map::attach = sub {
275 my ($obj, $name, @args) = @_; 325 my ($obj, $name, %arg) = @_;
276 326
277 _attach_attachment KLASS_OBJECT, $obj, $name, @args; 327 _attach_attachment $obj, $name, %arg;
278} 328};
279 329
330# all those should be optimised
331*cf::object::detach =
280sub cf::player::attach { 332*cf::player::detach =
333*cf::map::detach = sub {
281 my ($obj, $name, @args) = @_; 334 my ($obj, $name) = @_;
282 335
283 _attach_attachment KLASS_PLAYER, $obj, $name, @args; 336 delete $obj->{_attachment}{$name};
284} 337 reattach ($obj);
338};
285 339
286sub cf::map::attach { 340*cf::object::attached =
341*cf::player::attached =
342*cf::map::attached = sub {
287 my ($obj, $name, @args) = @_; 343 my ($obj, $name) = @_;
288 344
289 _attach_attachment KLASS_MAP, $obj, $name, @args; 345 exists $obj->{_attachment}{$name}
290} 346};
291 347
292sub attach_global { 348sub attach_global {
293 _attach @CB_GLOBAL, KLASS_GLOBAL, @_ 349 _attach @CB_GLOBAL, KLASS_GLOBAL, @_
294} 350}
295 351
296sub attach_to_type { 352sub attach_to_type {
297 my $type = shift; 353 my $type = shift;
354 my $subtype = shift;
298 355
299 _attach @{$CB_TYPE[$type]}, KLASS_OBJECT, @_ 356 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_
300} 357}
301 358
302sub attach_to_objects { 359sub attach_to_objects {
303 _attach @CB_OBJECT, KLASS_OBJECT, @_ 360 _attach @CB_OBJECT, KLASS_OBJECT, @_
304} 361}
312} 369}
313 370
314sub register_attachment { 371sub register_attachment {
315 my $name = shift; 372 my $name = shift;
316 373
374 $attachment{$name} = [[KLASS_OBJECT, @_]];
375}
376
377sub register_player_attachment {
378 my $name = shift;
379
380 $attachment{$name} = [[KLASS_PLAYER, @_]];
381}
382
383sub register_map_attachment {
384 my $name = shift;
385
317 $attachment{$name} = [@_]; 386 $attachment{$name} = [[KLASS_MAP, @_]];
318} 387}
319 388
320our $override; 389our $override;
321our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals? 390our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
322 391
336 for (@$callbacks) { 405 for (@$callbacks) {
337 eval { &{$_->[1]} }; 406 eval { &{$_->[1]} };
338 407
339 if ($@) { 408 if ($@) {
340 warn "$@"; 409 warn "$@";
341 warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n"; 410 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
342 override; 411 override;
343 } 412 }
344 413
345 return 1 if $override; 414 return 1 if $override;
346 } 415 }
347 416
348 0 417 0
349} 418}
419
420=item $bool = cf::invoke EVENT_GLOBAL_XXX, ...
421
422=item $bool = $object->invoke (EVENT_OBJECT_XXX, ...)
423
424=item $bool = $player->invoke (EVENT_PLAYER_XXX, ...)
425
426=item $bool = $map->invoke (EVENT_MAP_XXX, ...)
427
428Generate a global/object/player/map-specific event with the given arguments.
429
430This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be
431removed in future versions), and there is no public API to access override
432results (if you must, access C<@cf::invoke_results> directly).
433
434=back
435
436=cut
437
438#############################################################################
439
440=head2 METHODS VALID FOR ALL CORE OBJECTS
441
442=over 4
443
444=item $object->valid, $player->valid, $map->valid
445
446Just because you have a perl object does not mean that the corresponding
447C-level object still exists. If you try to access an object that has no
448valid C counterpart anymore you get an exception at runtime. This method
449can be used to test for existence of the C object part without causing an
450exception.
451
452=back
453
454=cut
455
456*cf::object::valid =
457*cf::player::valid =
458*cf::map::valid = \&cf::_valid;
350 459
351############################################################################# 460#############################################################################
352# object support 461# object support
353 462
354sub instantiate { 463sub instantiate {
356 465
357 $data = from_json $data; 466 $data = from_json $data;
358 467
359 for (@$data) { 468 for (@$data) {
360 my ($name, $args) = @$_; 469 my ($name, $args) = @$_;
361 attach $obj, $name, @{$args || [] }; 470
471 $obj->attach ($name, %{$args || {} });
362 } 472 }
363} 473}
364 474
365# basically do the same as instantiate, without calling instantiate 475# basically do the same as instantiate, without calling instantiate
366sub reattach { 476sub reattach {
367 warn "reattach<@_>\n";#d#
368 my ($obj) = @_; 477 my ($obj) = @_;
369 my $registry = $obj->registry; 478 my $registry = $obj->registry;
370 479
480 @$registry = ();
481
482 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
483
371 for my $name (@{ $obj->{_attachment} }) { 484 for my $name (keys %{ $obj->{_attachment} || {} }) {
372 if (my $attach = $attachment{$name}) { 485 if (my $attach = $attachment{$name}) {
486 for (@$attach) {
487 my ($klass, @attach) = @$_;
373 _attach @$registry, KLASS_OBJECT, @$attach; 488 _attach @$registry, $klass, @attach;
489 }
374 } else { 490 } else {
375 warn "object uses attachment '$name' that is not available, postponing.\n"; 491 warn "object uses attachment '$name' that is not available, postponing.\n";
376 } 492 }
377 } 493 }
378
379 warn "reattach<@_, $_>\n";
380} 494}
381 495
382sub object_freezer_save { 496sub object_freezer_save {
383 my ($filename, $objs) = @_; 497 my ($filename, $rdata, $objs) = @_;
384 warn "freeze $filename\n";#d#
385 use Data::Dumper; print Dumper $objs;
386 498
387 $filename .= ".pst"; 499 if (length $$rdata) {
500 warn sprintf "saving %s (%d,%d)\n",
501 $filename, length $$rdata, scalar @$objs;
388 502
389 if (@$objs) {
390 open my $fh, ">:raw", "$filename~"; 503 if (open my $fh, ">:raw", "$filename~") {
391 chmod $fh, SAVE_MODE; 504 chmod SAVE_MODE, $fh;
505 syswrite $fh, $$rdata;
506 close $fh;
507
508 if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
509 chmod SAVE_MODE, $fh;
392 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs }; 510 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
393 close $fh; 511 close $fh;
512 rename "$filename.pst~", "$filename.pst";
513 } else {
514 unlink "$filename.pst";
515 }
516
394 rename "$filename~", $filename; 517 rename "$filename~", $filename;
518 } else {
519 warn "FATAL: $filename~: $!\n";
520 }
395 } else { 521 } else {
396 unlink $filename; 522 unlink $filename;
523 unlink "$filename.pst";
397 } 524 }
398} 525}
399 526
400sub object_thawer_load { 527sub object_thawer_load {
401 my ($filename) = @_; 528 my ($filename) = @_;
402 529
403 warn "thaw $filename\n";#d# 530 local $/;
404 531
532 my $av;
533
534 #TODO: use sysread etc.
535 if (open my $data, "<:raw:perlio", $filename) {
536 $data = <$data>;
405 open my $fh, "<:raw:perlio", "$filename.pst" 537 if (open my $pst, "<:raw:perlio", "$filename.pst") {
406 or return; 538 $av = eval { (Storable::thaw <$pst>)->{objs} };
539 }
540 return ($data, $av);
541 }
407 542
408 eval { local $/; (Storable::thaw <$fh>)->{objs} } 543 ()
409} 544}
410 545
411attach_to_objects 546attach_to_objects
412 prio => -1000000, 547 prio => -1000000,
413 on_clone => sub { 548 on_clone => sub {
414 my ($src, $dst) = @_; 549 my ($src, $dst) = @_;
415 550
416 @{$dst->registry} = @{$src->registry}; 551 @{$dst->registry} = @{$src->registry};
417 warn "registry clone ", join ":", @{$src->registry};#d#
418 552
419 %$dst = %$src; 553 %$dst = %$src;
420 554
421 $dst->{_attachment} = [@{ $src->{_attachment} }] 555 %{$dst->{_attachment}} = %{$src->{_attachment}}
422 if exists $src->{_attachment}; 556 if exists $src->{_attachment};
423
424 warn "clone<@_>\n";#d#
425 }, 557 },
426; 558;
427 559
428############################################################################# 560#############################################################################
429# old plug-in events 561# old plug-in events
562 load_extension $ext; 694 load_extension $ext;
563 1 695 1
564 } or warn "$ext not loaded: $@"; 696 } or warn "$ext not loaded: $@";
565 } 697 }
566} 698}
567
568sub _perl_reload(&) {
569 my ($msg) = @_;
570
571 $msg->("reloading...");
572
573 eval {
574 # 1. cancel all watchers
575 $_->cancel for Event::all_watchers;
576
577 # 2. unload all extensions
578 for (@exts) {
579 $msg->("unloading <$_>");
580 unload_extension $_;
581 }
582
583 # 3. unload all modules loaded from $LIBDIR
584 while (my ($k, $v) = each %INC) {
585 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
586
587 $msg->("removing <$k>");
588 delete $INC{$k};
589
590 $k =~ s/\.pm$//;
591 $k =~ s/\//::/g;
592
593 if (my $cb = $k->can ("unload_module")) {
594 $cb->();
595 }
596
597 Symbol::delete_package $k;
598 }
599
600 # 4. get rid of safe::, as good as possible
601 Symbol::delete_package "safe::$_"
602 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
603
604 # 5. remove register_script_function callbacks
605 # TODO
606
607 # 6. unload cf.pm "a bit"
608 delete $INC{"cf.pm"};
609
610 # don't, removes xs symbols, too,
611 # and global variables created in xs
612 #Symbol::delete_package __PACKAGE__;
613
614 # 7. reload cf.pm
615 $msg->("reloading cf.pm");
616 require cf;
617 };
618 $msg->($@) if $@;
619
620 $msg->("reloaded");
621};
622
623sub perl_reload() {
624 _perl_reload {
625 warn $_[0];
626 print "$_[0]\n";
627 };
628}
629
630register_command "perl-reload", 0, sub {
631 my ($who, $arg) = @_;
632
633 if ($who->flag (FLAG_WIZ)) {
634 _perl_reload {
635 warn $_[0];
636 $who->message ($_[0]);
637 };
638 }
639};
640 699
641############################################################################# 700#############################################################################
642# extcmd framework, basically convert ext <msg> 701# extcmd framework, basically convert ext <msg>
643# into pkg::->on_extcmd_arg1 (...) while shortcutting a few 702# into pkg::->on_extcmd_arg1 (...) while shortcutting a few
644 703
669 my ($map) = @_; 728 my ($map) = @_;
670 729
671 my $path = $map->tmpname; 730 my $path = $map->tmpname;
672 defined $path or return; 731 defined $path or return;
673 732
674 unlink "$path.cfperl";
675 unlink "$path.pst"; 733 unlink "$path.pst";
676}; 734};
677 735
678*cf::mapsupport::on_swapin =
679*cf::mapsupport::on_load = sub {
680 my ($map) = @_;
681
682 my $path = $map->tmpname;
683 $path = $map->path unless defined $path;
684
685 open my $fh, "<:raw", "$path.cfperl"
686 or return; # no perl data
687
688 my $data = Storable::thaw do { local $/; <$fh> };
689
690 $data->{version} <= 1
691 or return; # too new
692
693 $map->_set_obs ($data->{obs});
694};
695
696attach_to_maps prio => -10000, package => cf::mapsupport::; 736attach_to_maps prio => -10000, package => cf::mapsupport::;
697 737
698############################################################################# 738#############################################################################
699# load/save perl data associated with player->ob objects 739# load/save perl data associated with player->ob objects
700 740
701sub all_objects(@) { 741sub all_objects(@) {
702 @_, map all_objects ($_->inv), @_ 742 @_, map all_objects ($_->inv), @_
703} 743}
704 744
745# TODO: compatibility cruft, remove when no longer needed
705attach_to_players 746attach_to_players
706 on_load => sub { 747 on_load => sub {
707 my ($pl, $path) = @_; 748 my ($pl, $path) = @_;
708 749
709 for my $o (all_objects $pl->ob) { 750 for my $o (all_objects $pl->ob) {
715 } 756 }
716 }, 757 },
717; 758;
718 759
719############################################################################# 760#############################################################################
720# core extensions - in perl 761
762=head2 CORE EXTENSIONS
763
764Functions and methods that extend core crossfire objects.
765
766=over 4
721 767
722=item cf::player::exists $login 768=item cf::player::exists $login
723 769
724Returns true when the given account exists. 770Returns true when the given account exists.
725 771
728sub cf::player::exists($) { 774sub cf::player::exists($) {
729 cf::player::find $_[0] 775 cf::player::find $_[0]
730 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2; 776 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
731} 777}
732 778
733=item $player->reply ($npc, $msg[, $flags]) 779=item $object->reply ($npc, $msg[, $flags])
734 780
735Sends a message to the player, as if the npc C<$npc> replied. C<$npc> 781Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
736can be C<undef>. Does the right thing when the player is currently in a 782can be C<undef>. Does the right thing when the player is currently in a
737dialogue with the given NPC character. 783dialogue with the given NPC character.
738 784
765 $msg{msgid} = $id; 811 $msg{msgid} = $id;
766 812
767 $self->send ("ext " . to_json \%msg); 813 $self->send ("ext " . to_json \%msg);
768} 814}
769 815
816=back
817
818=cut
819
770############################################################################# 820#############################################################################
771# map scripting support 821
822=head2 SAFE SCRIPTING
823
824Functions that provide a safe environment to compile and execute
825snippets of perl code without them endangering the safety of the server
826itself. Looping constructs, I/O operators and other built-in functionality
827is not available in the safe scripting environment, and the number of
828functions and methods that cna be called is greatly reduced.
829
830=cut
772 831
773our $safe = new Safe "safe"; 832our $safe = new Safe "safe";
774our $safe_hole = new Safe::Hole; 833our $safe_hole = new Safe::Hole;
775 834
776$SIG{FPE} = 'IGNORE'; 835$SIG{FPE} = 'IGNORE';
777 836
778$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 837$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
779 838
780# here we export the classes and methods available to script code 839# here we export the classes and methods available to script code
840
841=pod
842
843The following fucntions and emthods are available within a safe environment:
844
845 cf::object contr pay_amount pay_player
846 cf::object::player player
847 cf::player peaceful
848
849=cut
781 850
782for ( 851for (
783 ["cf::object" => qw(contr pay_amount pay_player)], 852 ["cf::object" => qw(contr pay_amount pay_player)],
784 ["cf::object::player" => qw(player)], 853 ["cf::object::player" => qw(player)],
785 ["cf::player" => qw(peaceful)], 854 ["cf::player" => qw(peaceful)],
788 my ($pkg, @funs) = @$_; 857 my ($pkg, @funs) = @$_;
789 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 858 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
790 for @funs; 859 for @funs;
791} 860}
792 861
862=over 4
863
864=item @retval = safe_eval $code, [var => value, ...]
865
866Compiled and executes the given perl code snippet. additional var/value
867pairs result in temporary local (my) scalar variables of the given name
868that are available in the code snippet. Example:
869
870 my $five = safe_eval '$first + $second', first => 1, second => 4;
871
872=cut
873
793sub safe_eval($;@) { 874sub safe_eval($;@) {
794 my ($code, %vars) = @_; 875 my ($code, %vars) = @_;
795 876
796 my $qcode = $code; 877 my $qcode = $code;
797 $qcode =~ s/"/‟/g; # not allowed in #line filenames 878 $qcode =~ s/"/‟/g; # not allowed in #line filenames
819 } 900 }
820 901
821 wantarray ? @res : $res[0] 902 wantarray ? @res : $res[0]
822} 903}
823 904
905=item cf::register_script_function $function => $cb
906
907Register a function that can be called from within map/npc scripts. The
908function should be reasonably secure and should be put into a package name
909like the extension.
910
911Example: register a function that gets called whenever a map script calls
912C<rent::overview>, as used by the C<rent> extension.
913
914 cf::register_script_function "rent::overview" => sub {
915 ...
916 };
917
918=cut
919
824sub register_script_function { 920sub register_script_function {
825 my ($fun, $cb) = @_; 921 my ($fun, $cb) = @_;
826 922
827 no strict 'refs'; 923 no strict 'refs';
828 *{"safe::$fun"} = $safe_hole->wrap ($cb); 924 *{"safe::$fun"} = $safe_hole->wrap ($cb);
829} 925}
830 926
927=back
928
929=cut
930
931#############################################################################
932
933=head2 EXTENSION DATABASE SUPPORT
934
935Crossfire maintains a very simple database for extension use. It can
936currently store anything that can be serialised using Storable, which
937excludes objects.
938
939The parameter C<$family> should best start with the name of the extension
940using it, it should be unique.
941
942=over 4
943
944=item $hashref = cf::db_get $family
945
946Return a hashref for use by the extension C<$family>, which can be
947modified. After modifications, you have to call C<cf::db_dirty> or
948C<cf::db_sync>.
949
950=item $value = cf::db_get $family => $key
951
952Returns a single value from the database
953
954=item cf::db_put $family => $hashref
955
956Stores the given family hashref into the database. Updates are delayed, if
957you want the data to be synced to disk immediately, use C<cf::db_sync>.
958
959=item cf::db_put $family => $key => $value
960
961Stores the given C<$value> in the family hash. Updates are delayed, if you
962want the data to be synced to disk immediately, use C<cf::db_sync>.
963
964=item cf::db_dirty
965
966Marks the database as dirty, to be updated at a later time.
967
968=item cf::db_sync
969
970Immediately write the database to disk I<if it is dirty>.
971
972=cut
973
974{
975 my $db;
976 my $path = cf::localdir . "/database.pst";
977
978 sub db_load() {
979 warn "loading database $path\n";#d# remove later
980 $db = stat $path ? Storable::retrieve $path : { };
981 }
982
983 my $pid;
984
985 sub db_save() {
986 warn "saving database $path\n";#d# remove later
987 waitpid $pid, 0 if $pid;
988 if (0 == ($pid = fork)) {
989 $db->{_meta}{version} = 1;
990 Storable::nstore $db, "$path~";
991 rename "$path~", $path;
992 cf::_exit 0 if defined $pid;
993 }
994 }
995
996 my $dirty;
997
998 sub db_sync() {
999 db_save if $dirty;
1000 undef $dirty;
1001 }
1002
1003 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, cb => sub {
1004 db_sync;
1005 });
1006
1007 sub db_dirty() {
1008 $dirty = 1;
1009 $idle->start;
1010 }
1011
1012 sub db_get($;$) {
1013 @_ >= 2
1014 ? $db->{$_[0]}{$_[1]}
1015 : ($db->{$_[0]} ||= { })
1016 }
1017
1018 sub db_put($$;$) {
1019 if (@_ >= 3) {
1020 $db->{$_[0]}{$_[1]} = $_[2];
1021 } else {
1022 $db->{$_[0]} = $_[1];
1023 }
1024 db_dirty;
1025 }
1026
1027 attach_global
1028 prio => 10000,
1029 on_cleanup => sub {
1030 db_sync;
1031 },
1032 ;
1033}
1034
831############################################################################# 1035#############################################################################
832# the server's main() 1036# the server's main()
833 1037
1038sub cfg_load {
1039 open my $fh, "<:utf8", cf::confdir . "/config"
1040 or return;
1041
1042 local $/;
1043 *CFG = YAML::Syck::Load <$fh>;
1044}
1045
834sub main { 1046sub main {
1047 cfg_load;
1048 db_load;
1049 load_extensions;
835 Event::loop; 1050 Event::loop;
836} 1051}
837 1052
838############################################################################# 1053#############################################################################
839# initialisation 1054# initialisation
840 1055
1056sub _perl_reload(&) {
1057 my ($msg) = @_;
1058
1059 $msg->("reloading...");
1060
1061 eval {
1062 # cancel all watchers
1063 $_->cancel for Event::all_watchers;
1064
1065 # unload all extensions
1066 for (@exts) {
1067 $msg->("unloading <$_>");
1068 unload_extension $_;
1069 }
1070
1071 # unload all modules loaded from $LIBDIR
1072 while (my ($k, $v) = each %INC) {
1073 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
1074
1075 $msg->("removing <$k>");
1076 delete $INC{$k};
1077
1078 $k =~ s/\.pm$//;
1079 $k =~ s/\//::/g;
1080
1081 if (my $cb = $k->can ("unload_module")) {
1082 $cb->();
1083 }
1084
1085 Symbol::delete_package $k;
1086 }
1087
1088 # sync database to disk
1089 cf::db_sync;
1090
1091 # get rid of safe::, as good as possible
1092 Symbol::delete_package "safe::$_"
1093 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
1094
1095 # remove register_script_function callbacks
1096 # TODO
1097
1098 # unload cf.pm "a bit"
1099 delete $INC{"cf.pm"};
1100
1101 # don't, removes xs symbols, too,
1102 # and global variables created in xs
1103 #Symbol::delete_package __PACKAGE__;
1104
1105 # reload cf.pm
1106 $msg->("reloading cf.pm");
1107 require cf;
1108
1109 # load config and database again
1110 cf::cfg_load;
1111 cf::db_load;
1112
1113 # load extensions
1114 $msg->("load extensions");
1115 cf::load_extensions;
1116
1117 # reattach attachments to objects
1118 $msg->("reattach");
1119 _global_reattach;
1120 };
1121 $msg->($@) if $@;
1122
1123 $msg->("reloaded");
1124};
1125
1126sub perl_reload() {
1127 _perl_reload {
1128 warn $_[0];
1129 print "$_[0]\n";
1130 };
1131}
1132
1133register_command "perl-reload", 0, sub {
1134 my ($who, $arg) = @_;
1135
1136 if ($who->flag (FLAG_WIZ)) {
1137 _perl_reload {
1138 warn $_[0];
1139 $who->message ($_[0]);
1140 };
1141 }
1142};
1143
841register "<global>", __PACKAGE__; 1144register "<global>", __PACKAGE__;
842 1145
843unshift @INC, $LIBDIR; 1146unshift @INC, $LIBDIR;
844
845load_extensions;
846 1147
847$TICK_WATCHER = Event->timer ( 1148$TICK_WATCHER = Event->timer (
848 prio => 1, 1149 prio => 1,
849 at => $NEXT_TICK || 1, 1150 at => $NEXT_TICK || 1,
850 cb => sub { 1151 cb => sub {

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines