1
0
Fork 0

Emesary mp support

Add support for transfer string
Change Type to NotificationType as Type can be confusing
This commit is contained in:
Richard Harrison 2016-04-12 23:25:11 +02:00
parent 7fa52ea4ee
commit 4ed25ac361
3 changed files with 135 additions and 52 deletions

View file

@ -275,7 +275,7 @@ var ANSPN46_System =
new_class.Receive = func(notification) new_class.Receive = func(notification)
{ {
if (notification.Type == "ANSPN46ActiveResponseNotification") if (notification.NotificationType == "ANSPN46ActiveResponseNotification")
{ {
if (notification.Tuned) if (notification.Tuned)
{ {

View file

@ -153,7 +153,7 @@ var Transmitter =
# SubClasses can add extra properties or methods. # SubClasses can add extra properties or methods.
# Properties: # Properties:
# Ident : Generic message identity. Can be an ident, or for simple messages a value that needs transmitting. # 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. # IsDistinct : non zero if this message supercedes previous messages of this type.
# Distinct messages are usually sent often and self contained # Distinct messages are usually sent often and self contained
# (i.e. no relative state changes such as toggle value) # (i.e. no relative state changes such as toggle value)
@ -168,7 +168,7 @@ var Notification =
{ {
var new_class = { parents: [Notification]}; var new_class = { parents: [Notification]};
new_class.Ident = _ident; new_class.Ident = _ident;
new_class.Type = _type; new_class.NotificationType = _type;
new_class.IsDistinct = 1; new_class.IsDistinct = 1;
new_class.FromIncomingBridge = 0; new_class.FromIncomingBridge = 0;
new_class.Callsign = nil; new_class.Callsign = nil;
@ -221,6 +221,43 @@ var TransferCoord =
return mp_broadcast.Binary.decodeCoord(v); 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 = var TransferByte =
{ {

View file

@ -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; # NOTES: Aircraft do not need to have both an incoming and outgoing bridge, but it is usual.
# - if distinct must be absolute and self contained as a later message will supercede any earlier ones in the outgoing queue. # Only the notifications specified will be routed via the bridge.
# - use the message type and ident to identify distinct messages # Once routed a message will by default not be re-rerouted again by the outgoing bridge.
# The outgoing 'port' is a multiplay/generic/string index. # Transmit frequency and message lifetime may need to be tuned.
# - ! is used as a seperator between the elements that are used to send the notification (typeid, sequence, notification) # 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 # - ; 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 # General Notes
# all clients over UDP). Clients will ignore messages that they have already received (based on the sequence id). #----------------------------------------------------------------------
# # Outgoing messages are sent in a scheduled manner, usually once per
# 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 # second, and each message has a lifetime (to allow for propogation to
# 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 # all clients over UDP). Clients will ignore messages that they have
# loaded which may only happen when visible so it may be necessary to track AI models in a seperate instantiatable incoming bridge manager. # 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. # The outgoing bridge would usually be created within the aircraft loading Nasal.
var OutgoingMPBridge = 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) if (_transmitter == nil)
_transmitter = emesary.GlobalTransmitter; _transmitter = emesary.GlobalTransmitter;
print("OutgoingMPBridge created for "~_ident); print("OutgoingMPBridge created for "~_ident);
var new_class = emesary.Recipient.new("OutgoingMPBridge "~_ident); var new_class = emesary.Recipient.new("OutgoingMPBridge "~_ident);
new_class.MessageIndex = 100; new_class.MessageIndex = 100;
@ -33,6 +74,9 @@ var OutgoingMPBridge =
else else
new_class.NotificationsToBridge = _notifications_to_bridge; new_class.NotificationsToBridge = _notifications_to_bridge;
foreach(var n ; new_class.NotificationsToBridge)
print(" bridge --> ",n.NotificationType);
new_class.MPout = ""; new_class.MPout = "";
new_class.MPidx = _mpidx; new_class.MPidx = _mpidx;
new_class.MessageLifeTime = 10; # seconds new_class.MessageLifeTime = 10; # seconds
@ -42,6 +86,7 @@ var OutgoingMPBridge =
new_class.Transmitter.Register(new_class); new_class.Transmitter.Register(new_class);
new_class.MpVariable = _root~"sim/multiplay/generic/string["~new_class.MPidx~"]"; new_class.MpVariable = _root~"sim/multiplay/generic/string["~new_class.MPidx~"]";
new_class.TransmitterActive = 0; new_class.TransmitterActive = 0;
new_class.TransmitFrequencySeconds = 1;
new_class.TransmitTimer = new_class.TransmitTimer =
maketimer(6, func maketimer(6, func
@ -49,9 +94,8 @@ var OutgoingMPBridge =
if(new_class.TransmitterActive) if(new_class.TransmitterActive)
new_class.Transmit(); new_class.Transmit();
new_class.TransmitTimer.restart(1); new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
}); });
new_class.TransmitTimer.restart(1);
new_class.Delete = func new_class.Delete = func
{ {
@ -72,20 +116,20 @@ var OutgoingMPBridge =
if (notification.FromIncomingBridge) if (notification.FromIncomingBridge)
return emesary.Transmitter.ReceiptStatus_NotProcessed; 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) 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; me.MessageIndex += 1;
notification.MessageExpiryTime = systime()+me.MessageLifeTime; notification.MessageExpiryTime = systime()+me.MessageLifeTime;
notification.BridgeMessageId = me.MessageIndex; 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 # 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.
notification.BridgeMessageTypeKey = notification.Type~"."~notification.Ident; notification.BridgeMessageNotificationTypeKey = notification.NotificationType~"."~notification.Ident;
#print("Received ",notification.BridgeMessageTypeKey," 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;
} }
@ -98,16 +142,16 @@ var OutgoingMPBridge =
{ {
for (var idx = 0; idx < size(me.OutgoingList); idx += 1) 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.OutgoingList[idx]= notification;
me.TransmitterActive = size(me.OutgoingList); me.TransmitterActive = size(me.OutgoingList);
return; return;
} }
} }
} }
#print("Added ",notification.BridgeMessageTypeKey); #print("Added ",notification.BridgeMessageNotificationTypeKey);
append(me.OutgoingList, notification); append(me.OutgoingList, notification);
me.TransmitterActive = size(me.OutgoingList); me.TransmitterActive = size(me.OutgoingList);
}; };
@ -133,7 +177,7 @@ var OutgoingMPBridge =
#print("Encode ",eidx,"=",encval); #print("Encode ",eidx,"=",encval);
eidx += 1; 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; outgoing = outgoing~sect;
me.OutgoingList[out_idx] = me.OutgoingList[idx]; me.OutgoingList[out_idx] = me.OutgoingList[idx];
# print("xmit ",idx," out=",out_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) for(var del_i=0; del_i < del_count; del_i += 1)
pop(me.OutgoingList); pop(me.OutgoingList);
}; };
new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
return new_class; return new_class;
}, },
}; };
@ -160,7 +205,7 @@ var OutgoingMPBridge =
# route messages to # route messages to
var IncomingMPBridge = 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) if (_transmitter == nil)
_transmitter = emesary.GlobalTransmitter; _transmitter = emesary.GlobalTransmitter;
@ -216,12 +261,12 @@ var IncomingMPBridge =
{ {
# get the message parts # get the message parts
var encoded_notification = split("!", encoded_val); 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); print("Error: emesary.IncomingBridge.ProcessIncoming bad msg ",encoded_val);
else else
{ {
var msg_idx = encoded_notification[0]; var msg_idx = encoded_notification[1];
var msg_type_id = encoded_notification[1]; var msg_type_id = encoded_notification[2];
if (msg_type_id >= size(me.NotificationsToBridge)) if (msg_type_id >= size(me.NotificationsToBridge))
{ {
print("Error: emesary.IncomingBridge.ProcessIncoming invalid type_id ",msg_type_id); print("Error: emesary.IncomingBridge.ProcessIncoming invalid type_id ",msg_type_id);
@ -229,14 +274,14 @@ var IncomingMPBridge =
else else
{ {
var msg = me.NotificationsToBridge[msg_type_id]; var msg = me.NotificationsToBridge[msg_type_id];
var msg_notify = encoded_notification[2]; var msg_notify = encoded_notification[3];
print("received idx=",msg_idx," ",msg_type_id,":",msg.Type); print("received idx=",msg_idx," ",msg_type_id,":",msg.NotificationType);
if(msg_idx <= me.IncomingMessageIndex) if(msg_idx <= me.IncomingMessageIndex)
print(" **Already processed"); print(" **Already processed");
else else
{ {
# raise notification # raise notification
var bridged_notification = msg; #emesary.Notification.new(msg.Type,"BridgedMessage"); var bridged_notification = msg; #emesary.Notification.new(msg.NotificationType,"BridgedMessage");
# populate fields # populate fields
var bridgedProperties = msg.bridgeProperties(); var bridgedProperties = msg.bridgeProperties();
var encvals=split(";", msg_notify); var encvals=split(";", msg_notify);
@ -267,30 +312,45 @@ var IncomingMPBridge =
} }
foreach(var n; new_class.NotificationsToBridge) foreach(var n; new_class.NotificationsToBridge)
{ {
print("IncomingBridge: ",n.Type); print("IncomingBridge: ",n.NotificationType);
} }
return new_class; 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) startMPBridge : func(notification_list)
{ {
var incomingBridgeList = {}; var incomingBridgeList = {};
#
# Create bridge whenever a client connects
#
setlistener("/ai/models/model-added", func(v) 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(); var path = v.getValue();
print("Model added ",path); print("Model added ",path);
# 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 ",callsign); print("Creating Emesary MPBridge for ",callsign);
if (callsign == "" or callsign == nil) if (callsign == "" or callsign == nil)
callsign = path; 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~"/"); incomingBridge.Connect(path~"/");
incomingBridgeList[path] = incomingBridge; incomingBridgeList[path] = incomingBridge;
} }
}); });
#
# when a client disconnects remove the associated bridge.
#
setlistener("/ai/models/model-removed", func(v){ setlistener("/ai/models/model-removed", func(v){
print("Model removed ",v.getValue()); print("Model removed ",v.getValue());
var path = 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);