aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs273
1 files changed, 200 insertions, 73 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 29ff86b..05ebcac 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -69,10 +69,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
69 private int m_cachedTextureSerial; 69 private int m_cachedTextureSerial;
70 private Timer m_clientPingTimer; 70 private Timer m_clientPingTimer;
71 71
72 private Timer m_terseUpdateTimer; 72 private Timer m_avatarTerseUpdateTimer;
73 private Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_terseUpdates = new Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); 73 private Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_avatarTerseUpdates = new Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
74 private ushort m_terseTimeDilationLast = 0; 74 private ushort m_terseTimeDilationLast = 0;
75 75
76 private Timer m_primTerseUpdateTimer;
77 private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_primTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
78 private Timer m_primFullUpdateTimer;
79 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates =
80 new List<ObjectUpdatePacket.ObjectDataBlock>();
81
76 private bool m_clientBlocked; 82 private bool m_clientBlocked;
77 83
78 private int m_probesWithNoIngressPackets; 84 private int m_probesWithNoIngressPackets;
@@ -121,8 +127,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
121 protected string m_activeGroupName = String.Empty; 127 protected string m_activeGroupName = String.Empty;
122 protected ulong m_activeGroupPowers; 128 protected ulong m_activeGroupPowers;
123 protected Dictionary<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>(); 129 protected Dictionary<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>();
124 protected int m_terseUpdateRate = 50; 130 protected int m_avatarTerseUpdateRate = 50;
125 protected int m_terseUpdatesPerPacket = 5; 131 protected int m_avatarTerseUpdatesPerPacket = 5;
132
133 // LL uses these limits, apparently. Compressed terse would be
134 // 23, but we don't have that yet
135 //
136 protected int m_primTerseUpdatesPerPacket = 10;
137 protected int m_primFullUpdatesPerPacket = 14;
138
139 protected int m_primTerseUpdateRate = 10;
140 protected int m_primFullUpdateRate = 14;
126 141
127 // LLClientView Only 142 // LLClientView Only
128 public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args); 143 public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args);
@@ -528,7 +543,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
528 543
529 // Shut down timers 544 // Shut down timers
530 m_clientPingTimer.Stop(); 545 m_clientPingTimer.Stop();
531 m_terseUpdateTimer.Stop(); 546 m_avatarTerseUpdateTimer.Stop();
547 m_primTerseUpdateTimer.Stop();
548 m_primFullUpdateTimer.Stop();
532 549
533 // This is just to give the client a reasonable chance of 550 // This is just to give the client a reasonable chance of
534 // flushing out all it's packets. There should probably 551 // flushing out all it's packets. There should probably
@@ -609,7 +626,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
609 { 626 {
610 // Shut down timers 627 // Shut down timers
611 m_clientPingTimer.Stop(); 628 m_clientPingTimer.Stop();
612 m_terseUpdateTimer.Stop(); 629 m_avatarTerseUpdateTimer.Stop();
630 m_primTerseUpdateTimer.Stop();
631 m_primFullUpdateTimer.Stop();
613 } 632 }
614 633
615 public void Restart() 634 public void Restart()
@@ -621,9 +640,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
621 m_clientPingTimer.Elapsed += CheckClientConnectivity; 640 m_clientPingTimer.Elapsed += CheckClientConnectivity;
622 m_clientPingTimer.Enabled = true; 641 m_clientPingTimer.Enabled = true;
623 642
624 m_terseUpdateTimer = new Timer(m_terseUpdateRate); 643 m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
625 m_terseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); 644 m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
626 m_terseUpdateTimer.AutoReset = false; 645 m_avatarTerseUpdateTimer.AutoReset = false;
646
647 m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate);
648 m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates);
649 m_primTerseUpdateTimer.AutoReset = false;
650
651 m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
652 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
653 m_primFullUpdateTimer.AutoReset = false;
627 } 654 }
628 655
629 public void Terminate() 656 public void Terminate()
@@ -845,10 +872,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
845 m_clientPingTimer.Elapsed += CheckClientConnectivity; 872 m_clientPingTimer.Elapsed += CheckClientConnectivity;
846 m_clientPingTimer.Enabled = true; 873 m_clientPingTimer.Enabled = true;
847 874
848 m_terseUpdateTimer = new Timer(m_terseUpdateRate); 875 m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
849 m_terseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); 876 m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
850 m_terseUpdateTimer.AutoReset = false; 877 m_avatarTerseUpdateTimer.AutoReset = false;
878
879 m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate);
880 m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates);
881 m_primTerseUpdateTimer.AutoReset = false;
851 882
883 m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
884 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
885 m_primFullUpdateTimer.AutoReset = false;
852 m_scene.AddNewClient(this); 886 m_scene.AddNewClient(this);
853 887
854 RefreshGroupMembership(); 888 RefreshGroupMembership();
@@ -2724,20 +2758,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2724 CreateAvatarImprovedBlock(localID, position, velocity, rotation); 2758 CreateAvatarImprovedBlock(localID, position, velocity, rotation);
2725 2759
2726 bool sendpacketnow = false; 2760 bool sendpacketnow = false;
2727 lock (m_terseUpdates) 2761 lock (m_avatarTerseUpdates)
2728 { 2762 {
2729 // Only one update per avatar per packet. No need to send old ones so just overwrite them. 2763 // Only one update per avatar per packet. No need to send old ones so just overwrite them.
2730 m_terseUpdates[localID] = terseBlock; 2764 m_avatarTerseUpdates[localID] = terseBlock;
2731 m_terseTimeDilationLast = timeDilation; 2765 m_terseTimeDilationLast = timeDilation;
2732 2766
2733 // If packet is full or own movement packet, send it. 2767 // If packet is full or own movement packet, send it.
2734 if (agentid == m_agentId || m_terseUpdates.Count >= m_terseUpdatesPerPacket) 2768 if (agentid == m_agentId || m_avatarTerseUpdates.Count >= m_avatarTerseUpdatesPerPacket)
2735 { 2769 {
2736 m_terseUpdateTimer.Stop(); 2770 m_avatarTerseUpdateTimer.Stop();
2737 sendpacketnow = true; 2771 sendpacketnow = true;
2738 } 2772 }
2739 else if (m_terseUpdates.Count == 1) 2773 else if (m_avatarTerseUpdates.Count == 1)
2740 m_terseUpdateTimer.Start(); 2774 m_avatarTerseUpdateTimer.Start();
2741 } 2775 }
2742 // Call ProcessAvatarTerseUpdates outside the lock 2776 // Call ProcessAvatarTerseUpdates outside the lock
2743 if (sendpacketnow) 2777 if (sendpacketnow)
@@ -2746,14 +2780,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2746 2780
2747 private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) 2781 private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e)
2748 { 2782 {
2749 lock (m_terseUpdates) 2783 lock (m_avatarTerseUpdates)
2750 { 2784 {
2751 ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); 2785 ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
2752 terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; 2786 terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle;
2753 terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[m_terseUpdates.Count]; 2787 terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[m_avatarTerseUpdates.Count];
2754 2788
2755 int i = 0; 2789 int i = 0;
2756 foreach (KeyValuePair<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> dbe in m_terseUpdates) 2790 foreach (KeyValuePair<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> dbe in m_avatarTerseUpdates)
2757 { 2791 {
2758 terse.ObjectData[i] = dbe.Value; 2792 terse.ObjectData[i] = dbe.Value;
2759 i++; 2793 i++;
@@ -2764,7 +2798,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2764 terse.Header.Zerocoded = true; 2798 terse.Header.Zerocoded = true;
2765 OutPacket(terse, ThrottleOutPacketType.Task); 2799 OutPacket(terse, ThrottleOutPacketType.Task);
2766 2800
2767 m_terseUpdates.Clear(); 2801 m_avatarTerseUpdates.Clear();
2768 } 2802 }
2769 } 2803 }
2770 2804
@@ -2859,81 +2893,124 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2859 if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) 2893 if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0)
2860 rotation = Quaternion.Identity; 2894 rotation = Quaternion.Identity;
2861 2895
2862 ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); 2896 ObjectUpdatePacket.ObjectDataBlock objectData =
2897 new ObjectUpdatePacket.ObjectDataBlock();
2863 2898
2899 objectData = CreatePrimUpdateBlock(primShape, flags);
2864 2900
2901 objectData.ID = localID;
2902 objectData.FullID = objectID;
2903 objectData.OwnerID = ownerID;
2865 2904
2866 // TODO: don't create new blocks if recycling an old packet 2905 objectData.Text = LLUtil.StringToPacketBytes(text);
2867 outPacket.RegionData.RegionHandle = regionHandle; 2906 objectData.TextColor[0] = color[0];
2868 outPacket.RegionData.TimeDilation = timeDilation; 2907 objectData.TextColor[1] = color[1];
2869 outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; 2908 objectData.TextColor[2] = color[2];
2870 2909 objectData.TextColor[3] = color[3];
2871 outPacket.ObjectData[0] = CreatePrimUpdateBlock(primShape, flags); 2910 objectData.ParentID = parentID;
2872 2911 objectData.PSBlock = particleSystem;
2873 outPacket.ObjectData[0].ID = localID; 2912 objectData.ClickAction = clickAction;
2874 outPacket.ObjectData[0].FullID = objectID; 2913 objectData.Material = material;
2875 outPacket.ObjectData[0].OwnerID = ownerID; 2914 objectData.Flags = 0;
2876
2877 outPacket.ObjectData[0].Text = LLUtil.StringToPacketBytes(text);
2878 outPacket.ObjectData[0].TextColor[0] = color[0];
2879 outPacket.ObjectData[0].TextColor[1] = color[1];
2880 outPacket.ObjectData[0].TextColor[2] = color[2];
2881 outPacket.ObjectData[0].TextColor[3] = color[3];
2882 outPacket.ObjectData[0].ParentID = parentID;
2883 outPacket.ObjectData[0].PSBlock = particleSystem;
2884 outPacket.ObjectData[0].ClickAction = clickAction;
2885 outPacket.ObjectData[0].Material = material;
2886 outPacket.ObjectData[0].Flags = 0;
2887 2915
2888 if (attachment) 2916 if (attachment)
2889 { 2917 {
2890 // Necessary??? 2918 // Necessary???
2891 outPacket.ObjectData[0].JointAxisOrAnchor = new Vector3(0, 0, 2); 2919 objectData.JointAxisOrAnchor = new Vector3(0, 0, 2);
2892 outPacket.ObjectData[0].JointPivot = new Vector3(0, 0, 0); 2920 objectData.JointPivot = new Vector3(0, 0, 0);
2893 2921
2894 // Item from inventory??? 2922 // Item from inventory???
2895 outPacket.ObjectData[0].NameValue = 2923 objectData.NameValue =
2896 Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid); 2924 Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid);
2897 outPacket.ObjectData[0].State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); 2925 objectData.State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16));
2898 } 2926 }
2899 2927
2900 // Xantor 20080528: Send sound info as well 2928 // Xantor 20080528: Send sound info as well
2901 // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again 2929 // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again
2902 outPacket.ObjectData[0].Sound = SoundId; 2930 objectData.Sound = SoundId;
2903 if (SoundId == UUID.Zero) 2931 if (SoundId == UUID.Zero)
2904 { 2932 {
2905 outPacket.ObjectData[0].OwnerID = UUID.Zero; 2933 objectData.OwnerID = UUID.Zero;
2906 outPacket.ObjectData[0].Gain = 0.0f; 2934 objectData.Gain = 0.0f;
2907 outPacket.ObjectData[0].Radius = 0.0f; 2935 objectData.Radius = 0.0f;
2908 outPacket.ObjectData[0].Flags = 0; 2936 objectData.Flags = 0;
2909 } 2937 }
2910 else 2938 else
2911 { 2939 {
2912 outPacket.ObjectData[0].OwnerID = ownerID; 2940 objectData.OwnerID = ownerID;
2913 outPacket.ObjectData[0].Gain = (float)SoundGain; 2941 objectData.Gain = (float)SoundGain;
2914 outPacket.ObjectData[0].Radius = (float)SoundRadius; 2942 objectData.Radius = (float)SoundRadius;
2915 outPacket.ObjectData[0].Flags = SoundFlags; 2943 objectData.Flags = SoundFlags;
2916 } 2944 }
2917 2945
2918 byte[] pb = pos.GetBytes(); 2946 byte[] pb = pos.GetBytes();
2919 Array.Copy(pb, 0, outPacket.ObjectData[0].ObjectData, 0, pb.Length); 2947 Array.Copy(pb, 0, objectData.ObjectData, 0, pb.Length);
2920 2948
2921 byte[] vel = velocity.GetBytes(); 2949 byte[] vel = velocity.GetBytes();
2922 Array.Copy(vel, 0, outPacket.ObjectData[0].ObjectData, pb.Length, vel.Length); 2950 Array.Copy(vel, 0, objectData.ObjectData, pb.Length, vel.Length);
2923 2951
2924 byte[] rot = rotation.GetBytes(); 2952 byte[] rot = rotation.GetBytes();
2925 Array.Copy(rot, 0, outPacket.ObjectData[0].ObjectData, 36, rot.Length); 2953 Array.Copy(rot, 0, objectData.ObjectData, 36, rot.Length);
2926 2954
2927 byte[] rvel = rotational_velocity.GetBytes(); 2955 byte[] rvel = rotational_velocity.GetBytes();
2928 Array.Copy(rvel, 0, outPacket.ObjectData[0].ObjectData, 36 + rot.Length, rvel.Length); 2956 Array.Copy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length);
2929 2957
2930 if (textureanim.Length > 0) 2958 if (textureanim.Length > 0)
2931 { 2959 {
2932 outPacket.ObjectData[0].TextureAnim = textureanim; 2960 objectData.TextureAnim = textureanim;
2933 } 2961 }
2934 outPacket.Header.Zerocoded = true;
2935 2962
2936 OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); 2963 lock (m_primFullUpdates)
2964 {
2965 if (m_primFullUpdates.Count == 0)
2966 m_primFullUpdateTimer.Start();
2967
2968 m_primFullUpdates.Add(objectData);
2969
2970 if (m_primFullUpdates.Count >= m_primFullUpdatesPerPacket)
2971 ProcessPrimFullUpdates(this, null);
2972 }
2973 }
2974
2975 void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e)
2976 {
2977 lock (m_primFullUpdates)
2978 {
2979 if (m_primFullUpdates.Count == 0)
2980 {
2981 m_primFullUpdateTimer.Stop();
2982
2983 return;
2984 }
2985
2986 ObjectUpdatePacket outPacket =
2987 (ObjectUpdatePacket)PacketPool.Instance.GetPacket(
2988 PacketType.ObjectUpdate);
2989
2990 outPacket.RegionData.RegionHandle =
2991 Scene.RegionInfo.RegionHandle;
2992 outPacket.RegionData.TimeDilation =
2993 (ushort)(Scene.TimeDilation * ushort.MaxValue);
2994
2995 int count = m_primFullUpdates.Count;
2996 if (count > m_primFullUpdatesPerPacket)
2997 count = m_primFullUpdatesPerPacket;
2998
2999 outPacket.ObjectData =
3000 new ObjectUpdatePacket.ObjectDataBlock[count];
3001
3002 for (int index = 0 ; index < count ; index++)
3003 {
3004 outPacket.ObjectData[index] = m_primFullUpdates[0];
3005 m_primFullUpdates.RemoveAt(0);
3006 }
3007
3008 outPacket.Header.Zerocoded = true;
3009 OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority);
3010
3011 if (m_primFullUpdates.Count == 0)
3012 m_primFullUpdateTimer.Stop();
3013 }
2937 } 3014 }
2938 3015
2939 /// <summary> 3016 /// <summary>
@@ -2947,15 +3024,65 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2947 3024
2948 if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) 3025 if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0)
2949 rotation = Quaternion.Identity; 3026 rotation = Quaternion.Identity;
2950 ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); 3027
2951 // TODO: don't create new blocks if recycling an old packet 3028 ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData =
2952 terse.RegionData.RegionHandle = regionHandle; 3029 CreatePrimImprovedBlock(localID, position, rotation,
2953 terse.RegionData.TimeDilation = timeDilation; 3030 velocity, rotationalvelocity, state);
2954 terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; 3031
2955 terse.ObjectData[0] = CreatePrimImprovedBlock(localID, position, rotation, velocity, rotationalvelocity, state); // AssetID should fall into here probably somehow... 3032 lock (m_primTerseUpdates)
2956 terse.Header.Reliable = false; 3033 {
2957 terse.Header.Zerocoded = true; 3034 if (m_primTerseUpdates.Count == 0)
2958 OutPacket(terse, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); 3035 m_primTerseUpdateTimer.Start();
3036
3037 m_primTerseUpdates.Add(objectData);
3038
3039 if (m_primTerseUpdates.Count >= m_primTerseUpdatesPerPacket)
3040 ProcessPrimTerseUpdates(this, null);
3041 }
3042 }
3043
3044 void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e)
3045 {
3046 lock (m_primTerseUpdates)
3047 {
3048 if (m_primTerseUpdates.Count == 0)
3049 {
3050 m_primTerseUpdateTimer.Stop();
3051
3052 return;
3053 }
3054
3055 ImprovedTerseObjectUpdatePacket outPacket =
3056 (ImprovedTerseObjectUpdatePacket)
3057 PacketPool.Instance.GetPacket(
3058 PacketType.ImprovedTerseObjectUpdate);
3059
3060 outPacket.RegionData.RegionHandle =
3061 Scene.RegionInfo.RegionHandle;
3062 outPacket.RegionData.TimeDilation =
3063 (ushort)(Scene.TimeDilation * ushort.MaxValue);
3064
3065 int count = m_primTerseUpdates.Count;
3066 if (count > m_primTerseUpdatesPerPacket)
3067 count = m_primTerseUpdatesPerPacket;
3068
3069 outPacket.ObjectData =
3070 new ImprovedTerseObjectUpdatePacket.
3071 ObjectDataBlock[count];
3072
3073 for (int index = 0 ; index < count ; index++)
3074 {
3075 outPacket.ObjectData[index] = m_primTerseUpdates[0];
3076 m_primTerseUpdates.RemoveAt(0);
3077 }
3078
3079 outPacket.Header.Reliable = false;
3080 outPacket.Header.Zerocoded = true;
3081 OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority);
3082
3083 if (m_primTerseUpdates.Count == 0)
3084 m_primTerseUpdateTimer.Stop();
3085 }
2959 } 3086 }
2960 3087
2961 public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) 3088 public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID)