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.92 by root, Thu Dec 21 06:42:28 2006 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines