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.61 by root, Sun Sep 3 22:45:56 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;
15 19
16_reload_1; 20_init_vars;
17 21
18our %COMMAND = (); 22our %COMMAND = ();
19our @EVENT; 23our @EVENT;
20our %PROP_TYPE;
21our %PROP_IDX;
22our $LIBDIR = maps_directory "perl"; 24our $LIBDIR = maps_directory "perl";
23 25
24our $TICK = MAX_TIME * 1e-6; 26our $TICK = MAX_TIME * 1e-6;
25our $TICK_WATCHER; 27our $TICK_WATCHER;
26our $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
27 59
28BEGIN { 60BEGIN {
29 *CORE::GLOBAL::warn = sub { 61 *CORE::GLOBAL::warn = sub {
30 my $msg = join "", @_; 62 my $msg = join "", @_;
31 $msg .= "\n" 63 $msg .= "\n"
34 print STDERR "cfperl: $msg"; 66 print STDERR "cfperl: $msg";
35 LOG llevError, "cfperl: $msg"; 67 LOG llevError, "cfperl: $msg";
36 }; 68 };
37} 69}
38 70
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'; 71@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
74 72
75# 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
76# within the Safe compartment. 74# 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)) { 75for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
87my @exts; 85my @exts;
88my @hook; 86my @hook;
89my %command; 87my %command;
90my %extcmd; 88my %extcmd;
91 89
92############################################################################# 90=head2 UTILITY FUNCTIONS
93# utility functions 91
92=over 4
93
94=cut
94 95
95use 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
96 103
97sub from_json($) { 104sub from_json($) {
98 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs 105 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
99 JSON::Syck::Load $_[0] 106 JSON::Syck::Load $_[0]
100} 107}
101 108
109=item $json = cf::to_json $ref
110
111Converts a perl data structure into its JSON representation.
112
113=cut
114
102sub to_json($) { 115sub to_json($) {
103 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 116 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
104 JSON::Syck::Dump $_[0] 117 JSON::Syck::Dump $_[0]
105} 118}
106 119
107############################################################################# 120=back
108# "new" plug-in system
109 121
122=cut
123
124#############################################################################
125
110=head3 EVENTS AND OBJECT ATTACHMENTS 126=head2 EVENTS AND OBJECT ATTACHMENTS
111 127
112=over 4 128=over 4
113 129
114=item $object->attach ($attachment, key => $value...) 130=item $object->attach ($attachment, key => $value...)
115 131
389 for (@$callbacks) { 405 for (@$callbacks) {
390 eval { &{$_->[1]} }; 406 eval { &{$_->[1]} };
391 407
392 if ($@) { 408 if ($@) {
393 warn "$@"; 409 warn "$@";
394 warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n"; 410 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
395 override; 411 override;
396 } 412 }
397 413
398 return 1 if $override; 414 return 1 if $override;
399 } 415 }
415removed in future versions), and there is no public API to access override 431removed in future versions), and there is no public API to access override
416results (if you must, access C<@cf::invoke_results> directly). 432results (if you must, access C<@cf::invoke_results> directly).
417 433
418=back 434=back
419 435
420=head2 methods valid for all pointers 436=cut
437
438#############################################################################
439
440=head2 METHODS VALID FOR ALL CORE OBJECTS
421 441
422=over 4 442=over 4
423 443
424=item $object->valid 444=item $object->valid, $player->valid, $map->valid
425
426=item $player->valid
427
428=item $map->valid
429 445
430Just because you have a perl object does not mean that the corresponding 446Just 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 447C-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 448valid 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 449can be used to test for existence of the C object part without causing an
679 1 695 1
680 } or warn "$ext not loaded: $@"; 696 } or warn "$ext not loaded: $@";
681 } 697 }
682} 698}
683 699
684sub _perl_reload(&) {
685 my ($msg) = @_;
686
687 $msg->("reloading...");
688
689 eval {
690 # 1. cancel all watchers
691 $_->cancel for Event::all_watchers;
692
693 # 2. unload all extensions
694 for (@exts) {
695 $msg->("unloading <$_>");
696 unload_extension $_;
697 }
698
699 # 3. unload all modules loaded from $LIBDIR
700 while (my ($k, $v) = each %INC) {
701 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
702
703 $msg->("removing <$k>");
704 delete $INC{$k};
705
706 $k =~ s/\.pm$//;
707 $k =~ s/\//::/g;
708
709 if (my $cb = $k->can ("unload_module")) {
710 $cb->();
711 }
712
713 Symbol::delete_package $k;
714 }
715
716 # 4. get rid of safe::, as good as possible
717 Symbol::delete_package "safe::$_"
718 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
719
720 # 5. remove register_script_function callbacks
721 # TODO
722
723 # 6. unload cf.pm "a bit"
724 delete $INC{"cf.pm"};
725
726 # don't, removes xs symbols, too,
727 # and global variables created in xs
728 #Symbol::delete_package __PACKAGE__;
729
730 # 7. reload cf.pm
731 $msg->("reloading cf.pm");
732 require cf;
733
734 $msg->("load extensions");
735 cf::load_extensions;
736 };
737 $msg->($@) if $@;
738
739 $msg->("reloaded");
740};
741
742sub perl_reload() {
743 _perl_reload {
744 warn $_[0];
745 print "$_[0]\n";
746 };
747}
748
749register_command "perl-reload", 0, sub {
750 my ($who, $arg) = @_;
751
752 if ($who->flag (FLAG_WIZ)) {
753 _perl_reload {
754 warn $_[0];
755 $who->message ($_[0]);
756 };
757 }
758};
759
760############################################################################# 700#############################################################################
761# extcmd framework, basically convert ext <msg> 701# extcmd framework, basically convert ext <msg>
762# into pkg::->on_extcmd_arg1 (...) while shortcutting a few 702# into pkg::->on_extcmd_arg1 (...) while shortcutting a few
763 703
764attach_to_players 704attach_to_players
816 } 756 }
817 }, 757 },
818; 758;
819 759
820############################################################################# 760#############################################################################
821# core extensions - in perl 761
762=head2 CORE EXTENSIONS
763
764Functions and methods that extend core crossfire objects.
765
766=over 4
822 767
823=item cf::player::exists $login 768=item cf::player::exists $login
824 769
825Returns true when the given account exists. 770Returns true when the given account exists.
826 771
829sub cf::player::exists($) { 774sub cf::player::exists($) {
830 cf::player::find $_[0] 775 cf::player::find $_[0]
831 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;
832} 777}
833 778
834=item $player->reply ($npc, $msg[, $flags]) 779=item $object->reply ($npc, $msg[, $flags])
835 780
836Sends 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>
837can 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
838dialogue with the given NPC character. 783dialogue with the given NPC character.
839 784
866 $msg{msgid} = $id; 811 $msg{msgid} = $id;
867 812
868 $self->send ("ext " . to_json \%msg); 813 $self->send ("ext " . to_json \%msg);
869} 814}
870 815
816=back
817
818=cut
819
871############################################################################# 820#############################################################################
872# 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
873 831
874our $safe = new Safe "safe"; 832our $safe = new Safe "safe";
875our $safe_hole = new Safe::Hole; 833our $safe_hole = new Safe::Hole;
876 834
877$SIG{FPE} = 'IGNORE'; 835$SIG{FPE} = 'IGNORE';
878 836
879$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));
880 838
881# 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
882 850
883for ( 851for (
884 ["cf::object" => qw(contr pay_amount pay_player)], 852 ["cf::object" => qw(contr pay_amount pay_player)],
885 ["cf::object::player" => qw(player)], 853 ["cf::object::player" => qw(player)],
886 ["cf::player" => qw(peaceful)], 854 ["cf::player" => qw(peaceful)],
889 my ($pkg, @funs) = @$_; 857 my ($pkg, @funs) = @$_;
890 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 858 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
891 for @funs; 859 for @funs;
892} 860}
893 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
894sub safe_eval($;@) { 874sub safe_eval($;@) {
895 my ($code, %vars) = @_; 875 my ($code, %vars) = @_;
896 876
897 my $qcode = $code; 877 my $qcode = $code;
898 $qcode =~ s/"/‟/g; # not allowed in #line filenames 878 $qcode =~ s/"/‟/g; # not allowed in #line filenames
920 } 900 }
921 901
922 wantarray ? @res : $res[0] 902 wantarray ? @res : $res[0]
923} 903}
924 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
925sub register_script_function { 920sub register_script_function {
926 my ($fun, $cb) = @_; 921 my ($fun, $cb) = @_;
927 922
928 no strict 'refs'; 923 no strict 'refs';
929 *{"safe::$fun"} = $safe_hole->wrap ($cb); 924 *{"safe::$fun"} = $safe_hole->wrap ($cb);
930} 925}
931 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
932############################################################################# 1035#############################################################################
933# the server's main() 1036# the server's main()
934 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
935sub main { 1046sub main {
1047 cfg_load;
1048 db_load;
936 load_extensions; 1049 load_extensions;
937 Event::loop; 1050 Event::loop;
938} 1051}
939 1052
940############################################################################# 1053#############################################################################
941# initialisation 1054# initialisation
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};
942 1143
943register "<global>", __PACKAGE__; 1144register "<global>", __PACKAGE__;
944 1145
945unshift @INC, $LIBDIR; 1146unshift @INC, $LIBDIR;
946 1147
959 $TICK_WATCHER->at ($NEXT_TICK); 1160 $TICK_WATCHER->at ($NEXT_TICK);
960 $TICK_WATCHER->start; 1161 $TICK_WATCHER->start;
961 }, 1162 },
962); 1163);
963 1164
964_reload_2;
965
9661 11651
967 1166

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines