Emesary mp support
Add support for transfer string Change Type to NotificationType as Type can be confusing
This commit is contained in:
parent
7fa52ea4ee
commit
4ed25ac361
3 changed files with 135 additions and 52 deletions
|
@ -275,7 +275,7 @@ var ANSPN46_System =
|
|||
|
||||
new_class.Receive = func(notification)
|
||||
{
|
||||
if (notification.Type == "ANSPN46ActiveResponseNotification")
|
||||
if (notification.NotificationType == "ANSPN46ActiveResponseNotification")
|
||||
{
|
||||
if (notification.Tuned)
|
||||
{
|
||||
|
|
|
@ -153,7 +153,7 @@ var Transmitter =
|
|||
# SubClasses can add extra properties or methods.
|
||||
# Properties:
|
||||
# Ident : Generic message identity. Can be an ident, or for simple messages a value that needs transmitting.
|
||||
# Type : Message Type
|
||||
# NotificationType : Notification Type
|
||||
# IsDistinct : non zero if this message supercedes previous messages of this type.
|
||||
# Distinct messages are usually sent often and self contained
|
||||
# (i.e. no relative state changes such as toggle value)
|
||||
|
@ -168,7 +168,7 @@ var Notification =
|
|||
{
|
||||
var new_class = { parents: [Notification]};
|
||||
new_class.Ident = _ident;
|
||||
new_class.Type = _type;
|
||||
new_class.NotificationType = _type;
|
||||
new_class.IsDistinct = 1;
|
||||
new_class.FromIncomingBridge = 0;
|
||||
new_class.Callsign = nil;
|
||||
|
@ -221,6 +221,43 @@ var TransferCoord =
|
|||
return mp_broadcast.Binary.decodeCoord(v);
|
||||
}
|
||||
};
|
||||
var TransferString =
|
||||
{
|
||||
#
|
||||
# just to pack a valid range and keep the lower and very upper control codes for seperators
|
||||
# that way we don't need to do anything special to encode the string.
|
||||
getalphanumericchar : func(v)
|
||||
{
|
||||
if (find(v,"-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz") > 0)
|
||||
return v;
|
||||
return nil;
|
||||
},
|
||||
encode : func(v)
|
||||
{
|
||||
var l = size(v);
|
||||
if (l > 16)
|
||||
l = 16;
|
||||
var rv = mp_broadcast.Binary.encodeByte(l);
|
||||
|
||||
print ("Encode string ",v," l=",l);
|
||||
for(var ii = 0; ii < l; ii = ii + 1)
|
||||
{
|
||||
printf("%d,%d (%s)\n",ii+1,1,rv);
|
||||
ev = TransferString.getalphanumericchar(substr(v,ii,1));
|
||||
if (ev != nil)
|
||||
rv = rv ~ ev;
|
||||
}
|
||||
print("String encode l=",l," val=",rv);
|
||||
return rv;
|
||||
},
|
||||
decode : func(v)
|
||||
{
|
||||
var l = mp_broadcast.Binary.decodeByte(v);
|
||||
var rv = substr(v,1,l-1);
|
||||
print("String decode l=",l," val=",rv);
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
var TransferByte =
|
||||
{
|
||||
|
|
|
@ -1,28 +1,69 @@
|
|||
#---------------------------------------------------------------------------
|
||||
#
|
||||
# Title : EMESARY multiplayer bridge
|
||||
#
|
||||
# File Type : Implementation File
|
||||
#
|
||||
# Description : Bridges selected emesary notifications over MP
|
||||
# : 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
|
||||
#
|
||||
# Author : Richard Harrison (richard@zaretto.com)
|
||||
#
|
||||
# Creation Date : 04 April 2016
|
||||
#
|
||||
# Version : 4.8
|
||||
#
|
||||
# Copyright © 2016 Richard Harrison Released under GPL V2
|
||||
#
|
||||
#---------------------------------------------------------------------------*/
|
||||
|
||||
# Example of connecting an incoming and outgoing bridge (should reside inside an aircraft nasal file)
|
||||
#
|
||||
# var routedNotifications = [notifications.TacticalNotification.new(nil)];
|
||||
# var incomingBridge = emesary_mp_bridge.IncomingMPBridge.startMPBridge(routedNotifications);
|
||||
# var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp",routedNotifications);
|
||||
#------------------------------------------------------------------
|
||||
#
|
||||
# bridge message rules;
|
||||
# - if distinct must be absolute and self contained as a later message will supercede any earlier ones in the outgoing queue.
|
||||
# - use the message type and ident to identify distinct messages
|
||||
# The outgoing 'port' is a multiplay/generic/string index.
|
||||
# - ! is used as a seperator between the elements that are used to send the notification (typeid, sequence, notification)
|
||||
# NOTES: Aircraft do not need to have both an incoming and outgoing bridge, but it is usual.
|
||||
# Only the notifications specified will be routed via the bridge.
|
||||
# Once routed a message will by default not be re-rerouted again by the outgoing bridge.
|
||||
# Transmit frequency and message lifetime may need to be tuned.
|
||||
# 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)
|
||||
# Use the message type and ident to identify distinct messages
|
||||
# The outgoing 'port' is a multiplay/generic/string index.
|
||||
# - ! is used as a seperator between the elements that are used to send the
|
||||
# notification (typeid, sequence, notification)
|
||||
# - 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
|
||||
#
|
||||
# Outgoing messages are sent in a scheduled manner, usually once per second, and each message has a lifetime (to allow for propogation to
|
||||
# all clients over UDP). Clients will ignore messages that they have already received (based on the sequence id).
|
||||
#
|
||||
# The incoming bridge will usually be created part of the aircraft 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 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 happen when visible so it may be necessary to track AI models in a seperate instantiatable incoming bridge manager.
|
||||
|
||||
# General Notes
|
||||
#----------------------------------------------------------------------
|
||||
# Outgoing messages are sent in a scheduled manner, usually once per
|
||||
# second, and each message has a lifetime (to allow for propogation to
|
||||
# all clients over UDP). Clients will ignore messages that they have
|
||||
# already received (based on the sequence id).
|
||||
|
||||
# The incoming bridge will usually be created part of the aircraft
|
||||
# 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
|
||||
# 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
|
||||
# happen when visible so it may be necessary to track AI models in a
|
||||
# seperate instantiatable incoming bridge manager.
|
||||
#
|
||||
# The outgoing bridge would usually be created within the aircraft loading Nasal.
|
||||
|
||||
var OutgoingMPBridge =
|
||||
{
|
||||
new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _root="", _transmitter=nil)
|
||||
new: func(_ident, _notifications_to_bridge=nil, _mpidx=18, _root="", _transmitter=nil)
|
||||
{
|
||||
if (_transmitter == nil)
|
||||
_transmitter = emesary.GlobalTransmitter;
|
||||
|
||||
print("OutgoingMPBridge created for "~_ident);
|
||||
|
||||
var new_class = emesary.Recipient.new("OutgoingMPBridge "~_ident);
|
||||
|
||||
new_class.MessageIndex = 100;
|
||||
|
@ -33,6 +74,9 @@ var OutgoingMPBridge =
|
|||
else
|
||||
new_class.NotificationsToBridge = _notifications_to_bridge;
|
||||
|
||||
foreach(var n ; new_class.NotificationsToBridge)
|
||||
print(" bridge --> ",n.NotificationType);
|
||||
|
||||
new_class.MPout = "";
|
||||
new_class.MPidx = _mpidx;
|
||||
new_class.MessageLifeTime = 10; # seconds
|
||||
|
@ -42,6 +86,7 @@ var OutgoingMPBridge =
|
|||
new_class.Transmitter.Register(new_class);
|
||||
new_class.MpVariable = _root~"sim/multiplay/generic/string["~new_class.MPidx~"]";
|
||||
new_class.TransmitterActive = 0;
|
||||
new_class.TransmitFrequencySeconds = 1;
|
||||
|
||||
new_class.TransmitTimer =
|
||||
maketimer(6, func
|
||||
|
@ -49,9 +94,8 @@ var OutgoingMPBridge =
|
|||
if(new_class.TransmitterActive)
|
||||
new_class.Transmit();
|
||||
|
||||
new_class.TransmitTimer.restart(1);
|
||||
new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
|
||||
});
|
||||
new_class.TransmitTimer.restart(1);
|
||||
|
||||
new_class.Delete = func
|
||||
{
|
||||
|
@ -72,20 +116,20 @@ var OutgoingMPBridge =
|
|||
if (notification.FromIncomingBridge)
|
||||
return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
||||
|
||||
#print("Receive ",notification.Type," (",notification.Ident);
|
||||
print("Receive ",notification.NotificationType," ",notification.Ident);
|
||||
for (var idx = 0; idx < size(me.NotificationsToBridge); idx += 1)
|
||||
{
|
||||
if(me.NotificationsToBridge[idx].Type == notification.Type)
|
||||
if(me.NotificationsToBridge[idx].NotificationType == notification.NotificationType)
|
||||
{
|
||||
me.MessageIndex += 1;
|
||||
notification.MessageExpiryTime = systime()+me.MessageLifeTime;
|
||||
notification.BridgeMessageId = me.MessageIndex;
|
||||
notification.BridgeMessageTypeId = idx;
|
||||
notification.BridgeMessageNotificationTypeId = idx;
|
||||
#
|
||||
# The message key is a composite of the type and ident to allow for multiple senders
|
||||
# of the same message type.
|
||||
notification.BridgeMessageTypeKey = notification.Type~"."~notification.Ident;
|
||||
#print("Received ",notification.BridgeMessageTypeKey," expire=",notification.MessageExpiryTime);
|
||||
notification.BridgeMessageNotificationTypeKey = notification.NotificationType~"."~notification.Ident;
|
||||
#print("Received ",notification.BridgeMessageNotificationTypeKey," expire=",notification.MessageExpiryTime);
|
||||
me.AddToOutgoing(notification);
|
||||
return emesary.Transmitter.ReceiptStatus_Pending;
|
||||
}
|
||||
|
@ -98,16 +142,16 @@ var OutgoingMPBridge =
|
|||
{
|
||||
for (var idx = 0; idx < size(me.OutgoingList); idx += 1)
|
||||
{
|
||||
if(me.OutgoingList[idx].BridgeMessageTypeKey == notification.BridgeMessageTypeKey)
|
||||
if(me.OutgoingList[idx].BridgeMessageNotificationTypeKey == notification.BridgeMessageNotificationTypeKey)
|
||||
{
|
||||
#print("Update ",me.OutgoingList[idx].BridgeMessageTypeKey);
|
||||
#print("Update ",me.OutgoingList[idx].BridgeMessageNotificationTypeKey);
|
||||
me.OutgoingList[idx]= notification;
|
||||
me.TransmitterActive = size(me.OutgoingList);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#print("Added ",notification.BridgeMessageTypeKey);
|
||||
#print("Added ",notification.BridgeMessageNotificationTypeKey);
|
||||
append(me.OutgoingList, notification);
|
||||
me.TransmitterActive = size(me.OutgoingList);
|
||||
};
|
||||
|
@ -133,7 +177,7 @@ var OutgoingMPBridge =
|
|||
#print("Encode ",eidx,"=",encval);
|
||||
eidx += 1;
|
||||
}
|
||||
sect = sprintf("%d!%d!%s",notification.BridgeMessageId, notification.BridgeMessageTypeId, encval);
|
||||
sect = sprintf("!%d!%d!%s",notification.BridgeMessageId, notification.BridgeMessageNotificationTypeId, encval);
|
||||
outgoing = outgoing~sect;
|
||||
me.OutgoingList[out_idx] = me.OutgoingList[idx];
|
||||
# print("xmit ",idx," out=",out_idx);
|
||||
|
@ -150,6 +194,7 @@ var OutgoingMPBridge =
|
|||
for(var del_i=0; del_i < del_count; del_i += 1)
|
||||
pop(me.OutgoingList);
|
||||
};
|
||||
new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
|
||||
return new_class;
|
||||
},
|
||||
};
|
||||
|
@ -160,7 +205,7 @@ var OutgoingMPBridge =
|
|||
# route messages to
|
||||
var IncomingMPBridge =
|
||||
{
|
||||
new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _transmitter=nil)
|
||||
new: func(_ident, _notifications_to_bridge=nil, _mpidx=18, _transmitter=nil)
|
||||
{
|
||||
if (_transmitter == nil)
|
||||
_transmitter = emesary.GlobalTransmitter;
|
||||
|
@ -216,12 +261,12 @@ var IncomingMPBridge =
|
|||
{
|
||||
# get the message parts
|
||||
var encoded_notification = split("!", encoded_val);
|
||||
if (size(encoded_notification) < 3)
|
||||
if (size(encoded_notification) < 4)
|
||||
print("Error: emesary.IncomingBridge.ProcessIncoming bad msg ",encoded_val);
|
||||
else
|
||||
{
|
||||
var msg_idx = encoded_notification[0];
|
||||
var msg_type_id = encoded_notification[1];
|
||||
var msg_idx = encoded_notification[1];
|
||||
var msg_type_id = encoded_notification[2];
|
||||
if (msg_type_id >= size(me.NotificationsToBridge))
|
||||
{
|
||||
print("Error: emesary.IncomingBridge.ProcessIncoming invalid type_id ",msg_type_id);
|
||||
|
@ -229,14 +274,14 @@ var IncomingMPBridge =
|
|||
else
|
||||
{
|
||||
var msg = me.NotificationsToBridge[msg_type_id];
|
||||
var msg_notify = encoded_notification[2];
|
||||
print("received idx=",msg_idx," ",msg_type_id,":",msg.Type);
|
||||
var msg_notify = encoded_notification[3];
|
||||
print("received idx=",msg_idx," ",msg_type_id,":",msg.NotificationType);
|
||||
if(msg_idx <= me.IncomingMessageIndex)
|
||||
print(" **Already processed");
|
||||
else
|
||||
{
|
||||
# raise notification
|
||||
var bridged_notification = msg; #emesary.Notification.new(msg.Type,"BridgedMessage");
|
||||
var bridged_notification = msg; #emesary.Notification.new(msg.NotificationType,"BridgedMessage");
|
||||
# populate fields
|
||||
var bridgedProperties = msg.bridgeProperties();
|
||||
var encvals=split(";", msg_notify);
|
||||
|
@ -267,30 +312,45 @@ var IncomingMPBridge =
|
|||
}
|
||||
foreach(var n; new_class.NotificationsToBridge)
|
||||
{
|
||||
print("IncomingBridge: ",n.Type);
|
||||
print("IncomingBridge: ",n.NotificationType);
|
||||
}
|
||||
return new_class;
|
||||
},
|
||||
#
|
||||
# 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)
|
||||
# Whenever a client connects over MP a new bridge is instantiated
|
||||
startMPBridge : func(notification_list)
|
||||
{
|
||||
var incomingBridgeList = {};
|
||||
|
||||
#
|
||||
# Create bridge whenever a client connects
|
||||
#
|
||||
setlistener("/ai/models/model-added", func(v)
|
||||
{
|
||||
#Model added /ai[0]/models[0]/multiplayer[0]
|
||||
# Model added will be eg: /ai[0]/models[0]/multiplayer[0]
|
||||
var path = v.getValue();
|
||||
|
||||
print("Model added ",path);
|
||||
# Ensure we only handle multiplayer elements
|
||||
if (find("/multiplayer",path) > 0)
|
||||
{
|
||||
var callsign = getprop(path~"/callsign");
|
||||
print("Creating Emesary MPBridge for ",callsign);
|
||||
if (callsign == "" or callsign == nil)
|
||||
callsign = path;
|
||||
var incomingBridge = emesary_mp_bridge.IncomingMPBridge.new(callsign~"mp", notification_list, 19);
|
||||
|
||||
var incomingBridge = emesary_mp_bridge.IncomingMPBridge.new(callsign~"mp", notification_list, 18);
|
||||
|
||||
incomingBridge.Connect(path~"/");
|
||||
incomingBridgeList[path] = incomingBridge;
|
||||
}
|
||||
});
|
||||
|
||||
#
|
||||
# when a client disconnects remove the associated bridge.
|
||||
#
|
||||
setlistener("/ai/models/model-removed", func(v){
|
||||
print("Model removed ",v.getValue());
|
||||
var path = v.getValue();
|
||||
|
@ -303,17 +363,3 @@ var IncomingMPBridge =
|
|||
});
|
||||
},
|
||||
};
|
||||
#to setup bridges;
|
||||
#io.load_nasal(getprop("/sim/fg-root") ~ "/Nasal/emesary_mp_bridge.nas");
|
||||
#var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp");
|
||||
#outgoingBridge.AddMessage(an_spn_46.ANSPN46ActiveNotification.new("template"));
|
||||
#incomingBridge = emesary_mp_bridge.IncomingMPBridge.new("F-15mp");
|
||||
#incomingBridge.AddMessage(an_spn_46.ANSPN46ActiveNotification.new("template"));
|
||||
#var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp");
|
||||
#outgoingBridge.AddMessage(an_spn_46.ANSPN46ActiveNotification.new("template"));
|
||||
# var m = emesary.notifications.TacticalNotification("rr",2);
|
||||
# emesary.GlobalTransmitter.NotifyAll(m);
|
||||
#var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp");
|
||||
#outgoingBridge.AddMessage(notifications.TacticalNotification.new(nil));
|
||||
# var m = emesary.notifications.TacticalNotification("rr",2);
|
||||
# emesary.GlobalTransmitter.NotifyAll(m);
|
||||
|
|
Loading…
Reference in a new issue