[Simian]
* Created an LLUDP folder to hold extensions that are purely LLUDP packet handlers. This is not a complete abstraction away from transport protocols, but it's a start * Moved physics code from Movement.cs into PhysicsSimple.cs, and moved the physics loop into a thread in SceneManager * Simian.ini cleanup git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2490 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
@@ -1,154 +0,0 @@
|
||||
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);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentThrottle, AgentThrottleHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.RegionHandshakeReply, RegionHandshakeReplyHandler);
|
||||
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);
|
||||
}
|
||||
|
||||
void AgentThrottleHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentThrottlePacket throttle = (AgentThrottlePacket)packet;
|
||||
|
||||
// TODO: These need to be transmitted to neighbor sims before child agent connections can be established
|
||||
//throttle.Throttle.Throttles
|
||||
|
||||
// Initiate the connection process for this agent to neighboring regions
|
||||
scene.InformClientOfNeighbors(agent);
|
||||
}
|
||||
|
||||
void RegionHandshakeReplyHandler(Packet packet, Agent agent)
|
||||
{
|
||||
// Send updates and appearances for every avatar to this new avatar
|
||||
SynchronizeStateTo(agent);
|
||||
}
|
||||
|
||||
// HACK: The reduction provider will deprecate this at some point
|
||||
void SynchronizeStateTo(Agent agent)
|
||||
{
|
||||
// Send the parcel overlay
|
||||
scene.Parcels.SendParcelOverlay(agent);
|
||||
|
||||
// Send object updates for objects and avatars
|
||||
scene.ForEachObject(delegate(SimulationObject obj)
|
||||
{
|
||||
ObjectUpdatePacket update = new ObjectUpdatePacket();
|
||||
update.RegionData.RegionHandle = scene.RegionHandle;
|
||||
update.RegionData.TimeDilation = (ushort)(scene.Physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1];
|
||||
update.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC);
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, update, PacketCategory.State);
|
||||
});
|
||||
|
||||
// Send appearances for all avatars
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent otherAgent)
|
||||
{
|
||||
if (otherAgent != agent)
|
||||
{
|
||||
// Send appearances for this avatar
|
||||
AvatarAppearancePacket appearance = otherAgent.BuildAppearancePacket();
|
||||
scene.UDP.SendPacket(agent.ID, appearance, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Send terrain data
|
||||
SendLayerData(agent);
|
||||
}
|
||||
|
||||
void SendLayerData(Agent agent)
|
||||
{
|
||||
for (int y = 0; y < 16; y++)
|
||||
{
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
float[,] heightmap = scene.GetTerrainPatch((uint)x, (uint)y);
|
||||
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap, x, y);
|
||||
scene.UDP.SendPacket(agent.ID, layer, PacketCategory.Terrain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,683 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,686 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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>
|
||||
{
|
||||
static readonly UUID WATER_TEXTURE = new UUID("af588c7c-52b0-4d9e-a888-1fe9d6c35f45");
|
||||
static readonly UUID HYPERGRID_MAP_TEXTURE = new UUID("3f1f56ad-7811-42e6-b3c1-98b79fc5c360");
|
||||
|
||||
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.MapItemRequest, MapItemRequestHandler);
|
||||
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;
|
||||
|
||||
// FIXME: Do this properly. Use the grid service to get the aggregated map layers
|
||||
// (lots of map tiles in a single texture == layer)
|
||||
|
||||
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 = WATER_TEXTURE;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void MapBlockRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MapBlockRequestPacket request = (MapBlockRequestPacket)packet;
|
||||
bool returnNonexistent = (request.AgentData.Flags == 0x10000);
|
||||
GridLayerType type = (GridLayerType)(request.AgentData.Flags &~0x10000);
|
||||
|
||||
IList<RegionInfo> regions = scene.Server.Grid.GetRegionsInArea(request.PositionData.MinX, request.PositionData.MinY,
|
||||
request.PositionData.MaxX, request.PositionData.MaxY);
|
||||
|
||||
MapBlockReplyPacket reply = new MapBlockReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.AgentData.Flags = (uint)type;
|
||||
|
||||
MapBlockReplyPacket.DataBlock[] blocks;
|
||||
|
||||
if (returnNonexistent)
|
||||
{
|
||||
int blockCountX = request.PositionData.MaxX + 1 - request.PositionData.MinX;
|
||||
int blockCountY = request.PositionData.MaxY + 1 - request.PositionData.MinY;
|
||||
blocks = new MapBlockReplyPacket.DataBlock[blockCountX * blockCountY];
|
||||
int i = 0;
|
||||
|
||||
for (int y = request.PositionData.MinY; y <= request.PositionData.MaxY; y++)
|
||||
{
|
||||
for (int x = request.PositionData.MinX; x <= request.PositionData.MaxX; x++)
|
||||
{
|
||||
blocks[i] = new MapBlockReplyPacket.DataBlock();
|
||||
blocks[i].X = (ushort)x;
|
||||
blocks[i].Y = (ushort)y;
|
||||
|
||||
// See if we have data for this region
|
||||
RegionInfo? region = null;
|
||||
for (int j = 0; j < regions.Count; j++)
|
||||
{
|
||||
if (regions[j].X == x && regions[j].Y == y)
|
||||
{
|
||||
region = regions[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (region.HasValue)
|
||||
{
|
||||
blocks[i].Access = (byte)SimAccess.Min;
|
||||
blocks[i].Agents = (byte)region.Value.AgentCount;
|
||||
blocks[i].MapImageID = region.Value.MapTextureID;
|
||||
blocks[i].Name = Utils.StringToBytes(region.Value.Name);
|
||||
blocks[i].RegionFlags = (uint)region.Value.Flags;
|
||||
blocks[i].WaterHeight = (byte)region.Value.WaterHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks[i].Name = Utils.EmptyBytes;
|
||||
blocks[i].MapImageID = WATER_TEXTURE;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks = new MapBlockReplyPacket.DataBlock[regions.Count];
|
||||
|
||||
for (int i = 0; i < regions.Count; i++)
|
||||
{
|
||||
RegionInfo region = regions[i];
|
||||
|
||||
blocks[i] = new MapBlockReplyPacket.DataBlock();
|
||||
blocks[i].X = (ushort)region.X;
|
||||
blocks[i].Y = (ushort)region.Y;
|
||||
blocks[i].Access = (byte)SimAccess.Min;
|
||||
blocks[i].Agents = (byte)region.AgentCount;
|
||||
blocks[i].MapImageID = region.MapTextureID;
|
||||
blocks[i].Name = Utils.StringToBytes(region.Name);
|
||||
blocks[i].RegionFlags = (uint)region.Flags;
|
||||
blocks[i].WaterHeight = (byte)region.WaterHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Handle large numbers of blocks by splitting things up
|
||||
reply.Data = blocks;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
void MapItemRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MapItemRequestPacket request = (MapItemRequestPacket)packet;
|
||||
|
||||
GridLayerType layerType = (GridLayerType)request.AgentData.Flags;
|
||||
GridItemType itemType = (GridItemType)request.RequestData.ItemType;
|
||||
|
||||
uint regionX, regionY;
|
||||
Utils.LongToUInts(request.RequestData.RegionHandle, out regionX, out regionY);
|
||||
|
||||
RegionInfo regionInfo;
|
||||
if (scene.Server.Grid.TryGetRegion(regionX, regionY, scene.RegionCertificate, out regionInfo))
|
||||
{
|
||||
Logger.Log("MapItemRequest for " + itemType + " from layer " + layerType + " in " + regionInfo.Name, Helpers.LogLevel.Info);
|
||||
|
||||
MapItemReplyPacket reply = new MapItemReplyPacket();
|
||||
reply.AgentData.AgentID = agent.ID;
|
||||
reply.AgentData.Flags = request.AgentData.Flags;
|
||||
reply.RequestData.ItemType = (uint)itemType;
|
||||
reply.Data = new MapItemReplyPacket.DataBlock[0];
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("MapItemRequest for " + itemType + " from layer " + layerType + " in unknown region at " + regionX + "," + regionY,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
// FIXME: Add XML config support for HyperGrid destinations
|
||||
/*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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,518 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,685 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +48,6 @@ namespace Simian
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -124,6 +122,21 @@ namespace Simian
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateParcel(Parcel parcel)
|
||||
{
|
||||
lock (parcels) parcels[parcel.LocalID] = parcel;
|
||||
}
|
||||
|
||||
public int GetParcelID(int x, int y)
|
||||
{
|
||||
return parcelOverlay[y * 64 + x];
|
||||
}
|
||||
|
||||
public bool TryGetParcel(int parcelID, out Parcel parcel)
|
||||
{
|
||||
return parcels.TryGetValue(parcelID, out parcel);
|
||||
}
|
||||
|
||||
void UpdateParcelSize(ref Parcel parcel)
|
||||
{
|
||||
int minX = 64;
|
||||
@@ -156,152 +169,5 @@ namespace Simian
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace Simian
|
||||
{
|
||||
public class PeriscopeTransferManager
|
||||
{
|
||||
public const string UPLOAD_DIR = "uploadedAssets";
|
||||
|
||||
ISceneProvider scene;
|
||||
GridClient client;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
|
||||
@@ -7,12 +7,25 @@ namespace Simian
|
||||
{
|
||||
public class PhysicsSimple : IExtension<ISceneProvider>, IPhysicsProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
// Run our own frames per second limiter on top of the limiting done by ISceneProvider
|
||||
const int FRAMES_PER_SECOND = 10;
|
||||
|
||||
public float TimeDilation
|
||||
{
|
||||
get { return 1.0f; }
|
||||
}
|
||||
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
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
ISceneProvider scene;
|
||||
float elapsedSinceUpdate;
|
||||
|
||||
public PhysicsSimple()
|
||||
{
|
||||
@@ -21,6 +34,8 @@ namespace Simian
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,6 +43,335 @@ namespace Simian
|
||||
{
|
||||
}
|
||||
|
||||
public void Update(float elapsedTime)
|
||||
{
|
||||
if (elapsedSinceUpdate >= 1f / (float)FRAMES_PER_SECOND)
|
||||
{
|
||||
elapsedTime = elapsedSinceUpdate;
|
||||
elapsedSinceUpdate = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsedSinceUpdate += elapsedTime;
|
||||
return;
|
||||
}
|
||||
|
||||
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 = elapsedTime * (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 * elapsedTime; //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 * elapsedTime;
|
||||
agent.Avatar.Prim.Velocity.Z += 0.75f * elapsedTime;
|
||||
|
||||
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 * elapsedTime;
|
||||
|
||||
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 * elapsedTime;
|
||||
|
||||
// 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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj)
|
||||
{
|
||||
Vector3 closestPoint = rayEnd;
|
||||
@@ -80,6 +424,28 @@ namespace Simian
|
||||
}
|
||||
}
|
||||
|
||||
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
|
||||
{
|
||||
// Recompute meshes for
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
|
||||
/// </summary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,359 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user