+ $ref = $thing->$sub($dxchan) if $thing->can($sub);
+ }
+ $dxchan->send(ref $ref ? @$ref : $ref) if $ref;
+}
+
+# broadcast to all except @_
+sub broadcast
+{
+ my $thing = shift;
+ dbg("Thingy::broadcast: " . $thing->ascii) if isdbg('thing');
+
+ foreach my $dxchan (DXChannel::get_all()) {
+ next if $dxchan == $main::me;
+ next if grep $dxchan == $_, @_;
+ $thing->send($dxchan);
+ }
+}
+
+# queue this thing for processing
+sub queue
+{
+ my $thing = shift;
+ my $dxchan = shift;
+ $thing->{dxchan} = $dxchan->call;
+ push @queue, $thing;
+}
+
+#
+# this is the main commutator loop. In due course it will
+# become the *only* commutator loop, This can be called in one
+# of two ways: either with 2 args or with none.
+#
+# The two arg form is an immediate "queue and handle" and does
+# a full cycle, immediately
+#
+sub process
+{
+ my $thing;
+ if (@_ == 2) {
+ $thing = shift;
+ $thing->queue(shift);
+ }
+ while (@queue) {
+ $thing = shift @queue;
+ my $dxchan = DXChannel::get($thing->{dxchan});
+ if ($dxchan) {
+ if ($thing->can('in_filter')) {
+ next unless $thing->in_filter($dxchan);
+ }
+
+ # remember any useful routes
+ RouteDB::update($thing->{origin}, $dxchan->{call}, $thing->{hopsaway});
+ RouteDB::update($thing->{user}, $dxchan->{call}, $thing->{hopsaway}) if exists $thing->{user};
+
+ $thing->handle($dxchan);
+ }
+ }
+
+ # per second and per minute processing
+ if ($main::systime != $lastsec) {
+ if ($main::systime >= $lastmin+60) {
+ foreach my $r (@permin) {
+ &{$r->[0]}();
+ }
+ $lastmin = $main::systime;
+ }
+ foreach my $r (@persec) {
+ &{$r->[0]}();
+ }
+ $lastsec = $main::systime;