[Simian]
* Initial framework support for multiple simulators. Currently only the first region config is loaded, but the framework is there * Big reorganization of config and data files * Started renaming extensions that handle LLUDP packets to start with the LL prefix. Work in progress * Initial SSL support git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2482 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
64
Programs/Simian/SceneExtensions/AvatarManager.cs
Normal file
64
Programs/Simian/SceneExtensions/AvatarManager.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
class AvatarManager : IExtension<ISceneProvider>, IAvatarProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
int currentWearablesSerialNum = -1;
|
||||
int currentAnimSequenceNum = 0;
|
||||
|
||||
public AvatarManager()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public bool SetDefaultAnimation(Agent agent, UUID animID)
|
||||
{
|
||||
return agent.Animations.SetDefaultAnimation(animID, ref currentAnimSequenceNum);
|
||||
}
|
||||
|
||||
public bool AddAnimation(Agent agent, UUID animID)
|
||||
{
|
||||
return agent.Animations.Add(animID, ref currentAnimSequenceNum);
|
||||
}
|
||||
|
||||
public bool RemoveAnimation(Agent agent, UUID animID)
|
||||
{
|
||||
return agent.Animations.Remove(animID);
|
||||
}
|
||||
|
||||
public bool ClearAnimations(Agent agent)
|
||||
{
|
||||
agent.Animations.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SendAnimations(Agent agent)
|
||||
{
|
||||
scene.ObjectAnimate(this, agent.ID, agent.ID, agent.Animations.GetAnimations());
|
||||
}
|
||||
|
||||
public void SendAlert(Agent agent, string message)
|
||||
{
|
||||
AlertMessagePacket alert = new AlertMessagePacket();
|
||||
alert.AlertData.Message = Utils.StringToBytes(message);
|
||||
scene.UDP.SendPacket(agent.ID, alert, PacketCategory.Transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Programs/Simian/SceneExtensions/ConnectionManagement.cs
Normal file
87
Programs/Simian/SceneExtensions/ConnectionManagement.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ConnectionManagement : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public ConnectionManagement()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.UseCircuitCode, UseCircuitCodeHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.StartPingCheck, StartPingCheckHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.LogoutRequest, LogoutRequestHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void UseCircuitCodeHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RegionHandshakePacket handshake = new RegionHandshakePacket();
|
||||
handshake.RegionInfo.BillableFactor = 0f;
|
||||
handshake.RegionInfo.CacheID = UUID.Random();
|
||||
handshake.RegionInfo.IsEstateManager = false;
|
||||
handshake.RegionInfo.RegionFlags = (uint)scene.RegionFlags;
|
||||
handshake.RegionInfo.SimOwner = UUID.Random();
|
||||
handshake.RegionInfo.SimAccess = (byte)SimAccess.Min;
|
||||
handshake.RegionInfo.SimName = Utils.StringToBytes(scene.RegionName);
|
||||
handshake.RegionInfo.WaterHeight = scene.WaterHeight;
|
||||
handshake.RegionInfo.TerrainBase0 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainBase1 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainBase2 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainBase3 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainDetail0 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainDetail1 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainDetail2 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainDetail3 = UUID.Zero;
|
||||
handshake.RegionInfo.TerrainHeightRange00 = 0f;
|
||||
handshake.RegionInfo.TerrainHeightRange01 = 20f;
|
||||
handshake.RegionInfo.TerrainHeightRange10 = 0f;
|
||||
handshake.RegionInfo.TerrainHeightRange11 = 20f;
|
||||
handshake.RegionInfo.TerrainStartHeight00 = 0f;
|
||||
handshake.RegionInfo.TerrainStartHeight01 = 40f;
|
||||
handshake.RegionInfo.TerrainStartHeight10 = 0f;
|
||||
handshake.RegionInfo.TerrainStartHeight11 = 40f;
|
||||
handshake.RegionInfo2.RegionID = scene.RegionID;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, handshake, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void StartPingCheckHandler(Packet packet, Agent agent)
|
||||
{
|
||||
StartPingCheckPacket start = (StartPingCheckPacket)packet;
|
||||
|
||||
CompletePingCheckPacket complete = new CompletePingCheckPacket();
|
||||
complete.Header.Reliable = false;
|
||||
complete.PingID.PingID = start.PingID.PingID;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Overhead);
|
||||
}
|
||||
|
||||
void LogoutRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
LogoutReplyPacket reply = new LogoutReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.AgentData.SessionID = agent.SessionID;
|
||||
reply.InventoryData = new LogoutReplyPacket.InventoryDataBlock[1];
|
||||
reply.InventoryData[0] = new LogoutReplyPacket.InventoryDataBlock();
|
||||
reply.InventoryData[0].ItemID = UUID.Zero;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
|
||||
scene.ObjectRemove(this, agent.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
278
Programs/Simian/SceneExtensions/ImageDelivery.cs
Normal file
278
Programs/Simian/SceneExtensions/ImageDelivery.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ImageDownload
|
||||
{
|
||||
public const int FIRST_IMAGE_PACKET_SIZE = 600;
|
||||
public const int IMAGE_PACKET_SIZE = 1000;
|
||||
|
||||
public AssetTexture Texture;
|
||||
public Agent Agent;
|
||||
public int DiscardLevel;
|
||||
public float Priority;
|
||||
public int CurrentPacket;
|
||||
public int StopPacket;
|
||||
|
||||
public ImageDownload(AssetTexture texture, Agent agent, int discardLevel, float priority, int packet)
|
||||
{
|
||||
Texture = texture;
|
||||
Agent = agent;
|
||||
Update(discardLevel, priority, packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an image transfer with new information and recalculates
|
||||
/// offsets
|
||||
/// </summary>
|
||||
/// <param name="discardLevel">New requested discard level</param>
|
||||
/// <param name="priority">New requested priority</param>
|
||||
/// <param name="packet">New requested packet offset</param>
|
||||
public void Update(int discardLevel, float priority, int packet)
|
||||
{
|
||||
Priority = priority;
|
||||
|
||||
if (Texture != null)
|
||||
{
|
||||
if (Texture.LayerInfo != null && Texture.LayerInfo.Length > 0)
|
||||
{
|
||||
DiscardLevel = Utils.Clamp(discardLevel, 0, Texture.LayerInfo.Length - 1);
|
||||
StopPacket = GetPacketForBytePosition(Texture.LayerInfo[(Texture.LayerInfo.Length - 1) - DiscardLevel].End);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No layers, send the full image
|
||||
DiscardLevel = 0;
|
||||
StopPacket = GetPacketForBytePosition(Texture.AssetData.Length - 1);
|
||||
}
|
||||
|
||||
CurrentPacket = Utils.Clamp(packet, 1, TexturePacketCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
DiscardLevel = discardLevel;
|
||||
Priority = priority;
|
||||
CurrentPacket = packet;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total number of packets needed to transfer this texture,
|
||||
/// including the first packet of size FIRST_IMAGE_PACKET_SIZE
|
||||
/// </summary>
|
||||
/// <returns>Total number of packets needed to transfer this texture</returns>
|
||||
public int TexturePacketCount()
|
||||
{
|
||||
return ((Texture.AssetData.Length - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current byte offset for this transfer, calculated from
|
||||
/// the CurrentPacket
|
||||
/// </summary>
|
||||
/// <returns>Current byte offset for this transfer</returns>
|
||||
public int CurrentBytePosition()
|
||||
{
|
||||
return FIRST_IMAGE_PACKET_SIZE + (CurrentPacket - 1) * IMAGE_PACKET_SIZE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the size, in bytes, of the last packet. This will be somewhere
|
||||
/// between 1 and IMAGE_PACKET_SIZE bytes
|
||||
/// </summary>
|
||||
/// <returns>Size of the last packet in the transfer</returns>
|
||||
public int LastPacketSize()
|
||||
{
|
||||
return Texture.AssetData.Length - (FIRST_IMAGE_PACKET_SIZE + ((TexturePacketCount() - 2) * IMAGE_PACKET_SIZE));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the packet number that contains a given byte position
|
||||
/// </summary>
|
||||
/// <param name="bytePosition">Byte position</param>
|
||||
/// <returns>Packet number that contains the given byte position</returns>
|
||||
int GetPacketForBytePosition(int bytePosition)
|
||||
{
|
||||
return ((bytePosition - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
public class ImageDelivery : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
Dictionary<UUID, ImageDownload> CurrentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public ImageDelivery()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void RequestImageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RequestImagePacket request = (RequestImagePacket)packet;
|
||||
|
||||
for (int i = 0; i < request.RequestImage.Length; i++)
|
||||
{
|
||||
RequestImagePacket.RequestImageBlock block = request.RequestImage[i];
|
||||
|
||||
ImageDownload download;
|
||||
bool downloadFound = CurrentDownloads.TryGetValue(block.Image, out download);
|
||||
|
||||
if (downloadFound)
|
||||
{
|
||||
lock (download)
|
||||
{
|
||||
if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
{
|
||||
Logger.DebugLog(String.Format("Image download {0} is aborting", block.Image));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (block.DiscardLevel < download.DiscardLevel)
|
||||
Logger.DebugLog(String.Format("Image download {0} is changing from DiscardLevel {1} to {2}",
|
||||
block.Image, download.DiscardLevel, block.DiscardLevel));
|
||||
|
||||
if (block.DownloadPriority != download.Priority)
|
||||
Logger.DebugLog(String.Format("Image download {0} is changing from Priority {1} to {2}",
|
||||
block.Image, download.Priority, block.DownloadPriority));
|
||||
|
||||
if (block.Packet != download.CurrentPacket)
|
||||
Logger.DebugLog(String.Format("Image download {0} is changing from Packet {1} to {2}",
|
||||
block.Image, download.CurrentPacket, block.Packet));
|
||||
}
|
||||
|
||||
// Update download
|
||||
download.Update(block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
}
|
||||
}
|
||||
else if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
{
|
||||
// Aborting a download we are not tracking, ignore
|
||||
}
|
||||
else
|
||||
{
|
||||
//bool bake = ((ImageType)block.Type == ImageType.Baked);
|
||||
|
||||
// New download, check if we have this image
|
||||
Asset asset;
|
||||
if (scene.Server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
|
||||
{
|
||||
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Request for a missing texture " + block.Image.ToString(), Helpers.LogLevel.Warning);
|
||||
|
||||
ImageNotInDatabasePacket notfound = new ImageNotInDatabasePacket();
|
||||
notfound.ImageID.ID = block.Image;
|
||||
scene.UDP.SendPacket(agent.ID, notfound, PacketCategory.Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
|
||||
{
|
||||
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"Starting new download for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
|
||||
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket,
|
||||
download.TexturePacketCount()));
|
||||
|
||||
// Send initial data
|
||||
ImageDataPacket data = new ImageDataPacket();
|
||||
data.ImageID.Codec = (byte)ImageCodec.J2C;
|
||||
data.ImageID.ID = download.Texture.AssetID;
|
||||
data.ImageID.Packets = (ushort)download.TexturePacketCount();
|
||||
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
|
||||
|
||||
// The first bytes of the image are always sent in the ImageData packet
|
||||
data.ImageData = new ImageDataPacket.ImageDataBlock();
|
||||
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
|
||||
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
|
||||
try
|
||||
{
|
||||
data.ImageData.Data = new byte[imageDataSize];
|
||||
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
|
||||
Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, data, PacketCategory.Texture);
|
||||
|
||||
// Check if ImagePacket packets need to be sent to complete this transfer
|
||||
if (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
// Insert this download into the dictionary
|
||||
lock (CurrentDownloads)
|
||||
CurrentDownloads[texture.AssetID] = download;
|
||||
|
||||
// Send all of the remaining packets
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
delegate(object obj)
|
||||
{
|
||||
while (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
if (download.Priority == 0.0f && download.DiscardLevel == -1)
|
||||
break;
|
||||
|
||||
lock (download)
|
||||
{
|
||||
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
|
||||
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
|
||||
|
||||
ImagePacketPacket transfer = new ImagePacketPacket();
|
||||
transfer.ImageID.ID = texture.AssetID;
|
||||
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
|
||||
transfer.ImageData.Data = new byte[imagePacketSize];
|
||||
|
||||
try
|
||||
{
|
||||
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
|
||||
transfer.ImageData.Data, 0, imagePacketSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
|
||||
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
|
||||
imagePacketSize), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, transfer, PacketCategory.Texture);
|
||||
|
||||
++download.CurrentPacket;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Completed image transfer for " + texture.AssetID.ToString());
|
||||
|
||||
// Transfer is complete, remove the reference
|
||||
lock (CurrentDownloads)
|
||||
CurrentDownloads.Remove(texture.AssetID);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
406
Programs/Simian/SceneExtensions/LLAgents.cs
Normal file
406
Programs/Simian/SceneExtensions/LLAgents.cs
Normal file
@@ -0,0 +1,406 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class LLAgents : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
int currentWearablesSerialNum = -1;
|
||||
int currentAnimSequenceNum = 0;
|
||||
Timer coarseLocationTimer;
|
||||
|
||||
public LLAgents()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, AvatarPropertiesRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentWearablesRequest, AgentWearablesRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentIsNowWearing, AgentIsNowWearingHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentSetAppearance, AgentSetAppearanceHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentCachedTexture, AgentCachedTextureHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentHeightWidth, AgentHeightWidthHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.SoundTrigger, SoundTriggerHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.UUIDNameRequest, UUIDNameRequestHandler);
|
||||
|
||||
if (coarseLocationTimer != null) coarseLocationTimer.Dispose();
|
||||
coarseLocationTimer = new Timer(coarseLocationTimer_Elapsed);
|
||||
coarseLocationTimer.Change(1000, 1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (coarseLocationTimer != null)
|
||||
{
|
||||
coarseLocationTimer.Dispose();
|
||||
coarseLocationTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void AgentAnimationHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentAnimationPacket animPacket = (AgentAnimationPacket)packet;
|
||||
bool changed = false;
|
||||
|
||||
for (int i = 0; i < animPacket.AnimationList.Length; i++)
|
||||
{
|
||||
AgentAnimationPacket.AnimationListBlock block = animPacket.AnimationList[i];
|
||||
|
||||
if (block.StartAnim)
|
||||
{
|
||||
if (agent.Animations.Add(block.AnimID, ref currentAnimSequenceNum))
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (agent.Animations.Remove(block.AnimID))
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
}
|
||||
|
||||
void ViewerEffectHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ViewerEffectPacket incomingEffects = (ViewerEffectPacket)packet;
|
||||
|
||||
ViewerEffect[] outgoingEffects = new ViewerEffect[incomingEffects.Effect.Length];
|
||||
|
||||
for (int i = 0; i < outgoingEffects.Length; i++)
|
||||
{
|
||||
ViewerEffectPacket.EffectBlock block = incomingEffects.Effect[i];
|
||||
outgoingEffects[i] = new ViewerEffect(block.ID, (EffectType)block.Type, block.AgentID,
|
||||
new Color4(block.Color, 0, true), block.Duration, block.TypeData);
|
||||
}
|
||||
|
||||
scene.TriggerEffects(this, outgoingEffects);
|
||||
}
|
||||
|
||||
void AvatarPropertiesRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AvatarPropertiesRequestPacket request = (AvatarPropertiesRequestPacket)packet;
|
||||
|
||||
Agent foundAgent;
|
||||
if (scene.TryGetAgent(request.AgentData.AvatarID, out foundAgent))
|
||||
{
|
||||
AvatarPropertiesReplyPacket reply = new AvatarPropertiesReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.AgentData.AvatarID = request.AgentData.AvatarID;
|
||||
reply.PropertiesData.AboutText = Utils.StringToBytes(foundAgent.Info.ProfileAboutText);
|
||||
reply.PropertiesData.BornOn = Utils.StringToBytes(foundAgent.Info.ProfileBornOn);
|
||||
reply.PropertiesData.CharterMember = new byte[1];
|
||||
reply.PropertiesData.FLAboutText = Utils.StringToBytes(foundAgent.Info.ProfileFirstText);
|
||||
reply.PropertiesData.Flags = (uint)foundAgent.Info.ProfileFlags;
|
||||
reply.PropertiesData.FLImageID = foundAgent.Info.ProfileFirstImage;
|
||||
reply.PropertiesData.ImageID = foundAgent.Info.ProfileImage;
|
||||
reply.PropertiesData.PartnerID = foundAgent.Info.PartnerID;
|
||||
reply.PropertiesData.ProfileURL = Utils.StringToBytes(foundAgent.Info.ProfileURL);
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("AvatarPropertiesRequest for unknown agent " + request.AgentData.AvatarID.ToString(),
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void AgentWearablesRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SendWearables(agent);
|
||||
}
|
||||
|
||||
void AgentIsNowWearingHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentIsNowWearingPacket wearing = (AgentIsNowWearingPacket)packet;
|
||||
|
||||
for (int i = 0; i < wearing.WearableData.Length; i++)
|
||||
{
|
||||
UUID itemID = wearing.WearableData[i].ItemID;
|
||||
|
||||
#region Update Wearables
|
||||
|
||||
switch ((WearableType)wearing.WearableData[i].WearableType)
|
||||
{
|
||||
case WearableType.Shape:
|
||||
agent.Info.ShapeItem = itemID;
|
||||
break;
|
||||
case WearableType.Skin:
|
||||
agent.Info.SkinItem = itemID;
|
||||
break;
|
||||
case WearableType.Hair:
|
||||
agent.Info.HairItem = itemID;
|
||||
break;
|
||||
case WearableType.Eyes:
|
||||
agent.Info.EyesItem = itemID;
|
||||
break;
|
||||
case WearableType.Shirt:
|
||||
agent.Info.ShirtItem = itemID;
|
||||
break;
|
||||
case WearableType.Pants:
|
||||
agent.Info.PantsItem = itemID;
|
||||
break;
|
||||
case WearableType.Shoes:
|
||||
agent.Info.ShoesItem = itemID;
|
||||
break;
|
||||
case WearableType.Socks:
|
||||
agent.Info.SocksItem = itemID;
|
||||
break;
|
||||
case WearableType.Jacket:
|
||||
agent.Info.JacketItem = itemID;
|
||||
break;
|
||||
case WearableType.Gloves:
|
||||
agent.Info.GlovesItem = itemID;
|
||||
break;
|
||||
case WearableType.Undershirt:
|
||||
agent.Info.UndershirtItem = itemID;
|
||||
break;
|
||||
case WearableType.Underpants:
|
||||
agent.Info.UnderpantsItem = itemID;
|
||||
break;
|
||||
case WearableType.Skirt:
|
||||
agent.Info.SkirtItem = itemID;
|
||||
break;
|
||||
}
|
||||
|
||||
#endregion Update Wearables
|
||||
}
|
||||
|
||||
// FIXME: GetCurrentWearables() is a very expensive call, remove it from this debug line
|
||||
Logger.DebugLog("Updated agent wearables, new count: " + GetCurrentWearables(agent).Count);
|
||||
}
|
||||
|
||||
void AgentSetAppearanceHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentSetAppearancePacket set = (AgentSetAppearancePacket)packet;
|
||||
|
||||
Logger.DebugLog("Updating avatar appearance");
|
||||
|
||||
//TODO: Store this for cached bake responses
|
||||
for (int i = 0; i < set.WearableData.Length; i++)
|
||||
{
|
||||
AppearanceManager.TextureIndex index = (AppearanceManager.TextureIndex)set.WearableData[i].TextureIndex;
|
||||
UUID cacheID = set.WearableData[i].CacheID;
|
||||
|
||||
Logger.DebugLog(String.Format("WearableData: {0} is now {1}", index, cacheID));
|
||||
}
|
||||
|
||||
// Create a TextureEntry
|
||||
Primitive.TextureEntry textureEntry = new Primitive.TextureEntry(set.ObjectData.TextureEntry, 0,
|
||||
set.ObjectData.TextureEntry.Length);
|
||||
|
||||
// Create a block of VisualParams
|
||||
byte[] visualParams = new byte[set.VisualParam.Length];
|
||||
for (int i = 0; i < set.VisualParam.Length; i++)
|
||||
visualParams[i] = set.VisualParam[i].ParamValue;
|
||||
|
||||
scene.AgentAppearance(this, agent, textureEntry, visualParams);
|
||||
}
|
||||
|
||||
void AgentCachedTextureHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentCachedTexturePacket cached = (AgentCachedTexturePacket)packet;
|
||||
|
||||
AgentCachedTextureResponsePacket response = new AgentCachedTextureResponsePacket();
|
||||
response.AgentData.AgentID = agent.ID;
|
||||
response.AgentData.SerialNum = cached.AgentData.SerialNum;
|
||||
|
||||
response.WearableData = new AgentCachedTextureResponsePacket.WearableDataBlock[cached.WearableData.Length];
|
||||
|
||||
// TODO: Respond back with actual cache entries if we have them
|
||||
for (int i = 0; i < cached.WearableData.Length; i++)
|
||||
{
|
||||
response.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock();
|
||||
response.WearableData[i].TextureIndex = cached.WearableData[i].TextureIndex;
|
||||
response.WearableData[i].TextureID = UUID.Zero;
|
||||
response.WearableData[i].HostName = Utils.EmptyBytes;
|
||||
}
|
||||
|
||||
response.Header.Zerocoded = true;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void AgentHeightWidthHandler(Packet packet, Agent agent)
|
||||
{
|
||||
//AgentHeightWidthPacket heightWidth = (AgentHeightWidthPacket)packet;
|
||||
|
||||
// TODO: These are the screen size dimensions. Useful when we start doing frustum culling
|
||||
//Logger.Log(String.Format("Agent wants to set height={0}, width={1}",
|
||||
// heightWidth.HeightWidthBlock.Height, heightWidth.HeightWidthBlock.Width), Helpers.LogLevel.Info);
|
||||
}
|
||||
|
||||
void SoundTriggerHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SoundTriggerPacket trigger = (SoundTriggerPacket)packet;
|
||||
TriggerSound(agent, trigger.SoundData.SoundID, trigger.SoundData.Gain);
|
||||
}
|
||||
|
||||
void UUIDNameRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
UUIDNameRequestPacket request = (UUIDNameRequestPacket)packet;
|
||||
|
||||
UUIDNameReplyPacket reply = new UUIDNameReplyPacket();
|
||||
reply.UUIDNameBlock = new UUIDNameReplyPacket.UUIDNameBlockBlock[request.UUIDNameBlock.Length];
|
||||
|
||||
for (int i = 0; i < request.UUIDNameBlock.Length; i++)
|
||||
{
|
||||
UUID id = request.UUIDNameBlock[i].ID;
|
||||
|
||||
reply.UUIDNameBlock[i] = new UUIDNameReplyPacket.UUIDNameBlockBlock();
|
||||
reply.UUIDNameBlock[i].ID = id;
|
||||
|
||||
Agent foundAgent;
|
||||
if (scene.TryGetAgent(id, out foundAgent))
|
||||
{
|
||||
reply.UUIDNameBlock[i].FirstName = Utils.StringToBytes(foundAgent.Info.FirstName);
|
||||
reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(foundAgent.Info.LastName);
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.UUIDNameBlock[i].FirstName = Utils.EmptyBytes;
|
||||
reply.UUIDNameBlock[i].LastName = Utils.EmptyBytes;
|
||||
}
|
||||
}
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void TriggerSound(Agent agent, UUID soundID, float gain)
|
||||
{
|
||||
scene.TriggerSound(this, agent.ID, agent.ID, agent.ID, soundID, agent.Avatar.Prim.Position, gain);
|
||||
}
|
||||
|
||||
void SendWearables(Agent agent)
|
||||
{
|
||||
AgentWearablesUpdatePacket update = new AgentWearablesUpdatePacket();
|
||||
update.AgentData.AgentID = agent.ID;
|
||||
|
||||
Dictionary<WearableType, InventoryItem> wearables = GetCurrentWearables(agent);
|
||||
update.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[wearables.Count];
|
||||
int i = 0;
|
||||
|
||||
foreach (KeyValuePair<WearableType, InventoryItem> kvp in wearables)
|
||||
{
|
||||
update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock();
|
||||
update.WearableData[i].AssetID = kvp.Value.AssetID;
|
||||
update.WearableData[i].ItemID = kvp.Value.ID;
|
||||
update.WearableData[i].WearableType = (byte)kvp.Key;
|
||||
++i;
|
||||
}
|
||||
|
||||
// Technically this should be per-agent, but if the only requirement is that it
|
||||
// increments this is easier
|
||||
update.AgentData.SerialNum = (uint)Interlocked.Increment(ref currentWearablesSerialNum);
|
||||
|
||||
Logger.DebugLog(String.Format("Sending info about {0} wearables", wearables.Count));
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, update, PacketCategory.Asset);
|
||||
}
|
||||
|
||||
Dictionary<WearableType, InventoryItem> GetCurrentWearables(Agent agent)
|
||||
{
|
||||
Dictionary<WearableType, InventoryItem> wearables = new Dictionary<WearableType, InventoryItem>();
|
||||
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Shape, agent.Info.ShapeItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Skin, agent.Info.SkinItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Hair, agent.Info.HairItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Eyes, agent.Info.EyesItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Shirt, agent.Info.ShirtItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Pants, agent.Info.PantsItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Shoes, agent.Info.ShoesItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Socks, agent.Info.SocksItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Jacket, agent.Info.JacketItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Gloves, agent.Info.GlovesItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Undershirt, agent.Info.UndershirtItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Underpants, agent.Info.UnderpantsItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Skirt, agent.Info.SkirtItem);
|
||||
|
||||
return wearables;
|
||||
}
|
||||
|
||||
bool TryAddWearable(UUID agentID, Dictionary<WearableType, InventoryItem> wearables, WearableType type, UUID itemID)
|
||||
{
|
||||
InventoryObject obj;
|
||||
if (itemID != UUID.Zero && scene.Server.Inventory.TryGetInventory(agentID, itemID, out obj) &&
|
||||
obj is InventoryItem)
|
||||
{
|
||||
wearables.Add(type, (InventoryItem)obj);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void coarseLocationTimer_Elapsed(object sender)
|
||||
{
|
||||
// Create lists containing all of the agent blocks
|
||||
List<CoarseLocationUpdatePacket.AgentDataBlock> agentDatas = new List<CoarseLocationUpdatePacket.AgentDataBlock>();
|
||||
List<CoarseLocationUpdatePacket.LocationBlock> agentLocations = new List<CoarseLocationUpdatePacket.LocationBlock>();
|
||||
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
CoarseLocationUpdatePacket.AgentDataBlock dataBlock = new CoarseLocationUpdatePacket.AgentDataBlock();
|
||||
dataBlock.AgentID = agent.ID;
|
||||
CoarseLocationUpdatePacket.LocationBlock locationBlock = new CoarseLocationUpdatePacket.LocationBlock();
|
||||
locationBlock.X = (byte)agent.Avatar.Prim.Position.X;
|
||||
locationBlock.Y = (byte)agent.Avatar.Prim.Position.Y;
|
||||
locationBlock.Z = (byte)((int)agent.Avatar.Prim.Position.Z / 4);
|
||||
|
||||
agentDatas.Add(dataBlock);
|
||||
agentLocations.Add(locationBlock);
|
||||
}
|
||||
);
|
||||
|
||||
// Send location updates out to each agent
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
CoarseLocationUpdatePacket update = new CoarseLocationUpdatePacket();
|
||||
update.Index.Prey = -1;
|
||||
update.Index.You = 0;
|
||||
|
||||
// Count the number of blocks to send out
|
||||
int count = 0;
|
||||
for (int i = 0; i < agentDatas.Count; i++)
|
||||
{
|
||||
if (agentDatas[i].AgentID != agent.ID)
|
||||
++count;
|
||||
}
|
||||
|
||||
update.AgentData = new CoarseLocationUpdatePacket.AgentDataBlock[count];
|
||||
update.Location = new CoarseLocationUpdatePacket.LocationBlock[count];
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < agentDatas.Count; i++)
|
||||
{
|
||||
if (agentDatas[i].AgentID != agent.ID)
|
||||
{
|
||||
update.AgentData[j] = agentDatas[i];
|
||||
update.Location[j] = agentLocations[i];
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, update, PacketCategory.State);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
683
Programs/Simian/SceneExtensions/LLInventory.cs
Normal file
683
Programs/Simian/SceneExtensions/LLInventory.cs
Normal file
@@ -0,0 +1,683 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class LLInventory : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public LLInventory()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.CreateInventoryItem, CreateInventoryItemHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.CreateInventoryFolder, CreateInventoryFolderHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.UpdateInventoryItem, UpdateInventoryItemHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.FetchInventoryDescendents, FetchInventoryDescendentsHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.FetchInventory, FetchInventoryHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.CopyInventoryItem, CopyInventoryItemHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MoveInventoryItem, MoveInventoryItemHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MoveInventoryFolder, MoveInventoryFolderHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.PurgeInventoryDescendents, PurgeInventoryDescendentsHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.DeRezObject, DeRezObjectHandler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void CreateInventoryItemHandler(Packet packet, Agent agent)
|
||||
{
|
||||
CreateInventoryItemPacket create = (CreateInventoryItemPacket)packet;
|
||||
UUID assetID;
|
||||
if (create.InventoryBlock.TransactionID != UUID.Zero)
|
||||
assetID = UUID.Combine(create.InventoryBlock.TransactionID, agent.SecureSessionID);
|
||||
else
|
||||
assetID = UUID.Random();
|
||||
|
||||
UUID parentID = (create.InventoryBlock.FolderID != UUID.Zero) ? create.InventoryBlock.FolderID : agent.Info.InventoryRoot;
|
||||
AssetType assetType = (AssetType)create.InventoryBlock.Type;
|
||||
|
||||
switch (assetType)
|
||||
{
|
||||
case AssetType.Gesture:
|
||||
Logger.Log("Need to create a default gesture asset!", Helpers.LogLevel.Warning);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parentID == UUID.Zero)
|
||||
parentID = agent.Info.InventoryRoot;
|
||||
|
||||
// Create the inventory item
|
||||
InventoryItem item = scene.Server.Inventory.CreateItem(agent.ID, Utils.BytesToString(create.InventoryBlock.Name),
|
||||
"Created in Simian", (InventoryType)create.InventoryBlock.InvType, assetType, assetID, parentID,
|
||||
PermissionMask.All, (PermissionMask)create.InventoryBlock.NextOwnerMask, agent.ID,
|
||||
agent.ID, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID);
|
||||
|
||||
// Send a success response
|
||||
SendItemCreatedPacket(agent, item, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID);
|
||||
}
|
||||
|
||||
void CreateInventoryFolderHandler(Packet packet, Agent agent)
|
||||
{
|
||||
CreateInventoryFolderPacket create = (CreateInventoryFolderPacket)packet;
|
||||
|
||||
UUID folderID = create.FolderData.FolderID;
|
||||
if (folderID == UUID.Zero)
|
||||
folderID = agent.Info.InventoryRoot;
|
||||
|
||||
scene.Server.Inventory.CreateFolder(agent.ID, folderID, Utils.BytesToString(create.FolderData.Name),
|
||||
(AssetType)create.FolderData.Type, create.FolderData.ParentID, agent.ID);
|
||||
}
|
||||
|
||||
void UpdateInventoryItemHandler(Packet packet, Agent agent)
|
||||
{
|
||||
UpdateInventoryItemPacket update = (UpdateInventoryItemPacket)packet;
|
||||
|
||||
// No packet is sent back to the client, we just need to update the
|
||||
// inventory item locally
|
||||
for (int i = 0; i < update.InventoryData.Length; i++)
|
||||
{
|
||||
UpdateInventoryItemPacket.InventoryDataBlock block = update.InventoryData[i];
|
||||
|
||||
InventoryObject obj;
|
||||
if (scene.Server.Inventory.TryGetInventory(agent.ID, block.ItemID, out obj) && obj is InventoryItem)
|
||||
{
|
||||
InventoryItem item = (InventoryItem)obj;
|
||||
|
||||
//item.Permissions.BaseMask = (PermissionMask)block.BaseMask;
|
||||
item.Permissions.BaseMask = PermissionMask.All;
|
||||
//item.Permissions.EveryoneMask = (PermissionMask)block.EveryoneMask;
|
||||
item.Permissions.EveryoneMask = PermissionMask.All;
|
||||
//item.Permissions.GroupMask = (PermissionMask)block.GroupMask;
|
||||
item.Permissions.GroupMask = PermissionMask.All;
|
||||
//item.Permissions.NextOwnerMask = (PermissionMask)block.NextOwnerMask;
|
||||
item.Permissions.NextOwnerMask = PermissionMask.All;
|
||||
//item.Permissions.OwnerMask = (PermissionMask)block.OwnerMask;
|
||||
item.Permissions.OwnerMask = PermissionMask.All;
|
||||
|
||||
//block.CRC;
|
||||
item.CreationDate = Utils.UnixTimeToDateTime(block.CreationDate);
|
||||
item.CreatorID = block.CreatorID;
|
||||
item.Name = Utils.BytesToString(block.Description);
|
||||
item.Flags = block.Flags;
|
||||
item.ParentID = block.FolderID;
|
||||
item.GroupID = block.GroupID;
|
||||
item.GroupOwned = block.GroupOwned;
|
||||
item.InventoryType = (InventoryType)block.InvType;
|
||||
item.Name = Utils.BytesToString(block.Name);
|
||||
item.OwnerID = block.OwnerID;
|
||||
item.SalePrice = block.SalePrice;
|
||||
item.SaleType = (SaleType)block.SaleType;
|
||||
item.AssetType = (AssetType)block.Type;
|
||||
item.AssetID = UUID.Combine(block.TransactionID, agent.SecureSessionID);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"UpdateInventoryItem: CallbackID: {0}, TransactionID: {1}",
|
||||
block.CallbackID, block.TransactionID));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Received an UpdateInventoryItem packet for unknown inventory item " +
|
||||
block.ItemID.ToString(), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FetchInventoryDescendentsHandler(Packet packet, Agent agent)
|
||||
{
|
||||
// A very safe estimate of the fixed minimum packet size
|
||||
const int PACKET_OVERHEAD = 96;
|
||||
|
||||
FetchInventoryDescendentsPacket fetch = (FetchInventoryDescendentsPacket)packet;
|
||||
bool sendFolders = fetch.InventoryData.FetchFolders;
|
||||
bool sendItems = fetch.InventoryData.FetchItems;
|
||||
// TODO: Obey SortOrder
|
||||
//InventorySortOrder order = (InventorySortOrder)fetch.InventoryData.SortOrder;
|
||||
|
||||
// TODO: Use OwnerID
|
||||
// TODO: Do we need to obey InventorySortOrder?
|
||||
InventoryObject invObject;
|
||||
if (scene.Server.Inventory.TryGetInventory(agent.ID, fetch.InventoryData.FolderID, out invObject) && invObject is InventoryFolder)
|
||||
{
|
||||
InventoryFolder folder = (InventoryFolder)invObject;
|
||||
|
||||
int descendCount;
|
||||
int version;
|
||||
|
||||
List<InventoryItem> items = new List<InventoryItem>();
|
||||
List<InventoryFolder> folders = new List<InventoryFolder>();
|
||||
InventoryDescendentsPacket.FolderDataBlock[] folderBlocks;
|
||||
InventoryDescendentsPacket.ItemDataBlock[] itemBlocks;
|
||||
|
||||
lock (folder.Children.Dictionary)
|
||||
{
|
||||
// These two are coupled to the actual items in the dictionary,
|
||||
// so they are set inside the lock
|
||||
descendCount = folder.Children.Count;
|
||||
version = folder.Version;
|
||||
|
||||
if (sendItems || sendFolders)
|
||||
{
|
||||
// Create a list of all of the folders and items under this folder
|
||||
folder.Children.ForEach(
|
||||
delegate(InventoryObject obj)
|
||||
{
|
||||
if (obj is InventoryItem)
|
||||
items.Add((InventoryItem)obj);
|
||||
else
|
||||
folders.Add((InventoryFolder)obj);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (sendFolders)
|
||||
{
|
||||
folderBlocks = new InventoryDescendentsPacket.FolderDataBlock[folders.Count];
|
||||
for (int i = 0; i < folders.Count; i++)
|
||||
{
|
||||
InventoryFolder currentFolder = folders[i];
|
||||
|
||||
folderBlocks[i] = new InventoryDescendentsPacket.FolderDataBlock();
|
||||
folderBlocks[i].FolderID = currentFolder.ID;
|
||||
folderBlocks[i].Name = Utils.StringToBytes(currentFolder.Name);
|
||||
folderBlocks[i].ParentID = currentFolder.ParentID;
|
||||
folderBlocks[i].Type = (sbyte)currentFolder.PreferredType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
folderBlocks = new InventoryDescendentsPacket.FolderDataBlock[0];
|
||||
}
|
||||
|
||||
if (sendItems)
|
||||
{
|
||||
itemBlocks = new InventoryDescendentsPacket.ItemDataBlock[items.Count];
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
InventoryItem currentItem = items[i];
|
||||
|
||||
itemBlocks[i] = new InventoryDescendentsPacket.ItemDataBlock();
|
||||
itemBlocks[i].AssetID = currentItem.AssetID;
|
||||
itemBlocks[i].BaseMask = (uint)currentItem.Permissions.BaseMask;
|
||||
itemBlocks[i].CRC = currentItem.CRC;
|
||||
itemBlocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(currentItem.CreationDate);
|
||||
itemBlocks[i].CreatorID = currentItem.CreatorID;
|
||||
itemBlocks[i].Description = Utils.StringToBytes(currentItem.Description);
|
||||
itemBlocks[i].EveryoneMask = (uint)currentItem.Permissions.EveryoneMask;
|
||||
itemBlocks[i].Flags = currentItem.Flags;
|
||||
itemBlocks[i].FolderID = currentItem.ParentID;
|
||||
itemBlocks[i].GroupID = currentItem.GroupID;
|
||||
itemBlocks[i].GroupMask = (uint)currentItem.Permissions.GroupMask;
|
||||
itemBlocks[i].GroupOwned = currentItem.GroupOwned;
|
||||
itemBlocks[i].InvType = (sbyte)currentItem.InventoryType;
|
||||
itemBlocks[i].ItemID = currentItem.ID;
|
||||
itemBlocks[i].Name = Utils.StringToBytes(currentItem.Name);
|
||||
itemBlocks[i].NextOwnerMask = (uint)currentItem.Permissions.NextOwnerMask;
|
||||
itemBlocks[i].OwnerID = currentItem.OwnerID;
|
||||
itemBlocks[i].OwnerMask = (uint)currentItem.Permissions.OwnerMask;
|
||||
itemBlocks[i].SalePrice = currentItem.SalePrice;
|
||||
itemBlocks[i].SaleType = (byte)currentItem.SaleType;
|
||||
itemBlocks[i].Type = (sbyte)currentItem.AssetType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
itemBlocks = new InventoryDescendentsPacket.ItemDataBlock[0];
|
||||
}
|
||||
|
||||
// FolderDataBlock and ItemDataBlock are both variable and possibly very large,
|
||||
// so we handle the splitting separately. This could be replaced by some custom
|
||||
// splitting
|
||||
if (folderBlocks.Length > 0)
|
||||
{
|
||||
List<int> splitPoints = Helpers.SplitBlocks(folderBlocks, PACKET_OVERHEAD);
|
||||
Logger.DebugLog(String.Format("Sending {0} InventoryDescendents packets containing {1} folders",
|
||||
splitPoints.Count, folderBlocks.Length));
|
||||
|
||||
for (int i = 0; i < splitPoints.Count; i++)
|
||||
{
|
||||
int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
|
||||
folderBlocks.Length - splitPoints[i];
|
||||
|
||||
InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
|
||||
descendents.AgentData.AgentID = agent.ID;
|
||||
descendents.AgentData.FolderID = folder.ID;
|
||||
descendents.AgentData.OwnerID = folder.OwnerID;
|
||||
descendents.AgentData.Descendents = descendCount;
|
||||
descendents.AgentData.Version = version;
|
||||
descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[count];
|
||||
descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
descendents.FolderData[j] = folderBlocks[splitPoints[i] + j];
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, descendents, PacketCategory.Inventory);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog("Sending a single InventoryDescendents for folders");
|
||||
|
||||
InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
|
||||
descendents.AgentData.AgentID = agent.ID;
|
||||
descendents.AgentData.FolderID = folder.ID;
|
||||
descendents.AgentData.OwnerID = folder.OwnerID;
|
||||
descendents.AgentData.Descendents = descendCount;
|
||||
descendents.AgentData.Version = version;
|
||||
descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
|
||||
descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, descendents, PacketCategory.Inventory);
|
||||
}
|
||||
|
||||
if (itemBlocks.Length > 0)
|
||||
{
|
||||
List<int> splitPoints = Helpers.SplitBlocks(itemBlocks, PACKET_OVERHEAD);
|
||||
Logger.DebugLog(String.Format("Sending {0} InventoryDescendents packets containing {1} items",
|
||||
splitPoints.Count, itemBlocks.Length));
|
||||
|
||||
for (int i = 0; i < splitPoints.Count; i++)
|
||||
{
|
||||
int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
|
||||
itemBlocks.Length - splitPoints[i];
|
||||
|
||||
InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
|
||||
descendents.AgentData.AgentID = agent.ID;
|
||||
descendents.AgentData.FolderID = folder.ID;
|
||||
descendents.AgentData.OwnerID = folder.OwnerID;
|
||||
descendents.AgentData.Descendents = descendCount;
|
||||
descendents.AgentData.Version = version;
|
||||
descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
|
||||
descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[count];
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
descendents.ItemData[j] = itemBlocks[splitPoints[i] + j];
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, descendents, PacketCategory.Inventory);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("FetchInventoryDescendents called for an unknown folder " + fetch.InventoryData.FolderID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void FetchInventoryHandler(Packet packet, Agent agent)
|
||||
{
|
||||
// This is probably too large, but better to be on the safe side
|
||||
const int PACKET_OVERHEAD = 32;
|
||||
|
||||
FetchInventoryPacket fetch = (FetchInventoryPacket)packet;
|
||||
|
||||
// Create all of the blocks first. These will be split up into different packets
|
||||
FetchInventoryReplyPacket.InventoryDataBlock[] blocks =
|
||||
new FetchInventoryReplyPacket.InventoryDataBlock[fetch.InventoryData.Length];
|
||||
|
||||
for (int i = 0; i < fetch.InventoryData.Length; i++)
|
||||
{
|
||||
UUID itemID = fetch.InventoryData[i].ItemID;
|
||||
|
||||
blocks[i] = new FetchInventoryReplyPacket.InventoryDataBlock();
|
||||
blocks[i].ItemID = itemID;
|
||||
|
||||
InventoryObject obj;
|
||||
if (scene.Server.Inventory.TryGetInventory(agent.ID, itemID, out obj) && obj is InventoryItem)
|
||||
{
|
||||
InventoryItem item = (InventoryItem)obj;
|
||||
|
||||
blocks[i].AssetID = item.AssetID;
|
||||
blocks[i].BaseMask = (uint)item.Permissions.BaseMask;
|
||||
blocks[i].CRC = item.CRC;
|
||||
blocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
|
||||
blocks[i].CreatorID = item.CreatorID;
|
||||
blocks[i].Description = Utils.StringToBytes(item.Description);
|
||||
blocks[i].EveryoneMask = (uint)item.Permissions.EveryoneMask;
|
||||
blocks[i].Flags = item.Flags;
|
||||
blocks[i].FolderID = item.ParentID;
|
||||
blocks[i].GroupID = item.GroupID;
|
||||
blocks[i].GroupMask = (uint)item.Permissions.GroupMask;
|
||||
blocks[i].GroupOwned = item.GroupOwned;
|
||||
blocks[i].InvType = (sbyte)item.InventoryType;
|
||||
blocks[i].Name = Utils.StringToBytes(item.Name);
|
||||
blocks[i].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
|
||||
blocks[i].OwnerID = item.OwnerID;
|
||||
blocks[i].OwnerMask = (uint)item.Permissions.OwnerMask;
|
||||
blocks[i].SalePrice = item.SalePrice;
|
||||
blocks[i].SaleType = (byte)item.SaleType;
|
||||
blocks[i].Type = (sbyte)item.AssetType;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("FetchInventory called for an unknown item " + itemID.ToString(),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
blocks[i].Name = Utils.EmptyBytes;
|
||||
blocks[i].Description = Utils.EmptyBytes;
|
||||
}
|
||||
}
|
||||
|
||||
// Split the blocks up into multiple packets
|
||||
List<int> splitPoints = Helpers.SplitBlocks(blocks, PACKET_OVERHEAD);
|
||||
for (int i = 0; i < splitPoints.Count; i++)
|
||||
{
|
||||
int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
|
||||
blocks.Length - splitPoints[i];
|
||||
|
||||
FetchInventoryReplyPacket reply = new FetchInventoryReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.InventoryData = new FetchInventoryReplyPacket.InventoryDataBlock[count];
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
reply.InventoryData[j] = blocks[splitPoints[i] + j];
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Inventory);
|
||||
}
|
||||
}
|
||||
|
||||
void CopyInventoryItemHandler(Packet packet, Agent agent)
|
||||
{
|
||||
CopyInventoryItemPacket copy = (CopyInventoryItemPacket)packet;
|
||||
|
||||
for (int i = 0; i < copy.InventoryData.Length; i++)
|
||||
{
|
||||
CopyInventoryItemPacket.InventoryDataBlock block = copy.InventoryData[i];
|
||||
|
||||
// TODO: This allows someone to copy objects from another
|
||||
// agent's inventory. Should we allow that, or do any
|
||||
// permission checks?
|
||||
|
||||
// Get the original object
|
||||
InventoryObject obj;
|
||||
if (scene.Server.Inventory.TryGetInventory(agent.ID, block.OldItemID, out obj) && obj is InventoryItem)
|
||||
{
|
||||
InventoryItem item = (InventoryItem)obj;
|
||||
|
||||
// Get the new folder
|
||||
InventoryObject folderObj;
|
||||
if (scene.Server.Inventory.TryGetInventory(agent.ID, block.NewFolderID, out folderObj) && folderObj is InventoryFolder)
|
||||
{
|
||||
string newName = Utils.BytesToString(block.NewName);
|
||||
if (String.IsNullOrEmpty(newName))
|
||||
newName = item.Name;
|
||||
|
||||
// Create the copy
|
||||
InventoryItem newItem = scene.Server.Inventory.CreateItem(agent.ID, newName, item.Description, item.InventoryType,
|
||||
item.AssetType, item.AssetID, folderObj.ID, item.Permissions.OwnerMask, item.Permissions.NextOwnerMask,
|
||||
agent.ID, item.CreatorID, UUID.Zero, block.CallbackID);
|
||||
|
||||
SendItemCreatedPacket(agent, newItem, UUID.Zero, block.CallbackID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("CopyInventoryItem called with an unknown target folder " + block.NewFolderID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("CopyInventoryItem called for an unknown item " + block.OldItemID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveInventoryItemHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MoveInventoryItemPacket move = (MoveInventoryItemPacket)packet;
|
||||
// TODO: What is move.AgentData.Stamp for?
|
||||
|
||||
List<InventoryObject> objs = new List<InventoryObject>(move.InventoryData.Length);
|
||||
|
||||
for (int i = 0; i < move.InventoryData.Length; i++)
|
||||
{
|
||||
MoveInventoryItemPacket.InventoryDataBlock block = move.InventoryData[i];
|
||||
UUID newFolderID = block.FolderID;
|
||||
if (newFolderID == UUID.Zero)
|
||||
newFolderID = agent.Info.InventoryRoot;
|
||||
InventoryObject obj = scene.Server.Inventory.MoveInventory(agent.ID, block.ItemID, newFolderID,
|
||||
Utils.BytesToString(block.NewName), UUID.Zero, 0);
|
||||
|
||||
if (obj != null) objs.Add(obj);
|
||||
}
|
||||
|
||||
SendBulkUpdate(agent, objs, UUID.Zero, 0);
|
||||
}
|
||||
|
||||
void MoveInventoryFolderHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MoveInventoryFolderPacket move = (MoveInventoryFolderPacket)packet;
|
||||
// TODO: What is move.AgentData.Stamp for?
|
||||
|
||||
List<InventoryObject> objs = new List<InventoryObject>(move.InventoryData.Length);
|
||||
|
||||
for (int i = 0; i < move.InventoryData.Length; i++)
|
||||
{
|
||||
MoveInventoryFolderPacket.InventoryDataBlock block = move.InventoryData[i];
|
||||
UUID newFolderID = block.ParentID;
|
||||
if (newFolderID == UUID.Zero)
|
||||
newFolderID = agent.Info.InventoryRoot;
|
||||
InventoryObject obj = scene.Server.Inventory.MoveInventory(agent.ID, block.FolderID, newFolderID,
|
||||
null, UUID.Zero, 0);
|
||||
|
||||
if (obj != null) objs.Add(obj);
|
||||
}
|
||||
|
||||
SendBulkUpdate(agent, objs, UUID.Zero, 0);
|
||||
}
|
||||
|
||||
void PurgeInventoryDescendentsHandler(Packet packet, Agent agent)
|
||||
{
|
||||
PurgeInventoryDescendentsPacket purge = (PurgeInventoryDescendentsPacket)packet;
|
||||
scene.Server.Inventory.PurgeFolder(agent.ID, purge.InventoryData.FolderID);
|
||||
}
|
||||
|
||||
void DeRezObjectHandler(Packet packet, Agent agent)
|
||||
{
|
||||
DeRezObjectPacket derez = (DeRezObjectPacket)packet;
|
||||
DeRezDestination destination = (DeRezDestination)derez.AgentBlock.Destination;
|
||||
|
||||
// TODO: Check permissions
|
||||
for (int i = 0; i < derez.ObjectData.Length; i++)
|
||||
{
|
||||
uint localID = derez.ObjectData[i].ObjectLocalID;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(localID, out obj))
|
||||
{
|
||||
switch (destination)
|
||||
{
|
||||
case DeRezDestination.AgentInventorySave:
|
||||
Logger.Log("DeRezObject: Got an AgentInventorySave, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.AgentInventoryCopy:
|
||||
Logger.Log("DeRezObject: Got an AgentInventorySave, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.TaskInventory:
|
||||
Logger.Log("DeRezObject: Got a TaskInventory, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.Attachment:
|
||||
Logger.Log("DeRezObject: Got an Attachment, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.AgentInventoryTake:
|
||||
Logger.Log("DeRezObject: Got an AgentInventoryTake, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.ForceToGodInventory:
|
||||
Logger.Log("DeRezObject: Got a ForceToGodInventory, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.TrashFolder:
|
||||
InventoryObject invObj;
|
||||
if (scene.Server.Inventory.TryGetInventory(agent.ID, derez.AgentBlock.DestinationID, out invObj) &&
|
||||
invObj is InventoryFolder)
|
||||
{
|
||||
// FIXME: Handle children
|
||||
InventoryFolder trash = (InventoryFolder)invObj;
|
||||
InventoryItem item = scene.Server.Inventory.CreateItem(agent.ID, obj.Prim.Properties.Name,
|
||||
obj.Prim.Properties.Description, InventoryType.Object, AssetType.Object, obj.Prim.ID,
|
||||
trash.ID, PermissionMask.All, PermissionMask.All, agent.ID, obj.Prim.Properties.CreatorID,
|
||||
derez.AgentBlock.TransactionID, 0);
|
||||
scene.ObjectRemove(this, obj.Prim.LocalID);
|
||||
|
||||
SendItemCreatedPacket(agent, item, derez.AgentBlock.TransactionID, 0);
|
||||
Logger.DebugLog(String.Format("Derezzed prim {0} to agent inventory trash", obj.Prim.LocalID));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("DeRezObject: Got a TrashFolder with an invalid trash folder: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
}
|
||||
break;
|
||||
case DeRezDestination.AttachmentToInventory:
|
||||
Logger.Log("DeRezObject: Got an AttachmentToInventory, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.AttachmentExists:
|
||||
Logger.Log("DeRezObject: Got an AttachmentExists, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.ReturnToOwner:
|
||||
Logger.Log("DeRezObject: Got a ReturnToOwner, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
case DeRezDestination.ReturnToLastOwner:
|
||||
Logger.Log("DeRezObject: Got a ReturnToLastOwner, DestID: " +
|
||||
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendItemCreatedPacket(Agent agent, InventoryItem item, UUID transactionID, uint callbackID)
|
||||
{
|
||||
UpdateCreateInventoryItemPacket update = new UpdateCreateInventoryItemPacket();
|
||||
update.AgentData.AgentID = agent.ID;
|
||||
update.AgentData.SimApproved = true;
|
||||
if (transactionID != UUID.Zero)
|
||||
update.AgentData.TransactionID = transactionID;
|
||||
else
|
||||
update.AgentData.TransactionID = UUID.Random();
|
||||
update.InventoryData = new UpdateCreateInventoryItemPacket.InventoryDataBlock[1];
|
||||
update.InventoryData[0] = new UpdateCreateInventoryItemPacket.InventoryDataBlock();
|
||||
update.InventoryData[0].AssetID = item.AssetID;
|
||||
update.InventoryData[0].BaseMask = (uint)item.Permissions.BaseMask;
|
||||
update.InventoryData[0].CallbackID = callbackID;
|
||||
update.InventoryData[0].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
|
||||
update.InventoryData[0].CRC = item.CRC;
|
||||
update.InventoryData[0].CreatorID = item.CreatorID;
|
||||
update.InventoryData[0].Description = Utils.StringToBytes(item.Description);
|
||||
update.InventoryData[0].EveryoneMask = (uint)item.Permissions.EveryoneMask;
|
||||
update.InventoryData[0].Flags = item.Flags;
|
||||
update.InventoryData[0].FolderID = item.ParentID;
|
||||
update.InventoryData[0].GroupID = item.GroupID;
|
||||
update.InventoryData[0].GroupMask = (uint)item.Permissions.GroupMask;
|
||||
update.InventoryData[0].GroupOwned = item.GroupOwned;
|
||||
update.InventoryData[0].InvType = (sbyte)item.InventoryType;
|
||||
update.InventoryData[0].ItemID = item.ID;
|
||||
update.InventoryData[0].Name = Utils.StringToBytes(item.Name);
|
||||
update.InventoryData[0].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
|
||||
update.InventoryData[0].OwnerID = item.OwnerID;
|
||||
update.InventoryData[0].OwnerMask = (uint)item.Permissions.OwnerMask;
|
||||
update.InventoryData[0].SalePrice = item.SalePrice;
|
||||
update.InventoryData[0].SaleType = (byte)item.SaleType;
|
||||
update.InventoryData[0].Type = (sbyte)item.AssetType;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, update, PacketCategory.Inventory);
|
||||
}
|
||||
|
||||
void SendBulkUpdate(Agent agent, List<InventoryObject> objs, UUID transactionID, uint callbackID)
|
||||
{
|
||||
BulkUpdateInventoryPacket update = new BulkUpdateInventoryPacket();
|
||||
update.AgentData.AgentID = agent.ID;
|
||||
update.AgentData.TransactionID = transactionID;
|
||||
|
||||
// Count the number of folders and items
|
||||
int items = 0;
|
||||
int folders = 0;
|
||||
for (int i = 0; i < objs.Count; i++)
|
||||
{
|
||||
if (objs[i] is InventoryItem)
|
||||
++items;
|
||||
else
|
||||
++folders;
|
||||
}
|
||||
|
||||
update.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[folders];
|
||||
update.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[items];
|
||||
|
||||
items = 0;
|
||||
folders = 0;
|
||||
|
||||
for (int i = 0; i < objs.Count; i++)
|
||||
{
|
||||
InventoryObject obj = objs[i];
|
||||
|
||||
if (obj is InventoryItem)
|
||||
{
|
||||
InventoryItem item = (InventoryItem)obj;
|
||||
|
||||
update.ItemData[items] = new BulkUpdateInventoryPacket.ItemDataBlock();
|
||||
update.ItemData[items].AssetID = item.AssetID;
|
||||
update.ItemData[items].BaseMask = (uint)item.Permissions.BaseMask;
|
||||
update.ItemData[items].CallbackID = callbackID;
|
||||
update.ItemData[items].CRC = item.CRC;
|
||||
update.ItemData[items].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
|
||||
update.ItemData[items].CreatorID = item.CreatorID;
|
||||
update.ItemData[items].Description = Utils.StringToBytes(item.Description);
|
||||
update.ItemData[items].EveryoneMask = (uint)item.Permissions.EveryoneMask;
|
||||
update.ItemData[items].Flags = item.Flags;
|
||||
update.ItemData[items].FolderID = item.ParentID;
|
||||
update.ItemData[items].GroupID = item.GroupID;
|
||||
update.ItemData[items].GroupMask = (uint)item.Permissions.GroupMask;
|
||||
update.ItemData[items].GroupOwned = item.GroupOwned;
|
||||
update.ItemData[items].InvType = (sbyte)item.InventoryType;
|
||||
update.ItemData[items].ItemID = item.ID;
|
||||
update.ItemData[items].Name = Utils.StringToBytes(item.Name);
|
||||
update.ItemData[items].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
|
||||
update.ItemData[items].OwnerID = item.OwnerID;
|
||||
update.ItemData[items].OwnerMask = (uint)item.Permissions.OwnerMask;
|
||||
update.ItemData[items].SalePrice = item.SalePrice;
|
||||
update.ItemData[items].SaleType = (byte)item.SaleType;
|
||||
update.ItemData[items].Type = (sbyte)item.InventoryType;
|
||||
|
||||
++items;
|
||||
}
|
||||
else
|
||||
{
|
||||
InventoryFolder folder = (InventoryFolder)obj;
|
||||
|
||||
update.FolderData[folders] = new BulkUpdateInventoryPacket.FolderDataBlock();
|
||||
update.FolderData[folders].FolderID = folder.ID;
|
||||
update.FolderData[folders].Name = Utils.StringToBytes(folder.Name);
|
||||
update.FolderData[folders].ParentID = folder.ParentID;
|
||||
update.FolderData[folders].Type = (sbyte)folder.PreferredType;
|
||||
|
||||
++folders;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Sending bulk update for " + items + " items and " + folders + " folders");
|
||||
scene.UDP.SendPacket(agent.ID, update, PacketCategory.Inventory);
|
||||
}
|
||||
}
|
||||
}
|
||||
595
Programs/Simian/SceneExtensions/LLMap.cs
Normal file
595
Programs/Simian/SceneExtensions/LLMap.cs
Normal file
@@ -0,0 +1,595 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Xml;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
class HyperGridLink
|
||||
{
|
||||
public string RegionName;
|
||||
public ulong RegionHandle;
|
||||
public UUID RegionID;
|
||||
public UUID RegionImage;
|
||||
public int UDPPort;
|
||||
public int RemotingPort;
|
||||
}
|
||||
|
||||
public class LLMap : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public LLMap()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MapLayerRequest, MapLayerRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MapBlockRequest, MapBlockRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.TeleportRequest, TeleportRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.TeleportLocationRequest, TeleportLocationRequestHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void MapLayerRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MapLayerRequestPacket request = (MapLayerRequestPacket)packet;
|
||||
GridLayerType type = (GridLayerType)request.AgentData.Flags;
|
||||
|
||||
MapLayerReplyPacket reply = new MapLayerReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.AgentData.Flags = (uint)type;
|
||||
reply.LayerData = new MapLayerReplyPacket.LayerDataBlock[1];
|
||||
reply.LayerData[0] = new MapLayerReplyPacket.LayerDataBlock();
|
||||
reply.LayerData[0].Bottom = 0;
|
||||
reply.LayerData[0].Left = 0;
|
||||
reply.LayerData[0].Top = UInt16.MaxValue;
|
||||
reply.LayerData[0].Right = UInt16.MaxValue;
|
||||
reply.LayerData[0].ImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void MapBlockRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MapBlockRequestPacket request = (MapBlockRequestPacket)packet;
|
||||
GridLayerType type = (GridLayerType)request.AgentData.Flags;
|
||||
|
||||
MapBlockReplyPacket reply = new MapBlockReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.AgentData.Flags = (uint)type;
|
||||
|
||||
reply.Data = new MapBlockReplyPacket.DataBlock[2];
|
||||
|
||||
reply.Data[0] = new MapBlockReplyPacket.DataBlock();
|
||||
reply.Data[0].Access = (byte)SimAccess.Min;
|
||||
reply.Data[0].Agents = (byte)scene.AgentCount();
|
||||
reply.Data[0].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
|
||||
reply.Data[0].Name = Utils.StringToBytes(scene.RegionName);
|
||||
reply.Data[0].RegionFlags = (uint)scene.RegionFlags;
|
||||
reply.Data[0].WaterHeight = (byte)scene.WaterHeight;
|
||||
reply.Data[0].X = (ushort)scene.RegionX;
|
||||
reply.Data[0].Y = (ushort)scene.RegionY;
|
||||
|
||||
reply.Data[1] = new MapBlockReplyPacket.DataBlock();
|
||||
reply.Data[1].Access = (byte)SimAccess.Min;
|
||||
reply.Data[1].Agents = 0;
|
||||
reply.Data[1].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
|
||||
reply.Data[1].Name = Utils.StringToBytes("HyperGrid Portal to OSGrid");
|
||||
reply.Data[1].RegionFlags = (uint)scene.RegionFlags;
|
||||
reply.Data[1].WaterHeight = (byte)scene.WaterHeight;
|
||||
reply.Data[1].X = (ushort)(scene.RegionX + 1);
|
||||
reply.Data[1].Y = (ushort)scene.RegionY;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void TeleportRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
TeleportRequestPacket request = (TeleportRequestPacket)packet;
|
||||
|
||||
// TODO: Stand the avatar up first
|
||||
|
||||
if (request.Info.RegionID == scene.RegionID)
|
||||
{
|
||||
// Local teleport
|
||||
agent.Avatar.Prim.Position = request.Info.Position;
|
||||
agent.CurrentLookAt = request.Info.LookAt;
|
||||
|
||||
// TODO: Actually adjust the agent's LookAt
|
||||
|
||||
TeleportLocalPacket reply = new TeleportLocalPacket();
|
||||
reply.Info.AgentID = agent.ID;
|
||||
reply.Info.LocationID = 0; // Unused by the client
|
||||
reply.Info.LookAt = agent.CurrentLookAt;
|
||||
reply.Info.Position = agent.Avatar.Prim.Position;
|
||||
// TODO: Need a "Flying" boolean for Agent
|
||||
reply.Info.TeleportFlags = (uint)TeleportFlags.ViaRegionID;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
TeleportFailedPacket reply = new TeleportFailedPacket();
|
||||
reply.Info.AgentID = agent.ID;
|
||||
reply.Info.Reason = Utils.StringToBytes("Unknown region");
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void TeleportLocationRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
TeleportLocationRequestPacket request = (TeleportLocationRequestPacket)packet;
|
||||
|
||||
// TODO: Stand the avatar up first
|
||||
|
||||
if (request.Info.RegionHandle == scene.RegionHandle)
|
||||
{
|
||||
// Local teleport
|
||||
agent.Avatar.Prim.Position = request.Info.Position;
|
||||
agent.CurrentLookAt = request.Info.LookAt;
|
||||
|
||||
TeleportLocalPacket reply = new TeleportLocalPacket();
|
||||
reply.Info.AgentID = agent.ID;
|
||||
reply.Info.LocationID = 0; // Unused by the client
|
||||
reply.Info.LookAt = agent.CurrentLookAt;
|
||||
reply.Info.Position = agent.Avatar.Prim.Position;
|
||||
// TODO: Need a "Flying" boolean for Agent
|
||||
reply.Info.TeleportFlags = (uint)TeleportFlags.ViaLocation;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
else if (request.Info.RegionHandle == Utils.UIntsToLong((scene.RegionX + 1) * 256, scene.RegionY * 256))
|
||||
{
|
||||
// Special case: adjacent simulator is the HyperGrid portal
|
||||
HyperGridTeleport(agent, new Uri("http://osl2.nac.uci.edu:9006/"), request.Info.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
TeleportFailedPacket reply = new TeleportFailedPacket();
|
||||
reply.Info.AgentID = agent.ID;
|
||||
reply.Info.Reason = Utils.StringToBytes("Unknown region");
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
}
|
||||
|
||||
bool HyperGridTeleport(Agent agent, Uri destination, Vector3 destPos)
|
||||
{
|
||||
HyperGridLink link;
|
||||
|
||||
TeleportProgress(agent, "Resolving destination IP address", TeleportFlags.ViaLocation);
|
||||
|
||||
IPHostEntry entry = Dns.GetHostEntry(destination.DnsSafeHost);
|
||||
if (entry.AddressList != null && entry.AddressList.Length >= 1)
|
||||
{
|
||||
TeleportProgress(agent, "Retrieving destination details", TeleportFlags.ViaLocation);
|
||||
|
||||
if (LinkRegion(destination, out link))
|
||||
{
|
||||
TeleportProgress(agent, "Creating foreign agent", TeleportFlags.ViaLocation);
|
||||
|
||||
// This is a crufty part of the HyperGrid protocol. We need to generate a fragment of a UUID
|
||||
// (missing the last four digits) and send that as the caps_path variable. Locally, we expand
|
||||
// that out to http://foreignsim:httpport/CAPS/fragment0000/ and use it as the seed caps path
|
||||
// that is sent to the client
|
||||
UUID seedID = UUID.Random();
|
||||
string seedCapFragment = seedID.ToString().Substring(0, 32);
|
||||
Uri seedCap = new Uri(destination, "/CAPS/" + seedCapFragment + "0000/");
|
||||
|
||||
if (ExpectHyperGridUser(agent, destination, destPos, link, seedCap))
|
||||
{
|
||||
TeleportProgress(agent, "Establishing foreign agent presence", TeleportFlags.ViaLocation);
|
||||
|
||||
if (CreateChildAgent(agent, destination, destPos, link, seedCapFragment))
|
||||
{
|
||||
// Send the final teleport message to the client
|
||||
if (scene.HasRunningEventQueue(agent))
|
||||
{
|
||||
uint x, y;
|
||||
Utils.LongToUInts(link.RegionHandle, out x, out y);
|
||||
x /= 256;
|
||||
y /= 256;
|
||||
Logger.Log(String.Format("HyperGrid teleporting to {0} ({1}, {2}) @ {3}",
|
||||
link.RegionName, x, y, destination), Helpers.LogLevel.Info);
|
||||
|
||||
OSDMap info = new OSDMap();
|
||||
info.Add("AgentID", OSD.FromUUID(agent.ID));
|
||||
info.Add("LocationID", OSD.FromInteger(4)); // Unused by the client
|
||||
info.Add("RegionHandle", OSD.FromULong(link.RegionHandle));
|
||||
info.Add("SeedCapability", OSD.FromUri(seedCap));
|
||||
info.Add("SimAccess", OSD.FromInteger((byte)SimAccess.Min));
|
||||
info.Add("SimIP", OSD.FromBinary(entry.AddressList[0].GetAddressBytes()));
|
||||
info.Add("SimPort", OSD.FromInteger(link.UDPPort));
|
||||
info.Add("TeleportFlags", OSD.FromUInteger((uint)TeleportFlags.ViaLocation));
|
||||
|
||||
OSDArray infoArray = new OSDArray(1);
|
||||
infoArray.Add(info);
|
||||
|
||||
OSDMap teleport = new OSDMap();
|
||||
teleport.Add("Info", infoArray);
|
||||
|
||||
scene.SendEvent(agent, "TeleportFinish", teleport);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("No running EventQueue for " + agent.FullName + ", sending TeleportFinish over UDP",
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
TeleportFinishPacket teleport = new TeleportFinishPacket();
|
||||
teleport.Info.AgentID = agent.ID;
|
||||
teleport.Info.LocationID = 0; // Unused by the client
|
||||
teleport.Info.RegionHandle = link.RegionHandle;
|
||||
teleport.Info.SeedCapability = Utils.StringToBytes(seedCap.ToString());
|
||||
teleport.Info.SimAccess = (byte)SimAccess.Min;
|
||||
teleport.Info.SimIP = Utils.BytesToUInt(entry.AddressList[0].GetAddressBytes());
|
||||
teleport.Info.SimPort = (ushort)link.UDPPort;
|
||||
teleport.Info.TeleportFlags = (uint)TeleportFlags.ViaLocation;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, teleport, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
// Remove the agent from the local scene (will also tear down the UDP connection)
|
||||
//scene.ObjectRemove(this, agent.ID);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinkRegion(Uri destination, out HyperGridLink link)
|
||||
{
|
||||
try
|
||||
{
|
||||
#region Build Request
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
|
||||
request.Method = "POST";
|
||||
request.ContentType = "text/xml";
|
||||
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
using (XmlWriter writer = XmlWriter.Create(memoryStream))
|
||||
{
|
||||
writer.WriteStartElement("methodCall");
|
||||
{
|
||||
writer.WriteElementString("methodName", "link_region");
|
||||
|
||||
writer.WriteStartElement("params");
|
||||
writer.WriteStartElement("param");
|
||||
writer.WriteStartElement("value");
|
||||
writer.WriteStartElement("struct");
|
||||
{
|
||||
WriteStringMember(writer, "region_name", String.Empty);
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
request.ContentLength = memoryStream.Length;
|
||||
|
||||
using (Stream writeStream = request.GetRequestStream())
|
||||
{
|
||||
writeStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
|
||||
}
|
||||
|
||||
#endregion Build Request
|
||||
|
||||
#region Parse Response
|
||||
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings();
|
||||
settings.IgnoreComments = true;
|
||||
settings.IgnoreWhitespace = true;
|
||||
|
||||
using (XmlReader reader = XmlReader.Create(response.GetResponseStream(), settings))
|
||||
{
|
||||
link = new HyperGridLink();
|
||||
|
||||
reader.ReadStartElement("methodResponse");
|
||||
{
|
||||
reader.ReadStartElement("params");
|
||||
reader.ReadStartElement("param");
|
||||
reader.ReadStartElement("value");
|
||||
reader.ReadStartElement("struct");
|
||||
{
|
||||
while (reader.Name == "member")
|
||||
{
|
||||
reader.ReadStartElement("member");
|
||||
{
|
||||
string name = reader.ReadElementContentAsString("name", String.Empty);
|
||||
|
||||
reader.ReadStartElement("value");
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "region_name":
|
||||
link.RegionName = reader.ReadElementContentAsString("string", String.Empty);
|
||||
break;
|
||||
case "handle":
|
||||
string handle = reader.ReadElementContentAsString("string", String.Empty);
|
||||
link.RegionHandle = UInt64.Parse(handle);
|
||||
break;
|
||||
case "uuid":
|
||||
string uuid = reader.ReadElementContentAsString("string", String.Empty);
|
||||
link.RegionID = UUID.Parse(uuid);
|
||||
break;
|
||||
case "internal_port":
|
||||
link.UDPPort = reader.ReadElementContentAsInt("string", String.Empty);
|
||||
break;
|
||||
case "region_image":
|
||||
string imageuuid = reader.ReadElementContentAsString("string", String.Empty);
|
||||
link.RegionImage = UUID.Parse(imageuuid);
|
||||
break;
|
||||
case "remoting_port":
|
||||
link.RemotingPort = reader.ReadElementContentAsInt("string", String.Empty);
|
||||
break;
|
||||
default:
|
||||
Logger.Log("[HyperGrid] Unrecognized response XML chunk: " + reader.ReadInnerXml(),
|
||||
Helpers.LogLevel.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
}
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Parse Response
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
|
||||
}
|
||||
|
||||
link = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExpectHyperGridUser(Agent agent, Uri destination, Vector3 destPos, HyperGridLink link, Uri seedCap)
|
||||
{
|
||||
try
|
||||
{
|
||||
#region Build Request
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
|
||||
request.Method = "POST";
|
||||
request.ContentType = "text/xml";
|
||||
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
using (XmlWriter writer = XmlWriter.Create(memoryStream))
|
||||
{
|
||||
writer.WriteStartElement("methodCall");
|
||||
{
|
||||
writer.WriteElementString("methodName", "expect_hg_user");
|
||||
|
||||
writer.WriteStartElement("params");
|
||||
writer.WriteStartElement("param");
|
||||
writer.WriteStartElement("value");
|
||||
writer.WriteStartElement("struct");
|
||||
{
|
||||
WriteStringMember(writer, "session_id", agent.SessionID.ToString());
|
||||
WriteStringMember(writer, "secure_session_id", agent.SecureSessionID.ToString());
|
||||
WriteStringMember(writer, "firstname", agent.Info.FirstName);
|
||||
WriteStringMember(writer, "lastname", agent.Info.LastName);
|
||||
WriteStringMember(writer, "agent_id", agent.ID.ToString());
|
||||
WriteStringMember(writer, "circuit_code", agent.CircuitCode.ToString());
|
||||
WriteStringMember(writer, "startpos_x", destPos.X.ToString(Utils.EnUsCulture));
|
||||
WriteStringMember(writer, "startpos_y", destPos.Y.ToString(Utils.EnUsCulture));
|
||||
WriteStringMember(writer, "startpos_z", destPos.Z.ToString(Utils.EnUsCulture));
|
||||
WriteStringMember(writer, "caps_path", seedCap.ToString());
|
||||
|
||||
WriteStringMember(writer, "region_uuid", link.RegionID.ToString());
|
||||
//WriteStringMember(writer, "userserver_id", "");
|
||||
//WriteStringMember(writer, "assetserver_id", "");
|
||||
//WriteStringMember(writer, "inventoryserver_id", "");
|
||||
WriteStringMember(writer, "root_folder_id", agent.Info.InventoryRoot.ToString());
|
||||
|
||||
string port = scene.Server.HttpUri.Port.ToString();
|
||||
|
||||
WriteStringMember(writer, "internal_port", port);
|
||||
WriteStringMember(writer, "regionhandle", scene.RegionHandle.ToString());
|
||||
WriteStringMember(writer, "home_address", IPAddress.Loopback.ToString());
|
||||
WriteStringMember(writer, "home_port", port);
|
||||
WriteStringMember(writer, "home_remoting", port);
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
request.ContentLength = memoryStream.Length;
|
||||
|
||||
using (Stream writeStream = request.GetRequestStream())
|
||||
{
|
||||
writeStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
|
||||
}
|
||||
|
||||
#endregion Build Request
|
||||
|
||||
#region Parse Response
|
||||
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings();
|
||||
settings.IgnoreComments = true;
|
||||
settings.IgnoreWhitespace = true;
|
||||
|
||||
using (XmlReader reader = XmlReader.Create(response.GetResponseStream(), settings))
|
||||
{
|
||||
bool success = false;
|
||||
string reason = String.Empty;
|
||||
|
||||
reader.ReadStartElement("methodResponse");
|
||||
{
|
||||
reader.ReadStartElement("params");
|
||||
reader.ReadStartElement("param");
|
||||
reader.ReadStartElement("value");
|
||||
reader.ReadStartElement("struct");
|
||||
{
|
||||
while (reader.Name == "member")
|
||||
{
|
||||
reader.ReadStartElement("member");
|
||||
{
|
||||
string name = reader.ReadElementContentAsString("name", String.Empty);
|
||||
|
||||
reader.ReadStartElement("value");
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "success":
|
||||
success = (reader.ReadElementContentAsString("string", String.Empty).ToUpper() == "TRUE");
|
||||
break;
|
||||
case "reason":
|
||||
reason = reader.ReadElementContentAsString("string", String.Empty);
|
||||
break;
|
||||
default:
|
||||
Logger.Log("[HyperGrid] Unrecognized response XML chunk: " + reader.ReadInnerXml(),
|
||||
Helpers.LogLevel.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
}
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
}
|
||||
reader.ReadEndElement();
|
||||
|
||||
if (!success)
|
||||
Logger.Log("[HyperGrid] Teleport failed, reason: " + reason, Helpers.LogLevel.Warning);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
#endregion Parse Response
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CreateChildAgent(Agent agent, Uri destination, Vector3 destPos, HyperGridLink link, string seedCapFragment)
|
||||
{
|
||||
try
|
||||
{
|
||||
destination = new Uri(destination, "/agent/" + agent.ID.ToString() + "/");
|
||||
|
||||
OSDMap args = new OSDMap();
|
||||
args["agent_id"] = OSD.FromUUID(agent.ID);
|
||||
args["base_folder"] = OSD.FromUUID(UUID.Zero);
|
||||
args["caps_path"] = OSD.FromString(seedCapFragment);
|
||||
args["children_seeds"] = OSD.FromBoolean(false);
|
||||
args["child"] = OSD.FromBoolean(false);
|
||||
args["circuit_code"] = OSD.FromString(agent.CircuitCode.ToString());
|
||||
args["first_name"] = OSD.FromString(agent.Info.FirstName);
|
||||
args["last_name"] = OSD.FromString(agent.Info.LastName);
|
||||
args["inventory_folder"] = OSD.FromUUID(agent.Info.InventoryRoot);
|
||||
args["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID);
|
||||
args["session_id"] = OSD.FromUUID(agent.SessionID);
|
||||
args["start_pos"] = OSD.FromString(destPos.ToString());
|
||||
args["destination_handle"] = OSD.FromString(link.RegionHandle.ToString());
|
||||
|
||||
LitJson.JsonData jsonData = OSDParser.SerializeJson(args);
|
||||
byte[] data = System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson());
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/json";
|
||||
request.ContentLength = data.Length;
|
||||
|
||||
using (Stream requestStream = request.GetRequestStream())
|
||||
{
|
||||
requestStream.Write(data, 0, data.Length);
|
||||
requestStream.Flush();
|
||||
}
|
||||
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
bool success = false;
|
||||
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
Boolean.TryParse(reader.ReadToEnd(), out success);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TeleportProgress(Agent agent, string message, TeleportFlags flags)
|
||||
{
|
||||
TeleportProgressPacket progress = new TeleportProgressPacket();
|
||||
progress.AgentData.AgentID = agent.ID;
|
||||
progress.Info.Message = Utils.StringToBytes(message);
|
||||
progress.Info.TeleportFlags = (uint)flags;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, progress, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
static void WriteStringMember(XmlWriter writer, string name, string value)
|
||||
{
|
||||
writer.WriteStartElement("member");
|
||||
{
|
||||
writer.WriteElementString("name", name);
|
||||
|
||||
writer.WriteStartElement("value");
|
||||
{
|
||||
writer.WriteElementString("string", value);
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
146
Programs/Simian/SceneExtensions/LLMessaging.cs
Normal file
146
Programs/Simian/SceneExtensions/LLMessaging.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class LLMessaging : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public LLMessaging()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ImprovedInstantMessage, ImprovedInstantMessageHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void ChatFromViewerHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ChatFromViewerPacket viewerChat = (ChatFromViewerPacket)packet;
|
||||
|
||||
scene.ObjectChat(this, agent.ID, agent.ID, ChatAudibleLevel.Fully, (ChatType)viewerChat.ChatData.Type,
|
||||
ChatSourceType.Agent, agent.FullName, agent.Avatar.GetSimulatorPosition(), viewerChat.ChatData.Channel,
|
||||
Utils.BytesToString(viewerChat.ChatData.Message));
|
||||
}
|
||||
|
||||
void ImprovedInstantMessageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet;
|
||||
InstantMessageDialog dialog = (InstantMessageDialog)im.MessageBlock.Dialog;
|
||||
|
||||
if (dialog == InstantMessageDialog.MessageFromAgent)
|
||||
{
|
||||
// HACK: Only works for agents currently online
|
||||
Agent recipient;
|
||||
if (scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
|
||||
{
|
||||
// FIXME: Look into the fields we are setting to default values
|
||||
ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
|
||||
sendIM.MessageBlock.RegionID = scene.RegionID;
|
||||
sendIM.MessageBlock.ParentEstateID = 1;
|
||||
sendIM.MessageBlock.FromGroup = false;
|
||||
sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.FullName);
|
||||
sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
|
||||
sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
|
||||
sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
|
||||
sendIM.MessageBlock.ID = agent.ID;
|
||||
sendIM.MessageBlock.Message = im.MessageBlock.Message;
|
||||
sendIM.MessageBlock.BinaryBucket = Utils.EmptyBytes;
|
||||
sendIM.MessageBlock.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
sendIM.MessageBlock.Position = agent.Avatar.GetSimulatorPosition();
|
||||
|
||||
sendIM.AgentData.AgentID = agent.ID;
|
||||
|
||||
scene.UDP.SendPacket(recipient.ID, sendIM, PacketCategory.Messaging);
|
||||
}
|
||||
}
|
||||
else if (dialog == InstantMessageDialog.FriendshipOffered ||
|
||||
dialog == InstantMessageDialog.FriendshipAccepted ||
|
||||
dialog == InstantMessageDialog.FriendshipDeclined)
|
||||
{
|
||||
// HACK: Only works for agents currently online
|
||||
Agent recipient;
|
||||
if (scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
|
||||
{
|
||||
ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
|
||||
sendIM.MessageBlock.RegionID = scene.RegionID;
|
||||
sendIM.MessageBlock.ParentEstateID = 1;
|
||||
sendIM.MessageBlock.FromGroup = false;
|
||||
sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.FullName);
|
||||
sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
|
||||
sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
|
||||
sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
|
||||
sendIM.MessageBlock.ID = agent.ID;
|
||||
sendIM.MessageBlock.Message = im.MessageBlock.Message;
|
||||
sendIM.MessageBlock.BinaryBucket = Utils.EmptyBytes;
|
||||
sendIM.MessageBlock.Timestamp = 0;
|
||||
sendIM.MessageBlock.Position = agent.Avatar.GetSimulatorPosition();
|
||||
|
||||
sendIM.AgentData.AgentID = agent.ID;
|
||||
|
||||
scene.UDP.SendPacket(recipient.ID, sendIM, PacketCategory.Transaction);
|
||||
|
||||
if (dialog == InstantMessageDialog.FriendshipAccepted)
|
||||
{
|
||||
bool receiverOnline = scene.ContainsObject(agent.ID);
|
||||
bool senderOnline = scene.ContainsObject(recipient.ID);
|
||||
|
||||
if (receiverOnline)
|
||||
{
|
||||
if (senderOnline)
|
||||
{
|
||||
OnlineNotificationPacket notify = new OnlineNotificationPacket();
|
||||
notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
|
||||
notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
|
||||
notify.AgentBlock[0].AgentID = agent.ID;
|
||||
scene.UDP.SendPacket(recipient.ID, notify, PacketCategory.State);
|
||||
}
|
||||
else
|
||||
{
|
||||
OfflineNotificationPacket notify = new OfflineNotificationPacket();
|
||||
notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
|
||||
notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
|
||||
notify.AgentBlock[0].AgentID = agent.ID;
|
||||
scene.UDP.SendPacket(recipient.ID, notify, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
|
||||
if (senderOnline)
|
||||
{
|
||||
if (receiverOnline)
|
||||
{
|
||||
OnlineNotificationPacket notify = new OnlineNotificationPacket();
|
||||
notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
|
||||
notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
|
||||
notify.AgentBlock[0].AgentID = recipient.ID;
|
||||
scene.UDP.SendPacket(agent.ID, notify, PacketCategory.State);
|
||||
}
|
||||
else
|
||||
{
|
||||
OfflineNotificationPacket notify = new OfflineNotificationPacket();
|
||||
notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
|
||||
notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
|
||||
notify.AgentBlock[0].AgentID = recipient.ID;
|
||||
scene.UDP.SendPacket(agent.ID, notify, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
67
Programs/Simian/SceneExtensions/LLMoney.cs
Normal file
67
Programs/Simian/SceneExtensions/LLMoney.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class LLMoney : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public LLMoney()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MoneyBalanceRequest, MoneyBalanceRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MoneyTransferRequest, MoneyTransferRequestHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void SendBalance(Agent agent, UUID transactionID, string message)
|
||||
{
|
||||
MoneyBalanceReplyPacket reply = new MoneyBalanceReplyPacket();
|
||||
reply.MoneyData.AgentID = agent.ID;
|
||||
reply.MoneyData.MoneyBalance = agent.Info.Balance;
|
||||
reply.MoneyData.TransactionID = transactionID;
|
||||
reply.MoneyData.Description = Utils.StringToBytes(message);
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void MoneyBalanceRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MoneyBalanceRequestPacket request = (MoneyBalanceRequestPacket)packet;
|
||||
|
||||
SendBalance(agent, request.MoneyData.TransactionID, String.Empty);
|
||||
}
|
||||
|
||||
void MoneyTransferRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MoneyTransferRequestPacket request = (MoneyTransferRequestPacket)packet;
|
||||
|
||||
if (request.MoneyData.Amount < 0 || request.MoneyData.Amount > agent.Info.Balance)
|
||||
return;
|
||||
|
||||
// HACK: Only works for sending money to someone who is online
|
||||
Agent recipient;
|
||||
if (scene.TryGetAgent(request.MoneyData.DestID, out recipient))
|
||||
{
|
||||
agent.Info.Balance -= request.MoneyData.Amount;
|
||||
recipient.Info.Balance += request.MoneyData.Amount;
|
||||
|
||||
SendBalance(agent, UUID.Zero, String.Format("You paid L${0} to {1}.", request.MoneyData.Amount, recipient.FullName));
|
||||
SendBalance(agent, UUID.Zero, String.Format("{1} paid you L${0}.", request.MoneyData.Amount, agent.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
518
Programs/Simian/SceneExtensions/Movement.cs
Normal file
518
Programs/Simian/SceneExtensions/Movement.cs
Normal file
@@ -0,0 +1,518 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.Rendering;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class Movement : IExtension<ISceneProvider>
|
||||
{
|
||||
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
|
||||
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
|
||||
const float GRAVITY = 9.8f; //meters/sec
|
||||
const float WALK_SPEED = 3f; //meters/sec
|
||||
const float RUN_SPEED = 5f; //meters/sec
|
||||
const float FLY_SPEED = 10f; //meters/sec
|
||||
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
||||
const float FALL_FORGIVENESS = .25f; //fall buffer in meters
|
||||
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
||||
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec
|
||||
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
||||
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
||||
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
||||
|
||||
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
|
||||
static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f);
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
ISceneProvider scene;
|
||||
Timer updateTimer;
|
||||
long lastTick;
|
||||
|
||||
public int LastTick
|
||||
{
|
||||
get { return (int) Interlocked.Read(ref lastTick); }
|
||||
set { Interlocked.Exchange(ref lastTick, value); }
|
||||
}
|
||||
|
||||
public Movement()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
||||
|
||||
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
||||
LastTick = Environment.TickCount;
|
||||
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
|
||||
{
|
||||
bool forceMeshing = false;
|
||||
bool forceTransform = false;
|
||||
|
||||
if ((update & UpdateFlags.Scale) != 0 ||
|
||||
(update & UpdateFlags.Position) != 0 ||
|
||||
(update & UpdateFlags.Rotation) != 0)
|
||||
{
|
||||
forceTransform = true;
|
||||
}
|
||||
|
||||
if ((update & UpdateFlags.PrimData) != 0)
|
||||
{
|
||||
forceMeshing = true;
|
||||
}
|
||||
|
||||
// TODO: This doesn't update children prims when their parents move
|
||||
obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform);
|
||||
}
|
||||
|
||||
void UpdateTimer_Elapsed(object sender)
|
||||
{
|
||||
int tick = Environment.TickCount;
|
||||
float seconds = (float)((tick - LastTick) / 1000f);
|
||||
LastTick = tick;
|
||||
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
|
||||
return;
|
||||
|
||||
bool animsChanged = false;
|
||||
|
||||
// Create forward and left vectors from the current avatar rotation
|
||||
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
|
||||
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
||||
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
||||
|
||||
// Check control flags
|
||||
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
||||
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
|
||||
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
|
||||
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
|
||||
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
|
||||
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
|
||||
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
|
||||
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
|
||||
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
||||
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
|
||||
|
||||
// direction in which the avatar is trying to move
|
||||
Vector3 move = Vector3.Zero;
|
||||
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
||||
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
||||
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
||||
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
||||
if (heldUp) { move.Z += 1; }
|
||||
if (heldDown) { move.Z -= 1; }
|
||||
|
||||
// is the avatar trying to move?
|
||||
bool moving = move != Vector3.Zero;
|
||||
bool jumping = agent.TickJump != 0;
|
||||
|
||||
// 2-dimensional speed multipler
|
||||
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
||||
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
||||
speed /= SQRT_TWO;
|
||||
|
||||
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
|
||||
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
agentPosition += (move * speed);
|
||||
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
if (!flying && newFloor != oldFloor)
|
||||
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
||||
|
||||
//HACK: distance from avatar center to the bottom of its feet
|
||||
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
|
||||
|
||||
float lowerLimit = newFloor + distanceFromFloor;
|
||||
|
||||
//"bridge" physics
|
||||
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
|
||||
{
|
||||
//start ray at our feet
|
||||
Vector3 rayStart = new Vector3(
|
||||
agent.Avatar.Prim.Position.X,
|
||||
agent.Avatar.Prim.Position.Y,
|
||||
agent.Avatar.Prim.Position.Z - distanceFromFloor
|
||||
);
|
||||
|
||||
//end ray at 0.01m below our feet
|
||||
Vector3 rayEnd = new Vector3(
|
||||
rayStart.X,
|
||||
rayStart.Y,
|
||||
rayStart.Z - 0.01f
|
||||
);
|
||||
|
||||
scene.ForEachObject(delegate(SimulationObject obj)
|
||||
{
|
||||
//HACK: check nearby objects (what did you expect, octree?)
|
||||
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
|
||||
{
|
||||
Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
|
||||
|
||||
if (collision != rayEnd) //we collided!
|
||||
{
|
||||
//check if we are any higher than before
|
||||
float height = collision.Z + distanceFromFloor;
|
||||
if (height > lowerLimit) lowerLimit = height;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Z acceleration resulting from gravity
|
||||
float gravity = 0f;
|
||||
|
||||
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
|
||||
|
||||
if (flying)
|
||||
{
|
||||
agent.TickFall = 0;
|
||||
agent.TickJump = 0;
|
||||
|
||||
//velocity falloff while flying
|
||||
agent.Avatar.Prim.Velocity.X *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Z *= 0.33f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z == lowerLimit)
|
||||
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //flying horizontally
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
|
||||
{ //falling, floating, or landing from a jump
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
|
||||
{ //above water
|
||||
|
||||
//override controls while drifting
|
||||
move = Vector3.Zero;
|
||||
|
||||
//keep most of our horizontal inertia
|
||||
agent.Avatar.Prim.Velocity.X *= 0.975f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.975f;
|
||||
|
||||
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
||||
|
||||
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
|
||||
{ //just started falling
|
||||
agent.TickFall = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
|
||||
|
||||
if (!jumping)
|
||||
{ //falling
|
||||
if (fallElapsed > FALL_DELAY)
|
||||
{ //falling long enough to trigger the animation
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
|
||||
{ //at the water line
|
||||
|
||||
gravity = 0f;
|
||||
agent.Avatar.Prim.Velocity *= 0.5f;
|
||||
agent.Avatar.Prim.Velocity.Z = 0f;
|
||||
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
|
||||
|
||||
if (move.Z > 0)
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.X != 0 || move.Y != 0)
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //underwater
|
||||
|
||||
gravity = 0f; //buoyant
|
||||
agent.Avatar.Prim.Velocity *= 0.5f * seconds;
|
||||
agent.Avatar.Prim.Velocity.Z += 0.75f * seconds;
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //on the ground
|
||||
|
||||
agent.TickFall = 0;
|
||||
|
||||
//friction
|
||||
agent.Avatar.Prim.Acceleration *= 0.2f;
|
||||
agent.Avatar.Prim.Velocity *= 0.2f;
|
||||
|
||||
agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
|
||||
if (move.Z > 0)
|
||||
{ //jumping
|
||||
if (!jumping)
|
||||
{ //begin prejump
|
||||
move.Z = 0; //override Z control
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.TickJump = Environment.TickCount;
|
||||
}
|
||||
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
|
||||
{ //start actual jump
|
||||
|
||||
if (agent.TickJump == -1)
|
||||
{
|
||||
//already jumping! end current jump
|
||||
agent.TickJump = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
|
||||
|
||||
agent.TickJump = -1; //flag that we are currently jumping
|
||||
}
|
||||
else move.Z = 0; //override Z control
|
||||
}
|
||||
|
||||
else
|
||||
{ //not jumping
|
||||
|
||||
agent.TickJump = 0;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //not walking
|
||||
|
||||
if (move.Z < 0)
|
||||
{ //crouchwalking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
if (moving)
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = move * speed;
|
||||
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = maxVel;
|
||||
}
|
||||
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
|
||||
|
||||
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
|
||||
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = maxVel;
|
||||
|
||||
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
|
||||
|
||||
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
|
||||
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
|
||||
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void AgentRequestSitHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentRequestSitPacket request = (AgentRequestSitPacket)packet;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(request.TargetObject.TargetID, out obj))
|
||||
{
|
||||
agent.RequestedSitTarget = request.TargetObject.TargetID;
|
||||
agent.RequestedSitOffset = request.TargetObject.Offset;
|
||||
|
||||
AvatarSitResponsePacket response = new AvatarSitResponsePacket();
|
||||
response.SitObject.ID = request.TargetObject.TargetID;
|
||||
response.SitTransform.AutoPilot = true;
|
||||
response.SitTransform.CameraAtOffset = Vector3.Zero;
|
||||
response.SitTransform.CameraEyeOffset = Vector3.Zero;
|
||||
response.SitTransform.ForceMouselook = false;
|
||||
response.SitTransform.SitPosition = request.TargetObject.Offset;
|
||||
response.SitTransform.SitRotation = obj.SitRotation;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.State);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: send error
|
||||
}
|
||||
}
|
||||
|
||||
void AgentSitHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentSitPacket sit = (AgentSitPacket)packet;
|
||||
|
||||
if (agent.RequestedSitTarget != UUID.Zero)
|
||||
{
|
||||
SimulationObject obj;
|
||||
SimulationObject avObj;
|
||||
if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj))
|
||||
{
|
||||
agent.Avatar.Prim.Flags &= ~PrimFlags.Physics;
|
||||
agent.Avatar.Prim.ParentID = obj.Prim.LocalID;
|
||||
agent.Avatar.Prim.Position = new Vector3(
|
||||
obj.Prim.Scale.X * 0.5f,
|
||||
obj.Prim.Scale.Z * 0.5f,
|
||||
agent.Avatar.Prim.Scale.Z * 0.33f);
|
||||
|
||||
scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
|
||||
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
|
||||
scene.Avatars.SetDefaultAnimation(agent, Animations.SIT);
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: send error
|
||||
}
|
||||
|
||||
agent.RequestedSitTarget = UUID.Zero;
|
||||
agent.RequestedSitOffset = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(agent.ID, out obj))
|
||||
{
|
||||
if (agent.Avatar.Prim.ParentID == 0)
|
||||
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
|
||||
|
||||
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
|
||||
agent.State = (AgentState)update.AgentData.State;
|
||||
agent.HideTitle = update.AgentData.Flags != 0;
|
||||
|
||||
// Check for standing up
|
||||
SimulationObject parent;
|
||||
if (scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) &&
|
||||
agent.Avatar.Prim.ParentID > 0 &&
|
||||
(agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP)
|
||||
{
|
||||
agent.Avatar.Prim.Position = parent.Prim.Position
|
||||
+ Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation))
|
||||
+ Vector3.UnitZ;
|
||||
|
||||
agent.Avatar.Prim.ParentID = 0;
|
||||
|
||||
scene.Avatars.SetDefaultAnimation(agent, Animations.STAND);
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
|
||||
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
|
||||
}
|
||||
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAlwaysRunHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
|
||||
|
||||
agent.Running = run.AgentData.AlwaysRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
685
Programs/Simian/SceneExtensions/ObjectManager.cs
Normal file
685
Programs/Simian/SceneExtensions/ObjectManager.cs
Normal file
@@ -0,0 +1,685 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Rendering;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ObjectManager : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public ObjectManager()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectAdd, ObjectAddHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectAttach, ObjectAttachHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectDuplicate, ObjectDuplicateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectSelect, ObjectSelectHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectDeselect, ObjectDeselectHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectLink, ObjectLinkHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectDelink, ObjectDelinkHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectShape, ObjectShapeHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectFlagUpdate, ObjectFlagUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectExtraParams, ObjectExtraParamsHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectImage, ObjectImageHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.Undo, UndoHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.Redo, RedoHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, MultipleObjectUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, RequestObjectPropertiesFamilyHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectAddHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectAddPacket add = (ObjectAddPacket)packet;
|
||||
|
||||
Vector3 position = Vector3.Zero;
|
||||
Vector3 scale = add.ObjectData.Scale;
|
||||
PCode pcode = (PCode)add.ObjectData.PCode;
|
||||
PrimFlags flags = (PrimFlags)add.ObjectData.AddFlags;
|
||||
//bool bypassRaycast = (add.ObjectData.BypassRaycast == 1);
|
||||
bool rayEndIsIntersection = (add.ObjectData.RayEndIsIntersection == 1);
|
||||
|
||||
#region Position Calculation
|
||||
|
||||
if (rayEndIsIntersection)
|
||||
{
|
||||
// HACK: Blindly trust where the client tells us to place
|
||||
position = add.ObjectData.RayEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (add.ObjectData.RayTargetID != UUID.Zero)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(add.ObjectData.RayTargetID, out obj))
|
||||
{
|
||||
// Test for a collision with the specified object
|
||||
position = scene.Physics.ObjectCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd, obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (position == Vector3.Zero)
|
||||
{
|
||||
// Test for a collision with the entire scene
|
||||
position = FullSceneCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Position lies on the face of another surface, either terrain of an object.
|
||||
// Back up along the ray so we are not colliding with the mesh.
|
||||
// HACK: This is really cheesy and should be done by a collision system
|
||||
Vector3 rayDir = Vector3.Normalize(add.ObjectData.RayEnd - add.ObjectData.RayStart);
|
||||
position -= rayDir * scale;
|
||||
|
||||
#endregion Position Calculation
|
||||
|
||||
#region Foliage Handling
|
||||
|
||||
// Set all foliage to phantom
|
||||
if (pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree)
|
||||
{
|
||||
flags |= PrimFlags.Phantom;
|
||||
|
||||
if (pcode != PCode.Grass)
|
||||
{
|
||||
// Resize based on the foliage type
|
||||
Tree tree = (Tree)add.ObjectData.State;
|
||||
|
||||
switch (tree)
|
||||
{
|
||||
case Tree.Cypress1:
|
||||
case Tree.Cypress2:
|
||||
scale = new Vector3(4f, 4f, 10f);
|
||||
break;
|
||||
default:
|
||||
scale = new Vector3(4f, 4f, 4f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Foliage Handling
|
||||
|
||||
// Create an object
|
||||
Primitive prim = new Primitive();
|
||||
|
||||
// TODO: Security check
|
||||
prim.GroupID = add.AgentData.GroupID;
|
||||
prim.ID = UUID.Random();
|
||||
prim.MediaURL = String.Empty;
|
||||
prim.OwnerID = agent.ID;
|
||||
prim.Position = position;
|
||||
|
||||
prim.PrimData.Material = (Material)add.ObjectData.Material;
|
||||
prim.PrimData.PathCurve = (PathCurve)add.ObjectData.PathCurve;
|
||||
prim.PrimData.ProfileCurve = (ProfileCurve)add.ObjectData.ProfileCurve;
|
||||
prim.PrimData.PathBegin = Primitive.UnpackBeginCut(add.ObjectData.PathBegin);
|
||||
prim.PrimData.PathEnd = Primitive.UnpackEndCut(add.ObjectData.PathEnd);
|
||||
prim.PrimData.PathScaleX = Primitive.UnpackPathScale(add.ObjectData.PathScaleX);
|
||||
prim.PrimData.PathScaleY = Primitive.UnpackPathScale(add.ObjectData.PathScaleY);
|
||||
prim.PrimData.PathShearX = Primitive.UnpackPathShear((sbyte)add.ObjectData.PathShearX);
|
||||
prim.PrimData.PathShearY = Primitive.UnpackPathShear((sbyte)add.ObjectData.PathShearY);
|
||||
prim.PrimData.PathTwist = Primitive.UnpackPathTwist(add.ObjectData.PathTwist);
|
||||
prim.PrimData.PathTwistBegin = Primitive.UnpackPathTwist(add.ObjectData.PathTwistBegin);
|
||||
prim.PrimData.PathRadiusOffset = Primitive.UnpackPathTwist(add.ObjectData.PathRadiusOffset);
|
||||
prim.PrimData.PathTaperX = Primitive.UnpackPathTaper(add.ObjectData.PathTaperX);
|
||||
prim.PrimData.PathTaperY = Primitive.UnpackPathTaper(add.ObjectData.PathTaperY);
|
||||
prim.PrimData.PathRevolutions = Primitive.UnpackPathRevolutions(add.ObjectData.PathRevolutions);
|
||||
prim.PrimData.PathSkew = Primitive.UnpackPathTwist(add.ObjectData.PathSkew);
|
||||
prim.PrimData.ProfileBegin = Primitive.UnpackBeginCut(add.ObjectData.ProfileBegin);
|
||||
prim.PrimData.ProfileEnd = Primitive.UnpackEndCut(add.ObjectData.ProfileEnd);
|
||||
prim.PrimData.ProfileHollow = Primitive.UnpackProfileHollow(add.ObjectData.ProfileHollow);
|
||||
prim.PrimData.PCode = pcode;
|
||||
|
||||
prim.Properties = new Primitive.ObjectProperties();
|
||||
prim.Properties.CreationDate = DateTime.Now;
|
||||
prim.Properties.CreatorID = agent.ID;
|
||||
prim.Properties.Description = String.Empty;
|
||||
prim.Properties.GroupID = add.AgentData.GroupID;
|
||||
prim.Properties.LastOwnerID = agent.ID;
|
||||
prim.Properties.Name = "New Object";
|
||||
prim.Properties.ObjectID = prim.ID;
|
||||
prim.Properties.OwnerID = prim.OwnerID;
|
||||
prim.Properties.Permissions = scene.Server.Permissions.GetDefaultPermissions();
|
||||
prim.Properties.SalePrice = 10;
|
||||
|
||||
prim.RegionHandle = scene.RegionHandle;
|
||||
prim.Rotation = add.ObjectData.Rotation;
|
||||
prim.Scale = scale;
|
||||
prim.TextColor = Color4.Black;
|
||||
|
||||
// Add this prim to the object database
|
||||
SimulationObject simObj = new SimulationObject(prim, scene);
|
||||
scene.ObjectAddOrUpdate(this, simObj, agent.ID, 0, flags, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void ObjectAttachHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectAttachPacket attach = (ObjectAttachPacket)packet;
|
||||
|
||||
for (int i = 0; i < attach.ObjectData.Length; i++)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(attach.ObjectData[i].ObjectLocalID, out obj))
|
||||
{
|
||||
obj.BeforeAttachmentRotation = attach.ObjectData[i].Rotation;
|
||||
|
||||
obj.Prim.ParentID = agent.Avatar.Prim.LocalID;
|
||||
obj.Prim.Position = obj.AttachmentPosition;
|
||||
obj.Prim.Rotation = obj.AttachmentRotation;
|
||||
|
||||
AttachmentPoint point = (AttachmentPoint)attach.AgentData.AttachmentPoint;
|
||||
obj.Prim.PrimData.AttachmentPoint = (point == AttachmentPoint.Default ? obj.LastAttachmentPoint : point);
|
||||
|
||||
// Send an update out to everyone
|
||||
scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, obj.Prim.Flags,
|
||||
UpdateFlags.ParentID | UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.AttachmentPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDuplicateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectDuplicatePacket duplicate = (ObjectDuplicatePacket)packet;
|
||||
|
||||
PrimFlags flags = (PrimFlags)duplicate.SharedData.DuplicateFlags;
|
||||
Vector3 offset = duplicate.SharedData.Offset;
|
||||
|
||||
for (int i = 0; i < duplicate.ObjectData.Length; i++)
|
||||
{
|
||||
uint dupeID = duplicate.ObjectData[i].ObjectLocalID;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(dupeID, out obj))
|
||||
{
|
||||
SimulationObject newObj = new SimulationObject(obj);
|
||||
newObj.Prim.Position += offset;
|
||||
newObj.Prim.ID = UUID.Zero;
|
||||
newObj.Prim.LocalID = 0;
|
||||
newObj.Prim.Properties.CreationDate = DateTime.Now;
|
||||
|
||||
scene.ObjectAddOrUpdate(this, newObj, agent.ID, 0, flags, UpdateFlags.FullUpdate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("ObjectDuplicate sent for missing object " + dupeID,
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
KillObjectPacket kill = new KillObjectPacket();
|
||||
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
|
||||
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
|
||||
kill.ObjectData[0].ID = dupeID;
|
||||
scene.UDP.SendPacket(agent.ID, kill, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectSelectHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectSelectPacket select = (ObjectSelectPacket)packet;
|
||||
|
||||
for (int i = 0; i < select.ObjectData.Length; i++)
|
||||
{
|
||||
ObjectPropertiesPacket properties = new ObjectPropertiesPacket();
|
||||
properties.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[1];
|
||||
properties.ObjectData[0] = new ObjectPropertiesPacket.ObjectDataBlock();
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(select.ObjectData[i].ObjectLocalID, out obj))
|
||||
{
|
||||
//Logger.DebugLog("Selecting object " + obj.Prim.LocalID);
|
||||
|
||||
properties.ObjectData[0].BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask;
|
||||
properties.ObjectData[0].CreationDate = Utils.DateTimeToUnixTime(obj.Prim.Properties.CreationDate);
|
||||
properties.ObjectData[0].CreatorID = obj.Prim.Properties.CreatorID;
|
||||
properties.ObjectData[0].Description = Utils.StringToBytes(obj.Prim.Properties.Description);
|
||||
properties.ObjectData[0].EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask;
|
||||
properties.ObjectData[0].GroupID = obj.Prim.Properties.GroupID;
|
||||
properties.ObjectData[0].GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask;
|
||||
properties.ObjectData[0].LastOwnerID = obj.Prim.Properties.LastOwnerID;
|
||||
properties.ObjectData[0].Name = Utils.StringToBytes(obj.Prim.Properties.Name);
|
||||
properties.ObjectData[0].NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask;
|
||||
properties.ObjectData[0].ObjectID = obj.Prim.ID;
|
||||
properties.ObjectData[0].OwnerID = obj.Prim.Properties.OwnerID;
|
||||
properties.ObjectData[0].OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask;
|
||||
properties.ObjectData[0].OwnershipCost = obj.Prim.Properties.OwnershipCost;
|
||||
properties.ObjectData[0].SalePrice = obj.Prim.Properties.SalePrice;
|
||||
properties.ObjectData[0].SaleType = (byte)obj.Prim.Properties.SaleType;
|
||||
properties.ObjectData[0].SitName = Utils.StringToBytes(obj.Prim.Properties.SitName);
|
||||
properties.ObjectData[0].TextureID = Utils.EmptyBytes; // FIXME: What is this?
|
||||
properties.ObjectData[0].TouchName = Utils.StringToBytes(obj.Prim.Properties.TouchName);
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("ObjectSelect sent for missing object " + select.ObjectData[i].ObjectLocalID,
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
properties.ObjectData[0].Description = Utils.EmptyBytes;
|
||||
properties.ObjectData[0].Name = Utils.EmptyBytes;
|
||||
properties.ObjectData[0].SitName = Utils.EmptyBytes;
|
||||
properties.ObjectData[0].TextureID = Utils.EmptyBytes;
|
||||
properties.ObjectData[0].TouchName = Utils.EmptyBytes;
|
||||
|
||||
KillObjectPacket kill = new KillObjectPacket();
|
||||
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
|
||||
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
|
||||
kill.ObjectData[0].ID = select.ObjectData[i].ObjectLocalID;
|
||||
scene.UDP.SendPacket(agent.ID, kill, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ObjectDeselectHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectDeselectPacket deselect = (ObjectDeselectPacket)packet;
|
||||
|
||||
for (int i = 0; i < deselect.ObjectData.Length; i++)
|
||||
{
|
||||
uint localID = deselect.ObjectData[i].ObjectLocalID;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(localID, out obj))
|
||||
{
|
||||
//Logger.DebugLog("Deselecting object " + obj.Prim.LocalID);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we need this at all?
|
||||
}
|
||||
|
||||
void ObjectLinkHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectLinkPacket link = (ObjectLinkPacket)packet;
|
||||
List<SimulationObject> linkSet = new List<SimulationObject>();
|
||||
for (int i = 0; i < link.ObjectData.Length; i++)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (!scene.TryGetObject(link.ObjectData[i].ObjectLocalID, out obj))
|
||||
{
|
||||
//TODO: Send an error message
|
||||
return;
|
||||
}
|
||||
else if (obj.Prim.OwnerID != agent.ID)
|
||||
{
|
||||
//TODO: Do a full permissions check
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
linkSet.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < linkSet.Count; i++)
|
||||
{
|
||||
linkSet[i].LinkNumber = i + 1;
|
||||
|
||||
if (linkSet[i].Prim.ParentID > 0)
|
||||
{
|
||||
// Previously linked children
|
||||
SimulationObject parent;
|
||||
if (scene.TryGetObject(linkSet[i].Prim.ParentID, out parent))
|
||||
{
|
||||
// Re-add old root orientation
|
||||
linkSet[i].Prim.Position = parent.Prim.Position + Vector3.Transform(linkSet[i].Prim.Position,
|
||||
Matrix4.CreateFromQuaternion(parent.Prim.Rotation));
|
||||
linkSet[i].Prim.Rotation *= parent.Prim.Rotation;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
// Subtract root prim orientation
|
||||
linkSet[i].Prim.Position = Vector3.Transform(linkSet[i].Prim.Position - linkSet[0].Prim.Position,
|
||||
Matrix4.CreateFromQuaternion(Quaternion.Identity / linkSet[0].Prim.Rotation));
|
||||
linkSet[i].Prim.Rotation /= linkSet[0].Prim.Rotation;
|
||||
|
||||
// Set parent ID
|
||||
linkSet[i].Prim.ParentID = linkSet[0].Prim.LocalID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Root prim
|
||||
linkSet[i].Prim.ParentID = 0;
|
||||
}
|
||||
|
||||
scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags,
|
||||
UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.ParentID);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDelinkHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectDelinkPacket delink = (ObjectDelinkPacket)packet;
|
||||
|
||||
List<SimulationObject> linkSet = new List<SimulationObject>();
|
||||
for (int i = 0; i < delink.ObjectData.Length; i++)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (!scene.TryGetObject(delink.ObjectData[i].ObjectLocalID, out obj))
|
||||
{
|
||||
//TODO: Send an error message
|
||||
return;
|
||||
}
|
||||
else if (obj.Prim.OwnerID != agent.ID)
|
||||
{
|
||||
//TODO: Do a full permissions check
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
linkSet.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < linkSet.Count; i++)
|
||||
{
|
||||
linkSet[i].Prim.ParentID = 0;
|
||||
linkSet[i].LinkNumber = 0;
|
||||
|
||||
// Add root prim orientation to child prims
|
||||
if (i > 0)
|
||||
{
|
||||
linkSet[i].Prim.Position = linkSet[0].Prim.Position + Vector3.Transform(linkSet[i].Prim.Position,
|
||||
Matrix4.CreateFromQuaternion(linkSet[0].Prim.Rotation));
|
||||
linkSet[i].Prim.Rotation *= linkSet[0].Prim.Rotation;
|
||||
}
|
||||
|
||||
scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags,
|
||||
UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.ParentID);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectShapeHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectShapePacket shape = (ObjectShapePacket)packet;
|
||||
|
||||
for (int i = 0; i < shape.ObjectData.Length; i++)
|
||||
{
|
||||
ObjectShapePacket.ObjectDataBlock block = shape.ObjectData[i];
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(block.ObjectLocalID, out obj))
|
||||
{
|
||||
Primitive.ConstructionData data = obj.Prim.PrimData;
|
||||
|
||||
data.PathBegin = Primitive.UnpackBeginCut(block.PathBegin);
|
||||
data.PathCurve = (PathCurve)block.PathCurve;
|
||||
data.PathEnd = Primitive.UnpackEndCut(block.PathEnd);
|
||||
data.PathRadiusOffset = Primitive.UnpackPathTwist(block.PathRadiusOffset);
|
||||
data.PathRevolutions = Primitive.UnpackPathRevolutions(block.PathRevolutions);
|
||||
data.PathScaleX = Primitive.UnpackPathScale(block.PathScaleX);
|
||||
data.PathScaleY = Primitive.UnpackPathScale(block.PathScaleY);
|
||||
data.PathShearX = Primitive.UnpackPathShear((sbyte)block.PathShearX);
|
||||
data.PathShearY = Primitive.UnpackPathShear((sbyte)block.PathShearY);
|
||||
data.PathSkew = Primitive.UnpackPathTwist(block.PathSkew);
|
||||
data.PathTaperX = Primitive.UnpackPathTaper(block.PathTaperX);
|
||||
data.PathTaperY = Primitive.UnpackPathTaper(block.PathTaperY);
|
||||
data.PathTwist = Primitive.UnpackPathTwist(block.PathTwist);
|
||||
data.PathTwistBegin = Primitive.UnpackPathTwist(block.PathTwistBegin);
|
||||
data.ProfileBegin = Primitive.UnpackBeginCut(block.ProfileBegin);
|
||||
data.profileCurve = block.ProfileCurve;
|
||||
data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd);
|
||||
data.ProfileHollow = Primitive.UnpackProfileHollow(block.ProfileHollow);
|
||||
|
||||
obj.Prim.PrimData = data;
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Got an ObjectShape packet for unknown object " + block.ObjectLocalID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFlagUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectFlagUpdatePacket update = (ObjectFlagUpdatePacket)packet;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(update.AgentData.ObjectLocalID, out obj))
|
||||
{
|
||||
PrimFlags flags = obj.Prim.Flags;
|
||||
|
||||
if (update.AgentData.CastsShadows)
|
||||
flags |= PrimFlags.CastShadows;
|
||||
else
|
||||
flags &= ~PrimFlags.CastShadows;
|
||||
|
||||
if (update.AgentData.IsPhantom)
|
||||
flags |= PrimFlags.Phantom;
|
||||
else
|
||||
flags &= ~PrimFlags.Phantom;
|
||||
|
||||
if (update.AgentData.IsTemporary)
|
||||
flags |= PrimFlags.Temporary;
|
||||
else
|
||||
flags &= ~PrimFlags.Temporary;
|
||||
|
||||
if (update.AgentData.UsePhysics)
|
||||
flags |= PrimFlags.Physics;
|
||||
else
|
||||
flags &= ~PrimFlags.Physics;
|
||||
|
||||
obj.Prim.Flags = flags;
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Got an ObjectFlagUpdate packet for unknown object " + update.AgentData.ObjectLocalID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectExtraParamsHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectExtraParamsPacket extra = (ObjectExtraParamsPacket)packet;
|
||||
|
||||
for (int i = 0; i < extra.ObjectData.Length; i++)
|
||||
{
|
||||
ObjectExtraParamsPacket.ObjectDataBlock block = extra.ObjectData[i];
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(block.ObjectLocalID, out obj))
|
||||
{
|
||||
ExtraParamType type = (ExtraParamType)block.ParamType;
|
||||
|
||||
if (block.ParamInUse)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ExtraParamType.Flexible:
|
||||
obj.Prim.Flexible = new Primitive.FlexibleData(block.ParamData, 0);
|
||||
break;
|
||||
case ExtraParamType.Light:
|
||||
obj.Prim.Light = new Primitive.LightData(block.ParamData, 0);
|
||||
break;
|
||||
case ExtraParamType.Sculpt:
|
||||
obj.Prim.Sculpt = new Primitive.SculptData(block.ParamData, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ExtraParamType.Flexible:
|
||||
obj.Prim.Flexible = null;
|
||||
break;
|
||||
case ExtraParamType.Light:
|
||||
obj.Prim.Light = null;
|
||||
break;
|
||||
case ExtraParamType.Sculpt:
|
||||
obj.Prim.Sculpt = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.ExtraData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectImageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectImagePacket image = (ObjectImagePacket)packet;
|
||||
|
||||
for (int i = 0; i < image.ObjectData.Length; i++)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(image.ObjectData[i].ObjectLocalID, out obj))
|
||||
{
|
||||
obj.Prim.MediaURL = Utils.BytesToString(image.ObjectData[i].MediaURL);
|
||||
obj.Prim.Textures = new Primitive.TextureEntry(image.ObjectData[i].TextureEntry, 0, image.ObjectData[i].TextureEntry.Length);
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.MediaURL | UpdateFlags.Textures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UndoHandler(Packet packet, Agent agent)
|
||||
{
|
||||
UndoPacket undo = (UndoPacket)packet;
|
||||
|
||||
for (int i = 0; i < undo.ObjectData.Length; i++)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(undo.ObjectData[i].ObjectID, out obj))
|
||||
scene.ObjectUndo(this, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void RedoHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RedoPacket redo = (RedoPacket)packet;
|
||||
|
||||
for (int i = 0; i < redo.ObjectData.Length; i++)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(redo.ObjectData[i].ObjectID, out obj))
|
||||
scene.ObjectRedo(this, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void MultipleObjectUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MultipleObjectUpdatePacket update = (MultipleObjectUpdatePacket)packet;
|
||||
|
||||
for (int i = 0; i < update.ObjectData.Length; i++)
|
||||
{
|
||||
bool scaled = false;
|
||||
MultipleObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[i];
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(block.ObjectLocalID, out obj))
|
||||
{
|
||||
UpdateType type = (UpdateType)block.Type;
|
||||
//bool linked = ((type & UpdateType.Linked) != 0);
|
||||
int pos = 0;
|
||||
Vector3 position = obj.Prim.Position;
|
||||
Quaternion rotation = obj.Prim.Rotation;
|
||||
Vector3 scale = obj.Prim.Scale;
|
||||
|
||||
UpdateFlags updateFlags = UpdateFlags.None;
|
||||
|
||||
if ((type & UpdateType.Position) != 0)
|
||||
{
|
||||
updateFlags |= UpdateFlags.Position;
|
||||
position = new Vector3(block.Data, pos);
|
||||
pos += 12;
|
||||
}
|
||||
if ((type & UpdateType.Rotation) != 0)
|
||||
{
|
||||
updateFlags |= UpdateFlags.Rotation;
|
||||
rotation = new Quaternion(block.Data, pos, true);
|
||||
pos += 12;
|
||||
}
|
||||
if ((type & UpdateType.Scale) != 0)
|
||||
{
|
||||
updateFlags |= UpdateFlags.Scale;
|
||||
scaled = true;
|
||||
scale = new Vector3(block.Data, pos);
|
||||
pos += 12;
|
||||
|
||||
// FIXME: Use this in linksets
|
||||
//bool uniform = ((type & UpdateType.Uniform) != 0);
|
||||
}
|
||||
|
||||
obj.Prim.Position = position;
|
||||
obj.Prim.Rotation = rotation;
|
||||
if (scaled) obj.Prim.Scale = scale;
|
||||
|
||||
scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, PrimFlags.None, updateFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ghosted prim, send a kill message to this agent
|
||||
KillObjectPacket kill = new KillObjectPacket();
|
||||
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
|
||||
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
|
||||
kill.ObjectData[0].ID = block.ObjectLocalID;
|
||||
scene.UDP.SendPacket(agent.ID, kill, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RequestObjectPropertiesFamilyHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RequestObjectPropertiesFamilyPacket request = (RequestObjectPropertiesFamilyPacket)packet;
|
||||
ReportType type = (ReportType)request.ObjectData.RequestFlags;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(request.ObjectData.ObjectID, out obj))
|
||||
{
|
||||
ObjectPropertiesFamilyPacket props = new ObjectPropertiesFamilyPacket();
|
||||
props.ObjectData.BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask;
|
||||
props.ObjectData.Category = (uint)obj.Prim.Properties.Category;
|
||||
props.ObjectData.Description = Utils.StringToBytes(obj.Prim.Properties.Description);
|
||||
props.ObjectData.EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask;
|
||||
props.ObjectData.GroupID = obj.Prim.Properties.GroupID;
|
||||
props.ObjectData.GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask;
|
||||
props.ObjectData.LastOwnerID = obj.Prim.Properties.LastOwnerID;
|
||||
props.ObjectData.Name = Utils.StringToBytes(obj.Prim.Properties.Name);
|
||||
props.ObjectData.NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask;
|
||||
props.ObjectData.ObjectID = obj.Prim.ID;
|
||||
props.ObjectData.OwnerID = obj.Prim.Properties.OwnerID;
|
||||
props.ObjectData.OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask;
|
||||
props.ObjectData.OwnershipCost = obj.Prim.Properties.OwnershipCost;
|
||||
props.ObjectData.RequestFlags = (uint)type;
|
||||
props.ObjectData.SalePrice = obj.Prim.Properties.SalePrice;
|
||||
props.ObjectData.SaleType = (byte)obj.Prim.Properties.SaleType;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, props, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("RequestObjectPropertiesFamily sent for unknown object " +
|
||||
request.ObjectData.ObjectID.ToString(), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 FullSceneCollisionTest(Vector3 rayStart, Vector3 rayEnd)
|
||||
{
|
||||
// HACK: For now
|
||||
Logger.DebugLog("Full scene collision test was requested, ignoring");
|
||||
return rayEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
307
Programs/Simian/SceneExtensions/ParcelManager.cs
Normal file
307
Programs/Simian/SceneExtensions/ParcelManager.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ParcelManager : IExtension<ISceneProvider>, IParcelProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
Dictionary<int, Parcel> parcels = new Dictionary<int, Parcel>();
|
||||
/// <summary>X,Y ordered 2D array of the parcelIDs for each sq. meter of a simulator</summary>
|
||||
int[] parcelOverlay = new int[64 * 64];
|
||||
|
||||
public ParcelManager()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
lock (parcels)
|
||||
parcels.Clear();
|
||||
|
||||
// Create a default parcel
|
||||
Parcel parcel = new Parcel(0);
|
||||
parcel.Desc = "Simian Parcel";
|
||||
parcel.Flags = Parcel.ParcelFlags.AllowAPrimitiveEntry | Parcel.ParcelFlags.AllowFly | Parcel.ParcelFlags.AllowGroupScripts |
|
||||
Parcel.ParcelFlags.AllowLandmark | Parcel.ParcelFlags.AllowOtherScripts | Parcel.ParcelFlags.AllowTerraform |
|
||||
Parcel.ParcelFlags.AllowVoiceChat | Parcel.ParcelFlags.CreateGroupObjects | Parcel.ParcelFlags.CreateObjects |
|
||||
Parcel.ParcelFlags.UseBanList;
|
||||
parcel.Landing = Parcel.LandingType.Direct;
|
||||
parcel.MaxPrims = 20000;
|
||||
parcel.Name = "Simian";
|
||||
parcel.Status = Parcel.ParcelStatus.Leased;
|
||||
parcel.UserLocation = (parcel.AABBMin + parcel.AABBMax) * 0.5f;
|
||||
|
||||
parcel.Bitmap = new byte[512];
|
||||
for (int i = 0; i < 512; i++)
|
||||
parcel.Bitmap[i] = Byte.MaxValue;
|
||||
|
||||
// The parcelOverlay defaults to all zeroes, and the LocalID of this parcel is zero,
|
||||
// so we don't need to modify parcelOverlay before calling this
|
||||
UpdateParcelSize(ref parcel);
|
||||
|
||||
// Add the default parcel to the list
|
||||
parcels[parcel.LocalID] = parcel;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public void SendParcelOverlay(Agent agent)
|
||||
{
|
||||
const int LAND_BLOCKS_PER_PACKET = 1024;
|
||||
|
||||
byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET];
|
||||
int byteArrayCount = 0;
|
||||
int sequenceID = 0;
|
||||
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 64; x++)
|
||||
{
|
||||
byte tempByte = 0; // The flags for the current 4x4m parcel square
|
||||
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(parcelOverlay[y * 64 + x], out parcel))
|
||||
{
|
||||
// Set the ownership/sale flag
|
||||
if (parcel.OwnerID == agent.ID)
|
||||
tempByte = (byte)ParcelOverlayType.OwnedBySelf;
|
||||
else if (parcel.AuctionID != 0)
|
||||
tempByte = (byte)ParcelOverlayType.Auction;
|
||||
else if (parcel.SalePrice > 0 && (parcel.AuthBuyerID == UUID.Zero || parcel.AuthBuyerID == agent.ID))
|
||||
tempByte = (byte)ParcelOverlayType.ForSale;
|
||||
else if (parcel.GroupID != UUID.Zero)
|
||||
tempByte = (byte)ParcelOverlayType.OwnedByGroup;
|
||||
else if (parcel.OwnerID != UUID.Zero)
|
||||
tempByte = (byte)ParcelOverlayType.OwnedByOther;
|
||||
else
|
||||
tempByte = (byte)ParcelOverlayType.Public;
|
||||
|
||||
// Set the border flags
|
||||
if (x == 0)
|
||||
tempByte |= (byte)ParcelOverlayType.BorderWest;
|
||||
else if (parcelOverlay[y * 64 + (x - 1)] != parcel.LocalID)
|
||||
// Parcel to the west is different from the current parcel
|
||||
tempByte |= (byte)ParcelOverlayType.BorderWest;
|
||||
|
||||
if (y == 0)
|
||||
tempByte |= (byte)ParcelOverlayType.BorderSouth;
|
||||
else if (parcelOverlay[(y - 1) * 64 + x] != parcel.LocalID)
|
||||
// Parcel to the south is different from the current parcel
|
||||
tempByte |= (byte)ParcelOverlayType.BorderSouth;
|
||||
|
||||
byteArray[byteArrayCount] = tempByte;
|
||||
++byteArrayCount;
|
||||
if (byteArrayCount >= LAND_BLOCKS_PER_PACKET)
|
||||
{
|
||||
// Send a ParcelOverlay packet
|
||||
ParcelOverlayPacket overlay = new ParcelOverlayPacket();
|
||||
overlay.ParcelData.SequenceID = sequenceID;
|
||||
overlay.ParcelData.Data = byteArray;
|
||||
scene.UDP.SendPacket(agent.ID, overlay, PacketCategory.State);
|
||||
|
||||
byteArrayCount = 0;
|
||||
++sequenceID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Parcel overlay references missing parcel " + parcelOverlay[y * 64 + x],
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateParcelSize(ref Parcel parcel)
|
||||
{
|
||||
int minX = 64;
|
||||
int minY = 64;
|
||||
int maxX = 0;
|
||||
int maxY = 0;
|
||||
int area = 0;
|
||||
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 64; x++)
|
||||
{
|
||||
if (parcelOverlay[y * 64 + x] == parcel.LocalID)
|
||||
{
|
||||
int x4 = x * 4;
|
||||
int y4 = y * 4;
|
||||
|
||||
if (minX > x4) minX = x4;
|
||||
if (minY > y4) minY = y4;
|
||||
if (maxX < x4) maxX = x4;
|
||||
if (maxX < y4) maxY = y4;
|
||||
area += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parcel.AABBMin.X = minX;
|
||||
parcel.AABBMin.Y = minY;
|
||||
parcel.AABBMax.X = maxX;
|
||||
parcel.AABBMax.Y = maxY;
|
||||
parcel.Area = area;
|
||||
}
|
||||
|
||||
void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result,
|
||||
Agent agent)
|
||||
{
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(parcelID, out parcel))
|
||||
{
|
||||
ParcelPropertiesPacket properties = new ParcelPropertiesPacket();
|
||||
properties.AgeVerificationBlock.RegionDenyAgeUnverified = false;
|
||||
properties.ParcelData.AABBMax = parcel.AABBMax;
|
||||
properties.ParcelData.AABBMin = parcel.AABBMin;
|
||||
properties.ParcelData.Area = parcel.Area;
|
||||
properties.ParcelData.AuctionID = parcel.AuctionID;
|
||||
properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID;
|
||||
properties.ParcelData.Bitmap = parcel.Bitmap;
|
||||
properties.ParcelData.Category = (byte)parcel.Category;
|
||||
properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate);
|
||||
properties.ParcelData.ClaimPrice = parcel.ClaimPrice;
|
||||
properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc);
|
||||
properties.ParcelData.GroupID = parcel.GroupID;
|
||||
properties.ParcelData.GroupPrims = parcel.GroupPrims;
|
||||
properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned;
|
||||
properties.ParcelData.LandingType = (byte)parcel.Landing;
|
||||
properties.ParcelData.LocalID = parcel.LocalID;
|
||||
properties.ParcelData.MaxPrims = parcel.MaxPrims;
|
||||
properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale;
|
||||
properties.ParcelData.MediaID = parcel.Media.MediaID;
|
||||
properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL);
|
||||
properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL);
|
||||
properties.ParcelData.Name = Utils.StringToBytes(parcel.Name);
|
||||
properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime;
|
||||
properties.ParcelData.OtherCount = parcel.OtherCount;
|
||||
properties.ParcelData.OtherPrims = parcel.OtherPrims;
|
||||
properties.ParcelData.OwnerID = parcel.OwnerID;
|
||||
properties.ParcelData.OwnerPrims = parcel.OwnerPrims;
|
||||
properties.ParcelData.ParcelFlags = (uint)parcel.Flags;
|
||||
properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus;
|
||||
properties.ParcelData.PassHours = parcel.PassHours;
|
||||
properties.ParcelData.PassPrice = parcel.PassPrice;
|
||||
properties.ParcelData.PublicCount = parcel.PublicCount;
|
||||
properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous;
|
||||
properties.ParcelData.RegionDenyIdentified = false; // Deprecated
|
||||
properties.ParcelData.RegionDenyTransacted = false; // Deprecated
|
||||
properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride;
|
||||
properties.ParcelData.RentPrice = parcel.RentPrice;
|
||||
properties.ParcelData.RequestResult = (int)result;
|
||||
properties.ParcelData.SalePrice = parcel.SalePrice;
|
||||
properties.ParcelData.SelectedPrims = 0; // TODO:
|
||||
properties.ParcelData.SelfCount = parcel.SelfCount;
|
||||
properties.ParcelData.SequenceID = sequenceID;
|
||||
properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims;
|
||||
properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims;
|
||||
properties.ParcelData.SnapSelection = snapSelection;
|
||||
properties.ParcelData.SnapshotID = parcel.SnapshotID;
|
||||
properties.ParcelData.Status = (byte)parcel.Status;
|
||||
properties.ParcelData.TotalPrims = parcel.TotalPrims;
|
||||
properties.ParcelData.UserLocation = parcel.UserLocation;
|
||||
properties.ParcelData.UserLookAt = parcel.UserLookAt;
|
||||
|
||||
// HACK: Make everyone think they are the owner of this parcel
|
||||
properties.ParcelData.OwnerID = agent.ID;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void ParcelPropertiesRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet;
|
||||
|
||||
// TODO: Replace with HashSet when we switch to .NET 3.5
|
||||
List<int> parcels = new List<int>();
|
||||
|
||||
// Convert the boundaries to integers
|
||||
int north = (int)Math.Round(request.ParcelData.North) / 4;
|
||||
int east = (int)Math.Round(request.ParcelData.East) / 4;
|
||||
int south = (int)Math.Round(request.ParcelData.South) / 4;
|
||||
int west = (int)Math.Round(request.ParcelData.West) / 4;
|
||||
|
||||
// Find all of the parcels within the given boundaries
|
||||
int xLen = east - west;
|
||||
int yLen = north - south;
|
||||
|
||||
for (int x = 0; x < xLen; x++)
|
||||
{
|
||||
for (int y = 0; y < yLen; y++)
|
||||
{
|
||||
if (west + x < 64 && south + y < 64)
|
||||
{
|
||||
int currentParcelID = parcelOverlay[(south + y) * 64 + (west + x)];
|
||||
if (!parcels.Contains(currentParcelID))
|
||||
parcels.Add(currentParcelID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParcelResult result = ParcelResult.NoData;
|
||||
if (parcels.Count == 1)
|
||||
result = ParcelResult.Single;
|
||||
else if (parcels.Count > 1)
|
||||
result = ParcelResult.Multiple;
|
||||
|
||||
for (int i = 0; i < parcels.Count; i++)
|
||||
SendParcelProperties(parcels[i], request.ParcelData.SequenceID, request.ParcelData.SnapSelection, result, agent);
|
||||
}
|
||||
|
||||
void ParcelPropertiesUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ParcelPropertiesUpdatePacket update = (ParcelPropertiesUpdatePacket)packet;
|
||||
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(update.ParcelData.LocalID, out parcel))
|
||||
{
|
||||
parcel.AuthBuyerID = update.ParcelData.AuthBuyerID;
|
||||
parcel.Category = (Parcel.ParcelCategory)update.ParcelData.Category;
|
||||
parcel.Desc = Utils.BytesToString(update.ParcelData.Desc);
|
||||
parcel.Flags = (Parcel.ParcelFlags)update.ParcelData.ParcelFlags;
|
||||
parcel.GroupID = update.ParcelData.GroupID;
|
||||
parcel.Landing = (Parcel.LandingType)update.ParcelData.LandingType;
|
||||
parcel.Media.MediaAutoScale = update.ParcelData.MediaAutoScale;
|
||||
parcel.Media.MediaID = update.ParcelData.MediaID;
|
||||
parcel.Media.MediaURL = Utils.BytesToString(update.ParcelData.MediaURL);
|
||||
parcel.MusicURL = Utils.BytesToString(update.ParcelData.MusicURL);
|
||||
parcel.Name = Utils.BytesToString(update.ParcelData.Name);
|
||||
parcel.PassHours = update.ParcelData.PassHours;
|
||||
parcel.PassPrice = update.ParcelData.PassPrice;
|
||||
parcel.SalePrice = update.ParcelData.SalePrice;
|
||||
parcel.SnapshotID = update.ParcelData.SnapshotID;
|
||||
parcel.UserLocation = update.ParcelData.UserLocation;
|
||||
parcel.UserLookAt = update.ParcelData.UserLookAt;
|
||||
|
||||
lock (parcels)
|
||||
parcels[parcel.LocalID] = parcel;
|
||||
|
||||
if (update.ParcelData.Flags != 0)
|
||||
SendParcelProperties(parcel.LocalID, 0, false, ParcelResult.Single, agent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Got a ParcelPropertiesUpdate for an unknown parcel " + update.ParcelData.LocalID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
585
Programs/Simian/SceneExtensions/Periscope.cs
Normal file
585
Programs/Simian/SceneExtensions/Periscope.cs
Normal file
@@ -0,0 +1,585 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class Periscope : IExtension<ISceneProvider>
|
||||
{
|
||||
// Change this to login to a different grid
|
||||
const string PERISCOPE_LOGIN_URI = Settings.AGNI_LOGIN_SERVER;
|
||||
|
||||
public Agent MasterAgent = null;
|
||||
|
||||
ISceneProvider scene;
|
||||
GridClient client;
|
||||
PeriscopeImageDelivery imageDelivery;
|
||||
PeriscopeMovement movement;
|
||||
PeriscopeTransferManager transferManager;
|
||||
bool ignoreObjectKill = false;
|
||||
object loginLock = new object();
|
||||
string lastPasswordHash = String.Empty;
|
||||
|
||||
public Periscope()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
client = new GridClient();
|
||||
Settings.LOG_LEVEL = Helpers.LogLevel.Info;
|
||||
client.Settings.MULTIPLE_SIMS = false;
|
||||
client.Settings.SEND_AGENT_UPDATES = false;
|
||||
|
||||
client.Network.OnCurrentSimChanged += Network_OnCurrentSimChanged;
|
||||
client.Objects.OnNewPrim += Objects_OnNewPrim;
|
||||
client.Objects.OnNewAvatar += Objects_OnNewAvatar;
|
||||
client.Objects.OnNewAttachment += Objects_OnNewAttachment;
|
||||
client.Objects.OnObjectKilled += Objects_OnObjectKilled;
|
||||
client.Objects.OnObjectUpdated += Objects_OnObjectUpdated;
|
||||
client.Avatars.OnAvatarAppearance += Avatars_OnAvatarAppearance;
|
||||
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
|
||||
client.Self.OnChat += new AgentManager.ChatCallback(Self_OnChat);
|
||||
client.Self.OnTeleport += new AgentManager.TeleportCallback(Self_OnTeleport);
|
||||
client.Network.RegisterCallback(PacketType.AvatarAnimation, AvatarAnimationHandler);
|
||||
client.Network.RegisterCallback(PacketType.RegionHandshake, RegionHandshakeHandler);
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectGrab, ObjectGrabHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectGrabUpdate, ObjectGrabUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ObjectDeGrab, ObjectDeGrabHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
|
||||
|
||||
imageDelivery = new PeriscopeImageDelivery(scene, client);
|
||||
movement = new PeriscopeMovement(scene, this);
|
||||
transferManager = new PeriscopeTransferManager(scene, client);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
transferManager.Stop();
|
||||
movement.Stop();
|
||||
imageDelivery.Stop();
|
||||
|
||||
if (client.Network.Connected)
|
||||
client.Network.Logout();
|
||||
}
|
||||
|
||||
void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, scene);
|
||||
if (MasterAgent != null)
|
||||
simObj.Prim.OwnerID = MasterAgent.ID;
|
||||
scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void Objects_OnNewAttachment(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, scene);
|
||||
scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
// Add the avatar to both the agents list and the scene objects
|
||||
SimulationObject obj = new SimulationObject(avatar, scene);
|
||||
Agent agent = new Agent(obj, AgentInfoFromAvatar(avatar));
|
||||
|
||||
scene.AgentAdd(this, agent, avatar.Flags);
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject obj;
|
||||
UpdateFlags flags = UpdateFlags.Acceleration | UpdateFlags.AngularVelocity | UpdateFlags.Position |
|
||||
UpdateFlags.Rotation | UpdateFlags.Velocity;
|
||||
|
||||
if (update.Avatar) flags |= UpdateFlags.CollisionPlane;
|
||||
if (update.Textures != null) flags |= UpdateFlags.Textures;
|
||||
|
||||
if (scene.TryGetObject(update.LocalID, out obj))
|
||||
{
|
||||
obj.Prim.Acceleration = update.Acceleration;
|
||||
obj.Prim.AngularVelocity = update.AngularVelocity;
|
||||
obj.Prim.Position = update.Position;
|
||||
obj.Prim.Rotation = update.Rotation;
|
||||
obj.Prim.Velocity = update.Velocity;
|
||||
if (update.Avatar) obj.Prim.CollisionPlane = update.CollisionPlane;
|
||||
if (update.Textures != null) obj.Prim.Textures = update.Textures;
|
||||
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, flags);
|
||||
}
|
||||
|
||||
if (update.LocalID == client.Self.LocalID)
|
||||
{
|
||||
MasterAgent.Avatar.Prim.Acceleration = update.Acceleration;
|
||||
MasterAgent.Avatar.Prim.AngularVelocity = update.AngularVelocity;
|
||||
MasterAgent.Avatar.Prim.Position = update.Position;
|
||||
MasterAgent.Avatar.Prim.Rotation = update.Rotation;
|
||||
MasterAgent.Avatar.Prim.Velocity = update.Velocity;
|
||||
if (update.Avatar) MasterAgent.Avatar.Prim.CollisionPlane = update.CollisionPlane;
|
||||
if (update.Textures != null) MasterAgent.Avatar.Prim.Textures = update.Textures;
|
||||
|
||||
scene.ObjectAddOrUpdate(this, MasterAgent.Avatar, MasterAgent.ID, 0, PrimFlags.None, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void Objects_OnObjectKilled(Simulator simulator, uint objectID)
|
||||
{
|
||||
if (!ignoreObjectKill)
|
||||
scene.ObjectRemove(this, objectID);
|
||||
}
|
||||
|
||||
void Avatars_OnAvatarAppearance(UUID avatarID, bool isTrial, Primitive.TextureEntryFace defaultTexture,
|
||||
Primitive.TextureEntryFace[] faceTextures, List<byte> visualParams)
|
||||
{
|
||||
Primitive.TextureEntry te = new Primitive.TextureEntry(defaultTexture);
|
||||
te.FaceTextures = faceTextures;
|
||||
byte[] vp = (visualParams != null && visualParams.Count > 1 ? visualParams.ToArray() : null);
|
||||
|
||||
Agent agent;
|
||||
if (scene.TryGetAgent(avatarID, out agent))
|
||||
{
|
||||
Logger.Log("[Periscope] Updating foreign avatar appearance for " + agent.FullName, Helpers.LogLevel.Info);
|
||||
scene.AgentAppearance(this, agent, te, vp);
|
||||
}
|
||||
|
||||
if (avatarID == client.Self.AgentID)
|
||||
{
|
||||
Logger.Log("[Periscope] Updating foreign avatar appearance for the MasterAgent", Helpers.LogLevel.Info);
|
||||
scene.AgentAppearance(this, MasterAgent, te, vp);
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
|
||||
{
|
||||
float[,] patchData = new float[16, 16];
|
||||
for (int py = 0; py < 16; py++)
|
||||
{
|
||||
for (int px = 0; px < 16; px++)
|
||||
{
|
||||
patchData[py, px] = data[py * 16 + px];
|
||||
}
|
||||
}
|
||||
|
||||
scene.SetTerrainPatch(this, (uint)x, (uint)y, patchData);
|
||||
}
|
||||
|
||||
void Self_OnChat(string message, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
|
||||
string fromName, UUID id, UUID ownerid, Vector3 position)
|
||||
{
|
||||
scene.ObjectChat(this, ownerid, id, audible, type, sourceType, fromName, position, 0, message);
|
||||
}
|
||||
|
||||
void Self_OnTeleport(string message, TeleportStatus status, TeleportFlags flags)
|
||||
{
|
||||
if (status == TeleportStatus.Finished)
|
||||
{
|
||||
ulong localRegionHandle = scene.RegionHandle;
|
||||
|
||||
scene.RemoveAllAgents(
|
||||
delegate(Agent agent)
|
||||
{ return agent.Avatar.Prim.RegionHandle != client.Network.CurrentSim.Handle && agent.Avatar.Prim.RegionHandle != localRegionHandle; });
|
||||
|
||||
scene.RemoveAllObjects(
|
||||
delegate(SimulationObject obj)
|
||||
{ return obj.Prim.RegionHandle != client.Network.CurrentSim.Handle && obj.Prim.RegionHandle != localRegionHandle; });
|
||||
}
|
||||
}
|
||||
|
||||
void Network_OnCurrentSimChanged(Simulator PreviousSimulator)
|
||||
{
|
||||
Logger.Log("[Periscope] Sending bot appearance", Helpers.LogLevel.Info);
|
||||
client.Appearance.SetPreviousAppearance(false);
|
||||
}
|
||||
|
||||
void AvatarAnimationHandler(Packet packet, Simulator simulator)
|
||||
{
|
||||
AvatarAnimationPacket animations = (AvatarAnimationPacket)packet;
|
||||
|
||||
Agent agent;
|
||||
if (scene.TryGetAgent(animations.Sender.ID, out agent))
|
||||
{
|
||||
agent.Animations.Clear();
|
||||
|
||||
for (int i = 0; i < animations.AnimationList.Length; i++)
|
||||
{
|
||||
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
|
||||
agent.Animations.Add(block.AnimID, block.AnimSequenceID);
|
||||
}
|
||||
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
}
|
||||
|
||||
if (animations.Sender.ID == client.Self.AgentID)
|
||||
{
|
||||
MasterAgent.Animations.Clear();
|
||||
|
||||
for (int i = 0; i < animations.AnimationList.Length; i++)
|
||||
{
|
||||
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
|
||||
MasterAgent.Animations.Add(block.AnimID, block.AnimSequenceID);
|
||||
}
|
||||
|
||||
scene.Avatars.SendAnimations(MasterAgent);
|
||||
}
|
||||
}
|
||||
|
||||
void RegionHandshakeHandler(Packet packet, Simulator simulator)
|
||||
{
|
||||
RegionHandshakePacket handshake = (RegionHandshakePacket)packet;
|
||||
|
||||
handshake.RegionInfo.SimOwner = (MasterAgent != null ? MasterAgent.ID : UUID.Zero);
|
||||
handshake.RegionInfo.RegionFlags &= ~(uint)RegionFlags.NoFly;
|
||||
handshake.RegionInfo2.RegionID = scene.RegionID;
|
||||
|
||||
// TODO: Need more methods to manipulate the scene so we can apply these properties.
|
||||
// Right now this only gets sent out to people who are logged in when the master avatar
|
||||
// is already logged in
|
||||
scene.UDP.BroadcastPacket(handshake, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
#region Simian client packet handlers
|
||||
|
||||
void ChatFromViewerHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ChatFromViewerPacket chat = (ChatFromViewerPacket)packet;
|
||||
|
||||
// Forward chat from the viewer to the foreign simulator
|
||||
string message = Utils.BytesToString(chat.ChatData.Message);
|
||||
|
||||
if (!String.IsNullOrEmpty(message) && message[0] == '/')
|
||||
{
|
||||
string[] messageParts = CommandLineParser.Parse(message);
|
||||
|
||||
switch (messageParts[0])
|
||||
{
|
||||
case "/teleport":
|
||||
{
|
||||
float x, y, z;
|
||||
|
||||
if (messageParts.Length == 5 &&
|
||||
Single.TryParse(messageParts[2], out x) &&
|
||||
Single.TryParse(messageParts[3], out y) &&
|
||||
Single.TryParse(messageParts[4], out z))
|
||||
{
|
||||
scene.Avatars.SendAlert(agent, String.Format("Attempting teleport to {0} <{1}, {2}, {3}>",
|
||||
messageParts[1], messageParts[2], messageParts[3], messageParts[4]));
|
||||
client.Self.Teleport(messageParts[1], new Vector3(x, y, z));
|
||||
}
|
||||
else
|
||||
{
|
||||
scene.Avatars.SendAlert(agent, "Usage: /teleport \"sim name\" x y z");
|
||||
}
|
||||
return;
|
||||
}
|
||||
case "/stats":
|
||||
scene.Avatars.SendAlert(agent, String.Format("Downloading textures: {0}, Queued textures: {1}",
|
||||
imageDelivery.Pipeline.CurrentCount, imageDelivery.Pipeline.QueuedCount));
|
||||
return;
|
||||
case "/objectkill":
|
||||
if (messageParts.Length == 2)
|
||||
{
|
||||
if (messageParts[1] == "off" || messageParts[1] == "0")
|
||||
{
|
||||
ignoreObjectKill = true;
|
||||
scene.Avatars.SendAlert(agent, "Ignoring upstream ObjectKill packets");
|
||||
}
|
||||
else
|
||||
{
|
||||
ignoreObjectKill = false;
|
||||
scene.Avatars.SendAlert(agent, "Enabling upstream ObjectKill packets");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case "/save":
|
||||
{
|
||||
if (messageParts.Length == 2)
|
||||
{
|
||||
string filename = messageParts[1];
|
||||
string directoryname = Path.GetFileNameWithoutExtension(filename);
|
||||
|
||||
Thread saveThread = new Thread(new ThreadStart(
|
||||
delegate()
|
||||
{
|
||||
Logger.Log(String.Format("Preparing to serialize {0} objects", scene.ObjectCount()), Helpers.LogLevel.Info);
|
||||
OarFile.SavePrims(scene, directoryname + "/objects", directoryname + "/assets", Simian.ASSET_CACHE_DIR);
|
||||
|
||||
try { Directory.Delete(directoryname + "/terrains", true); }
|
||||
catch (Exception) { }
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(directoryname + "/terrains");
|
||||
|
||||
using (FileStream stream = new FileStream(directoryname + "/terrains/heightmap.r32", FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
for (int y = 0; y < 256; y++)
|
||||
{
|
||||
for (int x = 0; x < 256; x++)
|
||||
{
|
||||
float t = scene.GetTerrainHeightAt(x, y);
|
||||
stream.Write(BitConverter.GetBytes(t), 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log("Failed saving terrain: " + ex.Message, Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
Logger.Log("Saving " + directoryname, Helpers.LogLevel.Info);
|
||||
OarFile.PackageArchive(directoryname, filename);
|
||||
|
||||
try
|
||||
{ System.IO.Directory.Delete(directoryname, true); }
|
||||
catch (Exception ex)
|
||||
{ Logger.Log("Failed to delete temporary directory " + directoryname + ": " + ex.Message, Helpers.LogLevel.Error); }
|
||||
|
||||
scene.Avatars.SendAlert(agent, "Finished OAR export to " + filename);
|
||||
}));
|
||||
|
||||
saveThread.Start();
|
||||
scene.Avatars.SendAlert(agent, "Starting OAR export to " + filename);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case "/nudemod":
|
||||
//int count = 0;
|
||||
// FIXME: AvatarAppearance locks the agents dictionary. Need to be able to copy the agents dictionary?
|
||||
/*foreach (Agent curAgent in agents.Values)
|
||||
{
|
||||
if (curAgent != agent && curAgent.VisualParams != null)
|
||||
{
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerBaked] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerJacket] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerPants] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerShoes] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerSocks] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerUnderpants] = null;
|
||||
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperBaked] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperGloves] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperJacket] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperShirt] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperUndershirt] = null;
|
||||
|
||||
scene.AvatarAppearance(this, curAgent, curAgent.Avatar.Textures, curAgent.VisualParams);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
scene.Avatars.SendAlert(agent, String.Format("Modified appearances for {0} avatar(s)", count));*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string finalMessage;
|
||||
if (agent.Info.FirstName == client.Self.FirstName && agent.Info.LastName == client.Self.LastName)
|
||||
finalMessage = message;
|
||||
else
|
||||
finalMessage = String.Format("<{0} {1}> {2}", agent.Info.FirstName, agent.Info.LastName, message);
|
||||
|
||||
client.Self.Chat(finalMessage, chat.ChatData.Channel, (ChatType)chat.ChatData.Type);
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
if (MasterAgent == null ||
|
||||
(!client.Network.Connected && client.Network.LoginStatusCode == LoginStatus.Failed && agent.Info.PasswordHash != lastPasswordHash))
|
||||
{
|
||||
MasterAgent = null;
|
||||
// To keep from repeatedly trying to login with a bad password
|
||||
lastPasswordHash = agent.Info.PasswordHash;
|
||||
|
||||
lock (loginLock)
|
||||
{
|
||||
// Double-checked locking to avoid hitting the loginLock each time
|
||||
if (MasterAgent == null && scene.TryGetAgent(update.AgentData.AgentID, out MasterAgent))
|
||||
{
|
||||
Logger.Log(String.Format("[Periscope] {0} {1} is the controlling agent",
|
||||
MasterAgent.Info.FirstName, MasterAgent.Info.LastName), Helpers.LogLevel.Info);
|
||||
|
||||
LoginParams login = client.Network.DefaultLoginParams(agent.Info.FirstName, agent.Info.LastName,
|
||||
agent.Info.PasswordHash, "Simian Periscope", "1.0.0");
|
||||
login.URI = PERISCOPE_LOGIN_URI;
|
||||
login.Start = "last";
|
||||
client.Network.Login(login);
|
||||
|
||||
if (client.Network.Connected)
|
||||
{
|
||||
Logger.Log("[Periscope] Connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = String.Format("Failed to connect to the foreign grid: ({0}): {1}", client.Network.LoginErrorKey,
|
||||
client.Network.LoginMessage);
|
||||
Logger.Log("[Periscope] " + error, Helpers.LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MasterAgent == null || update.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
update.AgentData.AgentID = client.Self.AgentID;
|
||||
update.AgentData.SessionID = client.Self.SessionID;
|
||||
client.Network.SendPacket(update);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGrabHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectGrabPacket grab = (ObjectGrabPacket)packet;
|
||||
|
||||
if (MasterAgent == null || grab.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
grab.AgentData.AgentID = client.Self.AgentID;
|
||||
grab.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(grab);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGrabUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectGrabUpdatePacket grabUpdate = (ObjectGrabUpdatePacket)packet;
|
||||
|
||||
if (MasterAgent == null || grabUpdate.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
grabUpdate.AgentData.AgentID = client.Self.AgentID;
|
||||
grabUpdate.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(grabUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDeGrabHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectDeGrabPacket degrab = (ObjectDeGrabPacket)packet;
|
||||
|
||||
if (MasterAgent == null || degrab.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
degrab.AgentData.AgentID = client.Self.AgentID;
|
||||
degrab.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(degrab);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerEffectHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ViewerEffectPacket effect = (ViewerEffectPacket)packet;
|
||||
|
||||
if (MasterAgent == null || effect.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
effect.AgentData.AgentID = client.Self.AgentID;
|
||||
effect.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(effect);
|
||||
}
|
||||
}
|
||||
|
||||
void AgentAnimationHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentAnimationPacket animation = (AgentAnimationPacket)packet;
|
||||
|
||||
if (MasterAgent == null || animation.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
animation.AgentData.AgentID = client.Self.AgentID;
|
||||
animation.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(animation);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Simian client packet handlers
|
||||
|
||||
AgentInfo AgentInfoFromAvatar(Avatar avatar)
|
||||
{
|
||||
// Some parts of the code use the FirstName/LastName, and other extensions
|
||||
// might try to access this data. Fill in enough to keep things sane
|
||||
AgentInfo info = new AgentInfo();
|
||||
info.AccessLevel = String.Empty;
|
||||
info.Balance = 0;
|
||||
info.CreationTime = Utils.DateTimeToUnixTime(Utils.Epoch);
|
||||
info.FirstName = avatar.FirstName;
|
||||
info.Height = 1.9f;
|
||||
info.ID = avatar.ID;
|
||||
info.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
info.LastName = avatar.LastName;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static void EraseTexture(Avatar avatar, AppearanceManager.TextureIndex texture)
|
||||
{
|
||||
Primitive.TextureEntryFace face = avatar.Textures.FaceTextures[(int)texture];
|
||||
if (face != null) face.TextureID = UUID.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandLineParser
|
||||
{
|
||||
public static string[] Parse(string str)
|
||||
{
|
||||
if (str == null || !(str.Length > 0)) return new string[0];
|
||||
int idx = str.Trim().IndexOf(" ");
|
||||
if (idx == -1) return new string[] { str };
|
||||
int count = str.Length;
|
||||
List<string> list = new List<string>();
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
if (str[0] == '"')
|
||||
{
|
||||
int temp = str.IndexOf("\"", 1, str.Length - 1);
|
||||
while (str[temp - 1] == '\\')
|
||||
{
|
||||
temp = str.IndexOf("\"", temp + 1, str.Length - temp - 1);
|
||||
}
|
||||
idx = temp + 1;
|
||||
}
|
||||
if (str[0] == '\'')
|
||||
{
|
||||
int temp = str.IndexOf("\'", 1, str.Length - 1);
|
||||
while (str[temp - 1] == '\\')
|
||||
{
|
||||
temp = str.IndexOf("\'", temp + 1, str.Length - temp - 1);
|
||||
}
|
||||
idx = temp + 1;
|
||||
}
|
||||
string s = str.Substring(0, idx);
|
||||
int left = count - idx;
|
||||
str = str.Substring(idx, left).Trim();
|
||||
list.Add(s.Trim('"'));
|
||||
count = str.Length;
|
||||
idx = str.IndexOf(" ");
|
||||
if (idx == -1)
|
||||
{
|
||||
string add = str.Trim('"', ' ');
|
||||
if (add.Length > 0)
|
||||
{
|
||||
list.Add(add);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
207
Programs/Simian/SceneExtensions/PeriscopeImageDelivery.cs
Normal file
207
Programs/Simian/SceneExtensions/PeriscopeImageDelivery.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class PeriscopeImageDelivery
|
||||
{
|
||||
public TexturePipeline Pipeline;
|
||||
|
||||
ISceneProvider scene;
|
||||
GridClient client;
|
||||
Dictionary<UUID, ImageDownload> currentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public PeriscopeImageDelivery(ISceneProvider scene, GridClient client)
|
||||
{
|
||||
this.scene = scene;
|
||||
this.client = client;
|
||||
|
||||
Pipeline = new TexturePipeline(client, 12);
|
||||
Pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Pipeline.Shutdown();
|
||||
}
|
||||
|
||||
void RequestImageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RequestImagePacket request = (RequestImagePacket)packet;
|
||||
|
||||
for (int i = 0; i < request.RequestImage.Length; i++)
|
||||
{
|
||||
RequestImagePacket.RequestImageBlock block = request.RequestImage[i];
|
||||
|
||||
ImageDownload download;
|
||||
bool downloadFound = currentDownloads.TryGetValue(block.Image, out download);
|
||||
|
||||
if (downloadFound)
|
||||
{
|
||||
lock (download)
|
||||
{
|
||||
if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
Logger.DebugLog(String.Format("Image download {0} is aborting", block.Image));
|
||||
|
||||
// Update download
|
||||
download.Update(block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
}
|
||||
}
|
||||
else if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
{
|
||||
// Aborting a download we are not tracking, this may be in the pipeline
|
||||
Pipeline.AbortDownload(block.Image);
|
||||
}
|
||||
else
|
||||
{
|
||||
//bool bake = ((ImageType)block.Type == ImageType.Baked);
|
||||
|
||||
// New download, check if we have this image
|
||||
Asset asset;
|
||||
if (scene.Server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
|
||||
{
|
||||
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have this texture, add it to the download queue and see if the bot can get it for us
|
||||
download = new ImageDownload(null, agent, block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
lock (currentDownloads)
|
||||
currentDownloads[block.Image] = download;
|
||||
|
||||
Pipeline.RequestTexture(block.Image, (ImageType)block.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pipeline_OnDownloadFinished(UUID id, bool success)
|
||||
{
|
||||
ImageDownload download;
|
||||
if (currentDownloads.TryGetValue(id, out download))
|
||||
{
|
||||
lock (currentDownloads)
|
||||
currentDownloads.Remove(id);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Set the texture to the downloaded texture data
|
||||
AssetTexture texture = new AssetTexture(id, Pipeline.GetTextureToRender(id).AssetData);
|
||||
download.Texture = texture;
|
||||
|
||||
Pipeline.RemoveFromPipeline(id);
|
||||
|
||||
// Store this texture in the local asset store for later
|
||||
scene.Server.Assets.StoreAsset(texture);
|
||||
|
||||
SendTexture(download.Agent, download.Texture, download.DiscardLevel, download.CurrentPacket, download.Priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Failed to download texture " + id.ToString(), Helpers.LogLevel.Warning);
|
||||
|
||||
ImageNotInDatabasePacket notfound = new ImageNotInDatabasePacket();
|
||||
notfound.ImageID.ID = id;
|
||||
scene.UDP.SendPacket(download.Agent.ID, notfound, PacketCategory.Texture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Pipeline downloaded a texture we're not tracking, " + id.ToString(), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
|
||||
{
|
||||
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"[Periscope] Starting new texture transfer for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
|
||||
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket, download.TexturePacketCount()));
|
||||
|
||||
// Send initial data
|
||||
ImageDataPacket data = new ImageDataPacket();
|
||||
data.ImageID.Codec = (byte)ImageCodec.J2C;
|
||||
data.ImageID.ID = download.Texture.AssetID;
|
||||
data.ImageID.Packets = (ushort)download.TexturePacketCount();
|
||||
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
|
||||
|
||||
// The first bytes of the image are always sent in the ImageData packet
|
||||
data.ImageData = new ImageDataPacket.ImageDataBlock();
|
||||
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
|
||||
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
|
||||
try
|
||||
{
|
||||
data.ImageData.Data = new byte[imageDataSize];
|
||||
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
|
||||
Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, data, PacketCategory.Texture);
|
||||
|
||||
// Check if ImagePacket packets need to be sent to complete this transfer
|
||||
if (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
// Insert this download into the dictionary
|
||||
lock (currentDownloads)
|
||||
currentDownloads[texture.AssetID] = download;
|
||||
|
||||
// Send all of the remaining packets
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
delegate(object obj)
|
||||
{
|
||||
while (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
if (download.Priority == 0.0f && download.DiscardLevel == -1)
|
||||
break;
|
||||
|
||||
lock (download)
|
||||
{
|
||||
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
|
||||
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
|
||||
|
||||
ImagePacketPacket transfer = new ImagePacketPacket();
|
||||
transfer.ImageID.ID = texture.AssetID;
|
||||
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
|
||||
transfer.ImageData.Data = new byte[imagePacketSize];
|
||||
|
||||
try
|
||||
{
|
||||
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
|
||||
transfer.ImageData.Data, 0, imagePacketSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
|
||||
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
|
||||
imagePacketSize), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, transfer, PacketCategory.Texture);
|
||||
|
||||
++download.CurrentPacket;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Completed image transfer for " + texture.AssetID.ToString());
|
||||
|
||||
// Transfer is complete, remove the reference
|
||||
lock (currentDownloads)
|
||||
currentDownloads.Remove(texture.AssetID);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
346
Programs/Simian/SceneExtensions/PeriscopeMovement.cs
Normal file
346
Programs/Simian/SceneExtensions/PeriscopeMovement.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class PeriscopeMovement
|
||||
{
|
||||
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
|
||||
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
|
||||
const float GRAVITY = 9.8f; //meters/sec
|
||||
const float WALK_SPEED = 3f; //meters/sec
|
||||
const float RUN_SPEED = 5f; //meters/sec
|
||||
const float FLY_SPEED = 10f; //meters/sec
|
||||
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
||||
const float FALL_FORGIVENESS = 0.25f; //fall buffer in meters
|
||||
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
||||
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec (no clue why this is so high)
|
||||
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
||||
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
||||
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
||||
|
||||
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
ISceneProvider scene;
|
||||
Periscope periscope;
|
||||
Timer updateTimer;
|
||||
long lastTick;
|
||||
|
||||
public int LastTick
|
||||
{
|
||||
get { return (int)Interlocked.Read(ref lastTick); }
|
||||
set { Interlocked.Exchange(ref lastTick, value); }
|
||||
}
|
||||
|
||||
public PeriscopeMovement(ISceneProvider scene, Periscope periscope)
|
||||
{
|
||||
this.scene = scene;
|
||||
this.periscope = periscope;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
||||
|
||||
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
||||
LastTick = Environment.TickCount;
|
||||
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTimer_Elapsed(object sender)
|
||||
{
|
||||
int tick = Environment.TickCount;
|
||||
float seconds = (float)((tick - LastTick) / 1000f);
|
||||
LastTick = tick;
|
||||
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
// Don't handle movement for the master agent or foreign agents
|
||||
if (agent != periscope.MasterAgent && agent.SessionID != UUID.Zero)
|
||||
{
|
||||
bool animsChanged = false;
|
||||
|
||||
// Create forward and left vectors from the current avatar rotation
|
||||
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
|
||||
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
||||
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
||||
|
||||
// Check control flags
|
||||
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
||||
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
|
||||
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
|
||||
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
|
||||
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
|
||||
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
|
||||
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
|
||||
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
|
||||
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
||||
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
|
||||
|
||||
// direction in which the avatar is trying to move
|
||||
Vector3 move = Vector3.Zero;
|
||||
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
||||
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
||||
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
||||
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
||||
if (heldUp) { move.Z += 1; }
|
||||
if (heldDown) { move.Z -= 1; }
|
||||
|
||||
// is the avatar trying to move?
|
||||
bool moving = move != Vector3.Zero;
|
||||
bool jumping = agent.TickJump != 0;
|
||||
|
||||
// 2-dimensional speed multipler
|
||||
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
||||
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
||||
speed /= SQRT_TWO;
|
||||
|
||||
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
|
||||
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
agentPosition += (move * speed);
|
||||
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
if (!flying && newFloor != oldFloor)
|
||||
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
||||
|
||||
// least possible distance from avatar to the ground
|
||||
// TODO: calculate to get rid of "bot squat"
|
||||
float lowerLimit = newFloor + agent.Avatar.Prim.Scale.Z / 2;
|
||||
|
||||
// Z acceleration resulting from gravity
|
||||
float gravity = 0f;
|
||||
|
||||
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
|
||||
|
||||
if (flying)
|
||||
{
|
||||
agent.TickFall = 0;
|
||||
agent.TickJump = 0;
|
||||
|
||||
//velocity falloff while flying
|
||||
agent.Avatar.Prim.Velocity.X *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Z *= 0.33f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z == lowerLimit)
|
||||
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //flying horizontally
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
|
||||
{ //falling, floating, or landing from a jump
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
|
||||
{ //above water
|
||||
|
||||
move = Vector3.Zero; //override controls while drifting
|
||||
agent.Avatar.Prim.Velocity *= 0.95f; //keep most of our inertia
|
||||
|
||||
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
||||
|
||||
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
|
||||
{ //just started falling
|
||||
agent.TickFall = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
|
||||
|
||||
if (!jumping)
|
||||
{ //falling
|
||||
if (fallElapsed > FALL_DELAY)
|
||||
{ //falling long enough to trigger the animation
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //on the ground
|
||||
|
||||
agent.TickFall = 0;
|
||||
|
||||
//friction
|
||||
agent.Avatar.Prim.Acceleration *= 0.2f;
|
||||
agent.Avatar.Prim.Velocity *= 0.2f;
|
||||
|
||||
agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
|
||||
if (move.Z > 0)
|
||||
{ //jumping
|
||||
if (!jumping)
|
||||
{ //begin prejump
|
||||
move.Z = 0; //override Z control
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.TickJump = Environment.TickCount;
|
||||
}
|
||||
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
|
||||
{ //start actual jump
|
||||
|
||||
if (agent.TickJump == -1)
|
||||
{
|
||||
//already jumping! end current jump
|
||||
agent.TickJump = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
|
||||
|
||||
agent.TickJump = -1; //flag that we are currently jumping
|
||||
}
|
||||
else
|
||||
{
|
||||
move.Z = 0; //override Z control
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //not jumping
|
||||
|
||||
agent.TickJump = 0;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //not walking
|
||||
|
||||
if (move.Z < 0)
|
||||
{ //crouchwalking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
if (moving)
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = move * speed;
|
||||
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = maxVel;
|
||||
}
|
||||
else
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = Vector3.Zero;
|
||||
}
|
||||
|
||||
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
|
||||
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = maxVel;
|
||||
|
||||
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
|
||||
|
||||
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
|
||||
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
|
||||
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
// Don't use the local physics to update the master agent
|
||||
if (agent != periscope.MasterAgent)
|
||||
{
|
||||
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
|
||||
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
|
||||
agent.State = (AgentState)update.AgentData.State;
|
||||
agent.HideTitle = update.AgentData.Flags != 0;
|
||||
}
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(update.AgentData.AgentID, out obj))
|
||||
{
|
||||
obj.Prim.Rotation = update.AgentData.BodyRotation;
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAlwaysRunHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
|
||||
|
||||
agent.Running = run.AgentData.AlwaysRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
407
Programs/Simian/SceneExtensions/PeriscopeTransferManager.cs
Normal file
407
Programs/Simian/SceneExtensions/PeriscopeTransferManager.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class PeriscopeTransferManager
|
||||
{
|
||||
public const string UPLOAD_DIR = "uploadedAssets";
|
||||
|
||||
ISceneProvider scene;
|
||||
GridClient client;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
/// <summary>A map from TransactionIDs to AvatarIDs</summary>
|
||||
Dictionary<UUID, KeyValuePair<UUID, UUID>> currentDownloads = new Dictionary<UUID, KeyValuePair<UUID, UUID>>();
|
||||
|
||||
public PeriscopeTransferManager(ISceneProvider scene, GridClient client)
|
||||
{
|
||||
this.scene = scene;
|
||||
this.client = client;
|
||||
|
||||
client.Assets.OnAssetReceived += new OpenMetaverse.AssetManager.AssetReceivedCallback(Assets_OnAssetReceived);
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
#region Xfer System
|
||||
|
||||
void AssetUploadRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet;
|
||||
UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID);
|
||||
|
||||
// Check if the asset is small enough to fit in a single packet
|
||||
if (request.AssetBlock.AssetData.Length != 0)
|
||||
{
|
||||
// Create a new asset from the completed upload
|
||||
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData);
|
||||
if (asset == null)
|
||||
{
|
||||
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType));
|
||||
|
||||
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
|
||||
|
||||
// Store the asset
|
||||
scene.Server.Assets.StoreAsset(asset);
|
||||
|
||||
// Send a success response
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = request.AssetBlock.Type;
|
||||
complete.AssetBlock.UUID = assetID;
|
||||
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Inventory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new (empty) asset for the upload
|
||||
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, null);
|
||||
if (asset == null)
|
||||
{
|
||||
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Starting upload for {0} ({1})", assetID, asset.AssetType));
|
||||
|
||||
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
|
||||
|
||||
RequestXferPacket xfer = new RequestXferPacket();
|
||||
xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile;
|
||||
xfer.XferID.FilePath = 0;
|
||||
xfer.XferID.Filename = Utils.EmptyBytes;
|
||||
xfer.XferID.ID = request.AssetBlock.TransactionID.GetULong();
|
||||
xfer.XferID.UseBigPackets = false;
|
||||
xfer.XferID.VFileID = asset.AssetID;
|
||||
xfer.XferID.VFileType = request.AssetBlock.Type;
|
||||
|
||||
// Add this asset to the current upload list
|
||||
lock (CurrentUploads)
|
||||
CurrentUploads[xfer.XferID.ID] = asset;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Inventory);
|
||||
}
|
||||
}
|
||||
|
||||
void SendXferPacketHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SendXferPacketPacket xfer = (SendXferPacketPacket)packet;
|
||||
|
||||
Asset asset;
|
||||
if (CurrentUploads.TryGetValue(xfer.XferID.ID, out asset))
|
||||
{
|
||||
if (asset.AssetData == null)
|
||||
{
|
||||
if (xfer.XferID.Packet != 0)
|
||||
{
|
||||
Logger.Log(String.Format("Received Xfer packet {0} before the first packet!",
|
||||
xfer.XferID.Packet), Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
uint size = Utils.BytesToUInt(xfer.DataPacket.Data);
|
||||
asset.AssetData = new byte[size];
|
||||
|
||||
Buffer.BlockCopy(xfer.DataPacket.Data, 4, asset.AssetData, 0, xfer.DataPacket.Data.Length - 4);
|
||||
|
||||
// Confirm the first upload packet
|
||||
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
|
||||
confirm.XferID.ID = xfer.XferID.ID;
|
||||
confirm.XferID.Packet = xfer.XferID.Packet;
|
||||
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(xfer.DataPacket.Data, 0, asset.AssetData, (int)xfer.XferID.Packet * 1000,
|
||||
xfer.DataPacket.Data.Length);
|
||||
|
||||
// Confirm this upload packet
|
||||
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
|
||||
confirm.XferID.ID = xfer.XferID.ID;
|
||||
confirm.XferID.Packet = xfer.XferID.Packet;
|
||||
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
|
||||
|
||||
if ((xfer.XferID.Packet & (uint)0x80000000) != 0)
|
||||
{
|
||||
// Asset upload finished
|
||||
Logger.DebugLog(String.Format("Completed Xfer upload of asset {0} ({1}", asset.AssetID, asset.AssetType));
|
||||
|
||||
lock (CurrentUploads)
|
||||
CurrentUploads.Remove(xfer.XferID.ID);
|
||||
|
||||
scene.Server.Assets.StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog("Received a SendXferPacket for an unknown upload");
|
||||
}
|
||||
}
|
||||
|
||||
void AbortXferHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AbortXferPacket abort = (AbortXferPacket)packet;
|
||||
|
||||
lock (CurrentUploads)
|
||||
{
|
||||
if (CurrentUploads.ContainsKey(abort.XferID.ID))
|
||||
{
|
||||
Logger.DebugLog(String.Format("Aborting Xfer {0}, result: {1}", abort.XferID.ID,
|
||||
(TransferError)abort.XferID.Result));
|
||||
|
||||
CurrentUploads.Remove(abort.XferID.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog(String.Format("Received an AbortXfer for an unknown xfer {0}",
|
||||
abort.XferID.ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Xfer System
|
||||
|
||||
#region Transfer System
|
||||
|
||||
void TransferRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
TransferRequestPacket request = (TransferRequestPacket)packet;
|
||||
|
||||
ChannelType channel = (ChannelType)request.TransferInfo.ChannelType;
|
||||
SourceType source = (SourceType)request.TransferInfo.SourceType;
|
||||
|
||||
if (channel == ChannelType.Asset)
|
||||
{
|
||||
if (source == SourceType.Asset)
|
||||
{
|
||||
// Parse the request
|
||||
UUID assetID = new UUID(request.TransferInfo.Params, 0);
|
||||
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 16);
|
||||
|
||||
// Check if we have this asset
|
||||
Asset asset;
|
||||
if (scene.Server.Assets.TryGetAsset(assetID, out asset))
|
||||
{
|
||||
if (asset.AssetType == type)
|
||||
{
|
||||
TransferToClient(asset, agent, request.TransferInfo.TransferID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Request for asset {0} with type {1} does not match actual asset type {2}",
|
||||
assetID, type, asset.AssetType), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the bot to try and request this asset
|
||||
lock (currentDownloads)
|
||||
{
|
||||
currentDownloads.Add(client.Assets.RequestAsset(assetID, type, false),
|
||||
new KeyValuePair<UUID, UUID>(agent.ID, request.TransferInfo.TransferID));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source == SourceType.SimEstate)
|
||||
{
|
||||
//UUID agentID = new UUID(request.TransferInfo.Params, 0);
|
||||
//UUID sessionID = new UUID(request.TransferInfo.Params, 16);
|
||||
//EstateAssetType type = (EstateAssetType)Utils.BytesToInt(request.TransferInfo.Params, 32);
|
||||
|
||||
Logger.Log("Please implement estate asset transfers", Helpers.LogLevel.Warning);
|
||||
}
|
||||
else if (source == SourceType.SimInventoryItem)
|
||||
{
|
||||
//UUID agentID = new UUID(request.TransferInfo.Params, 0);
|
||||
//UUID sessionID = new UUID(request.TransferInfo.Params, 16);
|
||||
//UUID ownerID = new UUID(request.TransferInfo.Params, 32);
|
||||
UUID taskID = new UUID(request.TransferInfo.Params, 48);
|
||||
//UUID itemID = new UUID(request.TransferInfo.Params, 64);
|
||||
//UUID assetID = new UUID(request.TransferInfo.Params, 80);
|
||||
//AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 96);
|
||||
|
||||
if (taskID != UUID.Zero)
|
||||
{
|
||||
// Task (prim) inventory request
|
||||
Logger.Log("Please implement task inventory transfers", Helpers.LogLevel.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Agent inventory request
|
||||
Logger.Log("Please implement agent inventory transfer", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
|
||||
channel, source), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
|
||||
channel, source), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void TransferToClient(Asset asset, Agent agent, UUID transferID)
|
||||
{
|
||||
Logger.Log(String.Format("Transferring asset {0} ({1})", asset.AssetID, asset.AssetType), Helpers.LogLevel.Info);
|
||||
|
||||
TransferInfoPacket response = new TransferInfoPacket();
|
||||
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
|
||||
response.TransferInfo.TransferID = transferID;
|
||||
|
||||
response.TransferInfo.Params = new byte[20];
|
||||
Buffer.BlockCopy(asset.AssetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
|
||||
Buffer.BlockCopy(Utils.IntToBytes((int)asset.AssetType), 0, response.TransferInfo.Params, 16, 4);
|
||||
|
||||
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
|
||||
response.TransferInfo.Size = asset.AssetData.Length;
|
||||
response.TransferInfo.Status = (int)StatusCode.OK;
|
||||
response.TransferInfo.TargetType = (int)TargetType.Unknown; // Doesn't seem to be used by the client
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
|
||||
|
||||
// Transfer system does not wait for ACKs, just sends all of the
|
||||
// packets for this transfer out
|
||||
const int MAX_CHUNK_SIZE = Settings.MAX_PACKET_SIZE - 100;
|
||||
int processedLength = 0;
|
||||
int packetNum = 0;
|
||||
while (processedLength < asset.AssetData.Length)
|
||||
{
|
||||
TransferPacketPacket transfer = new TransferPacketPacket();
|
||||
transfer.TransferData.ChannelType = (int)ChannelType.Asset;
|
||||
transfer.TransferData.TransferID = transferID;
|
||||
transfer.TransferData.Packet = packetNum++;
|
||||
|
||||
int chunkSize = Math.Min(asset.AssetData.Length - processedLength, MAX_CHUNK_SIZE);
|
||||
transfer.TransferData.Data = new byte[chunkSize];
|
||||
Buffer.BlockCopy(asset.AssetData, processedLength, transfer.TransferData.Data, 0, chunkSize);
|
||||
processedLength += chunkSize;
|
||||
|
||||
if (processedLength >= asset.AssetData.Length)
|
||||
transfer.TransferData.Status = (int)StatusCode.Done;
|
||||
else
|
||||
transfer.TransferData.Status = (int)StatusCode.OK;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, transfer, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Transfer System
|
||||
|
||||
void Assets_OnAssetReceived(AssetDownload transfer, Asset asset)
|
||||
{
|
||||
KeyValuePair<UUID, UUID> kvp;
|
||||
Agent agent;
|
||||
if (currentDownloads.TryGetValue(transfer.ID, out kvp))
|
||||
{
|
||||
currentDownloads.Remove(transfer.ID);
|
||||
|
||||
if (scene.TryGetAgent(kvp.Key, out agent))
|
||||
{
|
||||
if (transfer.Success)
|
||||
{
|
||||
scene.Server.Assets.StoreAsset(asset);
|
||||
TransferToClient(asset, agent, kvp.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Request for missing asset " + transfer.AssetID.ToString(), Helpers.LogLevel.Warning);
|
||||
|
||||
// Asset not found
|
||||
TransferInfoPacket response = new TransferInfoPacket();
|
||||
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
|
||||
response.TransferInfo.TransferID = kvp.Value;
|
||||
|
||||
response.TransferInfo.Params = new byte[20];
|
||||
Buffer.BlockCopy(transfer.AssetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
|
||||
Buffer.BlockCopy(Utils.IntToBytes((int)transfer.AssetType), 0, response.TransferInfo.Params, 16, 4);
|
||||
|
||||
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
|
||||
response.TransferInfo.Size = 0;
|
||||
response.TransferInfo.Status = (int)StatusCode.UnknownSource;
|
||||
response.TransferInfo.TargetType = (int)TargetType.Unknown;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Asset transfer finished for an untracked agent, ignoring", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Asset transfer finished for an untracked download, ignoring", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
Asset CreateAsset(AssetType type, UUID assetID, byte[] data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AssetType.Bodypart:
|
||||
return new AssetBodypart(assetID, data);
|
||||
case AssetType.Clothing:
|
||||
return new AssetClothing(assetID, data);
|
||||
case AssetType.LSLBytecode:
|
||||
return new AssetScriptBinary(assetID, data);
|
||||
case AssetType.LSLText:
|
||||
return new AssetScriptText(assetID, data);
|
||||
case AssetType.Notecard:
|
||||
return new AssetNotecard(assetID, data);
|
||||
case AssetType.Texture:
|
||||
return new AssetTexture(assetID, data);
|
||||
case AssetType.Animation:
|
||||
return new AssetAnimation(assetID, data);
|
||||
case AssetType.CallingCard:
|
||||
case AssetType.Folder:
|
||||
case AssetType.Gesture:
|
||||
case AssetType.ImageJPEG:
|
||||
case AssetType.ImageTGA:
|
||||
case AssetType.Landmark:
|
||||
case AssetType.LostAndFoundFolder:
|
||||
case AssetType.Object:
|
||||
case AssetType.RootFolder:
|
||||
case AssetType.Simstate:
|
||||
case AssetType.SnapshotFolder:
|
||||
case AssetType.Sound:
|
||||
return new AssetSound(assetID, data);
|
||||
case AssetType.SoundWAV:
|
||||
case AssetType.TextureTGA:
|
||||
case AssetType.TrashFolder:
|
||||
case AssetType.Unknown:
|
||||
default:
|
||||
Logger.Log("Asset type " + type.ToString() + " not implemented!", Helpers.LogLevel.Warning);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
410
Programs/Simian/SceneExtensions/PhysicsSimple.cs
Normal file
410
Programs/Simian/SceneExtensions/PhysicsSimple.cs
Normal file
@@ -0,0 +1,410 @@
|
||||
using System;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Rendering;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class PhysicsSimple : IExtension<ISceneProvider>, IPhysicsProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public float TimeDilation
|
||||
{
|
||||
get { return 1.0f; }
|
||||
}
|
||||
|
||||
public PhysicsSimple()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj)
|
||||
{
|
||||
Vector3 closestPoint = rayEnd;
|
||||
|
||||
if (rayStart == rayEnd)
|
||||
{
|
||||
Logger.DebugLog("RayStart is equal to RayEnd, returning given location");
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
Vector3 direction = Vector3.Normalize(rayEnd - rayStart);
|
||||
|
||||
// Get the mesh that has been transformed into world-space
|
||||
SimpleMesh mesh = obj.GetWorldMesh(DetailLevel.Low, false, false);
|
||||
if (mesh != null)
|
||||
{
|
||||
// Iterate through all of the triangles in the mesh, doing a ray-triangle intersection
|
||||
|
||||
float closestDistance = Single.MaxValue;
|
||||
for (int i = 0; i < mesh.Indices.Count; i += 3)
|
||||
{
|
||||
Vector3 point0 = mesh.Vertices[mesh.Indices[i + 0]].Position;
|
||||
Vector3 point1 = mesh.Vertices[mesh.Indices[i + 1]].Position;
|
||||
Vector3 point2 = mesh.Vertices[mesh.Indices[i + 2]].Position;
|
||||
|
||||
Vector3 collisionPoint;
|
||||
if (RayTriangleIntersection(rayStart, direction, point0, point1, point2, out collisionPoint))
|
||||
{
|
||||
if ((collisionPoint - rayStart).Length() < closestDistance)
|
||||
closestPoint = collisionPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
public bool TryGetObjectMass(UUID objectID, out float mass)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(objectID, out obj))
|
||||
{
|
||||
mass = CalculateMass(obj.Prim);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mass = 0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
|
||||
/// </summary>
|
||||
/// <param name="origin">Origin point of the ray</param>
|
||||
/// <param name="direction">Unit vector representing the direction of the ray</param>
|
||||
/// <param name="vert0">Position of the first triangle corner</param>
|
||||
/// <param name="vert1">Position of the second triangle corner</param>
|
||||
/// <param name="vert2">Position of the third triangle corner</param>
|
||||
/// <param name="collisionPoint">The collision point in the triangle</param>
|
||||
/// <returns>True if the ray passes through the triangle, otherwise false</returns>
|
||||
static bool RayTriangleIntersection(Vector3 origin, Vector3 direction, Vector3 vert0, Vector3 vert1, Vector3 vert2, out Vector3 collisionPoint)
|
||||
{
|
||||
const float EPSILON = 0.00001f;
|
||||
|
||||
Vector3 edge1, edge2, pvec;
|
||||
float determinant, invDeterminant;
|
||||
|
||||
// Find vectors for two edges sharing vert0
|
||||
edge1 = vert1 - vert0;
|
||||
edge2 = vert2 - vert0;
|
||||
|
||||
// Begin calculating the determinant
|
||||
pvec = Vector3.Cross(direction, edge2);
|
||||
|
||||
// If the determinant is near zero, ray lies in plane of triangle
|
||||
determinant = Vector3.Dot(edge1, pvec);
|
||||
|
||||
if (determinant > -EPSILON && determinant < EPSILON)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
invDeterminant = 1f / determinant;
|
||||
|
||||
// Calculate distance from vert0 to ray origin
|
||||
Vector3 tvec = origin - vert0;
|
||||
|
||||
// Calculate U parameter and test bounds
|
||||
float u = Vector3.Dot(tvec, pvec) * invDeterminant;
|
||||
if (u < 0.0f || u > 1.0f)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare to test V parameter
|
||||
Vector3 qvec = Vector3.Cross(tvec, edge1);
|
||||
|
||||
// Calculate V parameter and test bounds
|
||||
float v = Vector3.Dot(direction, qvec) * invDeterminant;
|
||||
if (v < 0.0f || u + v > 1.0f)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
//t = Vector3.Dot(edge2, qvec) * invDeterminant;
|
||||
|
||||
collisionPoint = new Vector3(
|
||||
vert0.X + u * (vert1.X - vert0.X) + v * (vert2.X - vert0.X),
|
||||
vert0.Y + u * (vert1.Y - vert0.Y) + v * (vert2.Y - vert0.Y),
|
||||
vert0.Z + u * (vert1.Z - vert0.Z) + v * (vert2.Z - vert0.Z));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from code written by Teravus for OpenSim
|
||||
/// </summary>
|
||||
/// <param name="prim">Primitive to calculate the mass of</param>
|
||||
/// <returns>Estimated mass of the given primitive</returns>
|
||||
static float CalculateMass(Primitive prim)
|
||||
{
|
||||
const float PRIM_DENSITY = 10.000006836f; // Aluminum g/cm3
|
||||
|
||||
float volume = 0f;
|
||||
float returnMass = 0f;
|
||||
|
||||
// TODO: Use the prim material in mass calculations once our physics
|
||||
// engine supports different materials
|
||||
|
||||
switch (prim.PrimData.ProfileCurve)
|
||||
{
|
||||
case ProfileCurve.Square:
|
||||
// Profile Volume
|
||||
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
|
||||
// If the user has 'hollowed out'
|
||||
if (prim.PrimData.ProfileHollow > 0.0f)
|
||||
{
|
||||
float hollowAmount = prim.PrimData.ProfileHollow;
|
||||
|
||||
// calculate the hollow volume by it's shape compared to the prim shape
|
||||
float hollowVolume = 0;
|
||||
switch (prim.PrimData.ProfileHole)
|
||||
{
|
||||
case HoleType.Square:
|
||||
case HoleType.Same:
|
||||
// Cube Hollow volume calculation
|
||||
float hollowsizex = prim.Scale.X * hollowAmount;
|
||||
float hollowsizey = prim.Scale.Y * hollowAmount;
|
||||
float hollowsizez = prim.Scale.Z * hollowAmount;
|
||||
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
|
||||
break;
|
||||
|
||||
case HoleType.Circle:
|
||||
// Hollow shape is a perfect cyllinder in respect to the cube's scale
|
||||
// Cyllinder hollow volume calculation
|
||||
float hRadius = prim.Scale.X * 0.5f;
|
||||
float hLength = prim.Scale.Z;
|
||||
|
||||
// pi * r2 * h
|
||||
hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
|
||||
break;
|
||||
|
||||
case HoleType.Triangle:
|
||||
// Equilateral Triangular Prism volume hollow calculation
|
||||
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
|
||||
|
||||
float aLength = prim.Scale.Y;
|
||||
// 1/2 abh
|
||||
hollowVolume = (float)((0.5 * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount);
|
||||
break;
|
||||
|
||||
default:
|
||||
hollowVolume = 0;
|
||||
break;
|
||||
}
|
||||
volume = volume - hollowVolume;
|
||||
}
|
||||
|
||||
break;
|
||||
case ProfileCurve.Circle:
|
||||
if (prim.PrimData.PathCurve == PathCurve.Line)
|
||||
{
|
||||
// Cylinder
|
||||
float volume1 = (float)(Math.PI * Math.Pow(prim.Scale.X / 2, 2) * prim.Scale.Z);
|
||||
float volume2 = (float)(Math.PI * Math.Pow(prim.Scale.Y / 2, 2) * prim.Scale.Z);
|
||||
|
||||
// Approximating the cylinder's irregularity.
|
||||
if (volume1 > volume2)
|
||||
{
|
||||
volume = (float)volume1 - (volume1 - volume2);
|
||||
}
|
||||
else if (volume2 > volume1)
|
||||
{
|
||||
volume = (float)volume2 - (volume2 - volume1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular cylinder
|
||||
volume = volume1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know what the shape is yet, so use default
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
}
|
||||
|
||||
// If the user has 'hollowed out'
|
||||
if (prim.PrimData.ProfileHollow > 0.0f)
|
||||
{
|
||||
float hollowAmount = prim.PrimData.ProfileHollow;
|
||||
|
||||
// calculate the hollow volume by it's shape compared to the prim shape
|
||||
float hollowVolume = 0f;
|
||||
switch (prim.PrimData.ProfileHole)
|
||||
{
|
||||
case HoleType.Circle:
|
||||
case HoleType.Same:
|
||||
// Hollow shape is a perfect cyllinder in respect to the cube's scale
|
||||
// Cyllinder hollow volume calculation
|
||||
float hRadius = prim.Scale.X * 0.5f;
|
||||
float hLength = prim.Scale.Z;
|
||||
|
||||
// pi * r2 * h
|
||||
hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
|
||||
break;
|
||||
|
||||
case HoleType.Square:
|
||||
// Cube Hollow volume calculation
|
||||
float hollowsizex = prim.Scale.X * hollowAmount;
|
||||
float hollowsizey = prim.Scale.Y * hollowAmount;
|
||||
float hollowsizez = prim.Scale.Z * hollowAmount;
|
||||
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
|
||||
break;
|
||||
|
||||
case HoleType.Triangle:
|
||||
// Equilateral Triangular Prism volume hollow calculation
|
||||
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
|
||||
|
||||
float aLength = prim.Scale.Y;
|
||||
// 1/2 abh
|
||||
hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount;
|
||||
break;
|
||||
|
||||
default:
|
||||
hollowVolume = 0;
|
||||
break;
|
||||
}
|
||||
volume = volume - hollowVolume;
|
||||
}
|
||||
break;
|
||||
|
||||
case ProfileCurve.HalfCircle:
|
||||
if (prim.PrimData.PathCurve == PathCurve.Circle)
|
||||
{
|
||||
if (prim.Scale.X == prim.Scale.Y && prim.Scale.Y == prim.Scale.Z)
|
||||
{
|
||||
// regular sphere
|
||||
// v = 4/3 * pi * r^3
|
||||
float sradius3 = (float)Math.Pow((prim.Scale.X * 0.5f), 3);
|
||||
volume = (4f / 3f) * (float)Math.PI * sradius3;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we treat this as a box currently
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know what the shape is yet, so use default
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
}
|
||||
break;
|
||||
|
||||
case ProfileCurve.EqualTriangle:
|
||||
float xA = -0.25f * prim.Scale.X;
|
||||
float yA = -0.45f * prim.Scale.Y;
|
||||
|
||||
float xB = 0.5f * prim.Scale.X;
|
||||
float yB = 0;
|
||||
|
||||
float xC = -0.25f * prim.Scale.X;
|
||||
float yC = 0.45f * prim.Scale.Y;
|
||||
|
||||
volume = (float)((Math.Abs((xB * yA - xA * yB) + (xC * yB - xB * yC) + (xA * yC - xC * yA)) / 2) * prim.Scale.Z);
|
||||
|
||||
// If the user has 'hollowed out'
|
||||
// ProfileHollow is one of those 0 to 50000 values :P
|
||||
// we like percentages better.. so turning into a percentage
|
||||
if (prim.PrimData.ProfileHollow > 0.0f)
|
||||
{
|
||||
float hollowAmount = prim.PrimData.ProfileHollow;
|
||||
|
||||
// calculate the hollow volume by it's shape compared to the prim shape
|
||||
float hollowVolume = 0f;
|
||||
|
||||
switch (prim.PrimData.ProfileHole)
|
||||
{
|
||||
case HoleType.Triangle:
|
||||
case HoleType.Same:
|
||||
// Equilateral Triangular Prism volume hollow calculation
|
||||
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
|
||||
|
||||
float aLength = prim.Scale.Y;
|
||||
// 1/2 abh
|
||||
hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount;
|
||||
break;
|
||||
|
||||
case HoleType.Square:
|
||||
// Cube Hollow volume calculation
|
||||
float hollowsizex = prim.Scale.X * hollowAmount;
|
||||
float hollowsizey = prim.Scale.Y * hollowAmount;
|
||||
float hollowsizez = prim.Scale.Z * hollowAmount;
|
||||
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
|
||||
break;
|
||||
|
||||
case HoleType.Circle:
|
||||
// Hollow shape is a perfect cyllinder in respect to the cube's scale
|
||||
// Cyllinder hollow volume calculation
|
||||
float hRadius = prim.Scale.X * 0.5f;
|
||||
float hLength = prim.Scale.Z;
|
||||
|
||||
// pi * r2 * h
|
||||
hollowVolume = ((float)((Math.PI * Math.Pow(hRadius, 2) * hLength) / 2) * hollowAmount);
|
||||
break;
|
||||
|
||||
default:
|
||||
hollowVolume = 0;
|
||||
break;
|
||||
}
|
||||
volume = volume - hollowVolume;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// we don't have all of the volume formulas yet so
|
||||
// use the common volume formula for all
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate Path cut effect on volume
|
||||
// Not exact, in the triangle hollow example
|
||||
// They should never be zero or less then zero..
|
||||
// we'll ignore it if it's less then zero
|
||||
|
||||
if (prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd > 0.0f)
|
||||
{
|
||||
float pathCutAmount = prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd;
|
||||
|
||||
// Check the return amount for sanity
|
||||
if (pathCutAmount >= 0.99f)
|
||||
pathCutAmount = 0.99f;
|
||||
|
||||
volume = volume - (volume * pathCutAmount);
|
||||
}
|
||||
|
||||
// Mass = density * volume
|
||||
if (prim.PrimData.PathTaperX != 1f)
|
||||
volume *= (prim.PrimData.PathTaperX / 3f) + 0.001f;
|
||||
if (prim.PrimData.PathTaperY != 1f)
|
||||
volume *= (prim.PrimData.PathTaperY / 3f) + 0.001f;
|
||||
|
||||
returnMass = PRIM_DENSITY * volume;
|
||||
|
||||
if (returnMass <= 0f)
|
||||
returnMass = 0.0001f; //ckrinke: Mass must be greater then zero.
|
||||
|
||||
return returnMass;
|
||||
}
|
||||
}
|
||||
}
|
||||
6333
Programs/Simian/SceneExtensions/ScriptApi.cs
Normal file
6333
Programs/Simian/SceneExtensions/ScriptApi.cs
Normal file
File diff suppressed because it is too large
Load Diff
229
Programs/Simian/SceneExtensions/ScriptConsole.cs
Normal file
229
Programs/Simian/SceneExtensions/ScriptConsole.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ScriptFunction
|
||||
{
|
||||
public MethodInfo Method;
|
||||
public string Name;
|
||||
public List<Type> Parameters;
|
||||
public Type Return;
|
||||
}
|
||||
|
||||
public class ScriptConsole : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
IScriptApi api;
|
||||
Dictionary<string, ScriptFunction> functions;
|
||||
|
||||
public ScriptConsole()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
// Create a single local scripting API instance that we will manage
|
||||
api = new ScriptApi();
|
||||
api.Start(scene, null, UUID.Zero, false, false);
|
||||
|
||||
// Create a dictionary of all of the scripting functions
|
||||
functions = new Dictionary<string, ScriptFunction>();
|
||||
Type apiType = typeof(IScriptApi);
|
||||
MethodInfo[] apiMethods = apiType.GetMethods();
|
||||
for (int i = 0; i < apiMethods.Length; i++)
|
||||
{
|
||||
MethodInfo method = apiMethods[i];
|
||||
|
||||
if (!method.IsConstructor && method.Name != "Start" && method.Name != "Stop")
|
||||
{
|
||||
ScriptFunction function = new ScriptFunction();
|
||||
function.Method = method;
|
||||
function.Name = method.Name;
|
||||
function.Return = method.ReturnParameter.ParameterType.UnderlyingSystemType;
|
||||
function.Parameters = new List<Type>();
|
||||
|
||||
ParameterInfo[] parms = method.GetParameters();
|
||||
for (int j = 0; j < parms.Length; j++)
|
||||
function.Parameters.Add(parms[j].ParameterType.UnderlyingSystemType);
|
||||
|
||||
functions.Add(function.Name, function);
|
||||
}
|
||||
}
|
||||
|
||||
scene.OnObjectChat += Scene_OnObjectChat;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (api)
|
||||
api.Stop();
|
||||
}
|
||||
|
||||
void PrintFunctionUsage(ScriptFunction function)
|
||||
{
|
||||
string message = "Usage: " + function.Return.Name + " " + function.Name + "(";
|
||||
for (int i = 0; i < function.Parameters.Count; i++)
|
||||
{
|
||||
message += function.Parameters[i].Name;
|
||||
if (i != function.Parameters.Count - 1)
|
||||
message += ", ";
|
||||
}
|
||||
message += ")";
|
||||
|
||||
scene.ObjectChat(this, UUID.Zero, UUID.Zero, ChatAudibleLevel.Fully, ChatType.OwnerSay, ChatSourceType.Object,
|
||||
"Script Console", Vector3.Zero, 0, message);
|
||||
}
|
||||
|
||||
List<string> ParseParameters(string paramsString)
|
||||
{
|
||||
List<string> parameters = new List<string>();
|
||||
int i = 0;
|
||||
|
||||
while (i < paramsString.Length)
|
||||
{
|
||||
int commaPos = paramsString.IndexOf(',', i);
|
||||
int bracketPos = paramsString.IndexOf('<', i);
|
||||
int endBracketPos = paramsString.IndexOf('>', i);
|
||||
string param;
|
||||
|
||||
if (bracketPos > 0 && bracketPos < commaPos)
|
||||
{
|
||||
if (endBracketPos > bracketPos)
|
||||
commaPos = paramsString.IndexOf(',', endBracketPos);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (commaPos > 0)
|
||||
{
|
||||
// ...,
|
||||
param = paramsString.Substring(i, commaPos - i);
|
||||
i = commaPos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...
|
||||
param = paramsString.Substring(i);
|
||||
i = paramsString.Length;
|
||||
}
|
||||
|
||||
parameters.Add(param);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
object[] ConvertParameters(ScriptFunction function, List<string> parameters)
|
||||
{
|
||||
object[] objParameters = new object[function.Parameters.Count];
|
||||
|
||||
for (int i = 0; i < objParameters.Length; i++)
|
||||
{
|
||||
Type paramType = function.Parameters[i];
|
||||
|
||||
if (paramType == typeof(int))
|
||||
{
|
||||
int value;
|
||||
if (Int32.TryParse(parameters[i], out value))
|
||||
objParameters[i] = value;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if (paramType == typeof(double))
|
||||
{
|
||||
double value;
|
||||
if (Double.TryParse(parameters[i], out value))
|
||||
objParameters[i] = value;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if (paramType == typeof(ScriptTypes.LSL_Vector))
|
||||
{
|
||||
ScriptTypes.LSL_Vector value;
|
||||
value = (ScriptTypes.LSL_Vector)parameters[i];
|
||||
objParameters[i] = value;
|
||||
}
|
||||
else if (paramType == typeof(ScriptTypes.LSL_Rotation))
|
||||
{
|
||||
ScriptTypes.LSL_Rotation value;
|
||||
value = (ScriptTypes.LSL_Rotation)parameters[i];
|
||||
objParameters[i] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// String value, or something that can (hopefully) be implicitly converted
|
||||
objParameters[i] = parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
return objParameters;
|
||||
}
|
||||
|
||||
void Scene_OnObjectChat(object sender, UUID ownerID, UUID sourceID, ChatAudibleLevel audible, ChatType type,
|
||||
ChatSourceType sourceType, string fromName, Vector3 position, int channel, string message)
|
||||
{
|
||||
if (sourceType == ChatSourceType.Agent && type == ChatType.Normal)
|
||||
{
|
||||
if (message.StartsWith("ll") && message.Contains("("))
|
||||
{
|
||||
int startParam = message.IndexOf('(');
|
||||
int endParam = message.IndexOf(')');
|
||||
|
||||
if (startParam > 2 && endParam > startParam)
|
||||
{
|
||||
// Try and parse this into a function call
|
||||
string name = message.Substring(0, startParam);
|
||||
|
||||
ScriptFunction function;
|
||||
if (functions.TryGetValue(name, out function))
|
||||
{
|
||||
// Parse the parameters
|
||||
++startParam;
|
||||
List<string> parameters = ParseParameters(message.Substring(startParam, endParam - startParam));
|
||||
|
||||
// Parameters sanity check
|
||||
if (parameters != null && parameters.Count == function.Parameters.Count)
|
||||
{
|
||||
// Convert the parameters into the required types
|
||||
object[] objParameters = ConvertParameters(function, parameters);
|
||||
|
||||
if (objParameters != null)
|
||||
{
|
||||
// Find the avatar that sent this chat
|
||||
SimulationObject avatar;
|
||||
if (scene.TryGetObject(ownerID, out avatar))
|
||||
{
|
||||
// Lock the API, setup the member values, and invoke the function
|
||||
lock (api)
|
||||
{
|
||||
api.Start(scene, avatar, UUID.Random(), false, false);
|
||||
|
||||
object ret = function.Method.Invoke(api, objParameters);
|
||||
|
||||
if (ret != null)
|
||||
{
|
||||
scene.ObjectChat(this, UUID.Zero, UUID.Zero, ChatAudibleLevel.Fully, ChatType.OwnerSay,
|
||||
ChatSourceType.Object, "Script Console", Vector3.Zero, 0, ret.ToString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintFunctionUsage(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Programs/Simian/SceneExtensions/TaskInventoryManager.cs
Normal file
60
Programs/Simian/SceneExtensions/TaskInventoryManager.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
// FIXME: Implement this class
|
||||
class TaskInventoryManager : IExtension<ISceneProvider>, ITaskInventoryProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public TaskInventoryManager()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public UUID CreateItem(UUID agentID, UUID containerObjectID, string name, string description, InventoryType invType,
|
||||
AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask,
|
||||
UUID ownerID, UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket)
|
||||
{
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
public bool RemoveItem(UUID agentID, UUID containerObjectID, UUID itemID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetAsset(UUID containerObjectID, UUID assetID, out Asset asset)
|
||||
{
|
||||
asset = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForEachItem(UUID containerObjectID, Action<InventoryTaskItem> action)
|
||||
{
|
||||
}
|
||||
|
||||
public InventoryTaskItem FindItem(UUID containerObjectID, Predicate<InventoryTaskItem> match)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<InventoryTaskItem> FindAllItems(UUID containerObjectID, Predicate<InventoryTaskItem> match)
|
||||
{
|
||||
return new List<InventoryTaskItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
359
Programs/Simian/SceneExtensions/TransferManager.cs
Normal file
359
Programs/Simian/SceneExtensions/TransferManager.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class TransferManager : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
|
||||
public TransferManager()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#region Xfer System
|
||||
|
||||
void AssetUploadRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet;
|
||||
UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID);
|
||||
|
||||
// Check if the asset is small enough to fit in a single packet
|
||||
if (request.AssetBlock.AssetData.Length != 0)
|
||||
{
|
||||
// Create a new asset from the completed upload
|
||||
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData);
|
||||
if (asset == null)
|
||||
{
|
||||
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType));
|
||||
|
||||
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
|
||||
|
||||
// Store the asset
|
||||
scene.Server.Assets.StoreAsset(asset);
|
||||
|
||||
// Send a success response
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = request.AssetBlock.Type;
|
||||
complete.AssetBlock.UUID = assetID;
|
||||
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Inventory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new (empty) asset for the upload
|
||||
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, null);
|
||||
if (asset == null)
|
||||
{
|
||||
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Starting upload for {0} ({1})", assetID, asset.AssetType));
|
||||
|
||||
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
|
||||
|
||||
RequestXferPacket xfer = new RequestXferPacket();
|
||||
xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile;
|
||||
xfer.XferID.FilePath = 0;
|
||||
xfer.XferID.Filename = Utils.EmptyBytes;
|
||||
xfer.XferID.ID = request.AssetBlock.TransactionID.GetULong();
|
||||
xfer.XferID.UseBigPackets = false;
|
||||
xfer.XferID.VFileID = asset.AssetID;
|
||||
xfer.XferID.VFileType = request.AssetBlock.Type;
|
||||
|
||||
// Add this asset to the current upload list
|
||||
lock (CurrentUploads)
|
||||
CurrentUploads[xfer.XferID.ID] = asset;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Inventory);
|
||||
}
|
||||
}
|
||||
|
||||
void SendXferPacketHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SendXferPacketPacket xfer = (SendXferPacketPacket)packet;
|
||||
|
||||
Asset asset;
|
||||
if (CurrentUploads.TryGetValue(xfer.XferID.ID, out asset))
|
||||
{
|
||||
if (asset.AssetData == null)
|
||||
{
|
||||
if (xfer.XferID.Packet != 0)
|
||||
{
|
||||
Logger.Log(String.Format("Received Xfer packet {0} before the first packet!",
|
||||
xfer.XferID.Packet), Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
uint size = Utils.BytesToUInt(xfer.DataPacket.Data);
|
||||
asset.AssetData = new byte[size];
|
||||
|
||||
Buffer.BlockCopy(xfer.DataPacket.Data, 4, asset.AssetData, 0, xfer.DataPacket.Data.Length - 4);
|
||||
|
||||
// Confirm the first upload packet
|
||||
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
|
||||
confirm.XferID.ID = xfer.XferID.ID;
|
||||
confirm.XferID.Packet = xfer.XferID.Packet;
|
||||
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(xfer.DataPacket.Data, 0, asset.AssetData, (int)xfer.XferID.Packet * 1000,
|
||||
xfer.DataPacket.Data.Length);
|
||||
|
||||
// Confirm this upload packet
|
||||
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
|
||||
confirm.XferID.ID = xfer.XferID.ID;
|
||||
confirm.XferID.Packet = xfer.XferID.Packet;
|
||||
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
|
||||
|
||||
if ((xfer.XferID.Packet & (uint)0x80000000) != 0)
|
||||
{
|
||||
// Asset upload finished
|
||||
Logger.DebugLog(String.Format("Completed Xfer upload of asset {0} ({1}", asset.AssetID, asset.AssetType));
|
||||
|
||||
lock (CurrentUploads)
|
||||
CurrentUploads.Remove(xfer.XferID.ID);
|
||||
|
||||
scene.Server.Assets.StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog("Received a SendXferPacket for an unknown upload");
|
||||
}
|
||||
}
|
||||
|
||||
void AbortXferHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AbortXferPacket abort = (AbortXferPacket)packet;
|
||||
|
||||
lock (CurrentUploads)
|
||||
{
|
||||
if (CurrentUploads.ContainsKey(abort.XferID.ID))
|
||||
{
|
||||
Logger.DebugLog(String.Format("Aborting Xfer {0}, result: {1}", abort.XferID.ID,
|
||||
(TransferError)abort.XferID.Result));
|
||||
|
||||
CurrentUploads.Remove(abort.XferID.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog(String.Format("Received an AbortXfer for an unknown xfer {0}",
|
||||
abort.XferID.ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Xfer System
|
||||
|
||||
#region Transfer System
|
||||
|
||||
void TransferRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
TransferRequestPacket request = (TransferRequestPacket)packet;
|
||||
|
||||
ChannelType channel = (ChannelType)request.TransferInfo.ChannelType;
|
||||
SourceType source = (SourceType)request.TransferInfo.SourceType;
|
||||
|
||||
if (channel == ChannelType.Asset)
|
||||
{
|
||||
// Construct the response packet
|
||||
TransferInfoPacket response = new TransferInfoPacket();
|
||||
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
|
||||
response.TransferInfo.TransferID = request.TransferInfo.TransferID;
|
||||
|
||||
if (source == SourceType.Asset)
|
||||
{
|
||||
// Parse the request
|
||||
UUID assetID = new UUID(request.TransferInfo.Params, 0);
|
||||
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 16);
|
||||
|
||||
// Set the response channel type
|
||||
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
|
||||
|
||||
// Params
|
||||
response.TransferInfo.Params = new byte[20];
|
||||
Buffer.BlockCopy(assetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
|
||||
Buffer.BlockCopy(Utils.IntToBytes((int)type), 0, response.TransferInfo.Params, 16, 4);
|
||||
|
||||
// Check if we have this asset
|
||||
Asset asset;
|
||||
if (scene.Server.Assets.TryGetAsset(assetID, out asset))
|
||||
{
|
||||
if (asset.AssetType == type)
|
||||
{
|
||||
Logger.DebugLog(String.Format("Transferring asset {0} ({1})", asset.AssetID, asset.AssetType));
|
||||
|
||||
// Asset found
|
||||
response.TransferInfo.Size = asset.AssetData.Length;
|
||||
response.TransferInfo.Status = (int)StatusCode.OK;
|
||||
response.TransferInfo.TargetType = (int)TargetType.Unknown; // Doesn't seem to be used by the client
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
|
||||
|
||||
// Transfer system does not wait for ACKs, just sends all of the
|
||||
// packets for this transfer out
|
||||
const int MAX_CHUNK_SIZE = Settings.MAX_PACKET_SIZE - 100;
|
||||
int processedLength = 0;
|
||||
int packetNum = 0;
|
||||
while (processedLength < asset.AssetData.Length)
|
||||
{
|
||||
TransferPacketPacket transfer = new TransferPacketPacket();
|
||||
transfer.TransferData.ChannelType = (int)ChannelType.Asset;
|
||||
transfer.TransferData.TransferID = request.TransferInfo.TransferID;
|
||||
transfer.TransferData.Packet = packetNum++;
|
||||
|
||||
int chunkSize = Math.Min(asset.AssetData.Length - processedLength, MAX_CHUNK_SIZE);
|
||||
transfer.TransferData.Data = new byte[chunkSize];
|
||||
Buffer.BlockCopy(asset.AssetData, processedLength, transfer.TransferData.Data, 0, chunkSize);
|
||||
processedLength += chunkSize;
|
||||
|
||||
if (processedLength >= asset.AssetData.Length)
|
||||
transfer.TransferData.Status = (int)StatusCode.Done;
|
||||
else
|
||||
transfer.TransferData.Status = (int)StatusCode.OK;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, transfer, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Request for asset {0} with type {1} does not match actual asset type {2}",
|
||||
assetID, type, asset.AssetType), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format("Request for missing asset {0} with type {1}",
|
||||
assetID, type), Helpers.LogLevel.Warning);
|
||||
|
||||
// Asset not found
|
||||
response.TransferInfo.Size = 0;
|
||||
response.TransferInfo.Status = (int)StatusCode.UnknownSource;
|
||||
response.TransferInfo.TargetType = (int)TargetType.Unknown;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
else if (source == SourceType.SimEstate)
|
||||
{
|
||||
UUID agentID = new UUID(request.TransferInfo.Params, 0);
|
||||
UUID sessionID = new UUID(request.TransferInfo.Params, 16);
|
||||
EstateAssetType type = (EstateAssetType)Utils.BytesToInt(request.TransferInfo.Params, 32);
|
||||
|
||||
Logger.Log("Please implement estate asset transfers", Helpers.LogLevel.Warning);
|
||||
}
|
||||
else if (source == SourceType.SimInventoryItem)
|
||||
{
|
||||
UUID agentID = new UUID(request.TransferInfo.Params, 0);
|
||||
UUID sessionID = new UUID(request.TransferInfo.Params, 16);
|
||||
UUID ownerID = new UUID(request.TransferInfo.Params, 32);
|
||||
UUID taskID = new UUID(request.TransferInfo.Params, 48);
|
||||
UUID itemID = new UUID(request.TransferInfo.Params, 64);
|
||||
UUID assetID = new UUID(request.TransferInfo.Params, 80);
|
||||
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 96);
|
||||
|
||||
if (taskID != UUID.Zero)
|
||||
{
|
||||
// Task (prim) inventory request
|
||||
Logger.Log("Please implement task inventory transfers", Helpers.LogLevel.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Agent inventory request
|
||||
Logger.Log("Please implement agent inventory transfer", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
|
||||
channel, source), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
|
||||
channel, source), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Transfer System
|
||||
|
||||
Asset CreateAsset(AssetType type, UUID assetID, byte[] data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AssetType.Bodypart:
|
||||
return new AssetBodypart(assetID, data);
|
||||
case AssetType.Clothing:
|
||||
return new AssetClothing(assetID, data);
|
||||
case AssetType.LSLBytecode:
|
||||
return new AssetScriptBinary(assetID, data);
|
||||
case AssetType.LSLText:
|
||||
return new AssetScriptText(assetID, data);
|
||||
case AssetType.Notecard:
|
||||
return new AssetNotecard(assetID, data);
|
||||
case AssetType.Texture:
|
||||
return new AssetTexture(assetID, data);
|
||||
case AssetType.Animation:
|
||||
return new AssetAnimation(assetID, data);
|
||||
case AssetType.CallingCard:
|
||||
case AssetType.Folder:
|
||||
case AssetType.Gesture:
|
||||
case AssetType.ImageJPEG:
|
||||
case AssetType.ImageTGA:
|
||||
case AssetType.Landmark:
|
||||
case AssetType.LostAndFoundFolder:
|
||||
case AssetType.Object:
|
||||
case AssetType.RootFolder:
|
||||
case AssetType.Simstate:
|
||||
case AssetType.SnapshotFolder:
|
||||
case AssetType.Sound:
|
||||
return new AssetSound(assetID, data);
|
||||
case AssetType.SoundWAV:
|
||||
case AssetType.TextureTGA:
|
||||
case AssetType.TrashFolder:
|
||||
case AssetType.Unknown:
|
||||
default:
|
||||
Logger.Log("Asset type " + type.ToString() + " not implemented!", Helpers.LogLevel.Warning);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
637
Programs/Simian/SceneExtensions/UDPManager.cs
Normal file
637
Programs/Simian/SceneExtensions/UDPManager.cs
Normal file
@@ -0,0 +1,637 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public struct IncomingPacket
|
||||
{
|
||||
public UDPClient Client;
|
||||
public Packet Packet;
|
||||
}
|
||||
|
||||
public class OutgoingPacket
|
||||
{
|
||||
public Packet Packet;
|
||||
/// <summary>Number of times this packet has been resent</summary>
|
||||
public int ResendCount;
|
||||
/// <summary>Environment.TickCount when this packet was last sent over the wire</summary>
|
||||
public int TickCount;
|
||||
/// <summary>Category this packet belongs to</summary>
|
||||
public PacketCategory Category;
|
||||
|
||||
public OutgoingPacket(Packet packet, PacketCategory category)
|
||||
{
|
||||
Packet = packet;
|
||||
Category = category;
|
||||
}
|
||||
}
|
||||
|
||||
public class UDPClient
|
||||
{
|
||||
/// <summary></summary>
|
||||
public Agent Agent;
|
||||
/// <summary></summary>
|
||||
public IPEndPoint Address;
|
||||
/// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
|
||||
public Queue<uint> PacketArchive = new Queue<uint>();
|
||||
/// <summary>Packets we have sent that need to be ACKed by the client</summary>
|
||||
public Dictionary<uint, OutgoingPacket> NeedAcks = new Dictionary<uint, OutgoingPacket>();
|
||||
/// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
|
||||
public SortedList<uint, uint> PendingAcks = new SortedList<uint, uint>();
|
||||
/// <summary>Current packet sequence number</summary>
|
||||
public int CurrentSequence = 0;
|
||||
|
||||
Timer ackTimer;
|
||||
UDPServer udpServer;
|
||||
|
||||
public UDPClient(UDPServer server, Agent agent, IPEndPoint address)
|
||||
{
|
||||
udpServer = server;
|
||||
Agent = agent;
|
||||
Address = address;
|
||||
ackTimer = new Timer(new TimerCallback(AckTimer_Elapsed), null, Settings.NETWORK_TICK_INTERVAL,
|
||||
Settings.NETWORK_TICK_INTERVAL);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
ackTimer.Dispose();
|
||||
}
|
||||
|
||||
private void AckTimer_Elapsed(object obj)
|
||||
{
|
||||
udpServer.SendAcks(this);
|
||||
udpServer.ResendUnacked(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class UDPManager : IExtension<ISceneProvider>, IUDPProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
UDPServer udpServer;
|
||||
|
||||
public event OutgoingPacketCallback OnOutgoingPacket;
|
||||
|
||||
public UDPManager()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
udpServer = new UDPServer(scene.IPAndPort, scene);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (udpServer != null)
|
||||
udpServer.Stop();
|
||||
}
|
||||
|
||||
public void AddClient(Agent agent, IPEndPoint endpoint)
|
||||
{
|
||||
udpServer.AddClient(agent, endpoint);
|
||||
}
|
||||
|
||||
public bool RemoveClient(Agent agent)
|
||||
{
|
||||
return udpServer.RemoveClient(agent);
|
||||
}
|
||||
|
||||
public uint CreateCircuit(Agent agent)
|
||||
{
|
||||
return udpServer.CreateCircuit(agent);
|
||||
}
|
||||
|
||||
public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
|
||||
{
|
||||
if (OnOutgoingPacket == null || OnOutgoingPacket(packet, agentID, category))
|
||||
udpServer.SendPacket(agentID, packet, category);
|
||||
}
|
||||
|
||||
public void BroadcastPacket(Packet packet, PacketCategory category)
|
||||
{
|
||||
if (OnOutgoingPacket == null || OnOutgoingPacket(packet, UUID.Zero, category))
|
||||
udpServer.BroadcastPacket(packet, category);
|
||||
}
|
||||
|
||||
public void RegisterPacketCallback(PacketType type, PacketCallback callback)
|
||||
{
|
||||
udpServer.RegisterPacketCallback(type, callback);
|
||||
}
|
||||
}
|
||||
|
||||
public class UDPServer : UDPBase
|
||||
{
|
||||
/// <summary>This is only used to fetch unassociated agents, which will
|
||||
/// be exposed through a login interface at some point</summary>
|
||||
ISceneProvider scene;
|
||||
/// <summary>Handlers for incoming packets</summary>
|
||||
PacketEventDictionary packetEvents = new PacketEventDictionary();
|
||||
/// <summary>Incoming packets that are awaiting handling</summary>
|
||||
BlockingQueue<IncomingPacket> packetInbox = new BlockingQueue<IncomingPacket>(Settings.PACKET_INBOX_SIZE);
|
||||
/// <summary></summary>
|
||||
DoubleDictionary<UUID, IPEndPoint, UDPClient> clients = new DoubleDictionary<UUID, IPEndPoint, UDPClient>();
|
||||
/// <summary></summary>
|
||||
Dictionary<uint, Agent> unassociatedAgents = new Dictionary<uint, Agent>();
|
||||
/// <summary></summary>
|
||||
int currentCircuitCode = 0;
|
||||
|
||||
// FIXME: Upgrade UDPBase to be able to listen on different endpoints
|
||||
public UDPServer(IPEndPoint endpoint, ISceneProvider scene)
|
||||
: base(endpoint.Port)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
Start();
|
||||
|
||||
// Start the incoming packet processing thread
|
||||
Thread incomingThread = new Thread(new ThreadStart(IncomingPacketHandler));
|
||||
incomingThread.Start();
|
||||
}
|
||||
|
||||
public void RegisterPacketCallback(PacketType type, PacketCallback callback)
|
||||
{
|
||||
packetEvents.RegisterEvent(type, callback);
|
||||
}
|
||||
|
||||
public void AddClient(Agent agent, IPEndPoint endpoint)
|
||||
{
|
||||
UDPClient client = new UDPClient(this, agent, endpoint);
|
||||
clients.Add(agent.ID, endpoint, client);
|
||||
}
|
||||
|
||||
public bool RemoveClient(Agent agent)
|
||||
{
|
||||
UDPClient client;
|
||||
if (clients.TryGetValue(agent.ID, out client))
|
||||
{
|
||||
client.Shutdown();
|
||||
return clients.Remove(agent.ID, client.Address);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint CreateCircuit(Agent agent)
|
||||
{
|
||||
uint circuitCode = (uint)Interlocked.Increment(ref currentCircuitCode);
|
||||
|
||||
// Put this client in the list of clients that have not been associated with an IPEndPoint yet
|
||||
lock (unassociatedAgents)
|
||||
unassociatedAgents[circuitCode] = agent;
|
||||
|
||||
Logger.Log("Created circuit " + circuitCode + " for " + agent.FullName, Helpers.LogLevel.Info);
|
||||
|
||||
return circuitCode;
|
||||
}
|
||||
|
||||
public void BroadcastPacket(Packet packet, PacketCategory category)
|
||||
{
|
||||
clients.ForEach(
|
||||
delegate(UDPClient client) { SendPacket(client, new OutgoingPacket(packet, category)); });
|
||||
}
|
||||
|
||||
public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
|
||||
{
|
||||
// Look up the UDPClient this is going to
|
||||
UDPClient client;
|
||||
if (clients.TryGetValue(agentID, out client))
|
||||
SendPacket(client, new OutgoingPacket(packet, category));
|
||||
}
|
||||
|
||||
void SendPacket(UDPClient client, OutgoingPacket outgoingPacket)
|
||||
{
|
||||
Packet packet = outgoingPacket.Packet;
|
||||
byte[] buffer;
|
||||
int bytes;
|
||||
|
||||
// Update the sent time for this packet
|
||||
outgoingPacket.TickCount = Environment.TickCount;
|
||||
|
||||
if (!packet.Header.Resent)
|
||||
{
|
||||
// Reset to zero if we've hit the upper sequence number limit
|
||||
Interlocked.CompareExchange(ref client.CurrentSequence, 0, 0xFFFFFF);
|
||||
// Increment and fetch the current sequence number
|
||||
uint sequence = (uint)Interlocked.Increment(ref client.CurrentSequence);
|
||||
packet.Header.Sequence = sequence;
|
||||
|
||||
if (packet.Header.Reliable)
|
||||
{
|
||||
// Add this packet to the list of ACK responses we are waiting on from this client
|
||||
lock (client.NeedAcks)
|
||||
client.NeedAcks[sequence] = outgoingPacket;
|
||||
|
||||
// This packet is reliable and not a resend, check if the conditions are favorable
|
||||
// to ACK appending
|
||||
if (packet.Type != PacketType.PacketAck)
|
||||
{
|
||||
lock (client.PendingAcks)
|
||||
{
|
||||
int count = client.PendingAcks.Count;
|
||||
|
||||
if (count > 0 && count < 10)
|
||||
{
|
||||
// Append all of the queued up outgoing ACKs to this packet
|
||||
packet.Header.AckList = new uint[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
packet.Header.AckList[i] = client.PendingAcks.Values[i];
|
||||
|
||||
client.PendingAcks.Clear();
|
||||
packet.Header.AppendedAcks = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This packet has already been sent out once, strip any appended ACKs
|
||||
// off it and reinsert them into the outgoing ACK queue under the
|
||||
// assumption that this packet will continually be rejected from the
|
||||
// client or that the appended ACKs are possibly making the delivery fail
|
||||
if (packet.Header.AckList.Length > 0)
|
||||
{
|
||||
Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.",
|
||||
packet.Header.Sequence, packet.GetType()));
|
||||
|
||||
lock (client.PendingAcks)
|
||||
{
|
||||
foreach (uint ack in packet.Header.AckList)
|
||||
{
|
||||
if (!client.PendingAcks.ContainsKey(ack))
|
||||
client.PendingAcks[ack] = ack;
|
||||
}
|
||||
}
|
||||
|
||||
packet.Header.AppendedAcks = false;
|
||||
packet.Header.AckList = new uint[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the packet
|
||||
buffer = packet.ToBytes();
|
||||
bytes = buffer.Length;
|
||||
//Stats.SentBytes += (ulong)bytes;
|
||||
//++Stats.SentPackets;
|
||||
|
||||
UDPPacketBuffer buf = new UDPPacketBuffer(client.Address);
|
||||
|
||||
// Zerocode if needed
|
||||
if (packet.Header.Zerocoded)
|
||||
bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data);
|
||||
else
|
||||
Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes);
|
||||
|
||||
buf.DataLength = bytes;
|
||||
|
||||
AsyncBeginSend(buf);
|
||||
}
|
||||
|
||||
void QueueAck(UDPClient client, uint ack)
|
||||
{
|
||||
// Add this packet to the list of ACKs that need to be sent out
|
||||
lock (client.PendingAcks)
|
||||
client.PendingAcks[ack] = ack;
|
||||
|
||||
// Send out ACKs if we have a lot of them
|
||||
if (client.PendingAcks.Count >= 10)
|
||||
SendAcks(client);
|
||||
}
|
||||
|
||||
void ProcessAcks(UDPClient client, List<uint> acks)
|
||||
{
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
foreach (uint ack in acks)
|
||||
client.NeedAcks.Remove(ack);
|
||||
}
|
||||
}
|
||||
|
||||
void SendAck(UDPClient client, uint ack)
|
||||
{
|
||||
PacketAckPacket acks = new PacketAckPacket();
|
||||
acks.Header.Reliable = false;
|
||||
acks.Packets = new PacketAckPacket.PacketsBlock[1];
|
||||
acks.Packets[0] = new PacketAckPacket.PacketsBlock();
|
||||
acks.Packets[0].ID = ack;
|
||||
|
||||
SendPacket(client, new OutgoingPacket(acks, PacketCategory.Overhead));
|
||||
}
|
||||
|
||||
public void SendAcks(UDPClient client)
|
||||
{
|
||||
PacketAckPacket acks = null;
|
||||
|
||||
lock (client.PendingAcks)
|
||||
{
|
||||
int count = client.PendingAcks.Count;
|
||||
|
||||
if (count > 250)
|
||||
{
|
||||
Logger.Log("Too many ACKs queued up!", Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
else if (count > 0)
|
||||
{
|
||||
acks = new PacketAckPacket();
|
||||
acks.Header.Reliable = false;
|
||||
acks.Packets = new PacketAckPacket.PacketsBlock[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
acks.Packets[i] = new PacketAckPacket.PacketsBlock();
|
||||
acks.Packets[i].ID = client.PendingAcks.Values[i];
|
||||
}
|
||||
|
||||
client.PendingAcks.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (acks != null)
|
||||
SendPacket(client, new OutgoingPacket(acks, PacketCategory.Overhead));
|
||||
}
|
||||
|
||||
public void ResendUnacked(UDPClient client)
|
||||
{
|
||||
if (client.NeedAcks.Count > 0)
|
||||
{
|
||||
OutgoingPacket[] array;
|
||||
int now = Environment.TickCount;
|
||||
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
// Create a temporary copy of the outgoing packets array to iterate over
|
||||
array = new OutgoingPacket[client.NeedAcks.Count];
|
||||
client.NeedAcks.Values.CopyTo(array, 0);
|
||||
}
|
||||
|
||||
// Resend packets
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
OutgoingPacket outgoing = array[i];
|
||||
|
||||
// FIXME: Make 4000 and 3 .ini settings
|
||||
if (outgoing.TickCount != 0 && now - outgoing.TickCount > 4000)
|
||||
{
|
||||
if (outgoing.ResendCount < 3)
|
||||
{
|
||||
//Logger.DebugLog(String.Format("Resending packet #{0} ({1}), {2}ms have passed",
|
||||
// outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), now - outgoing.TickCount));
|
||||
|
||||
// The TickCount will be set to the current time when the packet
|
||||
// is actually sent out again
|
||||
outgoing.TickCount = 0;
|
||||
outgoing.Packet.Header.Resent = true;
|
||||
++outgoing.ResendCount;
|
||||
|
||||
//++Stats.ResentPackets;
|
||||
|
||||
try
|
||||
{
|
||||
SendPacket(client, outgoing);
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
// Programming error elsewhere can put bad packets in the outgoing queue.
|
||||
// These will never successfully serialize, so just drop them
|
||||
Logger.Log(String.Format("Dropping bad packet #{0} ({1}) from the outgoing queue",
|
||||
outgoing.Packet.Header.Sequence, outgoing.Packet.GetType()),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
lock (client.NeedAcks) client.NeedAcks.Remove(outgoing.Packet.Header.Sequence);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format("Dropping packet #{0} ({1}) after {2} failed attempts",
|
||||
outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), outgoing.ResendCount),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
lock (client.NeedAcks) client.NeedAcks.Remove(outgoing.Packet.Header.Sequence);
|
||||
|
||||
//Disconnect an agent if no packets are received for some time
|
||||
//FIXME: Make 60000 an .ini setting
|
||||
if (Environment.TickCount - client.Agent.TickLastPacketReceived > 60000)
|
||||
{
|
||||
Logger.Log(String.Format("Ack timeout for {0}, disconnecting", client.Agent.FullName),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
scene.ObjectRemove(this, client.Agent.ID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PacketReceived(UDPPacketBuffer buffer)
|
||||
{
|
||||
UDPClient client = null;
|
||||
Packet packet = null;
|
||||
int packetEnd = buffer.DataLength - 1;
|
||||
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
|
||||
|
||||
// Decoding
|
||||
try
|
||||
{
|
||||
packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData);
|
||||
}
|
||||
catch (MalformedDataException)
|
||||
{
|
||||
Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}",
|
||||
Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
// Fail-safe check
|
||||
if (packet == null)
|
||||
{
|
||||
Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
//Stats.RecvBytes += (ulong)buffer.DataLength;
|
||||
//++Stats.RecvPackets;
|
||||
|
||||
if (packet.Type == PacketType.UseCircuitCode)
|
||||
{
|
||||
UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
|
||||
|
||||
Agent agent;
|
||||
if (CompleteAgentConnection(useCircuitCode.CircuitCode.Code, out agent))
|
||||
{
|
||||
// Sanity check that the agent isn't already logged in
|
||||
if (clients.ContainsKey(agent.ID))
|
||||
{
|
||||
Logger.Log("Client UDP reference already exists for " + agent.ID.ToString() + ", removing",
|
||||
Helpers.LogLevel.Warning);
|
||||
scene.ObjectRemove(this, agent.ID);
|
||||
clients.Remove(agent.ID);
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Adding client {0} from {1}", agent.FullName, address));
|
||||
AddClient(agent, address);
|
||||
if (clients.TryGetValue(agent.ID, out client))
|
||||
{
|
||||
Logger.Log("Activated UDP circuit " + useCircuitCode.CircuitCode.Code, Helpers.LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Failed to locate newly created UDPClient", Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Received a UseCircuitCode packet for an unrecognized circuit: " +
|
||||
useCircuitCode.CircuitCode.Code.ToString(), Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine which agent this packet came from
|
||||
if (!clients.TryGetValue(address, out client))
|
||||
{
|
||||
Logger.Log("Received UDP packet from an unrecognized source: " + address.ToString(),
|
||||
Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
client.Agent.TickLastPacketReceived = Environment.TickCount;
|
||||
|
||||
// Reliable handling
|
||||
if (packet.Header.Reliable)
|
||||
{
|
||||
// Queue up this sequence number for acknowledgement
|
||||
QueueAck(client, (uint)packet.Header.Sequence);
|
||||
//if (packet.Header.Resent) ++Stats.ReceivedResends;
|
||||
}
|
||||
|
||||
// Inbox insertion
|
||||
IncomingPacket incomingPacket;
|
||||
incomingPacket.Client = client;
|
||||
incomingPacket.Packet = packet;
|
||||
|
||||
// TODO: Prioritize the queue
|
||||
packetInbox.Enqueue(incomingPacket);
|
||||
}
|
||||
|
||||
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
|
||||
{
|
||||
}
|
||||
|
||||
void IncomingPacketHandler()
|
||||
{
|
||||
IncomingPacket incomingPacket = new IncomingPacket();
|
||||
Packet packet = null;
|
||||
UDPClient client = null;
|
||||
|
||||
while (IsRunning)
|
||||
{
|
||||
// Reset packet to null for the check below
|
||||
packet = null;
|
||||
|
||||
if (packetInbox.Dequeue(100, ref incomingPacket))
|
||||
{
|
||||
packet = incomingPacket.Packet;
|
||||
client = incomingPacket.Client;
|
||||
|
||||
if (packet != null)
|
||||
{
|
||||
#region ACK accounting
|
||||
|
||||
// Check the archives to see whether we already received this packet
|
||||
lock (client.PacketArchive)
|
||||
{
|
||||
if (client.PacketArchive.Contains(packet.Header.Sequence))
|
||||
{
|
||||
if (packet.Header.Resent)
|
||||
{
|
||||
Logger.DebugLog("Received resent packet #" + packet.Header.Sequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format("Received a duplicate of packet #{0}, current type: {1}",
|
||||
packet.Header.Sequence, packet.Type), Helpers.LogLevel.Warning);
|
||||
}
|
||||
|
||||
// Avoid firing a callback twice for the same packet
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep the PacketArchive size within a certain capacity
|
||||
while (client.PacketArchive.Count >= Settings.PACKET_ARCHIVE_SIZE)
|
||||
{
|
||||
client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
|
||||
client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
|
||||
}
|
||||
|
||||
client.PacketArchive.Enqueue(packet.Header.Sequence);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion ACK accounting
|
||||
|
||||
#region ACK handling
|
||||
|
||||
// Handle appended ACKs
|
||||
if (packet.Header.AppendedAcks)
|
||||
{
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
for (int i = 0; i < packet.Header.AckList.Length; i++)
|
||||
client.NeedAcks.Remove(packet.Header.AckList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle PacketAck packets
|
||||
if (packet.Type == PacketType.PacketAck)
|
||||
{
|
||||
PacketAckPacket ackPacket = (PacketAckPacket)packet;
|
||||
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
for (int i = 0; i < ackPacket.Packets.Length; i++)
|
||||
client.NeedAcks.Remove(ackPacket.Packets[i].ID);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion ACK handling
|
||||
|
||||
packetEvents.BeginRaiseEvent(packet.Type, packet, client.Agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CompleteAgentConnection(uint circuitCode, out Agent agent)
|
||||
{
|
||||
lock (unassociatedAgents)
|
||||
{
|
||||
if (unassociatedAgents.TryGetValue(circuitCode, out agent))
|
||||
{
|
||||
unassociatedAgents.Remove(circuitCode);
|
||||
scene.AgentAdd(this, agent, PrimFlags.None);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
Programs/Simian/SceneExtensions/XScriptEngine.cs
Normal file
110
Programs/Simian/SceneExtensions/XScriptEngine.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
// FIXME: Implement this class
|
||||
class XScriptEngine : IExtension<ISceneProvider>, IScriptEngine
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public XScriptEngine()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public bool PostScriptEvent(UUID scriptID, EventParams parms)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PostObjectEvent(UUID hostObjectID, EventParams parms)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetTimerEvent(UUID scriptID, double seconds)
|
||||
{
|
||||
}
|
||||
|
||||
public DetectParams GetDetectParams(UUID scriptID, int detectIndex)
|
||||
{
|
||||
DetectParams parms = new DetectParams();
|
||||
return parms;
|
||||
}
|
||||
|
||||
public bool GetScriptState(UUID scriptID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetScriptState(UUID scriptID, bool state)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetStartParameter(UUID scriptID, int startParam)
|
||||
{
|
||||
}
|
||||
|
||||
public int GetStartParameter(UUID scriptID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void SetScriptMinEventDelay(UUID scriptID, double minDelay)
|
||||
{
|
||||
}
|
||||
|
||||
public void TriggerState(UUID scriptID, string newState)
|
||||
{
|
||||
}
|
||||
|
||||
public void ApiResetScript(UUID scriptID)
|
||||
{
|
||||
}
|
||||
|
||||
public void ResetScript(UUID scriptID)
|
||||
{
|
||||
}
|
||||
|
||||
public int AddListener(UUID scriptID, UUID hostObjectID, int channel, string name, UUID keyID, string message)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void RemoveListener(UUID scriptID, int handle)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveListeners(UUID scriptID)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetListenerState(UUID scriptID, int handle, bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
public void SensorOnce(UUID scriptID, UUID hostObjectID, string name, UUID keyID, int type, double range, double arc)
|
||||
{
|
||||
}
|
||||
|
||||
public void SensorRepeat(UUID scriptID, UUID hostObjectID, string name, UUID keyID, int type, double range, double arc, double rate)
|
||||
{
|
||||
}
|
||||
|
||||
public void SensorRemove(UUID scriptID)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user