Firstly, libsecondlife rocks :-))))))
A lot of work has been going on at a low level to write the network packet data into the appropriate structures. Perhaps it could be possible to start to industrialize this?
For example, looking at Avatar.cs, we can see:
if(field.Layout.Name == "FromAgentID")
{
FromAgentID = (LLUUID)field.Data;
}
else if(field.Layout.Name == "ToAgentID")
{
ToAgentID = (LLUUID)field.Data;
}
else if(field.Layout.Name == "ParentEstateID")
{
ParentEstateID = (uint)field.Data;
}
else if(field.Layout.Name == "RegionID")
{
RegionID = (LLUUID)field.Data;
}
else if( field.Layout.Name == "Position")
{
Position = (LLVector3)field.Data;
}
else if(field.Layout.Name == "Offline")
{
Offline = (byte)field.Data;
}
else if(field.Layout.Name == "Dialog")
{
Dialog = (byte)field.Data;
}
else if( field.Layout.Name == "ID")
{
ID = (LLUUID)field.Data;
}
else if(field.Layout.Name == "Timestamp")
{
Timestamp = (uint)field.Data;
}
else if(field.Layout.Name == "AgentName")
{
AgentName = System.Text.Encoding.UTF8.GetString ((byte[])field.Data).Replace("\0", "");
}
else if(field.Layout.Name == "Message")
{
Message = System.Text.Encoding.UTF8.GetString ((byte[])field.Data).Replace("\0", "");
}
else if(field.Layout.Name == "BinaryBucket")
{
Bucket = System.Text.Encoding.UTF8.GetString ((byte[])field.Data).Replace("\0", "");
}
{
FromAgentID = (LLUUID)field.Data;
}
else if(field.Layout.Name == "ToAgentID")
{
ToAgentID = (LLUUID)field.Data;
}
else if(field.Layout.Name == "ParentEstateID")
{
ParentEstateID = (uint)field.Data;
}
else if(field.Layout.Name == "RegionID")
{
RegionID = (LLUUID)field.Data;
}
else if( field.Layout.Name == "Position")
{
Position = (LLVector3)field.Data;
}
else if(field.Layout.Name == "Offline")
{
Offline = (byte)field.Data;
}
else if(field.Layout.Name == "Dialog")
{
Dialog = (byte)field.Data;
}
else if( field.Layout.Name == "ID")
{
ID = (LLUUID)field.Data;
}
else if(field.Layout.Name == "Timestamp")
{
Timestamp = (uint)field.Data;
}
else if(field.Layout.Name == "AgentName")
{
AgentName = System.Text.Encoding.UTF8.GetString ((byte[])field.Data).Replace("\0", "");
}
else if(field.Layout.Name == "Message")
{
Message = System.Text.Encoding.UTF8.GetString ((byte[])field.Data).Replace("\0", "");
}
else if(field.Layout.Name == "BinaryBucket")
{
Bucket = System.Text.Encoding.UTF8.GetString ((byte[])field.Data).Replace("\0", "");
}
...
Clearly, that's a lot of code to write, and a lot of scope for errors.
In C++ it's pretty much obligatory to do something like that, however in C# we can actually directly read the list of properties in the class, and assign data to them directly! To get the list of properties, we can do:
Avatar newavatar = new Avatar();
foreach( MemberInfo memberinfo in newavatar.GetType().GetMembers() )
{
{
if( memberinfo.MemberType == MemberTypes.Field )
{
FieldInfo fi = target.GetType().GetField( memberinfo.Name );
fi.SetValue( newavatar, networkpacket[ memberinfo.Name ] );
fi.SetValue( newavatar, networkpacket[ memberinfo.Name ] );
}
}
}
(Assuming we stored the data from the networkpacket in a hashtable called networkpacket, for sake of clarity).
We can read the type of each class property, to allow us to do appropriate data conversions from the network packet data.
for certain data, eg ObjectUpdate, the data can be packed in various ways according to the exact maximum, minimum, and step value of the data. For example Shear might vary from 0 to 100 in 0.5 increments (not sure if it does, just an example). Can we handle this?
Yes we can! Its possible to define custom attributes that can be applied to each property. For example we can do:
class ObjectUpdate
{
[SecondLifeRpcIntAttribute( 0, 100, 0.5 )]
public float Shear;
}
We can read the custom attribute applied to a property quite easily:
foreach( object customattribute in memberinfo.GetCustomAttributes(false ) )
{
if( customattribute.GetType() == typeof( SecondLifeRpcIntAttribute ) )
{
// ... do something here
}
}
{
if( customattribute.GetType() == typeof( SecondLifeRpcIntAttribute ) )
{
// ... do something here
}
}
It's possible to create custom attributes quite simply:
[SecondLifeRpcIntAttribute( AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public class SecondLifeRpcIntAttribute : Attribute
{
}
public class SecondLifeRpcIntAttribute : Attribute
{
}
You can add parameters to the custom attributes by simply supplying an appropriate constructor to the attribute class:
[SecondLifeRpcIntAttribute( AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public class SecondLifeRpcIntAttribute : Attribute
{
public class SecondLifeRpcIntAttribute : Attribute
{
public int Minimum;
public int Maximum;
public int Step;
public SecondLifeRpcIntAttribute( int Min, int max, int step )
{
this.Minimum = Min; this.Maximum = max; this.Step = step;
}
}
}
Why do this? Well, it would probably make creating the appropriate class/struct for each rpc call relatively painless and easy to maintain?
_______________________________________________ libsecondlife-dev mailing list libsecondlife-dev@gna.org https://mail.gna.org/listinfo/libsecondlife-dev