New idea!!!
 
Ok, just taken a look at the snapshot version of libsecondlife and it's got a directory called Packets, which is an abstraction layer to create each outgoing packet.
 
It's essentially the Rpc layer, since what's coming into this layer is a plain old C# function call (eg Communication.ImprovedInstantMessage( ..., targetagentid, ..., message, ... ) ), and what's coming out is a network packet, albeit not actually serialized onto the wire, at least not just yet :-)
 
What could be ideal is a way of taking these function calls and *automatically* serializing them to Packet, and (possibly) writing them directly to the wire.  Reflection makes the parameters of the method available to us!  so we know the details about what we want to send.
 
We could do something like:
 
  public static Packet ImprovedInstantMessage(ProtocolManager protocol, LLUUID targetAgentID,
   LLUUID myAgentID, uint parentEstateID, LLUUID regionID, LLVector3 position, byte offline,
   byte dialog, LLUUID id, uint timestamp, string myAgentName, string message,
   byte[] binaryBucket)
  {
     return new BinaryMarshaller().MarshallThisCallToBinary( protocol, new object[]{ targetAgentID, myAgentID, parentEstateID, regionID, position, offline, ... );
  }
 
Somewhat simpler, but kindof a chore to have to populate the object[] array.
 
We can do better than that using Reflection.Emit.  Reflection.Emit lets us create classes on the fly, with the code to populate object[] array populated for us automatically!
 
We can cache the generated classes so we only need to generate each class once.
 
Using Reflection.Emit lets us do the following:
 
In Communication:
 
class Communication
{
  public Packet ImprovedInstantMessage(ProtocolManager protocol, LLUUID targetAgentID,
   LLUUID myAgentID, uint parentEstateID, LLUUID regionID, LLVector3 position, byte offline,
   byte dialog, LLUUID id, uint timestamp, string myAgentName, string message,
   byte[] binaryBucket)
  {
     return null;
  }
}
 
Yes that really does just return null!  Its just a placeholder.  The Reflection.Emit will handle this by creating a derived class that does the real work.  We can store the derived class in a standard Communication variable, and call the methods directly and easily!
 
Communication communication = outgoingbinaryrpc.CreateProxy( Communication ) as Communication;
Packet packet = communication.ImprovedInstantMessage( protocol, targetAgentID, myAgentID, parentEstateID, ... );
 
Quite easy to use right?
 
What we've done is essentially to create a transparent proxy that transparently and automatically generates our network packet for us!  It would be trivial to in fact add the logic to additionally automatically fire this packet across the network!
 
On 7/16/06, Hugh Perkins <[EMAIL PROTECTED]> wrote:
Christopher,
 
> One thing though: if we do go with the former, what happens when there are two fields with the same name in different blocks?
 
Christopher, that's a good point.  There's no obvious clean way to do that though there's various less clean ways.  To what extent does this situation arise?
 
If this is something that arises significantly maybe the second option is better?
 
On 7/16/06, Christopher Omega < [EMAIL PROTECTED]> wrote:
I personally would graitate to the former. Not needing to know what block a particular field belongs to seems to me more like a good thing.  One thing though: if we do go with the former, what happens when there are two fields with the same name in different blocks?

On 7/15/06, Hugh Perkins < [EMAIL PROTECTED]> wrote:
In avatar.cs, we can see for example:
 
   Hashtable blocks = new Hashtable();
   Hashtable fields = new Hashtable();
   fields["RegionHandle"] = regionHandle;
   fields["LookAt"] = lookAt;
   fields["Position"] = position;
   blocks[fields] = "Info";
   fields = new Hashtable();
   fields["AgentID"] = Client.Network.AgentID;
   fields["SessionID"] = Client.Network.SessionID;
   blocks[fields] = "AgentData";
   Packet packet = PacketBuilder.BuildPacket("TeleportLocationRequest", Client.Protocol, blocks);
 
It could be nice to handle this in a similar way as we can handle inbound binary calls, in order to make it as easy to read and maintain as possible?
 
Now, it's not quite as easy as inbound because of the issue with blocks, so there are a couple of ways of doing this.  I'm wondering if there are any opinions on which one is better?
 
Method 1, using custom attributes:
 
In avatar.cs:
 
   TeleportLocationRequest teleportlocationrequest = new TeleportLocationRequest();
            
   teleportlocationrequest.RegionHandle = regionHandle;
   teleportlocationrequest.LookAt = lookAt;
   teleportlocationrequest.Position = position;
   teleportlocationrequest.AgentID = Client.Network.AgentID;
   teleportlocationrequest.SessionID = Client.Network.SessionID;
           
   Packet packet = BinaryMarshaller.Marshall( teleportlocationrequest );
 
TeleportLocationRequest looks like this:
 

public class TeleportLocationRequest
{

    [BlockInfo]
        public U64 RegionHandle;
    [BlockInfo]
        public LLVector3 LookAt = new LLVector3();   
    [BlockInfo]
        public LLVector3 Position = new LLVector3();
    [BlockAgentData]
        public LLUUID AgentID = new LLUUID();
    [BlockAgentData]
        public LLUUID SessionID = new LLUUID();
}

Pretty easy to do, the attributes are fairly easy to read?

Method 2: use classes for each block

In avatar.cs:
 
   TeleportLocationRequest teleportlocationrequest = new TeleportLocationRequest();
            
   teleportlocationrequest.Info.RegionHandle = regionHandle;
   teleportlocationrequest.Info.LookAt = lookAt;
   teleportlocationrequest.Info.Position = position;
   teleportlocationrequest.AgentData.AgentID = Client.Network.AgentID;
   teleportlocationrequest.AgentData.SessionID = Client.Network.SessionID;
           
   Packet packet = BinaryMarshaller.Marshall( teleportlocationrequest );
 
Our TeleportLocationRequest looks something like:
 

public class TeleportLocationRequest
{
    public class _Info
    {
        public U64 RegionHandle;
        public LLVector3 LookAt = new LLVector3();  
        public LLVector3 Position = new LLVector3();
    }

    public class _AgentData
    {       
        public LLUUID AgentID = new LLUUID();
        public LLUUID SessionID = new LLUUID();
    }
   
    public _Info Info = new _Info();
    public _AgentData AgentData = new _AgentData();       
}

... or possibly some other variant on naming, to avoid using underscores.

Again, fairly easy to do, so unclear which is better.

Opinions?
 

_______________________________________________
libsecondlife-dev mailing list
libsecondlife-dev@gna.org
https://mail.gna.org/listinfo/libsecondlife-dev





--
"Anyone that would give up a little liberty for a little security, deserves neither and loses both." -Ben Franklin

_______________________________________________

 

_______________________________________________
libsecondlife-dev mailing list
libsecondlife-dev@gna.org
https://mail.gna.org/listinfo/libsecondlife-dev

Reply via email to