Phi: nicer scroll animation for METAR widget
[fg:fgdata.git] / Nasal / scenery.nas
1 ###############################################################################
2 ##
3 ##  Support tools for multiplayer enabled scenery objects.
4 ##
5 ##  Copyright (C) 2011 - 2013  Anders Gidenstam  (anders(at)gidenstam.org)
6 ##  This file is licensed under the GPL license version 2 or later.
7 ##
8 ###############################################################################
9
10 # The event channel for scenery objects.
11 # See mp_broadcast.EventChannel for documentation.
12 var events = nil;
13
14 ###############################################################################
15 # An extended aircraft.door that transmits the door events over MP using the
16 # scenery.events channel.
17 # Use only for single instance objects (e.g. static scenery objects).
18 #
19 # Note: Currently toggle() is the only shared event.
20 var sharedDoor = {
21     new: func(node, swingtime, pos = 0) {
22         var obj = aircraft.door.new(node, swingtime, pos);
23         obj.parents    = [sharedDoor] ~ obj.parents;
24         obj.event_hash = mp_broadcast.Binary.stringHash
25             (isa(node, props.Node) ? node.getPath() : node);
26         obj.clock      = mp_broadcast.LamportClock.new();
27         obj.loopid     = 0;
28         events.register(obj.event_hash,
29                         func (sender, msg) { obj._process(sender, msg) });
30         return obj;
31     },
32     toggle: func {
33         # Send current time, current position and target position.
34         me.clock.advance();
35         me.move(me.target);
36         me._loop(me.loopid += 1);
37     },
38     destroy : func {
39         me.loopid += 1;
40         events.deregister(me.event_hash);
41     },
42     _process : func (sender, msg) {
43         if (me.clock.merge(sender, msg)) {
44             me.setpos(mp_broadcast.Binary.decodeDouble
45                       (substr(msg, mp_broadcast.Binary.sizeOf["LamportTS"])));
46             me.target = mp_broadcast.Binary.decodeByte
47                 (substr(msg,
48                         mp_broadcast.Binary.sizeOf["LamportTS"] +
49                         mp_broadcast.Binary.sizeOf["double"]));
50             me.move(me.target);
51         }
52     },
53     _loop : func (id) {
54         id == me.loopid or return;
55         # Send current time, current position and target position.
56         events.send(me.event_hash,
57                     me.clock.timestamp() ~
58                     mp_broadcast.Binary.encodeDouble(me.positionN.getValue()) ~
59                     mp_broadcast.Binary.encodeByte(!me.target));
60         settimer(func { me._loop(id); }, 17, 1);
61     }
62 };
63
64 ###############################################################################
65 # Internals
66 var shared_pp = "scenery/share-events";
67
68 var _set_state = func {
69     if (getprop("/sim/signals/reinit")) return; # Ignore resets.
70     if (getprop(shared_pp)) {
71         #print("scenery.nas: starting event sharing.");
72         events.start();
73     } else {
74         #print("scenery.nas: stopping event sharing.");
75         events.stop();
76     }
77
78 }
79
80 _setlistener("sim/signals/nasal-dir-initialized", func {
81     events = mp_broadcast.EventChannel.new("scenery/events");
82     if (getprop(shared_pp)) {
83         #print("scenery.nas: starting event sharing.");
84         events.start();
85     } else {
86         #print("scenery.nas: stopping event sharing.");
87         events.stop();
88     }
89     setlistener(shared_pp, _set_state);
90 });