1
0
Fork 0

Emesary fixes

- Transfer encoding rewritten to handle negative numbers properly and fix scaling.
- Change the "no receive method" to be just a warning as it is unwise rather than
  being wrong.

Emesary MP bridge fixes from Nikolai

OutgoingBridge:

- When transmitting queue, make sure transmitted
  messages cannot remain in the queue when not completely emptied, and
  thus be sent again, and eventually fill up the queue.

- Also allow smaller messages to sent even though there was not room for
  a larger message (the removal of the 'break' command).

- To prevent coding the same message again with the 'break' removed,
  store an already coded string that was rejected due to too little room
  inside the message so when there is room for it, it was coded only
  once.

- Stop recalculating MessageExpiryTime when there is no
  room for a notification. If it has asked to expire after expire time,
  let it expire.

IncomingBridge:

- Make sure listeners on emesary[x] properties are
  removed when a mp aircraft is invalid, otherwise a hidden bridge will
  sit in background and process same notifications a new bridge is
  already processing.

- Change order of setting IncomingMessageIndex to ease
  debugging in reciever.

- Since a bridge class can handle multiple emesary
  properties on same aircraft, use a vector per aircraft to store the
  bridge instances in, to make sure Remove can be called on all
  instances listening to same aircraft.
This commit is contained in:
Richard Harrison 2020-07-25 14:11:07 +02:00
parent 37729a8661
commit 3666ba1511
2 changed files with 263 additions and 260 deletions

View file

