ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.9
Committed: Fri Mar 17 03:22:00 2006 UTC (18 years, 2 months ago) by root
Branch: MAIN
Changes since 1.8: +3 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3     use Symbol;
4     use List::Util;
5 root 1.6 use Storable;
6 root 1.1
7     use strict;
8    
9     our %COMMAND;
10     our @EVENT;
11     our %PROP_TYPE;
12     our %PROP_IDX;
13    
14     BEGIN {
15     @EVENT = map lc, @EVENT;
16    
17     *CORE::GLOBAL::warn = sub {
18     my $msg = join "", @_;
19     $msg .= "\n"
20     unless $msg =~ /\n$/;
21    
22     print STDERR "cfperl: $msg";
23     LOG llevError, "cfperl: $msg";
24     };
25     }
26    
27 root 1.9 my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
28    
29 root 1.1 # generate property mutators
30     sub prop_gen {
31     my ($prefix, $class) = @_;
32    
33     no strict 'refs';
34    
35     for my $prop (keys %PROP_TYPE) {
36     $prop =~ /^\Q$prefix\E_(.*$)/ or next;
37     my $sub = lc $1;
38    
39     my $type = $PROP_TYPE{$prop};
40     my $idx = $PROP_IDX {$prop};
41    
42     *{"$class\::get_$sub"} = *{"$class\::$sub"} = sub {
43     $_[0]->get_property ($type, $idx)
44     };
45    
46     *{"$class\::set_$sub"} = sub {
47     $_[0]->set_property ($type, $idx, $_[1]);
48 root 1.9 } unless $ignore_set{$prop};
49 root 1.1 }
50     }
51    
52     # auto-generate most of the API
53    
54     prop_gen OBJECT_PROP => "cf::object";
55     # CFAPI_OBJECT_ANIMATION?
56     prop_gen PLAYER_PROP => "cf::object::player";
57    
58     prop_gen MAP_PROP => "cf::map";
59     prop_gen ARCH_PROP => "cf::arch";
60    
61     # guessed hierarchies
62    
63     @cf::object::player::ISA = 'cf::object';
64     @cf::object::map::ISA = 'cf::object';
65    
66 root 1.5 my %ext_pkg;
67 root 1.1 my @exts;
68     my @hook;
69     my %command;
70    
71     sub inject_event {
72     my ($data) = @_;
73    
74 root 1.5 my $cb = $hook[$data->{event_code}]{$data->{extension}}
75     or return;
76    
77     $cb->($data)
78     }
79    
80     sub inject_global_event {
81     my ($data) = @_;
82    
83 root 1.1 my $cb = $hook[$data->{event_code}]
84     or return;
85    
86     $_->($data) for values %$cb;
87    
88     ()
89     }
90    
91     sub inject_command {
92     my ($name, $obj, $params) = @_;
93    
94     for my $cmd (@{ $command{$name} }) {
95     $cmd->[1]->($obj, $params);
96     }
97    
98     -1
99     }
100    
101     sub register_command {
102     my ($name, $time, $cb) = @_;
103    
104     my $caller = caller;
105    
106 root 1.4 warn "registering command '$name/$time' to '$caller'";
107    
108 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
109     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
110     }
111    
112 root 1.6 sub register {
113     my ($base, $pkg) = @_;
114    
115     for my $idx (0 .. $#EVENT) {
116     if (my $ref = $pkg->can ("on_$EVENT[$idx]")) {
117     warn "registering $EVENT[$idx] hook to '$pkg'\n";
118     $hook[$idx]{$base} = $ref;
119     }
120     }
121     }
122    
123 root 1.1 sub load_extension {
124     my ($path) = @_;
125    
126     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
127 root 1.5 my $base = $1;
128 root 1.1 my $pkg = $1;
129     $pkg =~ s/[^[:word:]]/_/g;
130     $pkg = "cf::ext::$pkg";
131    
132     warn "loading '$path' into '$pkg'\n";
133    
134     open my $fh, "<:utf8", $path
135     or die "$path: $!";
136    
137     my $source =
138     "package $pkg; use strict; use utf8;\n"
139     . "#line 1 \"$path\"\n{\n"
140     . (do { local $/; <$fh> })
141     . "\n};\n1";
142    
143     eval $source
144     or die "$path: $@";
145    
146     push @exts, $pkg;
147 root 1.5 $ext_pkg{$base} = $pkg;
148 root 1.1
149 root 1.6 # no strict 'refs';
150 root 1.1 # @{"$pkg\::ISA"} = cf::ext::;
151    
152 root 1.6 register $base, $pkg;
153 root 1.1 }
154    
155     sub unload_extension {
156     my ($pkg) = @_;
157    
158     warn "removing extension $pkg\n";
159    
160     # remove hooks
161     for my $idx (0 .. $#EVENT) {
162     delete $hook[$idx]{$pkg};
163     }
164    
165     # remove commands
166     for my $name (keys %command) {
167     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
168    
169     if (@cb) {
170     $command{$name} = \@cb;
171     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
172     } else {
173     delete $command{$name};
174     delete $COMMAND{"$name\000"};
175     }
176     }
177    
178     Symbol::delete_package $pkg;
179     }
180    
181     sub load_extensions {
182     my $LIBDIR = maps_directory "perl";
183    
184     for my $ext (<$LIBDIR/*.ext>) {
185 root 1.3 next unless -r $ext;
186 root 1.2 eval {
187     load_extension $ext;
188     1
189     } or warn "$ext not loaded: $@";
190 root 1.1 }
191     }
192    
193     register_command "perl-reload", 0, sub {
194     my ($who, $arg) = @_;
195    
196     if ($who->flag (FLAG_WIZ)) {
197 root 1.3 $who->message ("reloading...");
198    
199 root 1.1 warn "reloading...\n";
200 root 1.4 eval {
201     unload_extension $_ for @exts;
202     delete $INC{"cf.pm"};
203    
204     # don't, removes xs symbols, too
205     #Symbol::delete_package $pkg;
206    
207     require cf;
208     };
209     warn $@ if $@;
210     $who->message ($@) if $@;
211 root 1.1 warn "reloaded\n";
212 root 1.3
213     $who->message ("reloaded");
214     } else {
215     $who->message ("Intruder Alert!");
216 root 1.1 }
217     };
218    
219 root 1.8 #############################################################################
220     # load/save/clean perl data associated with a map
221    
222 root 1.7 *on_mapclean = sub {
223     my $map = shift->{map};
224    
225     my $path = $map->tmpname;
226     defined $path or return;
227    
228     unlink "$path.cfperl";
229     };
230    
231 root 1.6 *on_mapin =
232     *on_mapload = sub {
233     my $map = shift->{map};
234    
235     my $path = $map->tmpname;
236     $path = $map->path unless defined $path;
237    
238     open my $fh, "<:raw", "$path.cfperl"
239     or return; # no perl data
240    
241     my $data = Storable::thaw do { local $/; <$fh> };
242    
243     $data->{version} <= 1
244     or return; # too new
245    
246     $map->_set_obs ($data->{obs});
247     };
248    
249     *on_mapout = sub {
250     my $map = shift->{map};
251    
252     my $path = $map->tmpname;
253     $path = $map->path unless defined $path;
254    
255     my $obs = $map->_get_obs;
256    
257     if (defined $obs) {
258     open my $fh, ">:raw", "$path.cfperl"
259     or die "$path.cfperl: $!";
260    
261 root 1.8 stat $path;
262    
263     print $fh Storable::nfreeze {
264     size => (stat _)[7],
265     time => (stat _)[9],
266     version => 1,
267     obs => $obs,
268     };
269    
270     chmod SAVE_MODE, "$path.cfperl"; # very racy, but cf-compatible *g*
271     } else {
272     unlink "$path.cfperl";
273     }
274     };
275    
276     #############################################################################
277     # load/save perl data associated with player->ob objects
278    
279     *on_player_load = sub {
280     my ($event) = @_;
281     my $path = $event->{message};
282     my $ob = $event->{who};
283    
284     open my $fh, "<:raw", "$path.cfperl"
285     or return; # no perl data
286    
287     my $data = Storable::thaw do { local $/; <$fh> };
288    
289     $data->{version} <= 1
290     or return; # too new
291    
292     %$ob = %{$data->{ob}};
293     };
294    
295     *on_player_save = sub {
296     my ($event) = @_;
297     my $path = $event->{message};
298     my $ob = $event->{who};
299    
300     if (keys %$ob) {
301     open my $fh, ">:raw", "$path.cfperl"
302     or die "$path.cfperl: $!";
303    
304     stat $path;
305    
306 root 1.6 print $fh Storable::nfreeze {
307 root 1.8 size => (stat _)[7],
308     time => (stat _)[9],
309 root 1.6 version => 1,
310 root 1.8 ob => $ob,
311 root 1.6 };
312 root 1.7
313     chmod SAVE_MODE, "$path.cfperl"; # very racy, but cf-compatible *g*
314 root 1.6 } else {
315     unlink "$path.cfperl";
316     }
317     };
318    
319     register "<global>", __PACKAGE__;
320    
321 root 1.1 load_extensions;
322    
323     1
324