ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.18
Committed: Tue Jul 11 14:24:15 2006 UTC (17 years, 10 months ago) by root
Branch: MAIN
Changes since 1.17: +5 -0 lines
Log Message:
Prepared perl plug-in for event support
removed on_clock (use more efficient Event->timer).

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