@ -20,7 +20,7 @@
# #
# Version : 4.8 # Version : 4.8
# #
# Copyright © 2016 Richard Harrison Released under GPL V2 # Copyright © 2016 Richard Harrison Released under GPL V2
# #
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Classes in this file: # Classes in this file:
@ -95,11 +95,12 @@ var Transmitter =
{ {
logprint(LOG_INFO, "Transmitter.Register: argument is not a Recipient object"); logprint(LOG_INFO, "Transmitter.Register: argument is not a Recipient object");
} }
#not having a Receive function is an error # Warn if recipient doesn't have a Receive function - this is not an error because
#a receive function could be added after the recipient has been registered - so it is
# deprecated to do this.
if (!isfunc(recipient["Receive"])) if (!isfunc(recipient["Receive"]))
{ {
logprint(DEV_ALERT, "Transmitter.Register: Error, argument has no Receive method!"); logprint(DEV_ALERT, "Transmitter.Register: Error, argument has no Receive method!");
return 0;
} }
foreach (var r; me.Recipients) foreach (var r; me.Recipients)
{ {
@ -309,18 +310,30 @@ var Notification =
logprint(DEV_ALERT, "Notification.new: _type must be a scalar!"); logprint(DEV_ALERT, "Notification.new: _type must be a scalar!");
return nil; return nil;
} }
if (!isscalar(_ident)) { if (!isscalar(_ident) and _ident != nil) {
logprint(DEV_ALERT, "Notification.new: _ident is not scalar but ", typeof(_ident)); logprint(DEV_ALERT, "Notification.new: _ident is not scalar but ", typeof(_ident));
return nil; return nil;
} }
if (_typeid == 0) { # typeID of 0 means that the notification does not have an assigned type ID
NotificationAutoTypeId += 1; # <0 means an automatic ID is required
# >= 16 is a reserved ID
# normally the typeID should be unique across all of FlightGear.
# use of automatic ID's is really only for notifications that will never be bridged,
# or more accurate when bridged the type isn't going to be known fully.
if (_typeid < 0) {
if (_ident != nil){
logprint(DEV_ALERT, "_typeid can only be omitted when registering class");
return nil;
}
# IDs >= 16 are reserved; see http://wiki.flightgear.org/Emesary_Notifications # IDs >= 16 are reserved; see http://wiki.flightgear.org/Emesary_Notifications
if (NotificationAutoTypeId == 16) { if (NotificationAutoTypeId >= 16) {
logprint(LOG_ALERT, "Notification: AutoTypeID limit exceeded: "~NotificationAutoTypeId); logprint(LOG_ALERT, "Notification: AutoTypeID limit exceeded: "~NotificationAutoTypeId);
return nil; return nil;
} }
NotificationAutoTypeId += 1;
_typeid = NotificationAutoTypeId; _typeid = NotificationAutoTypeId;
} }
@ -411,12 +424,12 @@ var BinaryAsciiTransfer =
{ {
#excluded chars 32 (<space>), 33 (!), 35 (#), 36($), 126 (~), 127 (<del>) #excluded chars 32 (<space>), 33 (!), 35 (#), 36($), 126 (~), 127 (<del>)
alphabet : alphabet :
chr(1) ~chr(2) ~chr(3) ~chr(4) ~chr(5) ~chr(6) ~chr(7) ~chr(8) ~chr(9) chr(1)~chr(2)~chr(3)~chr(4)~chr(5)~chr(6)~chr(7)~chr(8)
~chr(10)~chr(11)~chr(12)~chr(13)~chr(14)~chr(15)~chr(16)~chr(17)~chr(18)~chr(19) ~chr(9)~chr(10)~chr(11)~chr(12)~chr(13)~chr(14)~chr(15)~chr(16)~chr(17)~chr(18)~chr(19)
~chr(20)~chr(21)~chr(22)~chr(23)~chr(24)~chr(25)~chr(26)~chr(27)~chr(28)~chr(29) ~chr(20)~chr(21)~chr(22)~chr(23)~chr(24)~chr(25)~chr(26)~chr(27)~chr(28)~chr(29)
~chr(30)~chr(31) ~chr(34) ~chr(30)~chr(31)~chr(34)
~"%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}" ~"%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}"
~chr(128)~chr(129) ~chr(128)~chr(129)
~chr(130)~chr(131)~chr(132)~chr(133)~chr(134)~chr(135)~chr(136)~chr(137)~chr(138)~chr(139) ~chr(130)~chr(131)~chr(132)~chr(133)~chr(134)~chr(135)~chr(136)~chr(137)~chr(138)~chr(139)
~chr(140)~chr(141)~chr(142)~chr(143)~chr(144)~chr(145)~chr(146)~chr(147)~chr(148)~chr(149) ~chr(140)~chr(141)~chr(142)~chr(143)~chr(144)~chr(145)~chr(146)~chr(147)~chr(148)~chr(149)
~chr(150)~chr(151)~chr(152)~chr(153)~chr(154)~chr(155)~chr(156)~chr(157)~chr(158)~chr(159) ~chr(150)~chr(151)~chr(152)~chr(153)~chr(154)~chr(155)~chr(156)~chr(157)~chr(158)~chr(159)
@ -439,13 +452,12 @@ var BinaryAsciiTransfer =
empty_encoding: chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1), empty_encoding: chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1),
encodeNumeric : func(_num,length,factor) encodeNumeric : func(_num,length,factor)
{ {
#print(BinaryAsciiTransfer.po2[1]);
var irange = int(BinaryAsciiTransfer.po2[length] / factor);
var scale = int(irange / factor);
#print("EC ",irange, " sc=",scale);
var num = int(_num / factor); var num = int(_num / factor);
if (num < -scale) num = -scale;
else if (num > scale) num = scale; var irange = int(BinaryAsciiTransfer.po2[length]);
if (num < -irange) num = -irange;
else if (num > irange) num = irange;
num = int(num + irange); num = int(num + irange);
@ -467,7 +479,7 @@ var BinaryAsciiTransfer =
retval : {value:0, pos:0}, retval : {value:0, pos:0},
decodeNumeric : func(str, length, factor, pos) decodeNumeric : func(str, length, factor, pos)
{ {
var irange = int(BinaryAsciiTransfer.po2[length]/factor); var irange = int(BinaryAsciiTransfer.po2[length]);
var power = length-1; var power = length-1;
BinaryAsciiTransfer.retval.value = 0; BinaryAsciiTransfer.retval.value = 0;
BinaryAsciiTransfer.retval.pos = pos; BinaryAsciiTransfer.retval.pos = pos;
@ -573,11 +585,11 @@ var TransferFixedDouble =
{ {
encode : func(v, length, factor) encode : func(v, length, factor)
{ {
return BinaryAsciiTransfer.encodeNumeric(int(v), length, factor); return BinaryAsciiTransfer.encodeNumeric(v, length, factor);
}, },
decode : func(v, length, factor, pos) decode : func(v, length, factor, pos)
{ {
return BinaryAsciiTransfer.decodeNumeric(v, length, pos, factor); return BinaryAsciiTransfer.decodeNumeric(v, length, factor, pos);
} }
}; };
@ -611,24 +623,27 @@ var TransferByte =
var TransferCoord = var TransferCoord =
{ {
# 28 bits = 268435456 (268 435 456) # LatLon scaling;
# to transfer lat lon (360 degree range) 268435456/360=745654 # 1 degree = 110574 meters;
# we could use different factors for lat lon due to the differing range, however # requires 4 bytes for 1 meter resolution.
# this will be fine. # permits 0.1 meter resolution.
# 1 degree = 110574 meters; LatLonLength: 4,
LatLonFactor: 0.000001,
AltLength: 3,
encode : func(v) encode : func(v)
{ {
return BinaryAsciiTransfer.encodeNumeric((v.lat()+90)*745654,5, 1.0) return BinaryAsciiTransfer.encodeNumeric(v.lat(), TransferCoord.LatLonLength, TransferCoord.LatLonFactor)
~ BinaryAsciiTransfer.encodeNumeric((v.lon()+180)*745654,5, 1.0) ~ BinaryAsciiTransfer.encodeNumeric(v.lon(), TransferCoord.LatLonLength, TransferCoord.LatLonFactor)
~ TransferNumeric.encode(v.alt(), 3, 1.0); ~ emesary.TransferInt.encode(v.alt(), TransferCoord.AltLength);
}, },
decode : func(v,pos) decode : func(v,pos)
{ {
var dv = BinaryAsciiTransfer.decodeNumeric(v,5, 1.0 ,pos); var dv = BinaryAsciiTransfer.decodeNumeric(v, TransferCoord.LatLonLength, TransferCoord.LatLonFactor, pos);
var lat = (dv.value / 745654)-90; var lat = (dv.value);
dv = BinaryAsciiTransfer.decodeNumeric(v,5, 1.0 ,dv.pos); dv = BinaryAsciiTransfer.decodeNumeric(v, TransferCoord.LatLonLength, TransferCoord.LatLonFactor, dv.pos);
var lon = (dv.value / 745654)-180; var lon = (dv.value);
dv = TransferNumeric.decode(v, 3, 1.0 ,dv.pos); dv = emesary.TransferInt.decode(v, TransferCoord.AltLength, dv.pos);
var alt =dv.value; var alt =dv.value;
dv.value = geo.Coord.new().set_latlon(lat, lon).set_alt(alt); dv.value = geo.Coord.new().set_latlon(lat, lon).set_alt(alt);

View file

@ -1,31 +1,31 @@
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# #
# Title : EMESARY multiplayer bridge # Title : EMESARY multiplayer bridge
# #
# File Type : Implementation File # File Type : Implementation File
# #
# Description : Bridges selected emesary notifications over MP # Description : Bridges selected emesary notifications over MP
# : To send a message use a Transmitter with an object. That's all there is to it. # : To send a message use a Transmitter with an object. That's all there is to it.
# #
# References : http://chateau-logic.com/content/emesary-nasal-implementation-flightgear # References : http://chateau-logic.com/content/emesary-nasal-implementation-flightgear
# #
# Author : Richard Harrison (richard@zaretto.com) # Author : Richard Harrison (richard@zaretto.com)
# #
# Creation Date : 04 April 2016 # Creation Date : 04 April 2016
# #
# Version : 4.8 # Version : 4.8
# #
# Copyright © 2016 Richard Harrison Released under GPL V2 # Copyright © 2016 Richard Harrison Released under GPL V2
# #
#---------------------------------------------------------------------------*/ #---------------------------------------------------------------------------*/
# Example of connecting an incoming and outgoing bridge (should reside inside an aircraft nasal file) # Example of connecting an incoming and outgoing bridge (should reside inside an aircraft nasal file)
# #
# var routedNotifications = [notifications.TacticalNotification.new(nil)]; # var routedNotifications = [notifications.TacticalNotification.new(nil)];
# var incomingBridge = emesary_mp_bridge.IncomingMPBridge.startMPBridge(routedNotifications); # var incomingBridge = emesary_mp_bridge.IncomingMPBridge.startMPBridge(routedNotifications);
# var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp",routedNotifications); # var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp",routedNotifications);
#------------------------------------------------------------------ #------------------------------------------------------------------
# #
# NOTES: # NOTES:
# * Aircraft do not need to have both an incoming and outgoing bridge, but it is usual. # * Aircraft do not need to have both an incoming and outgoing bridge, but it is usual.
# #
@ -36,80 +36,79 @@
# * Transmit frequency and message lifetime may need to be tuned. # * Transmit frequency and message lifetime may need to be tuned.
# #
# * IsDistinct messages must be absolute and self contained as a later message will # * IsDistinct messages must be absolute and self contained as a later message will
# supercede any earlier ones in the outgoing queue (possibly prior to receipt) # supercede any earlier ones in the outgoing queue (possibly prior to receipt)
# #
# * Use the message type and ident to identify distinct messages # * Use the message type and ident to identify distinct messages
# #
# * The outgoing 'port' is a multiplay/emesary/bridge index, however any available string property # * The outgoing 'port' is a multiplay/emesary/bridge index, however any available string property
# can be used by specifying it in the construction of the incoming or outgoing bridge. # can be used by specifying it in the construction of the incoming or outgoing bridge.
# NOTE: This should not often be changed as it different versions of FG or model will # NOTE: This should not often be changed as it different versions of FG or model will
# have to use the same properties to be able to communicate # have to use the same properties to be able to communicate
# #
# * multiplay/emesary/bridge-type is used to identify the bridge that is in use. This is to # * multiplay/emesary/bridge-type is used to identify the bridge that is in use. This is to
# protect against bridges being used for different purposes by different models. # protect against bridges being used for different purposes by different models.
# #
# * The bridge-type property should contain an ID that identifies the purpose # * The bridge-type property should contain an ID that identifies the purpose
# and thereore the message set that the bridge will be using. # and thereore the message set that the bridge will be using.
# #
# - ! is used as a seperator between the elements that are used to send the # - ! is used as a seperator between the elements that are used to send the
# notification (typeid, sequence, notification) # notification (typeid, sequence, notification)
# - There is an extra ! at the start of the message that is used to indicate protocol version. # - There is an extra ! at the start of the message that is used to indicate protocol version.
# - ; is used to seperate serialzied elemnts of the notification # - ; is used to seperate serialzied elemnts of the notification
# General Notes # General Notes
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Outgoing messages are sent in a scheduled manner, usually once per # Outgoing messages are sent in a scheduled manner, usually once per
# second, and each message has a lifetime (to allow for propogation to # second, and each message has a lifetime (to allow for propogation to
# all clients over UDP). Clients will ignore messages that they have # all clients over UDP). Clients will ignore messages that they have
# already received (based on the sequence id). # already received (based on the sequence id).
# The incoming bridge will usually be created part of the aircraft # The incoming bridge will usually be created part of the aircraft
# model file; it is important to understand that each AI/MP model will # model file; it is important to understand that each AI/MP model will
# have an incoming bridge as each element in /ai/models needs its own # have an incoming bridge as each element in /ai/models needs its own
# bridge to keep up with the incoming sequence id. This scheme may not # bridge to keep up with the incoming sequence id. This scheme may not
# work properly as it relies on the model being loaded which may only # work properly as it relies on the model being loaded which may only
# happen when visible so it may be necessary to track AI models in a # happen when visible so it may be necessary to track AI models in a
# seperate instantiatable incoming bridge manager. # seperate instantiatable incoming bridge manager.
# #
# The outgoing bridge would usually be created within the aircraft loading Nasal. # The outgoing bridge would usually be created within the aircraft loading Nasal.
var EmesaryMPBridgeDefaultPropertyIndex=19; var EmesaryMPBridgeDefaultPropertyIndex=19;
var OutgoingMPBridge = var OutgoingMPBridge =
{ {
SeperatorChar : "!", SeperatorChar : "!",
MessageEndChar : "~", MessageEndChar : "~",
StartMessageIndex : 11, StartMessageIndex : 11,
DefaultMessageLifetimeSeconds : 10, DefaultMessageLifetimeSeconds : 10,
MPStringMaxLen: 128, MPStringMaxLen: 128,
new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _root="", _transmitter=nil, _propertybase="emesary/bridge") new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _root="", _transmitter=nil, _propertybase="emesary/bridge")
{ {
if (_transmitter == nil) if (_transmitter == nil)
_transmitter = emesary.GlobalTransmitter; _transmitter = emesary.GlobalTransmitter;
print("OutgoingMPBridge created for "~_ident," mp=",_mpidx); print("OutgoingMPBridge created for "~_ident," mp=",_mpidx);
var new_class = emesary.Recipient.new("OutgoingMPBridge "~_ident); var new_class = emesary.Recipient.new("OutgoingMPBridge "~_ident);
if(_notifications_to_bridge == nil) if (_notifications_to_bridge == nil)
new_class.NotificationsToBridge = []; new_class.NotificationsToBridge = [];
else else
new_class.NotificationsToBridge = _notifications_to_bridge; new_class.NotificationsToBridge = _notifications_to_bridge;
new_class.NotificationsToBridge_Lookup = {}; new_class.NotificationsToBridge_Lookup = {};
foreach(var n ; new_class.NotificationsToBridge) { foreach (var n ; new_class.NotificationsToBridge) {
print(" ",_ident," outwards bridge[",n,"] notifications of type --> ",n.NotificationType, " Id ",n.TypeId); print(" ",_ident," outwards bridge[",n,"] notifications of type --> ",n.NotificationType, " Id ",n.TypeId);
n.MessageIndex = OutgoingMPBridge.StartMessageIndex; n.MessageIndex = OutgoingMPBridge.StartMessageIndex;
new_class.NotificationsToBridge_Lookup[n.TypeId] = n; new_class.NotificationsToBridge_Lookup[n.TypeId] = n;
} }
new_class.MPout = ""; new_class.MPout = "";
new_class.MPidx = _mpidx; new_class.MPidx = _mpidx;
new_class.MessageLifeTime = 10; # seconds new_class.MessageLifeTime = 10; # seconds
new_class.OutgoingList = []; new_class.OutgoingList = [];
new_class.Transmitter = _transmitter; new_class.Transmitter = _transmitter;
new_class.TransmitRequired=0; new_class.TransmitRequired=0;
new_class.Transmitter.Register(new_class);
new_class.MpVariable = _root~"sim/multiplay/"~_propertybase~"["~new_class.MPidx~"]"; new_class.MpVariable = _root~"sim/multiplay/"~_propertybase~"["~new_class.MPidx~"]";
new_class.TransmitterActive = 0; new_class.TransmitterActive = 0;
new_class.TransmitFrequencySeconds = 1; new_class.TransmitFrequencySeconds = 1;
@ -119,8 +118,8 @@ var OutgoingMPBridge =
new_class.TransmitTimer = new_class.TransmitTimer =
maketimer(6, func maketimer(6, func
{ {
if(new_class.TransmitterActive) if (new_class.TransmitterActive)
new_class.Transmit(); new_class.Transmit();
new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds); new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
}); });
@ -135,52 +134,51 @@ var OutgoingMPBridge =
new_class.AddMessage = func(m) new_class.AddMessage = func(m)
{ {
append(me.NotificationsToBridge, m); append(me.NotificationsToBridge, m);
}; };
#------------------------------------------- #-------------------------------------------
# Receive override: # Receive override:
new_class.Receive = func(notification) new_class.Receive = func(notification)
{ {
if (notification.FromIncomingBridge) if (notification.FromIncomingBridge)
return emesary.Transmitter.ReceiptStatus_NotProcessed; return emesary.Transmitter.ReceiptStatus_NotProcessed;
#print("Receive ",notification.NotificationType," ",notification.Ident); #print("Receive ",notification.NotificationType," ",notification.Ident);
for (var idx = 0; idx < size(me.NotificationsToBridge); idx += 1) { for (var idx = 0; idx < size(me.NotificationsToBridge); idx += 1) {
if(me.NotificationsToBridge[idx].NotificationType == notification.NotificationType) { if (me.NotificationsToBridge[idx].NotificationType == notification.NotificationType) {
me.NotificationsToBridge[idx].MessageIndex += 1; me.NotificationsToBridge[idx].MessageIndex += 1;
notification.MessageExpiryTime = systime()+me.MessageLifeTime; notification.MessageExpiryTime = systime()+me.MessageLifeTime;
notification.Expired = 0; notification.Expired = 0;
notification.BridgeMessageId = me.NotificationsToBridge[idx].MessageIndex; notification.BridgeMessageId = me.NotificationsToBridge[idx].MessageIndex;
notification.BridgeMessageNotificationTypeId = me.NotificationsToBridge[idx].TypeId; notification.BridgeMessageNotificationTypeId = me.NotificationsToBridge[idx].TypeId;
# #
# The message key is a composite of the type and ident to allow for multiple senders # The message key is a composite of the type and ident to allow for multiple senders
# of the same message type. # of the same message type.
#print("Received ",notification.BridgeMessageNotificationTypeKey," expire=",notification.MessageExpiryTime); #print("Received ",notification.BridgeMessageNotificationTypeKey," expire=",notification.MessageExpiryTime);
me.AddToOutgoing(notification); me.AddToOutgoing(notification);
return emesary.Transmitter.ReceiptStatus_Pending; return emesary.Transmitter.ReceiptStatus_Pending;
} }
} }
return emesary.Transmitter.ReceiptStatus_NotProcessed; return emesary.Transmitter.ReceiptStatus_NotProcessed;
}; };
new_class.AddToOutgoing = func(notification)
{
if (notification.IsDistinct) {
for (var idx = 0; idx < size(me.OutgoingList); idx += 1) {
if (me.trace)
print("Compare [",idx,"] qId=",me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() ," noti --> ",notification.GetBridgeMessageNotificationTypeKey());
if(me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() == notification.GetBridgeMessageNotificationTypeKey()) { new_class.AddToOutgoing = func(notification)
if (me.trace) {
print(" --> Update ",me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() ," noti --> ",notification.GetBridgeMessageNotificationTypeKey()); if (notification.IsDistinct) {
me.OutgoingList[idx]= notification; for (var idx = 0; idx < size(me.OutgoingList); idx += 1) {
me.TransmitterActive = size(me.OutgoingList); if (me.trace)
return; print("Compare [",idx,"] qId=",me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() ," noti --> ",notification.GetBridgeMessageNotificationTypeKey());
} if (me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() == notification.GetBridgeMessageNotificationTypeKey()) {
} if (me.trace)
} print(" --> Update ",me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() ," noti --> ",notification.GetBridgeMessageNotificationTypeKey());
else me.OutgoingList[idx]= notification;
if (me.trace) me.TransmitterActive = size(me.OutgoingList);
print("Not distinct, adding always"); return;
}
}
} else
if (me.trace)
print("Not distinct, adding always");
if (me.trace) if (me.trace)
print(" --> Added ",notification.GetBridgeMessageNotificationTypeKey()); print(" --> Added ",notification.GetBridgeMessageNotificationTypeKey());
@ -191,100 +189,86 @@ var OutgoingMPBridge =
{ {
var outgoing = ""; var outgoing = "";
var cur_time=systime(); var cur_time=systime();
var first_time = 1;
me.OutgoingListNew = [];
for (var idx = 0; idx < size(me.OutgoingList); idx += 1) { for (var idx = 0; idx < size(me.OutgoingList); idx += 1) {
var sect = ""; var sect = "";
var notification = me.OutgoingList[idx]; var notification = me.OutgoingList[idx];
if (!notification.Expired and notification.MessageExpiryTime > cur_time) { if (!notification.Expired and notification.MessageExpiryTime > cur_time) {
var encval=""; if (notification["sect"] == nil) {
var first_time = 1; # This is first time attempting to transmit this notification
var eidx = 0; var encval="";
notification.Expired = 0;
var eidx = 0;
notification.Expired = 0;
foreach(var p ; notification.bridgeProperties()) { foreach(var p ; notification.bridgeProperties()) {
var nv = p.getValue(); var nv = p.getValue();
encval = encval ~ nv; encval = encval ~ nv;
eidx += 1; eidx += 1;
}
# !idx!typ!encv~
sect = sprintf("%s%s%s%s%s%s%s",
OutgoingMPBridge.SeperatorChar, emesary.BinaryAsciiTransfer.encodeInt(notification.BridgeMessageId,4),
OutgoingMPBridge.SeperatorChar, emesary.BinaryAsciiTransfer.encodeInt(notification.BridgeMessageNotificationTypeId,1),
OutgoingMPBridge.SeperatorChar, encval, OutgoingMPBridge.MessageEndChar);
} else {
# This notification has already been coded, but was previously not sent due to too little space.
sect = notification.sect;
} }
# !idx!typ!encv~
sect = sprintf("%s%s%s%s%s%s%s",
OutgoingMPBridge.SeperatorChar, emesary.BinaryAsciiTransfer.encodeInt(notification.BridgeMessageId,4),
OutgoingMPBridge.SeperatorChar, emesary.BinaryAsciiTransfer.encodeInt(notification.BridgeMessageNotificationTypeId,1),
OutgoingMPBridge.SeperatorChar, encval, OutgoingMPBridge.MessageEndChar);
if (size(outgoing) + size(sect) < me.MPStringMaxLen) { if (size(outgoing) + size(sect) < me.MPStringMaxLen) {
outgoing = outgoing~sect; outgoing = outgoing~sect;
} else { } else {
print("Emesary: ERROR [",me.Ident,"] out of space for ",notification.NotificationType, " transmitted count=",idx, " queue size ",size(me.OutgoingList)); if (first_time) {
notification.MessageExpiryTime = systime()+me.MessageLifeTime; print("Emesary: ERROR [",me.Ident,"] out of space for ",notification.NotificationType, " transmitted count=",idx, " queue size ",size(me.OutgoingList));
break; first_time = 0;
}
#notification.MessageExpiryTime = systime()+me.MessageLifeTime;
#break;
notification.sect = sect;
append(me.OutgoingListNew, notification);
} }
} else { } else {
notification.Expired = 1; notification.Expired = 1;
} }
} }
me.OutgoingList = me.OutgoingListNew;
me.TransmitterActive = size(me.OutgoingList); me.TransmitterActive = size(me.OutgoingList);
setprop(me.MpVariable,outgoing); setprop(me.MpVariable,outgoing);
# print("Set ",me.MpVariable," size(",size(outgoing));
#loopback test:
# incomingBridge.ProcessIncoming(outgoing);
#
# Now remove any expired messages from the outgoing queue.
# The only real way of doing this is via the pop() function
# so we have to delete the expired items from the list by rebuilding
# the list from the start with non-expired items, and then
# all of the items past the end (of the rebuilt list) can be popped
# (pop removes the last element from a vector)
var outSize = size(me.OutgoingList)-1;
var out_idx = 0;
for (var idx = 0; idx <= outSize; idx += 1) {
#print("Q1 [",idx,"] ",me.OutgoingList[idx].MessageExpiryTime-cur_time," Expired=",me.OutgoingList[idx].Expired);
if(!me.OutgoingList[idx].Expired) {
#print("move ",idx, " => ",out_idx);
var mmove = me.OutgoingList[idx];
me.OutgoingList[out_idx] = me.OutgoingList[idx];
out_idx += 1;
}
}
var to_del = (outSize+1) - out_idx;
#print("--> out idx",out_idx, " to delete ",to_del);
while(to_del > 0) {
#print("--> pop ");
pop(me.OutgoingList);
to_del = to_del - 1;
}
}; };
new_class.Transmitter.Register(new_class);
new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds); new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
return new_class; return new_class;
}, },
};
};
# #
# #
# one of these for each model instantiated in the model XML - it will # one of these for each model instantiated in the model XML - it will
# route messages to # route messages to
var IncomingMPBridge = var IncomingMPBridge =
{ {
trace : 0, trace : 0,
new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _transmitter=nil, _propertybase="emesary/bridge") new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _transmitter=nil, _propertybase="emesary/bridge")
{ {
if (_transmitter == nil) if (_transmitter == nil)
_transmitter = emesary.GlobalTransmitter; _transmitter = emesary.GlobalTransmitter;
print("IncomingMPBridge created for "~_ident," mp=",_mpidx, " using Transmitter ",_transmitter.Ident, " with property base sim/multiplayer/"~_propertybase); print("IncomingMPBridge created for "~_ident," mp=",_mpidx, " using Transmitter ",_transmitter.Ident, " with property base sim/multiplayer/"~_propertybase);
var new_class = emesary.Transmitter.new("IncominggMPBridge "~_ident); var new_class = emesary.Transmitter.new("IncominggMPBridge "~_ident);
if(_notifications_to_bridge == nil) if (_notifications_to_bridge == nil)
new_class.NotificationsToBridge = []; new_class.NotificationsToBridge = [];
else else
new_class.NotificationsToBridge = _notifications_to_bridge; new_class.NotificationsToBridge = _notifications_to_bridge;
new_class.NotificationsToBridge_Lookup = {}; new_class.NotificationsToBridge_Lookup = {};
foreach(var n ; new_class.NotificationsToBridge) { foreach (var n ; new_class.NotificationsToBridge) {
print(" Incoming bridge notification type --> ",n.NotificationType); print(" Incoming bridge notification type --> ",n.NotificationType);
var new_n = {parents: [n]}; var new_n = {parents: [n]};
new_n.IncomingMessageIndex = OutgoingMPBridge.StartMessageIndex; new_n.IncomingMessageIndex = OutgoingMPBridge.StartMessageIndex;
@ -304,92 +288,89 @@ var IncomingMPBridge =
me.MpVariable = _root~"sim/multiplay/"~new_class.MPpropertyBase~"["~new_class.MPidx~"]"; me.MpVariable = _root~"sim/multiplay/"~new_class.MPpropertyBase~"["~new_class.MPidx~"]";
me.CallsignPath = _root~"callsign"; me.CallsignPath = _root~"callsign";
me.PropertyRoot = _root; me.PropertyRoot = _root;
me.mp_listener = setlistener(me.MpVariable, func(v)
setlistener(me.MpVariable, func(v)
{ {
#print("incoming ",getprop(me.CallsignPath)," -->",me.PropertyRoot," v=",v.getValue()); #print("incoming ",getprop(me.CallsignPath)," -->",me.PropertyRoot," v=",v.getValue());
me.ProcessIncoming(v.getValue()); me.ProcessIncoming(v.getValue());
},0,0); },0,0);
}; };
new_class.setprop = func(property, value){ new_class.setprop = func(property, value){
if (IncomingMPBridge.trace == 2) if (IncomingMPBridge.trace == 2)
print("setprop ",new_class.PropertyRoot~property," = ",value); print("setprop ",new_class.PropertyRoot~property," = ",value);
setprop(new_class.PropertyRoot~property,value); setprop(new_class.PropertyRoot~property,value);
}; };
new_class.GetCallsign = func new_class.GetCallsign = func
{ {
return getprop(me.CallsignPath); return getprop(me.CallsignPath);
}; };
new_class.AddMessage = func(m) new_class.AddMessage = func(m)
{ {
append(me.NotificationsToBridge, m); append(me.NotificationsToBridge, m);
}; };
new_class.Remove = func new_class.Remove = func
{ {
print("Emesary IncomingMPBridge Remove() ",me.Ident); print("Emesary IncomingMPBridge Remove() ",me.Ident," Property: ",me.MpVariable);
me.Transmitter.DeRegister(me); me.Transmitter.DeRegister(me);
if (me["mp_listener"] != nil)
removelistener(me.mp_listener);
me.mp_listener = nil;
}; };
#------------------------------------------- #-------------------------------------------
# Receive override: # Receive override:
new_class.ProcessIncoming = func(encoded_val) new_class.ProcessIncoming = func(encoded_val)
{ {
if (encoded_val == "") if (encoded_val == "")
return; return;
if(right(encoded_val,1) != OutgoingMPBridge.MessageEndChar) if(right(encoded_val,1) != OutgoingMPBridge.MessageEndChar)
printf("Error: emesary.IncomingBridge.ProcessIncoming Missing endChar. From %s. Message=%s",me.GetCallsign(),encoded_val); printf("Error: emesary.IncomingBridge.ProcessIncoming Missing endChar. From %s. Message=%s",me.GetCallsign(),encoded_val);
var encoded_notifications = split(OutgoingMPBridge.MessageEndChar, encoded_val); var encoded_notifications = split(OutgoingMPBridge.MessageEndChar, encoded_val);
for (var idx = 0; idx < size(encoded_notifications); idx += 1) { for (var idx = 0; idx < size(encoded_notifications); idx += 1) {
if (encoded_notifications[idx] == "") if (encoded_notifications[idx] == "")
continue ; continue ;
# get the message parts # get the message parts
var encoded_notification = split(OutgoingMPBridge.SeperatorChar, encoded_notifications[idx]); var encoded_notification = split(OutgoingMPBridge.SeperatorChar, encoded_notifications[idx]);
if (size(encoded_notification) < 4) if (size(encoded_notification) < 4)
print("Error: emesary.IncomingBridge.ProcessIncoming bad msg ",encoded_notifications[idx]); print("Error: emesary.IncomingBridge.ProcessIncoming bad msg ",encoded_notifications[idx]);
else { else {
var msg_idx = emesary.BinaryAsciiTransfer.decodeInt(encoded_notification[1],4,0).value; var msg_idx = emesary.BinaryAsciiTransfer.decodeInt(encoded_notification[1],4,0).value;
var msg_type_id = emesary.BinaryAsciiTransfer.decodeInt(encoded_notification[2],1,0).value; var msg_type_id = emesary.BinaryAsciiTransfer.decodeInt(encoded_notification[2],1,0).value;
var bridged_notification = new_class.NotificationsToBridge_Lookup[msg_type_id]; var bridged_notification = new_class.NotificationsToBridge_Lookup[msg_type_id];
if (bridged_notification == nil) { if (bridged_notification == nil) {
print("Error: emesary.IncomingBridge.ProcessIncoming invalid type_id ",msg_type_id); print("Error: emesary.IncomingBridge.ProcessIncoming invalid type_id ",msg_type_id);
} else { } else {
bridged_notification.FromIncomingBridge = 1; bridged_notification.FromIncomingBridge = 1;
bridged_notification.Callsign = me.GetCallsign(); bridged_notification.Callsign = me.GetCallsign();
if(IncomingMPBridge.trace>1) if(IncomingMPBridge.trace>1)
print("ProcessIncoming callsign=",bridged_notification.Callsign," ",me.PropertyRoot, " msg_type=",msg_type_id," idx=",msg_idx, " bridge_idx=",bridged_notification.IncomingMessageIndex); print("ProcessIncoming callsign=",bridged_notification.Callsign," ",me.PropertyRoot, " msg_type=",msg_type_id," idx=",msg_idx, " bridge_idx=",bridged_notification.IncomingMessageIndex);
if (msg_idx > bridged_notification.IncomingMessageIndex) { if (msg_idx > bridged_notification.IncomingMessageIndex) {
if(IncomingMPBridge.trace==1) if(IncomingMPBridge.trace==1)
print("ProcessIncoming callsign=",bridged_notification.Callsign," ",me.PropertyRoot, " msg_type=",msg_type_id," idx=",msg_idx, " bridge_idx=",bridged_notification.IncomingMessageIndex); print("ProcessIncoming callsign=",bridged_notification.Callsign," ",me.PropertyRoot, " msg_type=",msg_type_id," idx=",msg_idx, " bridge_idx=",bridged_notification.IncomingMessageIndex);
var msg_body = encoded_notification[3]; var msg_body = encoded_notification[3];
if (IncomingMPBridge.trace > 2) if (IncomingMPBridge.trace > 2)
print("received idx=",msg_idx," ",msg_type_id,":",bridged_notification.NotificationType); print("received idx=",msg_idx," ",msg_type_id,":",bridged_notification.NotificationType);
# populate fields # populate fields
var bridgedProperties = bridged_notification.bridgeProperties(); var bridgedProperties = bridged_notification.bridgeProperties();
var msglen = size(msg_body); var msglen = size(msg_body);
if (IncomingMPBridge.trace > 2) if (IncomingMPBridge.trace > 2)
print("Process ",msg_body," len=",msglen, " BPsize = ",size(bridgedProperties)); print("Process ",msg_body," len=",msglen, " BPsize = ",size(bridgedProperties));
var pos = 0; var pos = 0;
for (var bpi = 0; bpi < size(bridgedProperties); bpi += 1) {
for (var bpi = 0; bpi < size(bridgedProperties); bpi += 1) { if (pos < msglen) {
if (pos < msglen) { if (IncomingMPBridge.trace > 2)
print("dec: pos ",pos);
if (IncomingMPBridge.trace > 2) var bp = bridgedProperties[bpi];
print("dec: pos ",pos); dv = bp.setValue(msg_body, me, pos);
if (IncomingMPBridge.trace > 2)
var bp = bridgedProperties[bpi];
dv = bp.setValue(msg_body, me, pos);
if (IncomingMPBridge.trace > 2)
print(" --> next pos ", dv.pos); print(" --> next pos ", dv.pos);
if (dv.pos == pos or dv.pos > msglen) if (dv.pos == pos or dv.pos > msglen)
break; break;
pos = dv.pos;
pos = dv.pos;
} else { } else {
print("Error: emesary.IncomingBridge.ProcessIncoming: [",bridged_notification.NotificationType,"] supplementary encoded value at position ",bpi); print("Error: emesary.IncomingBridge.ProcessIncoming: [",bridged_notification.NotificationType,"] supplementary encoded value at position ",bpi);
break; break;
@ -401,8 +382,9 @@ var IncomingMPBridge =
if (bridged_notification.Ident == "none") if (bridged_notification.Ident == "none")
bridged_notification.Ident = "mp-bridge"; bridged_notification.Ident = "mp-bridge";
me.Transmitter.NotifyAll(bridged_notification);
bridged_notification.IncomingMessageIndex = msg_idx; bridged_notification.IncomingMessageIndex = msg_idx;
me.Transmitter.NotifyAll(bridged_notification);
} }
} }
} }
@ -417,7 +399,11 @@ var IncomingMPBridge =
var incomingBridge = emesary_mp_bridge.IncomingMPBridge.new(path, notification_list, mpidx, transmitter, _propertybase); var incomingBridge = emesary_mp_bridge.IncomingMPBridge.new(path, notification_list, mpidx, transmitter, _propertybase);
incomingBridge.Connect(path~"/"); incomingBridge.Connect(path~"/");
me.incomingBridgeList[path] = incomingBridge; if (me.incomingBridgeList[path] == nil) {
me.incomingBridgeList[path] = [incomingBridge];
} else {
append(me.incomingBridgeList[path],incomingBridge);
}
return incomingBridge; return incomingBridge;
}, },
@ -425,7 +411,7 @@ var IncomingMPBridge =
# Each multiplayer object will have its own incoming bridge. This is necessary to allow message ID # Each multiplayer object will have its own incoming bridge. This is necessary to allow message ID
# tracking (as the bridge knows which messages have been already processed) # tracking (as the bridge knows which messages have been already processed)
# Whenever a client connects over MP a new bridge is instantiated # Whenever a client connects over MP a new bridge is instantiated
startMPBridge : func(notification_list, mpidx=19, transmitter=nil, _propertybase="emesary/bridge") startMPBridge : func(notification_list, mpidx=19, transmitter=nil, _propertybase="emesary/bridge")
{ {
me.incomingBridgeList = {}; me.incomingBridgeList = {};
@ -433,19 +419,19 @@ var IncomingMPBridge =
# Create bridge whenever a client connects # Create bridge whenever a client connects
# #
setlistener("/ai/models/model-added", func(v) setlistener("/ai/models/model-added", func(v)
{ {
# Model added will be eg: /ai[0]/models[0]/multiplayer[0] # Model added will be eg: /ai[0]/models[0]/multiplayer[0]
var path = v.getValue(); var path = v.getValue();
# Ensure we only handle multiplayer elements # Ensure we only handle multiplayer elements
if (find("/multiplayer",path) > 0) { if (find("/multiplayer",path) > 0) {
var callsign = getprop(path~"/callsign"); var callsign = getprop(path~"/callsign");
print("Creating Emesary MPBridge for ",path); print("Creating Emesary MPBridge for ",path);
if (callsign == "" or callsign == nil) if (callsign == "" or callsign == nil)
callsign = path; callsign = path;
me.connectIncomingBridge(path, notification_list, mpidx, transmitter, _propertybase); me.connectIncomingBridge(path, notification_list, mpidx, transmitter, _propertybase);
} }
}); });
# #
@ -453,12 +439,14 @@ var IncomingMPBridge =
# #
setlistener("/ai/models/model-removed", func(v){ setlistener("/ai/models/model-removed", func(v){
var path = v.getValue(); var path = v.getValue();
var bridge = me.incomingBridgeList[path]; var bridges = me.incomingBridgeList[path];
if (bridge != nil) { if (bridges != nil) {
# print("Bridge removed for ",v.getValue()); foreach(bridge;bridges) {
bridge.Remove(); bridge.Remove();
me.incomingBridgeList[path]=nil; # print("Bridge removed for ",v.getValue());
} }
me.incomingBridgeList[path] = nil;
}
}); });
}, },
}; };