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.57 by root, Wed Aug 30 11:21:24 2006 UTC vs.
Revision 1.71 by root, Sun Oct 1 10:59:30 2006 UTC

11use Event; 11use Event;
12$Event::Eval = 1; # no idea why this is required, but it is 12$Event::Eval = 1; # no idea why this is required, but it is
13 13
14use strict; 14use strict;
15 15
16_reload_1; 16_init_vars;
17 17
18our %COMMAND = (); 18our %COMMAND = ();
19our @EVENT; 19our @EVENT;
20our %PROP_TYPE;
21our %PROP_IDX;
22our $LIBDIR = maps_directory "perl"; 20our $LIBDIR = maps_directory "perl";
23 21
24our $TICK = MAX_TIME * 1e-6; 22our $TICK = MAX_TIME * 1e-6;
25our $TICK_WATCHER; 23our $TICK_WATCHER;
26our $NEXT_TICK; 24our $NEXT_TICK;
25
26our %CFG;
27
28#############################################################################
29
30=head2 GLOBAL VARIABLES
31
32=over 4
33
34=item $cf::LIBDIR
35
36The perl library directory, where extensions and cf-specific modules can
37be found. It will be added to C<@INC> automatically.
38
39=item $cf::TICK
40
41The interval between server ticks, in seconds.
42
43=item %cf::CFG
44
45Configuration for the server, loaded from C</etc/crossfire/config>, or
46from wherever your confdir points to.
47
48=back
49
50=cut
27 51
28BEGIN { 52BEGIN {
29 *CORE::GLOBAL::warn = sub { 53 *CORE::GLOBAL::warn = sub {
30 my $msg = join "", @_; 54 my $msg = join "", @_;
31 $msg .= "\n" 55 $msg .= "\n"
34 print STDERR "cfperl: $msg"; 58 print STDERR "cfperl: $msg";
35 LOG llevError, "cfperl: $msg"; 59 LOG llevError, "cfperl: $msg";
36 }; 60 };
37} 61}
38 62
39my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
40
41# generate property mutators
42sub 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 } unless $ignore_set{$prop};
61 }
62}
63
64# auto-generate most of the API
65
66prop_gen OBJECT_PROP => "cf::object";
67# CFAPI_OBJECT_ANIMATION?
68prop_gen PLAYER_PROP => "cf::object::player";
69
70prop_gen MAP_PROP => "cf::map";
71prop_gen ARCH_PROP => "cf::arch";
72
73@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; 63@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
74 64
75# we bless all objects into (empty) derived classes to force a method lookup 65# we bless all objects into (empty) derived classes to force a method lookup
76# within the Safe compartment. 66# within the Safe compartment.
77for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) { 67for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
87my @exts; 77my @exts;
88my @hook; 78my @hook;
89my %command; 79my %command;
90my %extcmd; 80my %extcmd;
91 81
92############################################################################# 82=head2 UTILITY FUNCTIONS
93# utility functions 83
84=over 4
85
86=cut
94 87
95use JSON::Syck (); # TODO# replace by JSON::PC once working 88use JSON::Syck (); # TODO# replace by JSON::PC once working
89
90=item $ref = cf::from_json $json
91
92Converts a JSON string into the corresponding perl data structure.
93
94=cut
96 95
97sub from_json($) { 96sub from_json($) {
98 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs 97 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
99 JSON::Syck::Load $_[0] 98 JSON::Syck::Load $_[0]
100} 99}
101 100
101=item $json = cf::to_json $ref
102
103Converts a perl data structure into its JSON representation.
104
105=cut
106
102sub to_json($) { 107sub to_json($) {
103 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 108 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
104 JSON::Syck::Dump $_[0] 109 JSON::Syck::Dump $_[0]
105} 110}
106 111
107############################################################################# 112=back
108# "new" plug-in system
109 113
114=cut
115
116#############################################################################
117
110=head3 EVENTS AND OBJECT ATTACHMENTS 118=head2 EVENTS AND OBJECT ATTACHMENTS
111 119
112=over 4 120=over 4
113 121
114=item $object->attach ($attachment, key => $value...) 122=item $object->attach ($attachment, key => $value...)
115 123
389 for (@$callbacks) { 397 for (@$callbacks) {
390 eval { &{$_->[1]} }; 398 eval { &{$_->[1]} };
391 399
392 if ($@) { 400 if ($@) {
393 warn "$@"; 401 warn "$@";
394 warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n"; 402 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
395 override; 403 override;
396 } 404 }
397 405
398 return 1 if $override; 406 return 1 if $override;
399 } 407 }
415removed in future versions), and there is no public API to access override 423removed in future versions), and there is no public API to access override
416results (if you must, access C<@cf::invoke_results> directly). 424results (if you must, access C<@cf::invoke_results> directly).
417 425
418=back 426=back
419 427
420=head2 methods valid for all pointers 428=cut
429
430#############################################################################
431
432=head2 METHODS VALID FOR ALL CORE OBJECTS
421 433
422=over 4 434=over 4
423 435
424=item $object->valid 436=item $object->valid, $player->valid, $map->valid
425
426=item $player->valid
427
428=item $map->valid
429 437
430Just because you have a perl object does not mean that the corresponding 438Just because you have a perl object does not mean that the corresponding
431C-level object still exists. If you try to access an object that has no 439C-level object still exists. If you try to access an object that has no
432valid C counterpart anymore you get an exception at runtime. This method 440valid C counterpart anymore you get an exception at runtime. This method
433can be used to test for existence of the C object part without causing an 441can be used to test for existence of the C object part without causing an
476 } 484 }
477 } 485 }
478} 486}
479 487
480sub object_freezer_save { 488sub object_freezer_save {
481 my ($filename, $objs) = @_; 489 my ($filename, $rdata, $objs) = @_;
482 490
483 if (@$objs) { 491 if (length $$rdata) {
492 warn sprintf "saving %s (%d,%d)\n",
493 $filename, length $$rdata, scalar @$objs;
494
484 open my $fh, ">:raw", "$filename.pst~"; 495 if (open my $fh, ">:raw", "$filename~") {
496 chmod SAVE_MODE, $fh;
497 syswrite $fh, $$rdata;
498 close $fh;
499
500 if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
501 chmod SAVE_MODE, $fh;
485 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs }; 502 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
486 close $fh; 503 close $fh;
487 unlink "$filename.cfperl";
488 chmod SAVE_MODE, "$filename.pst~";
489 rename "$filename.pst~", "$filename.pst"; 504 rename "$filename.pst~", "$filename.pst";
505 } else {
506 unlink "$filename.pst";
507 }
508
509 rename "$filename~", $filename;
510 } else {
511 warn "FATAL: $filename~: $!\n";
512 }
490 } else { 513 } else {
514 unlink $filename;
491 unlink "$filename.pst"; 515 unlink "$filename.pst";
492 } 516 }
493
494 chmod SAVE_MODE, "$filename~";
495 rename "$filename~", $filename;
496} 517}
497 518
498sub object_thawer_load { 519sub object_thawer_load {
499 my ($filename) = @_; 520 my ($filename) = @_;
500 521
522 local $/;
523
524 my $av;
525
526 #TODO: use sysread etc.
527 if (open my $data, "<:raw:perlio", $filename) {
528 $data = <$data>;
501 open my $fh, "<:raw:perlio", "$filename.pst" 529 if (open my $pst, "<:raw:perlio", "$filename.pst") {
502 or return; 530 $av = eval { (Storable::thaw <$pst>)->{objs} };
531 }
532 return ($data, $av);
533 }
503 534
504 eval { local $/; (Storable::thaw <$fh>)->{objs} } 535 ()
505} 536}
506 537
507attach_to_objects 538attach_to_objects
508 prio => -1000000, 539 prio => -1000000,
509 on_clone => sub { 540 on_clone => sub {
655 load_extension $ext; 686 load_extension $ext;
656 1 687 1
657 } or warn "$ext not loaded: $@"; 688 } or warn "$ext not loaded: $@";
658 } 689 }
659} 690}
660
661sub _perl_reload(&) {
662 my ($msg) = @_;
663
664 $msg->("reloading...");
665
666 eval {
667 # 1. cancel all watchers
668 $_->cancel for Event::all_watchers;
669
670 # 2. unload all extensions
671 for (@exts) {
672 $msg->("unloading <$_>");
673 unload_extension $_;
674 }
675
676 # 3. unload all modules loaded from $LIBDIR
677 while (my ($k, $v) = each %INC) {
678 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
679
680 $msg->("removing <$k>");
681 delete $INC{$k};
682
683 $k =~ s/\.pm$//;
684 $k =~ s/\//::/g;
685
686 if (my $cb = $k->can ("unload_module")) {
687 $cb->();
688 }
689
690 Symbol::delete_package $k;
691 }
692
693 # 4. get rid of safe::, as good as possible
694 Symbol::delete_package "safe::$_"
695 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
696
697 # 5. remove register_script_function callbacks
698 # TODO
699
700 # 6. unload cf.pm "a bit"
701 delete $INC{"cf.pm"};
702
703 # don't, removes xs symbols, too,
704 # and global variables created in xs
705 #Symbol::delete_package __PACKAGE__;
706
707 # 7. reload cf.pm
708 $msg->("reloading cf.pm");
709 require cf;
710 };
711 $msg->($@) if $@;
712
713 $msg->("reloaded");
714};
715
716sub perl_reload() {
717 _perl_reload {
718 warn $_[0];
719 print "$_[0]\n";
720 };
721}
722
723register_command "perl-reload", 0, sub {
724 my ($who, $arg) = @_;
725
726 if ($who->flag (FLAG_WIZ)) {
727 _perl_reload {
728 warn $_[0];
729 $who->message ($_[0]);
730 };
731 }
732};
733 691
734############################################################################# 692#############################################################################
735# extcmd framework, basically convert ext <msg> 693# extcmd framework, basically convert ext <msg>
736# into pkg::->on_extcmd_arg1 (...) while shortcutting a few 694# into pkg::->on_extcmd_arg1 (...) while shortcutting a few
737 695
765 defined $path or return; 723 defined $path or return;
766 724
767 unlink "$path.pst"; 725 unlink "$path.pst";
768}; 726};
769 727
770# old style persistent data, TODO: remove #d#
771*cf::mapsupport::on_swapin = sub {
772 my ($map) = @_;
773
774 my $path = $map->tmpname;
775 $path = $map->path unless defined $path;
776
777 open my $fh, "<:raw", "$path.cfperl"
778 or return; # no perl data
779
780 my $data = Storable::thaw do { local $/; <$fh> };
781
782 $data->{version} <= 1
783 or return; # too new
784
785 $map->_set_obs ($data->{obs});
786 $map->invoke (EVENT_MAP_UPGRADE);
787};
788
789attach_to_maps prio => -10000, package => cf::mapsupport::; 728attach_to_maps prio => -10000, package => cf::mapsupport::;
790 729
791############################################################################# 730#############################################################################
792# load/save perl data associated with player->ob objects 731# load/save perl data associated with player->ob objects
793 732
794sub all_objects(@) { 733sub all_objects(@) {
795 @_, map all_objects ($_->inv), @_ 734 @_, map all_objects ($_->inv), @_
796} 735}
797 736
737# TODO: compatibility cruft, remove when no longer needed
798attach_to_players 738attach_to_players
799 on_load => sub { 739 on_load => sub {
800 my ($pl, $path) = @_; 740 my ($pl, $path) = @_;
801 741
802 for my $o (all_objects $pl->ob) { 742 for my $o (all_objects $pl->ob) {
808 } 748 }
809 }, 749 },
810; 750;
811 751
812############################################################################# 752#############################################################################
813# core extensions - in perl 753
754=head2 CORE EXTENSIONS
755
756Functions and methods that extend core crossfire objects.
757
758=over 4
814 759
815=item cf::player::exists $login 760=item cf::player::exists $login
816 761
817Returns true when the given account exists. 762Returns true when the given account exists.
818 763
858 $msg{msgid} = $id; 803 $msg{msgid} = $id;
859 804
860 $self->send ("ext " . to_json \%msg); 805 $self->send ("ext " . to_json \%msg);
861} 806}
862 807
808=back
809
810=cut
811
863############################################################################# 812#############################################################################
864# map scripting support 813
814=head2 SAFE SCRIPTING
815
816Functions that provide a safe environment to compile and execute
817snippets of perl code without them endangering the safety of the server
818itself. Looping constructs, I/O operators and other built-in functionality
819is not available in the safe scripting environment, and the number of
820functions and methods that cna be called is greatly reduced.
821
822=cut
865 823
866our $safe = new Safe "safe"; 824our $safe = new Safe "safe";
867our $safe_hole = new Safe::Hole; 825our $safe_hole = new Safe::Hole;
868 826
869$SIG{FPE} = 'IGNORE'; 827$SIG{FPE} = 'IGNORE';
870 828
871$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 829$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
872 830
873# here we export the classes and methods available to script code 831# here we export the classes and methods available to script code
832
833=pod
834
835The following fucntions and emthods are available within a safe environment:
836
837 cf::object contr pay_amount pay_player
838 cf::object::player player
839 cf::player peaceful
840
841=cut
874 842
875for ( 843for (
876 ["cf::object" => qw(contr pay_amount pay_player)], 844 ["cf::object" => qw(contr pay_amount pay_player)],
877 ["cf::object::player" => qw(player)], 845 ["cf::object::player" => qw(player)],
878 ["cf::player" => qw(peaceful)], 846 ["cf::player" => qw(peaceful)],
881 my ($pkg, @funs) = @$_; 849 my ($pkg, @funs) = @$_;
882 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 850 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
883 for @funs; 851 for @funs;
884} 852}
885 853
854=over 4
855
856=item @retval = safe_eval $code, [var => value, ...]
857
858Compiled and executes the given perl code snippet. additional var/value
859pairs result in temporary local (my) scalar variables of the given name
860that are available in the code snippet. Example:
861
862 my $five = safe_eval '$first + $second', first => 1, second => 4;
863
864=cut
865
886sub safe_eval($;@) { 866sub safe_eval($;@) {
887 my ($code, %vars) = @_; 867 my ($code, %vars) = @_;
888 868
889 my $qcode = $code; 869 my $qcode = $code;
890 $qcode =~ s/"/‟/g; # not allowed in #line filenames 870 $qcode =~ s/"/‟/g; # not allowed in #line filenames
912 } 892 }
913 893
914 wantarray ? @res : $res[0] 894 wantarray ? @res : $res[0]
915} 895}
916 896
897=item cf::register_script_function $function => $cb
898
899Register a function that can be called from within map/npc scripts. The
900function should be reasonably secure and should be put into a package name
901like the extension.
902
903Example: register a function that gets called whenever a map script calls
904C<rent::overview>, as used by the C<rent> extension.
905
906 cf::register_script_function "rent::overview" => sub {
907 ...
908 };
909
910=cut
911
917sub register_script_function { 912sub register_script_function {
918 my ($fun, $cb) = @_; 913 my ($fun, $cb) = @_;
919 914
920 no strict 'refs'; 915 no strict 'refs';
921 *{"safe::$fun"} = $safe_hole->wrap ($cb); 916 *{"safe::$fun"} = $safe_hole->wrap ($cb);
922} 917}
923 918
919=back
920
921=cut
922
923#############################################################################
924
925=head2 EXTENSION DATABASE SUPPORT
926
927Crossfire maintains a very simple database for extension use. It can
928currently store anything that can be serialised using Storable, which
929excludes objects.
930
931The parameter C<$family> should best start with the name of the extension
932using it, it should be unique.
933
934=over 4
935
936=item $hashref = cf::db_get $family
937
938Return a hashref for use by the extension C<$family>, which can be
939modified. After modifications, you have to call C<cf::db_dirty> or
940C<cf::db_sync>.
941
942=item $value = cf::db_get $family => $key
943
944Returns a single value from the database
945
946=item cf::db_put $family => $hashref
947
948Stores the given family hashref into the database. Updates are delayed, if
949you want the data to be synced to disk immediately, use C<cf::db_sync>.
950
951=item cf::db_put $family => $key => $value
952
953Stores the given C<$value> in the family hash. Updates are delayed, if you
954want the data to be synced to disk immediately, use C<cf::db_sync>.
955
956=item cf::db_dirty
957
958Marks the database as dirty, to be updated at a later time.
959
960=item cf::db_sync
961
962Immediately write the database to disk I<if it is dirty>.
963
964=cut
965
966{
967 my $db;
968 my $path = cf::localdir . "/database.pst";
969
970 sub db_load() {
971 warn "loading database $path\n";#d# remove later
972 $db = stat $path ? Storable::retrieve $path : { };
973 }
974
975 my $pid;
976
977 sub db_save() {
978 warn "saving database $path\n";#d# remove later
979 waitpid $pid, 0 if $pid;
980 if (0 == ($pid = fork)) {
981 $db->{_meta}{version} = 1;
982 Storable::nstore $db, "$path~";
983 rename "$path~", $path;
984 cf::_exit 0 if defined $pid;
985 }
986 }
987
988 my $dirty;
989
990 sub db_sync() {
991 db_save if $dirty;
992 undef $dirty;
993 }
994
995 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, cb => sub {
996 db_sync;
997 });
998
999 sub db_dirty() {
1000 $dirty = 1;
1001 $idle->start;
1002 }
1003
1004 sub db_get($;$) {
1005 @_ >= 2
1006 ? $db->{$_[0]}{$_[1]}
1007 : ($db->{$_[0]} ||= { })
1008 }
1009
1010 sub db_put($$;$) {
1011 if (@_ >= 3) {
1012 $db->{$_[0]}{$_[1]} = $_[2];
1013 } else {
1014 $db->{$_[0]} = $_[1];
1015 }
1016 db_dirty;
1017 }
1018
1019 attach_global
1020 prio => 10000,
1021 on_cleanup => sub {
1022 db_sync;
1023 },
1024 ;
1025}
1026
924############################################################################# 1027#############################################################################
925# the server's main() 1028# the server's main()
926 1029
927sub main { 1030sub main {
1031 db_load;
1032 load_extensions;
928 Event::loop; 1033 Event::loop;
929} 1034}
930 1035
931############################################################################# 1036#############################################################################
932# initialisation 1037# initialisation
933 1038
1039sub _perl_reload(&) {
1040 my ($msg) = @_;
1041
1042 $msg->("reloading...");
1043
1044 eval {
1045 # cancel all watchers
1046 $_->cancel for Event::all_watchers;
1047
1048 # unload all extensions
1049 for (@exts) {
1050 $msg->("unloading <$_>");
1051 unload_extension $_;
1052 }
1053
1054 # unload all modules loaded from $LIBDIR
1055 while (my ($k, $v) = each %INC) {
1056 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
1057
1058 $msg->("removing <$k>");
1059 delete $INC{$k};
1060
1061 $k =~ s/\.pm$//;
1062 $k =~ s/\//::/g;
1063
1064 if (my $cb = $k->can ("unload_module")) {
1065 $cb->();
1066 }
1067
1068 Symbol::delete_package $k;
1069 }
1070
1071 # sync database to disk
1072 cf::db_sync;
1073
1074 # get rid of safe::, as good as possible
1075 Symbol::delete_package "safe::$_"
1076 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
1077
1078 # remove register_script_function callbacks
1079 # TODO
1080
1081 # unload cf.pm "a bit"
1082 delete $INC{"cf.pm"};
1083
1084 # don't, removes xs symbols, too,
1085 # and global variables created in xs
1086 #Symbol::delete_package __PACKAGE__;
1087
1088 # reload cf.pm
1089 $msg->("reloading cf.pm");
1090 require cf;
1091
1092 # load database again
1093 cf::db_load;
1094
1095 # load extensions
1096 $msg->("load extensions");
1097 cf::load_extensions;
1098
1099 # reattach attachments to objects
1100 $msg->("reattach");
1101 _global_reattach;
1102 };
1103 $msg->($@) if $@;
1104
1105 $msg->("reloaded");
1106};
1107
1108sub perl_reload() {
1109 _perl_reload {
1110 warn $_[0];
1111 print "$_[0]\n";
1112 };
1113}
1114
1115register_command "perl-reload", 0, sub {
1116 my ($who, $arg) = @_;
1117
1118 if ($who->flag (FLAG_WIZ)) {
1119 _perl_reload {
1120 warn $_[0];
1121 $who->message ($_[0]);
1122 };
1123 }
1124};
1125
934register "<global>", __PACKAGE__; 1126register "<global>", __PACKAGE__;
935 1127
936unshift @INC, $LIBDIR; 1128unshift @INC, $LIBDIR;
937
938load_extensions;
939 1129
940$TICK_WATCHER = Event->timer ( 1130$TICK_WATCHER = Event->timer (
941 prio => 1, 1131 prio => 1,
942 at => $NEXT_TICK || 1, 1132 at => $NEXT_TICK || 1,
943 cb => sub { 1133 cb => sub {
952 $TICK_WATCHER->at ($NEXT_TICK); 1142 $TICK_WATCHER->at ($NEXT_TICK);
953 $TICK_WATCHER->start; 1143 $TICK_WATCHER->start;
954 }, 1144 },
955); 1145);
956 1146
957_reload_2;
958
9591 11471
960 1148

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines