ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.45
Committed: Sat Aug 26 23:36:32 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.44: +100 -53 lines
Log Message:
intermediate check-in, per-object events work

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.23 use Opcode;
7     use Safe;
8     use Safe::Hole;
9 root 1.19
10 root 1.32 use Time::HiRes;
11 root 1.18 use Event;
12 root 1.19 $Event::Eval = 1; # no idea why this is required, but it is
13 root 1.1
14     use strict;
15    
16 root 1.39 our %COMMAND = ();
17 root 1.1 our @EVENT;
18     our %PROP_TYPE;
19     our %PROP_IDX;
20 root 1.27 our $LIBDIR = maps_directory "perl";
21 root 1.1
22 root 1.35 our $TICK = MAX_TIME * 1e-6;
23     our $TICK_WATCHER;
24     our $NEXT_TICK;
25    
26 root 1.1 BEGIN {
27     *CORE::GLOBAL::warn = sub {
28     my $msg = join "", @_;
29     $msg .= "\n"
30     unless $msg =~ /\n$/;
31    
32     print STDERR "cfperl: $msg";
33     LOG llevError, "cfperl: $msg";
34     };
35     }
36    
37 root 1.9 my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
38    
39 root 1.1 # generate property mutators
40     sub 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 root 1.9 } unless $ignore_set{$prop};
59 root 1.1 }
60     }
61    
62     # auto-generate most of the API
63    
64     prop_gen OBJECT_PROP => "cf::object";
65     # CFAPI_OBJECT_ANIMATION?
66     prop_gen PLAYER_PROP => "cf::object::player";
67    
68     prop_gen MAP_PROP => "cf::map";
69     prop_gen ARCH_PROP => "cf::arch";
70    
71 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
72 root 1.25
73 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
74 root 1.25 # within the Safe compartment.
75 root 1.45 for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch)) {
76 root 1.25 no strict 'refs';
77 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
78 root 1.25 }
79 root 1.1
80 root 1.18 $Event::DIED = sub {
81     warn "error in event callback: @_";
82     };
83    
84 root 1.5 my %ext_pkg;
85 root 1.1 my @exts;
86     my @hook;
87     my %command;
88 root 1.15 my %extcmd;
89 root 1.1
90 root 1.39 #############################################################################
91 root 1.45 # utility functions
92 root 1.44
93 root 1.45 use JSON::Syck (); # TODO# replace by JSON::PC once working
94 root 1.44
95 root 1.45 sub from_json($) {
96     $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
97     JSON::Syck::Load $_[0]
98 root 1.44 }
99    
100 root 1.45 sub to_json($) {
101     $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
102     JSON::Syck::Dump $_[0]
103 root 1.44 }
104    
105     #############################################################################
106 root 1.39 # "new" plug-in system
107    
108     =item cf::object::attach ... # NYI
109    
110 root 1.40 =item cf::attach_global ...
111 root 1.39
112 root 1.45 =item cf::attach_to_type $object_type, ...
113 root 1.39
114 root 1.40 =item cf::attach_to_objects ...
115 root 1.39
116 root 1.40 =item cf::attach_to_players ...
117 root 1.39
118 root 1.40 =item cf::attach_to_maps ...
119 root 1.39
120 root 1.45 =item cf:register_attachment $name, ...
121    
122 root 1.40 prio => $number, # lower is earlier
123 root 1.39 on_xxx => \&cb,
124     package => package::,
125    
126     =cut
127    
128 root 1.40 # the following variables are defined in .xs and must not be re-created
129 root 1.39 our @CB_GLOBAL = (); # registry for all global events
130 root 1.45 our @CB_OBJECT = (); # all objects (should not be used except in emergency)
131 root 1.40 our @CB_PLAYER = ();
132 root 1.39 our @CB_TYPE = (); # registry for type (cf-object class) based events
133 root 1.40 our @CB_MAP = ();
134 root 1.39
135 root 1.45 my %attachment;
136    
137 root 1.39 sub _attach_cb($\%$$$) {
138     my ($registry, $undo, $event, $prio, $cb) = @_;
139    
140     use sort 'stable';
141    
142     $cb = [$prio, $cb];
143    
144     @{$registry->[$event]} = sort
145     { $a->[0] cmp $b->[0] }
146     @{$registry->[$event] || []}, $cb;
147    
148     push @{$undo->{cb}}, [$event, $cb];
149     }
150    
151     # attach handles attaching event callbacks
152     # the only thing the caller has to do is pass the correct
153     # registry (== where the callback attaches to).
154 root 1.45 sub _attach(\@$@) {
155     my ($registry, $klass, @arg) = @_;
156 root 1.39
157     my $prio = 0;
158    
159     my %undo = (
160     registry => $registry,
161     cb => [],
162     );
163    
164     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
165    
166 root 1.45 while (@arg) {
167     my $type = shift @arg;
168 root 1.39
169     if ($type eq "prio") {
170 root 1.45 $prio = shift @arg;
171 root 1.39
172     } elsif ($type eq "package") {
173 root 1.45 my $pkg = shift @arg;
174 root 1.39
175     while (my ($name, $id) = each %cb_id) {
176     if (my $cb = $pkg->can ($name)) {
177     _attach_cb $registry, %undo, $id, $prio, $cb;
178     }
179     }
180    
181     } elsif (exists $cb_id{$type}) {
182 root 1.45 _attach_cb $registry, %undo, $cb_id{$type}, $prio, shift @arg;
183 root 1.39
184     } elsif (ref $type) {
185     warn "attaching objects not supported, ignoring.\n";
186    
187     } else {
188 root 1.45 shift @arg;
189 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
190     }
191     }
192    
193     \%undo
194     }
195    
196     sub cf::object::attach {
197     die;
198     }
199    
200     sub attach_global {
201     _attach @CB_GLOBAL, KLASS_GLOBAL, @_
202     }
203    
204 root 1.40 sub attach_to_type {
205 root 1.39 my $type = shift;
206 root 1.45
207 root 1.40 _attach @{$CB_TYPE[$type]}, KLASS_OBJECT, @_
208 root 1.39 }
209    
210     sub attach_to_objects {
211 root 1.40 _attach @CB_OBJECT, KLASS_OBJECT, @_
212 root 1.39 }
213    
214     sub attach_to_players {
215 root 1.40 _attach @CB_PLAYER, KLASS_PLAYER, @_
216 root 1.39 }
217    
218     sub attach_to_maps {
219 root 1.40 _attach @CB_MAP, KLASS_MAP, @_
220 root 1.39 }
221    
222 root 1.45 sub register_attachment {
223     my $name = shift;
224    
225     $attachment{$name} = [@_];
226     }
227    
228 root 1.39 our $override;
229 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
230 root 1.39
231 root 1.45 sub override {
232     $override = 1;
233     @invoke_results = ();
234 root 1.39 }
235    
236 root 1.45 sub do_invoke {
237 root 1.39 my $event = shift;
238 root 1.40 my $callbacks = shift;
239 root 1.39
240 root 1.45 @invoke_results = ();
241    
242 root 1.39 local $override;
243    
244 root 1.40 for (@$callbacks) {
245 root 1.39 eval { &{$_->[1]} };
246    
247     if ($@) {
248     warn "$@";
249     warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n";
250     override;
251     }
252    
253     return 1 if $override;
254     }
255    
256     0
257     }
258    
259     #############################################################################
260 root 1.45 # object support
261    
262     sub instantiate {
263     my ($obj, $data) = @_;
264     my $registry = $obj->registry;
265    
266     $data = from_json $data;
267    
268     for (@$data) {
269     my ($pri, $name, @args) = @$_;
270    
271     if (my $attach = $attachment{$name}) {
272     _attach @$registry, KLASS_OBJECT, @$attach;
273    
274     if (my $cb = delete $registry->[EVENT_OBJECT_INSTANTIATE]) {
275     for (@$cb) {
276     eval { $_->[1]->($obj, @args); };
277     if ($@) {
278     warn "$@";
279     warn "... while processing '$name' instantiate with args <@args>\n";
280     }
281     }
282     }
283     } else {
284     warn "object uses attachment $name that is not available, postponing.\n";
285     }
286    
287     push @{$obj->{_attachment}}, $name;
288     }
289     }
290    
291     # basically do the same as instantiate, without calling instantiate
292     sub reattach {
293     my ($obj) = @_;
294     my $registry = $obj->registry;
295    
296     warn "reattach<@_, $_>\n";
297     }
298    
299     attach_to_objects
300     prio => -1000000,
301     on_clone => sub {
302     my ($src, $dst) = @_;
303    
304     @{$dst->registry} = @{$src->registry};
305     warn "registry clone ", join ":", @{$src->registry};#d#
306    
307     %$dst = %$src;
308    
309     $dst->{_attachment} = [@{ $src->{_attachment} }]
310     if exists $src->{_attachment};
311    
312     warn "clone<@_>\n";#d#
313     },
314     ;
315    
316     #############################################################################
317 root 1.39 # old plug-in events
318    
319 root 1.1 sub inject_event {
320 root 1.14 my $extension = shift;
321     my $event_code = shift;
322 root 1.1
323 root 1.14 my $cb = $hook[$event_code]{$extension}
324 root 1.5 or return;
325    
326 root 1.14 &$cb
327 root 1.5 }
328    
329     sub inject_global_event {
330 root 1.12 my $event = shift;
331 root 1.5
332 root 1.12 my $cb = $hook[$event]
333 root 1.1 or return;
334    
335 root 1.12 List::Util::max map &$_, values %$cb
336 root 1.1 }
337    
338     sub inject_command {
339     my ($name, $obj, $params) = @_;
340    
341     for my $cmd (@{ $command{$name} }) {
342     $cmd->[1]->($obj, $params);
343     }
344    
345     -1
346     }
347    
348     sub register_command {
349     my ($name, $time, $cb) = @_;
350    
351     my $caller = caller;
352 root 1.16 #warn "registering command '$name/$time' to '$caller'";
353 root 1.4
354 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
355     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
356     }
357    
358 root 1.16 sub register_extcmd {
359     my ($name, $cb) = @_;
360    
361     my $caller = caller;
362     #warn "registering extcmd '$name' to '$caller'";
363    
364     $extcmd{$name} = [$cb, $caller];
365     }
366    
367 root 1.6 sub register {
368     my ($base, $pkg) = @_;
369    
370 root 1.45 #TODO
371 root 1.6 }
372    
373 root 1.1 sub load_extension {
374     my ($path) = @_;
375    
376     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
377 root 1.5 my $base = $1;
378 root 1.1 my $pkg = $1;
379     $pkg =~ s/[^[:word:]]/_/g;
380 root 1.41 $pkg = "ext::$pkg";
381 root 1.1
382     warn "loading '$path' into '$pkg'\n";
383    
384     open my $fh, "<:utf8", $path
385     or die "$path: $!";
386    
387     my $source =
388     "package $pkg; use strict; use utf8;\n"
389     . "#line 1 \"$path\"\n{\n"
390     . (do { local $/; <$fh> })
391     . "\n};\n1";
392    
393     eval $source
394     or die "$path: $@";
395    
396     push @exts, $pkg;
397 root 1.5 $ext_pkg{$base} = $pkg;
398 root 1.1
399 root 1.6 # no strict 'refs';
400 root 1.23 # @{"$pkg\::ISA"} = ext::;
401 root 1.1
402 root 1.6 register $base, $pkg;
403 root 1.1 }
404    
405     sub unload_extension {
406     my ($pkg) = @_;
407    
408     warn "removing extension $pkg\n";
409    
410     # remove hooks
411 root 1.45 #TODO
412     # for my $idx (0 .. $#PLUGIN_EVENT) {
413     # delete $hook[$idx]{$pkg};
414     # }
415 root 1.1
416     # remove commands
417     for my $name (keys %command) {
418     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
419    
420     if (@cb) {
421     $command{$name} = \@cb;
422     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
423     } else {
424     delete $command{$name};
425     delete $COMMAND{"$name\000"};
426     }
427     }
428    
429 root 1.15 # remove extcmds
430 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
431     delete $extcmd{$name};
432 root 1.15 }
433    
434 root 1.43 if (my $cb = $pkg->can ("unload")) {
435 elmex 1.31 eval {
436     $cb->($pkg);
437     1
438     } or warn "$pkg unloaded, but with errors: $@";
439     }
440    
441 root 1.1 Symbol::delete_package $pkg;
442     }
443    
444     sub load_extensions {
445     my $LIBDIR = maps_directory "perl";
446    
447     for my $ext (<$LIBDIR/*.ext>) {
448 root 1.3 next unless -r $ext;
449 root 1.2 eval {
450     load_extension $ext;
451     1
452     } or warn "$ext not loaded: $@";
453 root 1.1 }
454     }
455    
456 root 1.36 sub _perl_reload(&) {
457     my ($msg) = @_;
458    
459     $msg->("reloading...");
460    
461     eval {
462     # 1. cancel all watchers
463     $_->cancel for Event::all_watchers;
464    
465     # 2. unload all extensions
466     for (@exts) {
467     $msg->("unloading <$_>");
468     unload_extension $_;
469     }
470    
471     # 3. unload all modules loaded from $LIBDIR
472     while (my ($k, $v) = each %INC) {
473     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
474    
475     $msg->("removing <$k>");
476     delete $INC{$k};
477 root 1.1
478 root 1.36 $k =~ s/\.pm$//;
479     $k =~ s/\//::/g;
480 root 1.3
481 root 1.36 if (my $cb = $k->can ("unload_module")) {
482     $cb->();
483 root 1.27 }
484    
485 root 1.36 Symbol::delete_package $k;
486     }
487 root 1.27
488 root 1.41 # 4. get rid of safe::, as good as possible
489     Symbol::delete_package "safe::$_"
490 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
491 root 1.36
492     # 5. remove register_script_function callbacks
493     # TODO
494    
495     # 6. unload cf.pm "a bit"
496     delete $INC{"cf.pm"};
497    
498 root 1.41 # don't, removes xs symbols, too,
499     # and global variables created in xs
500 root 1.36 #Symbol::delete_package __PACKAGE__;
501    
502     # 7. reload cf.pm
503     $msg->("reloading cf.pm");
504     require cf;
505     };
506     $msg->($@) if $@;
507 root 1.27
508 root 1.36 $msg->("reloaded");
509     };
510 root 1.27
511 root 1.36 sub perl_reload() {
512     _perl_reload {
513     warn $_[0];
514     print "$_[0]\n";
515     };
516     }
517 root 1.27
518 root 1.36 register_command "perl-reload", 0, sub {
519     my ($who, $arg) = @_;
520 root 1.27
521 root 1.36 if ($who->flag (FLAG_WIZ)) {
522     _perl_reload {
523     warn $_[0];
524     $who->message ($_[0]);
525 root 1.4 };
526 root 1.1 }
527     };
528    
529 root 1.8 #############################################################################
530 root 1.28 # extcmd framework, basically convert ext <msg>
531 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
532    
533 root 1.44 attach_to_players
534 root 1.43 on_extcmd => sub {
535     my ($pl, $buf) = @_;
536    
537     my $msg = eval { from_json $buf };
538    
539     if (ref $msg) {
540     if (my $cb = $extcmd{$msg->{msgtype}}) {
541     if (my %reply = $cb->[0]->($pl, $msg)) {
542     $pl->ext_reply ($msg->{msgid}, %reply);
543     }
544 root 1.28 }
545 root 1.43 } else {
546     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
547 root 1.28 }
548 root 1.15
549 root 1.43 cf::override;
550     },
551     ;
552 root 1.15
553     #############################################################################
554 root 1.8 # load/save/clean perl data associated with a map
555    
556 root 1.39 *cf::mapsupport::on_clean = sub {
557 root 1.13 my ($map) = @_;
558 root 1.7
559     my $path = $map->tmpname;
560     defined $path or return;
561    
562     unlink "$path.cfperl";
563     };
564    
565 root 1.39 *cf::mapsupport::on_swapin =
566     *cf::mapsupport::on_load = sub {
567 root 1.13 my ($map) = @_;
568 root 1.6
569     my $path = $map->tmpname;
570     $path = $map->path unless defined $path;
571    
572     open my $fh, "<:raw", "$path.cfperl"
573     or return; # no perl data
574    
575     my $data = Storable::thaw do { local $/; <$fh> };
576    
577     $data->{version} <= 1
578     or return; # too new
579    
580     $map->_set_obs ($data->{obs});
581     };
582    
583 root 1.39 *cf::mapsupport::on_swapout = sub {
584 root 1.13 my ($map) = @_;
585 root 1.6
586     my $path = $map->tmpname;
587     $path = $map->path unless defined $path;
588    
589     my $obs = $map->_get_obs;
590    
591     if (defined $obs) {
592     open my $fh, ">:raw", "$path.cfperl"
593     or die "$path.cfperl: $!";
594    
595 root 1.8 stat $path;
596    
597     print $fh Storable::nfreeze {
598     size => (stat _)[7],
599     time => (stat _)[9],
600     version => 1,
601     obs => $obs,
602     };
603    
604     chmod SAVE_MODE, "$path.cfperl"; # very racy, but cf-compatible *g*
605     } else {
606     unlink "$path.cfperl";
607     }
608     };
609    
610 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
611    
612 root 1.8 #############################################################################
613     # load/save perl data associated with player->ob objects
614    
615 root 1.33 sub all_objects(@) {
616     @_, map all_objects ($_->inv), @_
617     }
618    
619 root 1.39 attach_to_players
620     on_load => sub {
621     my ($pl, $path) = @_;
622    
623     for my $o (all_objects $pl->ob) {
624     if (my $value = $o->get_ob_key_value ("_perl_data")) {
625     $o->set_ob_key_value ("_perl_data");
626 root 1.8
627 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
628     }
629 root 1.11 }
630 root 1.39 },
631     on_save => sub {
632     my ($pl, $path) = @_;
633 root 1.8
634 root 1.39 $_->set_ob_key_value (_perl_data => unpack "H*", Storable::nfreeze $_)
635     for grep %$_, all_objects $pl->ob;
636     },
637     ;
638 root 1.6
639 root 1.22 #############################################################################
640     # core extensions - in perl
641    
642 root 1.23 =item cf::player::exists $login
643    
644     Returns true when the given account exists.
645    
646     =cut
647    
648     sub cf::player::exists($) {
649     cf::player::find $_[0]
650     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
651     }
652    
653 root 1.28 =item $player->reply ($npc, $msg[, $flags])
654    
655     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
656     can be C<undef>. Does the right thing when the player is currently in a
657     dialogue with the given NPC character.
658    
659     =cut
660    
661 root 1.22 # rough implementation of a future "reply" method that works
662     # with dialog boxes.
663 root 1.23 sub cf::object::player::reply($$$;$) {
664     my ($self, $npc, $msg, $flags) = @_;
665    
666     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
667 root 1.22
668 root 1.24 if ($self->{record_replies}) {
669     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
670     } else {
671     $msg = $npc->name . " says: $msg" if $npc;
672     $self->message ($msg, $flags);
673     }
674 root 1.22 }
675    
676 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
677    
678     Sends an ext reply to the player.
679    
680     =cut
681    
682     sub cf::player::ext_reply($$$%) {
683     my ($self, $id, %msg) = @_;
684    
685     $msg{msgid} = $id;
686    
687     $self->send ("ext " . to_json \%msg);
688     }
689    
690 root 1.22 #############################################################################
691 root 1.23 # map scripting support
692    
693 root 1.42 our $safe = new Safe "safe";
694 root 1.23 our $safe_hole = new Safe::Hole;
695    
696     $SIG{FPE} = 'IGNORE';
697    
698     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
699    
700 root 1.25 # here we export the classes and methods available to script code
701    
702     for (
703 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
704 root 1.25 ["cf::object::player" => qw(player)],
705     ["cf::player" => qw(peaceful)],
706     ) {
707     no strict 'refs';
708     my ($pkg, @funs) = @$_;
709 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
710 root 1.25 for @funs;
711     }
712 root 1.23
713     sub safe_eval($;@) {
714     my ($code, %vars) = @_;
715    
716     my $qcode = $code;
717     $qcode =~ s/"/‟/g; # not allowed in #line filenames
718     $qcode =~ s/\n/\\n/g;
719    
720     local $_;
721 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
722 root 1.23
723 root 1.42 my $eval =
724 root 1.23 "do {\n"
725     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
726     . "#line 0 \"{$qcode}\"\n"
727     . $code
728     . "\n}"
729 root 1.25 ;
730    
731     sub_generation_inc;
732 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
733 root 1.25 sub_generation_inc;
734    
735 root 1.42 if ($@) {
736     warn "$@";
737     warn "while executing safe code '$code'\n";
738     warn "with arguments " . (join " ", %vars) . "\n";
739     }
740    
741 root 1.25 wantarray ? @res : $res[0]
742 root 1.23 }
743    
744     sub register_script_function {
745     my ($fun, $cb) = @_;
746    
747     no strict 'refs';
748 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
749 root 1.23 }
750    
751     #############################################################################
752 root 1.34 # the server's main()
753    
754 root 1.39 sub main {
755 root 1.34 Event::loop;
756     }
757    
758     #############################################################################
759 root 1.22 # initialisation
760    
761 root 1.6 register "<global>", __PACKAGE__;
762    
763 root 1.27 unshift @INC, $LIBDIR;
764 root 1.17
765 root 1.1 load_extensions;
766    
767 root 1.35 $TICK_WATCHER = Event->timer (
768     prio => 1,
769     at => $NEXT_TICK || 1,
770     cb => sub {
771     cf::server_tick; # one server iteration
772    
773     my $NOW = Event::time;
774     $NEXT_TICK += $TICK;
775    
776 root 1.37 # if we are delayed by four ticks, skip them all
777     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
778 root 1.35
779     $TICK_WATCHER->at ($NEXT_TICK);
780     $TICK_WATCHER->start;
781     },
782     );
783    
784 root 1.1 1
785