[Simian]
* Initial framework support for multiple simulators. Currently only the first region config is loaded, but the framework is there * Big reorganization of config and data files * Started renaming extensions that handle LLUDP packets to start with the LL prefix. Work in progress * Initial SSL support git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2482 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
@@ -4,49 +4,46 @@ using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class AccountManager : IExtension<Simian>, IAccountProvider, IPersistable
|
||||
{
|
||||
Simian server;
|
||||
DoubleDictionary<string, UUID, Agent> accounts = new DoubleDictionary<string, UUID, Agent>();
|
||||
DoubleDictionary<string, UUID, AgentInfo> accounts = new DoubleDictionary<string, UUID, AgentInfo>();
|
||||
|
||||
public AccountManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public void AddAccount(Agent agent)
|
||||
public void AddAccount(AgentInfo agent)
|
||||
{
|
||||
accounts.Add(agent.FullName, agent.ID, agent);
|
||||
accounts.Add(agent.FirstName + " " + agent.LastName, agent.ID, agent);
|
||||
}
|
||||
|
||||
public bool RemoveAccount(UUID agentID)
|
||||
{
|
||||
Agent agent;
|
||||
AgentInfo agent;
|
||||
if (accounts.TryGetValue(agentID, out agent))
|
||||
return accounts.Remove(agent.FullName, agentID);
|
||||
return accounts.Remove(agent.FirstName + " " + agent.LastName, agentID);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public Agent CreateInstance(UUID agentID)
|
||||
public AgentInfo CreateInstance(UUID agentID)
|
||||
{
|
||||
Agent agent;
|
||||
AgentInfo agent;
|
||||
if (accounts.TryGetValue(agentID, out agent))
|
||||
{
|
||||
// Random session IDs
|
||||
agent.SessionID = UUID.Random();
|
||||
agent.SecureSessionID = UUID.Random();
|
||||
|
||||
return agent;
|
||||
}
|
||||
else
|
||||
@@ -57,12 +54,12 @@ namespace Simian.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetAccount(UUID agentID, out Agent agent)
|
||||
public bool TryGetAccount(UUID agentID, out AgentInfo agent)
|
||||
{
|
||||
return accounts.TryGetValue(agentID, out agent);
|
||||
}
|
||||
|
||||
public bool TryGetAccount(string fullName, out Agent agent)
|
||||
public bool TryGetAccount(string fullName, out AgentInfo agent)
|
||||
{
|
||||
return accounts.TryGetValue(fullName, out agent);
|
||||
}
|
||||
@@ -73,10 +70,9 @@ namespace Simian.Extensions
|
||||
{
|
||||
OSDArray array = new OSDArray(accounts.Count);
|
||||
|
||||
accounts.ForEach(delegate(Agent agent)
|
||||
accounts.ForEach(delegate(AgentInfo agent)
|
||||
{
|
||||
OSDMap agentMap = OSD.SerializeMembers(agent);
|
||||
agentMap["AgentID"] = OSD.FromUUID(agent.ID);
|
||||
array.Add(agentMap);
|
||||
});
|
||||
|
||||
@@ -94,17 +90,14 @@ namespace Simian.Extensions
|
||||
|
||||
for (int i = 0; i < array.Count; i++)
|
||||
{
|
||||
Avatar avatar = new Avatar();
|
||||
SimulationObject obj = new SimulationObject(avatar, server);
|
||||
Agent agent = new Agent(obj);
|
||||
object agentRef = (object)agent;
|
||||
OSDMap map = array[i] as OSDMap;
|
||||
|
||||
AgentInfo agentInfo = new AgentInfo();
|
||||
object agentRef = (object)agentInfo;
|
||||
OSD.DeserializeMembers(ref agentRef, map);
|
||||
agent = (Agent)agentRef;
|
||||
agentInfo = (AgentInfo)agentRef;
|
||||
|
||||
agent.Avatar.Prim.ID = map["AgentID"].AsUUID();
|
||||
|
||||
accounts.Add(agent.FullName, agent.ID, agent);
|
||||
accounts.Add(agentInfo.FirstName + " " + agentInfo.LastName, agentInfo.ID, agentInfo);
|
||||
}
|
||||
|
||||
Logger.Log(String.Format("Deserialized the agent store with {0} entries", accounts.Count),
|
||||
|
||||
@@ -5,7 +5,7 @@ using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class AssetManager : IExtension<Simian>, IAssetProvider
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
@@ -27,16 +27,25 @@ namespace Simian.Extensions
|
||||
if (!Directory.Exists(Simian.DATA_DIR))
|
||||
{
|
||||
try { Directory.CreateDirectory(Simian.DATA_DIR); }
|
||||
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Warning, ex); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!Directory.Exists(UploadDir))
|
||||
{
|
||||
try { Directory.CreateDirectory(UploadDir); }
|
||||
catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Warning, ex); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LoadAssets(Simian.DATA_DIR);
|
||||
LoadAssets(UploadDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class AuthFreeForAll : IExtension<Simian>, IAuthenticationProvider
|
||||
{
|
||||
@@ -12,9 +12,10 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -23,87 +24,83 @@ namespace Simian.Extensions
|
||||
|
||||
public UUID Authenticate(string firstName, string lastName, string password)
|
||||
{
|
||||
AgentInfo agentInfo;
|
||||
ISceneProvider scene = server.Grid.GetDefaultLocalScene();
|
||||
string fullName = String.Format("{0} {1}", firstName, lastName);
|
||||
|
||||
Agent agent;
|
||||
if (!server.Accounts.TryGetAccount(fullName, out agent))
|
||||
if (!server.Accounts.TryGetAccount(fullName, out agentInfo))
|
||||
{
|
||||
// Account doesn't exist, create it now
|
||||
Avatar avatar = new Avatar();
|
||||
SimulationObject obj = new SimulationObject(avatar, server);
|
||||
agent = new Agent(obj);
|
||||
agent.AccessLevel = "M";
|
||||
agent.Avatar.Prim.ID = UUID.Random();
|
||||
agent.Balance = 1000;
|
||||
agent.CreationTime = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
agent.CurrentLookAt = Vector3.UnitZ;
|
||||
agent.Avatar.Prim.Position = new Vector3(128f, 128f, 25f);
|
||||
agent.CurrentRegionHandle = Utils.UIntsToLong(256 * server.Scene.RegionX, 256 * server.Scene.RegionY);
|
||||
agent.FirstName = firstName;
|
||||
agent.GodLevel = 0;
|
||||
agent.HomeLookAt = agent.CurrentLookAt;
|
||||
agent.HomePosition = agent.Avatar.Prim.Position;
|
||||
agent.HomeRegionHandle = agent.CurrentRegionHandle;
|
||||
agent.LastName = lastName;
|
||||
agent.PasswordHash = password;
|
||||
agentInfo = new AgentInfo();
|
||||
agentInfo.AccessLevel = "M";
|
||||
agentInfo.ID = UUID.Random();
|
||||
agentInfo.Balance = 1000;
|
||||
agentInfo.CreationTime = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
agentInfo.FirstName = firstName;
|
||||
agentInfo.GodLevel = 0;
|
||||
agentInfo.HomeLookAt = scene.DefaultLookAt;
|
||||
agentInfo.HomePosition = scene.DefaultPosition;
|
||||
agentInfo.HomeRegionHandle = scene.RegionHandle;
|
||||
agentInfo.LastName = lastName;
|
||||
agentInfo.PasswordHash = password;
|
||||
|
||||
// Create a very basic inventory skeleton
|
||||
UUID rootFolder = UUID.Random();
|
||||
server.Inventory.CreateRootFolder(agent.ID, rootFolder, "Inventory", agent.ID);
|
||||
server.Inventory.CreateRootFolder(agentInfo.ID, rootFolder, "Inventory", agentInfo.ID);
|
||||
UUID libraryRootFolder = UUID.Random();
|
||||
server.Inventory.CreateRootFolder(agent.ID, libraryRootFolder, "Library", agent.ID);
|
||||
server.Inventory.CreateRootFolder(agentInfo.ID, libraryRootFolder, "Library", agentInfo.ID);
|
||||
|
||||
agent.InventoryRoot = rootFolder;
|
||||
agent.InventoryLibraryOwner = agent.ID;
|
||||
agent.InventoryLibraryRoot = libraryRootFolder;
|
||||
agentInfo.InventoryRoot = rootFolder;
|
||||
agentInfo.InventoryLibraryOwner = agentInfo.ID;
|
||||
agentInfo.InventoryLibraryRoot = libraryRootFolder;
|
||||
|
||||
// Create some inventory items for appearance
|
||||
UUID clothingFolder = UUID.Random();
|
||||
server.Inventory.CreateFolder(agent.ID, clothingFolder, "Clothing", AssetType.Clothing,
|
||||
agent.InventoryRoot, agent.ID);
|
||||
server.Inventory.CreateFolder(agentInfo.ID, clothingFolder, "Clothing", AssetType.Clothing,
|
||||
agentInfo.InventoryRoot, agentInfo.ID);
|
||||
UUID defaultOutfitFolder = UUID.Random();
|
||||
server.Inventory.CreateFolder(agent.ID, defaultOutfitFolder, "Default Outfit", AssetType.Unknown,
|
||||
clothingFolder, agent.ID);
|
||||
server.Inventory.CreateFolder(agentInfo.ID, defaultOutfitFolder, "Default Outfit", AssetType.Unknown,
|
||||
clothingFolder, agentInfo.ID);
|
||||
|
||||
UUID hairAsset = new UUID("dc675529-7ba5-4976-b91d-dcb9e5e36188");
|
||||
UUID hairItem = server.Inventory.CreateItem(agent.ID, "Default Hair", "Default Hair",
|
||||
UUID hairItem = server.Inventory.CreateItem(agentInfo.ID, "Default Hair", "Default Hair",
|
||||
InventoryType.Wearable, AssetType.Bodypart, hairAsset, defaultOutfitFolder,
|
||||
PermissionMask.All, PermissionMask.All, agent.ID, agent.ID, UUID.Random(), 0, false);
|
||||
PermissionMask.All, PermissionMask.All, agentInfo.ID, agentInfo.ID, UUID.Random(), 0).ID;
|
||||
UUID pantsAsset = new UUID("3e8ee2d6-4f21-4a55-832d-77daa505edff");
|
||||
UUID pantsItem = server.Inventory.CreateItem(agent.ID, "Default Pants", "Default Pants",
|
||||
UUID pantsItem = server.Inventory.CreateItem(agentInfo.ID, "Default Pants", "Default Pants",
|
||||
InventoryType.Wearable, AssetType.Clothing, pantsAsset, defaultOutfitFolder,
|
||||
PermissionMask.All, PermissionMask.All, agent.ID, agent.ID, UUID.Random(), 0, false);
|
||||
PermissionMask.All, PermissionMask.All, agentInfo.ID, agentInfo.ID, UUID.Random(), 0).ID;
|
||||
UUID shapeAsset = new UUID("530a2614-052e-49a2-af0e-534bb3c05af0");
|
||||
UUID shapeItem = server.Inventory.CreateItem(agent.ID, "Default Shape", "Default Shape",
|
||||
UUID shapeItem = server.Inventory.CreateItem(agentInfo.ID, "Default Shape", "Default Shape",
|
||||
InventoryType.Wearable, AssetType.Clothing, shapeAsset, defaultOutfitFolder,
|
||||
PermissionMask.All, PermissionMask.All, agent.ID, agent.ID, UUID.Random(), 0, false);
|
||||
PermissionMask.All, PermissionMask.All, agentInfo.ID, agentInfo.ID, UUID.Random(), 0).ID;
|
||||
UUID shirtAsset = new UUID("6a714f37-fe53-4230-b46f-8db384465981");
|
||||
UUID shirtItem = server.Inventory.CreateItem(agent.ID, "Default Shirt", "Default Shirt",
|
||||
UUID shirtItem = server.Inventory.CreateItem(agentInfo.ID, "Default Shirt", "Default Shirt",
|
||||
InventoryType.Wearable, AssetType.Clothing, shirtAsset, defaultOutfitFolder,
|
||||
PermissionMask.All, PermissionMask.All, agent.ID, agent.ID, UUID.Random(), 0, false);
|
||||
PermissionMask.All, PermissionMask.All, agentInfo.ID, agentInfo.ID, UUID.Random(), 0).ID;
|
||||
UUID skinAsset = new UUID("5f787f25-f761-4a35-9764-6418ee4774c4");
|
||||
UUID skinItem = server.Inventory.CreateItem(agent.ID, "Default Skin", "Default Skin",
|
||||
UUID skinItem = server.Inventory.CreateItem(agentInfo.ID, "Default Skin", "Default Skin",
|
||||
InventoryType.Wearable, AssetType.Clothing, skinAsset, defaultOutfitFolder,
|
||||
PermissionMask.All, PermissionMask.All, agent.ID, agent.ID, UUID.Random(), 0, false);
|
||||
PermissionMask.All, PermissionMask.All, agentInfo.ID, agentInfo.ID, UUID.Random(), 0).ID;
|
||||
UUID eyesAsset = new UUID("78d20332-9b07-44a2-bf74-3b368605f4b5");
|
||||
UUID eyesItem = server.Inventory.CreateItem(agent.ID, "Default Eyes", "Default Eyes",
|
||||
UUID eyesItem = server.Inventory.CreateItem(agentInfo.ID, "Default Eyes", "Default Eyes",
|
||||
InventoryType.Wearable, AssetType.Bodypart, eyesAsset, defaultOutfitFolder,
|
||||
PermissionMask.All, PermissionMask.All, agent.ID, agent.ID, UUID.Random(), 0, false);
|
||||
PermissionMask.All, PermissionMask.All, agentInfo.ID, agentInfo.ID, UUID.Random(), 0).ID;
|
||||
|
||||
agent.HairItem = hairItem;
|
||||
agent.PantsItem = pantsItem;
|
||||
agent.ShapeItem = shapeItem;
|
||||
agent.ShirtItem = shirtItem;
|
||||
agent.SkinItem = skinItem;
|
||||
agent.EyesItem = eyesItem;
|
||||
agentInfo.HairItem = hairItem;
|
||||
agentInfo.PantsItem = pantsItem;
|
||||
agentInfo.ShapeItem = shapeItem;
|
||||
agentInfo.ShirtItem = shirtItem;
|
||||
agentInfo.SkinItem = skinItem;
|
||||
agentInfo.EyesItem = eyesItem;
|
||||
|
||||
server.Accounts.AddAccount(agent);
|
||||
server.Accounts.AddAccount(agentInfo);
|
||||
|
||||
Logger.Log("Created new account for " + fullName, Helpers.LogLevel.Info);
|
||||
}
|
||||
|
||||
if (password == agent.PasswordHash)
|
||||
return agent.ID;
|
||||
if (password == agentInfo.PasswordHash)
|
||||
return agentInfo.ID;
|
||||
else
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
class AvatarManager : IExtension<Simian>, IAvatarProvider
|
||||
{
|
||||
Simian server;
|
||||
int currentWearablesSerialNum = -1;
|
||||
int currentAnimSequenceNum = 0;
|
||||
Timer CoarseLocationTimer;
|
||||
|
||||
public AvatarManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, AvatarPropertiesRequestHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentWearablesRequest, AgentWearablesRequestHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentIsNowWearing, AgentIsNowWearingHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentSetAppearance, AgentSetAppearanceHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentCachedTexture, AgentCachedTextureHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentHeightWidth, AgentHeightWidthHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.SoundTrigger, SoundTriggerHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.UUIDNameRequest, UUIDNameRequestHandler);
|
||||
|
||||
if (CoarseLocationTimer != null) CoarseLocationTimer.Dispose();
|
||||
CoarseLocationTimer = new Timer(CoarseLocationTimer_Elapsed);
|
||||
CoarseLocationTimer.Change(1000, 1000);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (CoarseLocationTimer != null)
|
||||
{
|
||||
CoarseLocationTimer.Dispose();
|
||||
CoarseLocationTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetDefaultAnimation(Agent agent, UUID animID)
|
||||
{
|
||||
return agent.Animations.SetDefaultAnimation(animID, ref currentAnimSequenceNum);
|
||||
}
|
||||
|
||||
public bool AddAnimation(Agent agent, UUID animID)
|
||||
{
|
||||
return agent.Animations.Add(animID, ref currentAnimSequenceNum);
|
||||
}
|
||||
|
||||
public bool RemoveAnimation(Agent agent, UUID animID)
|
||||
{
|
||||
return agent.Animations.Remove(animID);
|
||||
}
|
||||
|
||||
public bool ClearAnimations(Agent agent)
|
||||
{
|
||||
agent.Animations.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SendAnimations(Agent agent)
|
||||
{
|
||||
server.Scene.ObjectAnimate(this, agent.ID, agent.ID, agent.Animations.GetAnimations());
|
||||
}
|
||||
|
||||
public void TriggerSound(Agent agent, UUID soundID, float gain)
|
||||
{
|
||||
server.Scene.TriggerSound(this, agent.ID, agent.ID, agent.ID, soundID, agent.Avatar.Prim.Position, gain);
|
||||
}
|
||||
|
||||
public void SendAlert(Agent agent, string message)
|
||||
{
|
||||
AlertMessagePacket alert = new AlertMessagePacket();
|
||||
alert.AlertData.Message = Utils.StringToBytes(message);
|
||||
server.UDP.SendPacket(agent.ID, alert, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
server.Scene.TriggerEffects(this, outgoingEffects);
|
||||
}
|
||||
|
||||
void AvatarPropertiesRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AvatarPropertiesRequestPacket request = (AvatarPropertiesRequestPacket)packet;
|
||||
|
||||
Agent foundAgent;
|
||||
if (server.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.ProfileAboutText);
|
||||
reply.PropertiesData.BornOn = Utils.StringToBytes(foundAgent.ProfileBornOn);
|
||||
reply.PropertiesData.CharterMember = new byte[1];
|
||||
reply.PropertiesData.FLAboutText = Utils.StringToBytes(foundAgent.ProfileFirstText);
|
||||
reply.PropertiesData.Flags = (uint)foundAgent.ProfileFlags;
|
||||
reply.PropertiesData.FLImageID = foundAgent.ProfileFirstImage;
|
||||
reply.PropertiesData.ImageID = foundAgent.ProfileImage;
|
||||
reply.PropertiesData.PartnerID = foundAgent.PartnerID;
|
||||
reply.PropertiesData.ProfileURL = Utils.StringToBytes(foundAgent.ProfileURL);
|
||||
|
||||
server.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("AvatarPropertiesRequest for unknown agent " + request.AgentData.AvatarID.ToString(),
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
bool TryAddWearable(UUID agentID, Dictionary<WearableType, InventoryItem> wearables, WearableType type, UUID itemID)
|
||||
{
|
||||
InventoryObject obj;
|
||||
if (itemID != UUID.Zero && server.Inventory.TryGetInventory(agentID, itemID, out obj) &&
|
||||
obj is InventoryItem)
|
||||
{
|
||||
wearables.Add(type, (InventoryItem)obj);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<WearableType, InventoryItem> GetCurrentWearables(Agent agent)
|
||||
{
|
||||
Dictionary<WearableType, InventoryItem> wearables = new Dictionary<WearableType, InventoryItem>();
|
||||
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Shape, agent.ShapeItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Skin, agent.SkinItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Hair, agent.HairItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Eyes, agent.EyesItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Shirt, agent.ShirtItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Pants, agent.PantsItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Shoes, agent.ShoesItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Socks, agent.SocksItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Jacket, agent.JacketItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Gloves, agent.GlovesItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Undershirt, agent.UndershirtItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Underpants, agent.UnderpantsItem);
|
||||
TryAddWearable(agent.ID, wearables, WearableType.Skirt, agent.SkirtItem);
|
||||
|
||||
return wearables;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
server.UDP.SendPacket(agent.ID, update, PacketCategory.Asset);
|
||||
}
|
||||
|
||||
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.ShapeItem = itemID;
|
||||
break;
|
||||
case WearableType.Skin:
|
||||
agent.SkinItem = itemID;
|
||||
break;
|
||||
case WearableType.Hair:
|
||||
agent.HairItem = itemID;
|
||||
break;
|
||||
case WearableType.Eyes:
|
||||
agent.EyesItem = itemID;
|
||||
break;
|
||||
case WearableType.Shirt:
|
||||
agent.ShirtItem = itemID;
|
||||
break;
|
||||
case WearableType.Pants:
|
||||
agent.PantsItem = itemID;
|
||||
break;
|
||||
case WearableType.Shoes:
|
||||
agent.ShoesItem = itemID;
|
||||
break;
|
||||
case WearableType.Socks:
|
||||
agent.SocksItem = itemID;
|
||||
break;
|
||||
case WearableType.Jacket:
|
||||
agent.JacketItem = itemID;
|
||||
break;
|
||||
case WearableType.Gloves:
|
||||
agent.GlovesItem = itemID;
|
||||
break;
|
||||
case WearableType.Undershirt:
|
||||
agent.UndershirtItem = itemID;
|
||||
break;
|
||||
case WearableType.Underpants:
|
||||
agent.UnderpantsItem = itemID;
|
||||
break;
|
||||
case WearableType.Skirt:
|
||||
agent.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;
|
||||
|
||||
server.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;
|
||||
|
||||
server.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 (server.Scene.TryGetAgent(id, out foundAgent))
|
||||
{
|
||||
reply.UUIDNameBlock[i].FirstName = Utils.StringToBytes(foundAgent.FirstName);
|
||||
reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(foundAgent.LastName);
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.UUIDNameBlock[i].FirstName = Utils.EmptyBytes;
|
||||
reply.UUIDNameBlock[i].LastName = Utils.EmptyBytes;
|
||||
}
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
server.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
|
||||
server.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;
|
||||
}
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.ID, update, PacketCategory.State);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using OpenMetaverse;
|
||||
using OpenMetaverse.Http;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class CapsManager : IExtension<Simian>, ICapabilitiesProvider
|
||||
{
|
||||
@@ -18,11 +18,12 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
capsServer = new CapsServer(server.HttpServer, @"^/caps/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
|
||||
capsServer.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -34,21 +35,13 @@ namespace Simian.Extensions
|
||||
public Uri CreateCapability(CapsRequestCallback localHandler, bool clientCertRequired, object state)
|
||||
{
|
||||
UUID capID = capsServer.CreateCapability(localHandler, clientCertRequired, state);
|
||||
return new Uri(
|
||||
(server.SSL ? "https://" : "http://") +
|
||||
server.HostName +
|
||||
(server.HttpPort == 80 ? String.Empty : ":" + server.HttpPort) +
|
||||
"/caps/" + capID.ToString());
|
||||
return new Uri(server.HttpUri, "/caps/" + capID.ToString());
|
||||
}
|
||||
|
||||
public Uri CreateCapability(Uri remoteHandler, bool clientCertRequired)
|
||||
{
|
||||
UUID capID = capsServer.CreateCapability(remoteHandler, clientCertRequired);
|
||||
return new Uri(
|
||||
(server.SSL ? "https://" : "http://") +
|
||||
server.HostName +
|
||||
(server.HttpPort == 80 ? String.Empty : ":" + server.HttpPort) +
|
||||
"/caps/" + capID.ToString());
|
||||
return new Uri(server.HttpUri, "/caps/" + capID.ToString());
|
||||
}
|
||||
|
||||
public bool RemoveCapability(Uri cap)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
using System;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class ConnectionManagement : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public ConnectionManagement()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.UseCircuitCode, UseCircuitCodeHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.StartPingCheck, StartPingCheckHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.LogoutRequest, LogoutRequestHandler);
|
||||
}
|
||||
|
||||
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)server.Scene.RegionFlags;
|
||||
handshake.RegionInfo.SimOwner = UUID.Random();
|
||||
handshake.RegionInfo.SimAccess = (byte)SimAccess.Min;
|
||||
handshake.RegionInfo.SimName = Utils.StringToBytes(server.Scene.RegionName);
|
||||
handshake.RegionInfo.WaterHeight = server.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 = server.Scene.RegionID;
|
||||
|
||||
server.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;
|
||||
|
||||
server.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;
|
||||
|
||||
server.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
|
||||
|
||||
server.Scene.ObjectRemove(this, agent.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class FriendManager : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public FriendManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.ImprovedInstantMessage, new PacketCallback(ImprovedInstantMessageHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void ImprovedInstantMessageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet;
|
||||
InstantMessageDialog dialog = (InstantMessageDialog)im.MessageBlock.Dialog;
|
||||
|
||||
if (dialog == InstantMessageDialog.FriendshipOffered || dialog == InstantMessageDialog.FriendshipAccepted || dialog == InstantMessageDialog.FriendshipDeclined)
|
||||
{
|
||||
// HACK: Only works for agents currently online
|
||||
Agent recipient;
|
||||
if (server.Scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
|
||||
{
|
||||
ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
|
||||
sendIM.MessageBlock.RegionID = server.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;
|
||||
|
||||
server.UDP.SendPacket(recipient.ID, sendIM, PacketCategory.Transaction);
|
||||
|
||||
if (dialog == InstantMessageDialog.FriendshipAccepted)
|
||||
{
|
||||
bool receiverOnline = server.Scene.ContainsObject(agent.ID);
|
||||
bool senderOnline = server.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;
|
||||
server.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;
|
||||
server.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;
|
||||
server.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;
|
||||
server.UDP.SendPacket(agent.ID, notify, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
70
Programs/Simian/Extensions/GridLocal.cs
Normal file
70
Programs/Simian/Extensions/GridLocal.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class GridLocal : IExtension<Simian>, IGridProvider
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public GridLocal()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public bool TryRegisterGridSpace(RegionInfo region, out UUID regionID)
|
||||
{
|
||||
regionID = UUID.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryRegisterAnyGridSpace(RegionInfo region, bool isolated, out UUID regionID)
|
||||
{
|
||||
regionID = UUID.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool UnregisterGridSpace(RegionInfo region)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RegionUpdate(RegionInfo region)
|
||||
{
|
||||
}
|
||||
|
||||
public void RegionHeartbeat(RegionInfo region)
|
||||
{
|
||||
}
|
||||
|
||||
public bool TryGetRegion(UUID regionID, out RegionInfo region)
|
||||
{
|
||||
region = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetRegion(uint x, uint y, out RegionInfo region)
|
||||
{
|
||||
region = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public ISceneProvider GetDefaultLocalScene()
|
||||
{
|
||||
if (server.Scenes.Count > 0)
|
||||
return server.Scenes[0];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
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<Simian>
|
||||
{
|
||||
Simian server;
|
||||
Dictionary<UUID, ImageDownload> CurrentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public ImageDelivery()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
}
|
||||
|
||||
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 (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;
|
||||
server.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);
|
||||
}
|
||||
|
||||
server.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);
|
||||
}
|
||||
|
||||
server.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);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class InventoryManager : IExtension<Simian>, IInventoryProvider, IPersistable
|
||||
{
|
||||
@@ -14,620 +14,23 @@ namespace Simian.Extensions
|
||||
/// is also a dictionary itself</summary>
|
||||
Dictionary<UUID, Dictionary<UUID, InventoryObject>> Inventory =
|
||||
new Dictionary<UUID, Dictionary<UUID, InventoryObject>>();
|
||||
/// <summary>Global shared inventory for all agent</summary>
|
||||
/// <summary>Global shared inventory for all agents</summary>
|
||||
Dictionary<UUID, InventoryObject> Library = new Dictionary<UUID, InventoryObject>();
|
||||
|
||||
public InventoryManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.CreateInventoryItem, new PacketCallback(CreateInventoryItemHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.CreateInventoryFolder, new PacketCallback(CreateInventoryFolderHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.UpdateInventoryItem, new PacketCallback(UpdateInventoryItemHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.FetchInventoryDescendents, new PacketCallback(FetchInventoryDescendentsHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.FetchInventory, new PacketCallback(FetchInventoryHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.CopyInventoryItem, new PacketCallback(CopyInventoryItemHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.MoveInventoryItem, new PacketCallback(MoveInventoryItemHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.MoveInventoryFolder, new PacketCallback(MoveInventoryFolderHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.PurgeInventoryDescendents, new PacketCallback(PurgeInventoryDescendentsHandler));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
Dictionary<UUID, InventoryObject> GetAgentInventory(UUID agentID)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> agentInventory;
|
||||
if (!Inventory.TryGetValue(agentID, out agentInventory))
|
||||
{
|
||||
Logger.Log("Creating an empty inventory store for agent " + agentID.ToString(),
|
||||
Helpers.LogLevel.Info);
|
||||
|
||||
agentInventory = new Dictionary<UUID, InventoryObject>();
|
||||
lock (Inventory)
|
||||
Inventory[agentID] = agentInventory;
|
||||
}
|
||||
|
||||
return agentInventory;
|
||||
}
|
||||
|
||||
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.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.InventoryRoot;
|
||||
|
||||
// Create the inventory item
|
||||
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, true);
|
||||
}
|
||||
|
||||
void CreateInventoryFolderHandler(Packet packet, Agent agent)
|
||||
{
|
||||
CreateInventoryFolderPacket create = (CreateInventoryFolderPacket)packet;
|
||||
|
||||
UUID folderID = create.FolderData.FolderID;
|
||||
if (folderID == UUID.Zero)
|
||||
folderID = agent.InventoryRoot;
|
||||
|
||||
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];
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agent.ID);
|
||||
|
||||
InventoryObject obj;
|
||||
if (agentInventory.TryGetValue(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;
|
||||
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agent.ID);
|
||||
|
||||
// TODO: Use OwnerID
|
||||
// TODO: Do we need to obey InventorySortOrder?
|
||||
InventoryObject invObject;
|
||||
if (agentInventory.TryGetValue(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];
|
||||
|
||||
server.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];
|
||||
|
||||
server.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];
|
||||
|
||||
server.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;
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agent.ID);
|
||||
|
||||
blocks[i] = new FetchInventoryReplyPacket.InventoryDataBlock();
|
||||
blocks[i].ItemID = itemID;
|
||||
|
||||
InventoryObject obj;
|
||||
if (agentInventory.TryGetValue(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];
|
||||
|
||||
server.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?
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(block.OldAgentID);
|
||||
|
||||
// Get the original object
|
||||
InventoryObject obj;
|
||||
if (agentInventory.TryGetValue(block.OldItemID, out obj) && obj is InventoryItem)
|
||||
{
|
||||
InventoryItem item = (InventoryItem)obj;
|
||||
|
||||
// Get the new folder
|
||||
InventoryObject folderObj;
|
||||
if (agentInventory.TryGetValue(block.NewFolderID, out folderObj) && folderObj is InventoryFolder)
|
||||
{
|
||||
string newName = Utils.BytesToString(block.NewName);
|
||||
if (String.IsNullOrEmpty(newName))
|
||||
newName = item.Name;
|
||||
|
||||
// Create the copy
|
||||
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, true);
|
||||
}
|
||||
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?
|
||||
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agent.ID);
|
||||
|
||||
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.InventoryRoot;
|
||||
MoveInventory(agent, agentInventory, block.ItemID, newFolderID, Utils.BytesToString(block.NewName),
|
||||
UUID.Zero, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MoveInventoryFolderHandler(Packet packet, Agent agent)
|
||||
{
|
||||
MoveInventoryFolderPacket move = (MoveInventoryFolderPacket)packet;
|
||||
// TODO: What is move.AgentData.Stamp for?
|
||||
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agent.ID);
|
||||
|
||||
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.InventoryRoot;
|
||||
MoveInventory(agent, agentInventory, block.FolderID, newFolderID, null, UUID.Zero, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SendBulkUpdate(Agent agent, InventoryObject obj, UUID transactionID, uint callbackID)
|
||||
{
|
||||
BulkUpdateInventoryPacket update = new BulkUpdateInventoryPacket();
|
||||
update.AgentData.AgentID = agent.ID;
|
||||
update.AgentData.TransactionID = transactionID;
|
||||
|
||||
if (obj is InventoryItem)
|
||||
{
|
||||
InventoryItem item = (InventoryItem)obj;
|
||||
|
||||
update.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[0];
|
||||
update.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[1];
|
||||
update.ItemData[0] = new BulkUpdateInventoryPacket.ItemDataBlock();
|
||||
update.ItemData[0].AssetID = item.AssetID;
|
||||
update.ItemData[0].BaseMask = (uint)item.Permissions.BaseMask;
|
||||
update.ItemData[0].CallbackID = callbackID;
|
||||
update.ItemData[0].CRC = item.CRC;
|
||||
update.ItemData[0].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
|
||||
update.ItemData[0].CreatorID = item.CreatorID;
|
||||
update.ItemData[0].Description = Utils.StringToBytes(item.Description);
|
||||
update.ItemData[0].EveryoneMask = (uint)item.Permissions.EveryoneMask;
|
||||
update.ItemData[0].Flags = item.Flags;
|
||||
update.ItemData[0].FolderID = item.ParentID;
|
||||
update.ItemData[0].GroupID = item.GroupID;
|
||||
update.ItemData[0].GroupMask = (uint)item.Permissions.GroupMask;
|
||||
update.ItemData[0].GroupOwned = item.GroupOwned;
|
||||
update.ItemData[0].InvType = (sbyte)item.InventoryType;
|
||||
update.ItemData[0].ItemID = item.ID;
|
||||
update.ItemData[0].Name = Utils.StringToBytes(item.Name);
|
||||
update.ItemData[0].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
|
||||
update.ItemData[0].OwnerID = item.OwnerID;
|
||||
update.ItemData[0].OwnerMask = (uint)item.Permissions.OwnerMask;
|
||||
update.ItemData[0].SalePrice = item.SalePrice;
|
||||
update.ItemData[0].SaleType = (byte)item.SaleType;
|
||||
update.ItemData[0].Type = (sbyte)item.InventoryType;
|
||||
}
|
||||
else
|
||||
{
|
||||
InventoryFolder folder = (InventoryFolder)obj;
|
||||
|
||||
update.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[0];
|
||||
update.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[1];
|
||||
update.FolderData[0] = new BulkUpdateInventoryPacket.FolderDataBlock();
|
||||
update.FolderData[0].FolderID = folder.ID;
|
||||
update.FolderData[0].Name = Utils.StringToBytes(folder.Name);
|
||||
update.FolderData[0].ParentID = folder.ParentID;
|
||||
update.FolderData[0].Type = (sbyte)folder.PreferredType;
|
||||
}
|
||||
|
||||
Logger.DebugLog("Sending bulk update for inventory object " + obj.ID);
|
||||
|
||||
server.UDP.SendPacket(agent.ID, update, PacketCategory.Inventory);
|
||||
}
|
||||
|
||||
void MoveInventory(Agent agent, Dictionary<UUID, InventoryObject> agentInventory, UUID objectID,
|
||||
UUID newFolderID, string newName, UUID transactionID, uint callbackID)
|
||||
{
|
||||
InventoryObject obj;
|
||||
if (agentInventory.TryGetValue(objectID, out obj))
|
||||
{
|
||||
lock (agentInventory)
|
||||
{
|
||||
InventoryObject newParentObj;
|
||||
if (agentInventory.TryGetValue(newFolderID, out newParentObj) && newParentObj is InventoryFolder)
|
||||
{
|
||||
// Remove this item from the current parent
|
||||
if (obj.Parent != null)
|
||||
{
|
||||
InventoryFolder parent = (InventoryFolder)obj.Parent;
|
||||
lock (parent.Children.Dictionary)
|
||||
parent.Children.Dictionary.Remove(obj.ID);
|
||||
}
|
||||
|
||||
// Update the new parent
|
||||
InventoryFolder newParent = (InventoryFolder)newParentObj;
|
||||
newParent.Children.Dictionary[obj.ID] = obj;
|
||||
|
||||
// Update the object
|
||||
obj.ParentID = newParent.ID;
|
||||
obj.Parent = newParent;
|
||||
if (!String.IsNullOrEmpty(newName))
|
||||
obj.Name = newName;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("MoveInventory called with an unknown destination folder " + newFolderID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
|
||||
SendBulkUpdate(agent, obj, transactionID, callbackID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("MoveInventory called for an unknown object " + objectID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void PurgeInventoryDescendentsHandler(Packet packet, Agent agent)
|
||||
{
|
||||
PurgeInventoryDescendentsPacket purge = (PurgeInventoryDescendentsPacket)packet;
|
||||
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agent.ID);
|
||||
|
||||
InventoryObject obj;
|
||||
if (agentInventory.TryGetValue(purge.InventoryData.FolderID, out obj) && obj is InventoryFolder)
|
||||
{
|
||||
lock (agentInventory)
|
||||
PurgeFolder(agentInventory, (InventoryFolder)obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("PurgeInventoryDescendents called on a missing folder " + purge.InventoryData.FolderID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void PurgeFolder(Dictionary<UUID, InventoryObject> inventory, InventoryFolder folder)
|
||||
{
|
||||
folder.Children.ForEach(
|
||||
delegate(InventoryObject child)
|
||||
{
|
||||
inventory.Remove(child.ID);
|
||||
|
||||
if (child is InventoryFolder)
|
||||
{
|
||||
InventoryFolder childFolder = (InventoryFolder)child;
|
||||
PurgeFolder(inventory, childFolder);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
lock (folder.Children.Dictionary)
|
||||
folder.Children.Dictionary.Clear();
|
||||
}
|
||||
|
||||
public bool CreateRootFolder(UUID agentID, UUID folderID, string name, UUID ownerID)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agentID);
|
||||
@@ -710,9 +113,9 @@ namespace Simian.Extensions
|
||||
return false;
|
||||
}
|
||||
|
||||
public UUID CreateItem(UUID agentID, string name, string description, InventoryType invType, AssetType type,
|
||||
public InventoryItem CreateItem(UUID agentID, string name, string description, InventoryType invType, AssetType type,
|
||||
UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask, UUID ownerID,
|
||||
UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket)
|
||||
UUID creatorID, UUID transactionID, uint callbackID)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agentID);
|
||||
|
||||
@@ -752,43 +155,7 @@ namespace Simian.Extensions
|
||||
lock (parentFolder.Children.Dictionary)
|
||||
parentFolder.Children.Dictionary[item.ID] = item;
|
||||
|
||||
// Send a success response
|
||||
UpdateCreateInventoryItemPacket update = new UpdateCreateInventoryItemPacket();
|
||||
update.AgentData.AgentID = agentID;
|
||||
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 = assetID;
|
||||
update.InventoryData[0].BaseMask = (uint)PermissionMask.All;
|
||||
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;
|
||||
|
||||
if (sendPacket)
|
||||
server.UDP.SendPacket(agentID, update, PacketCategory.Inventory);
|
||||
|
||||
return item.ID;
|
||||
return item;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -796,11 +163,87 @@ namespace Simian.Extensions
|
||||
"Cannot create new inventory item, folder {0} does not exist",
|
||||
parentID), Helpers.LogLevel.Warning);
|
||||
|
||||
return UUID.Zero;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InventoryObject MoveInventory(UUID agentID, UUID objectID, UUID newFolderID, string newName, UUID transactionID, uint callbackID)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agentID);
|
||||
|
||||
InventoryObject obj;
|
||||
if (agentInventory.TryGetValue(objectID, out obj))
|
||||
{
|
||||
lock (agentInventory)
|
||||
{
|
||||
InventoryObject newParentObj;
|
||||
if (agentInventory.TryGetValue(newFolderID, out newParentObj) && newParentObj is InventoryFolder)
|
||||
{
|
||||
// Remove this item from the current parent
|
||||
if (obj.Parent != null)
|
||||
{
|
||||
InventoryFolder parent = (InventoryFolder)obj.Parent;
|
||||
lock (parent.Children.Dictionary)
|
||||
parent.Children.Dictionary.Remove(obj.ID);
|
||||
}
|
||||
|
||||
// Update the new parent
|
||||
InventoryFolder newParent = (InventoryFolder)newParentObj;
|
||||
newParent.Children.Dictionary[obj.ID] = obj;
|
||||
|
||||
// Update the object
|
||||
obj.ParentID = newParent.ID;
|
||||
obj.Parent = newParent;
|
||||
if (!String.IsNullOrEmpty(newName))
|
||||
obj.Name = newName;
|
||||
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("MoveInventory called with an unknown destination folder " + newFolderID,
|
||||
Helpers.LogLevel.Warning);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("MoveInventory called for an unknown object " + objectID,
|
||||
Helpers.LogLevel.Warning);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void PurgeFolder(UUID agentID, UUID folderID)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> agentInventory = GetAgentInventory(agentID);
|
||||
|
||||
InventoryObject obj;
|
||||
if (TryGetInventory(agentID, folderID, out obj) && obj is InventoryFolder)
|
||||
{
|
||||
InventoryFolder folder = (InventoryFolder)obj;
|
||||
|
||||
folder.Children.ForEach(
|
||||
delegate(InventoryObject child)
|
||||
{
|
||||
agentInventory.Remove(child.ID);
|
||||
|
||||
if (child is InventoryFolder)
|
||||
PurgeFolder(agentID, child.ID);
|
||||
}
|
||||
);
|
||||
|
||||
lock (folder.Children.Dictionary)
|
||||
folder.Children.Dictionary.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("PurgeFolder called on a missing folder " + folderID, Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetInventory(UUID agentID, UUID objectID, out InventoryObject obj)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> inventory;
|
||||
@@ -856,6 +299,27 @@ namespace Simian.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
public bool InventoryExists(UUID agentID)
|
||||
{
|
||||
return Inventory.ContainsKey(agentID);
|
||||
}
|
||||
|
||||
Dictionary<UUID, InventoryObject> GetAgentInventory(UUID agentID)
|
||||
{
|
||||
Dictionary<UUID, InventoryObject> agentInventory;
|
||||
if (!Inventory.TryGetValue(agentID, out agentInventory))
|
||||
{
|
||||
Logger.Log("Creating an empty inventory store for agent " + agentID.ToString(),
|
||||
Helpers.LogLevel.Info);
|
||||
|
||||
agentInventory = new Dictionary<UUID, InventoryObject>();
|
||||
lock (Inventory)
|
||||
Inventory[agentID] = agentInventory;
|
||||
}
|
||||
|
||||
return agentInventory;
|
||||
}
|
||||
|
||||
#region Persistence
|
||||
|
||||
OSDMap SerializeItem(InventoryItem item)
|
||||
|
||||
@@ -7,7 +7,7 @@ using ExtensionLoader;
|
||||
using HttpServer;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class LindenLogin : IExtension<Simian>
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Simian.Extensions
|
||||
|
||||
// Client LLSD login
|
||||
server.HttpServer.AddHandler("post", "application/xml", "^/$", LoginLLSDPostHandler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -175,61 +175,69 @@ namespace Simian.Extensions
|
||||
|
||||
LoginResponseData HandleLogin(string firstName, string lastName, string password, string start, string version, string channel)
|
||||
{
|
||||
LoginResponseData response = new LoginResponseData();
|
||||
Agent agent;
|
||||
ISceneProvider scene = server.Grid.GetDefaultLocalScene();
|
||||
|
||||
LoginResponseData response = new LoginResponseData();
|
||||
AgentInfo agentInfo;
|
||||
|
||||
// Attempt to authenticate
|
||||
UUID agentID = server.Authentication.Authenticate(firstName, lastName, password);
|
||||
if (agentID != UUID.Zero)
|
||||
{
|
||||
// Authentication successful, create a login instance of this agent
|
||||
agent = server.Accounts.CreateInstance(agentID);
|
||||
|
||||
// Create a seed capability for this agent
|
||||
Uri seedCap = server.Capabilities.CreateCapability(server.Scene.SeedCapabilityHandler, false, agentID);
|
||||
|
||||
if (agent != null)
|
||||
agentInfo = server.Accounts.CreateInstance(agentID);
|
||||
if (agentInfo != null)
|
||||
{
|
||||
Agent agent = new Agent(new SimulationObject(new Avatar(), scene), agentInfo);
|
||||
|
||||
// Set the avatar ID
|
||||
agent.Avatar.Prim.ID = agentInfo.ID;
|
||||
|
||||
// Random session IDs
|
||||
agent.SessionID = UUID.Random();
|
||||
agent.SecureSessionID = UUID.Random();
|
||||
|
||||
// Create a seed capability for this agent
|
||||
agent.SeedCapability = server.Capabilities.CreateCapability(scene.SeedCapabilityHandler, false, agentID);
|
||||
|
||||
// Assign a circuit code and insert the agent into the unassociatedAgents dictionary
|
||||
agent.CircuitCode = server.UDP.CreateCircuit(agent);
|
||||
agent.CircuitCode = scene.UDP.CreateCircuit(agent);
|
||||
|
||||
agent.TickLastPacketReceived = Environment.TickCount;
|
||||
agent.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
agent.Info.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
|
||||
// Get this machine's IP address
|
||||
IPHostEntry entry = Dns.GetHostEntry(System.Environment.MachineName);
|
||||
IPAddress simIP = entry.AddressList.Length > 0 ?
|
||||
entry.AddressList[entry.AddressList.Length - 1] : IPAddress.Loopback;
|
||||
|
||||
agent.CurrentRegionHandle = server.Scene.RegionHandle;
|
||||
agent.HomeRegionHandle = server.Scene.RegionHandle;
|
||||
|
||||
response.AgentID = agent.ID;
|
||||
response.SecureSessionID = agent.SecureSessionID;
|
||||
response.SessionID = agent.SessionID;
|
||||
response.CircuitCode = agent.CircuitCode;
|
||||
response.AgentAccess = agent.AccessLevel;
|
||||
response.AgentAccess = agent.Info.AccessLevel;
|
||||
response.BuddyList = null; // FIXME:
|
||||
response.FirstName = agent.FirstName;
|
||||
response.HomeLookAt = agent.HomeLookAt;
|
||||
response.HomePosition = agent.HomePosition;
|
||||
response.HomeRegion = agent.HomeRegionHandle;
|
||||
response.InventoryRoot = agent.InventoryRoot;
|
||||
response.FirstName = agent.Info.FirstName;
|
||||
response.HomeLookAt = agent.Info.HomeLookAt;
|
||||
response.HomePosition = agent.Info.HomePosition;
|
||||
response.HomeRegion = agent.Info.HomeRegionHandle;
|
||||
response.InventoryRoot = agent.Info.InventoryRoot;
|
||||
response.InventorySkeleton = null; // FIXME:
|
||||
response.LastName = agent.LastName;
|
||||
response.LibraryOwner = agent.InventoryLibraryOwner;
|
||||
response.LibraryRoot = agent.InventoryLibraryRoot;
|
||||
response.LastName = agent.Info.LastName;
|
||||
response.LibraryOwner = agent.Info.InventoryLibraryOwner;
|
||||
response.LibraryRoot = agent.Info.InventoryLibraryRoot;
|
||||
response.LibrarySkeleton = null; // FIXME:
|
||||
response.LookAt = agent.CurrentLookAt;
|
||||
response.Message = "Welcome to Simian";
|
||||
response.Reason = String.Empty;
|
||||
|
||||
response.RegionX = server.Scene.RegionX * 256;
|
||||
response.RegionY = server.Scene.RegionY * 256;
|
||||
response.RegionX = scene.RegionX * 256;
|
||||
response.RegionY = scene.RegionY * 256;
|
||||
|
||||
response.SecondsSinceEpoch = DateTime.Now;
|
||||
response.SeedCapability = seedCap.ToString();
|
||||
response.SeedCapability = agent.SeedCapability.ToString();
|
||||
response.SimIP = simIP;
|
||||
response.SimPort = (ushort)server.UDPPort;
|
||||
response.SimPort = (ushort)scene.IPAndPort.Port;
|
||||
response.StartLocation = "last"; // FIXME:
|
||||
response.Success = true;
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class Messaging : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public Messaging()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, new PacketCallback(ChatFromViewerHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ImprovedInstantMessage, new PacketCallback(ImprovedInstantMessageHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void ChatFromViewerHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ChatFromViewerPacket viewerChat = (ChatFromViewerPacket)packet;
|
||||
|
||||
server.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 (server.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 = server.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;
|
||||
|
||||
server.UDP.SendPacket(recipient.ID, sendIM, PacketCategory.Messaging);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
// FIXME: Implement this class
|
||||
class MessagingLocal : IExtension<Simian>, IMessagingProvider
|
||||
@@ -16,9 +16,10 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
class Money : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public Money()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.MoneyBalanceRequest, new PacketCallback(MoneyBalanceRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.MoneyTransferRequest, new PacketCallback(MoneyTransferRequestHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void SendBalance(Agent agent, UUID transactionID, string message)
|
||||
{
|
||||
MoneyBalanceReplyPacket reply = new MoneyBalanceReplyPacket();
|
||||
reply.MoneyData.AgentID = agent.ID;
|
||||
reply.MoneyData.MoneyBalance = agent.Balance;
|
||||
reply.MoneyData.TransactionID = transactionID;
|
||||
reply.MoneyData.Description = Utils.StringToBytes(message);
|
||||
|
||||
server.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.Balance)
|
||||
return;
|
||||
|
||||
// HACK: Only works for sending money to someone who is online
|
||||
Agent recipient;
|
||||
if (server.Scene.TryGetAgent(request.MoneyData.DestID, out recipient))
|
||||
{
|
||||
agent.Balance -= request.MoneyData.Amount;
|
||||
recipient.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,517 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.Rendering;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class Movement : IExtension<Simian>
|
||||
{
|
||||
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;
|
||||
|
||||
Simian server;
|
||||
Timer updateTimer;
|
||||
long lastTick;
|
||||
|
||||
public int LastTick
|
||||
{
|
||||
get { return (int) Interlocked.Read(ref lastTick); }
|
||||
set { Interlocked.Exchange(ref lastTick, value); }
|
||||
}
|
||||
|
||||
public Movement()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.Scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
||||
|
||||
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
||||
LastTick = Environment.TickCount;
|
||||
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void 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;
|
||||
|
||||
server.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 = server.Scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
agentPosition += (move * speed);
|
||||
float newFloor = server.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
|
||||
);
|
||||
|
||||
server.Scene.ForEachObject(delegate(SimulationObject obj)
|
||||
{
|
||||
//HACK: check nearby objects (what did you expect, octree?)
|
||||
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
|
||||
{
|
||||
Vector3 collision = server.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 = server.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 (server.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (server.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 > server.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 (server.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 (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.X != 0 || move.Y != 0)
|
||||
{
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (server.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 (server.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 (server.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 (server.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 (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
server.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 (server.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;
|
||||
|
||||
server.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 (server.Scene.TryGetObject(agent.RequestedSitTarget, out obj) && server.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);
|
||||
|
||||
server.Scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
|
||||
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
|
||||
server.Avatars.SetDefaultAnimation(agent, Animations.SIT);
|
||||
server.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 (server.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 (server.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;
|
||||
|
||||
server.Avatars.SetDefaultAnimation(agent, Animations.STAND);
|
||||
server.Avatars.SendAnimations(agent);
|
||||
|
||||
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
|
||||
}
|
||||
|
||||
server.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,765 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Rendering;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class ObjectManager : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public ObjectManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectAdd, new PacketCallback(ObjectAddHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectAttach, new PacketCallback(ObjectAttachHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectDuplicate, new PacketCallback(ObjectDuplicateHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectSelect, new PacketCallback(ObjectSelectHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectDeselect, new PacketCallback(ObjectDeselectHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectLink, new PacketCallback(ObjectLinkHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectDelink, new PacketCallback(ObjectDelinkHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectShape, new PacketCallback(ObjectShapeHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectFlagUpdate, new PacketCallback(ObjectFlagUpdateHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectExtraParams, new PacketCallback(ObjectExtraParamsHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectImage, new PacketCallback(ObjectImageHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.Undo, new PacketCallback(UndoHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.Redo, new PacketCallback(RedoHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.DeRezObject, new PacketCallback(DeRezObjectHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, new PacketCallback(MultipleObjectUpdateHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, new PacketCallback(RequestObjectPropertiesFamilyHandler));
|
||||
}
|
||||
|
||||
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 (server.Scene.TryGetObject(add.ObjectData.RayTargetID, out obj))
|
||||
{
|
||||
// Test for a collision with the specified object
|
||||
position = server.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 = server.Permissions.GetDefaultPermissions();
|
||||
prim.Properties.SalePrice = 10;
|
||||
|
||||
prim.RegionHandle = server.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, server);
|
||||
server.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 (server.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
|
||||
server.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 (server.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;
|
||||
|
||||
server.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;
|
||||
server.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 (server.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);
|
||||
|
||||
server.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;
|
||||
server.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 (server.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 (!server.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 (server.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;
|
||||
}
|
||||
|
||||
server.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 (!server.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;
|
||||
}
|
||||
|
||||
server.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 (server.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;
|
||||
server.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 (server.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;
|
||||
server.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 (server.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;
|
||||
}
|
||||
}
|
||||
|
||||
server.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 (server.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);
|
||||
server.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 (server.Scene.TryGetObject(undo.ObjectData[i].ObjectID, out obj))
|
||||
server.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 (server.Scene.TryGetObject(redo.ObjectData[i].ObjectID, out obj))
|
||||
server.Scene.ObjectRedo(this, obj);
|
||||
}
|
||||
}
|
||||
|
||||
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 (server.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 (server.Inventory.TryGetInventory(agent.ID, derez.AgentBlock.DestinationID, out invObj) &&
|
||||
invObj is InventoryFolder)
|
||||
{
|
||||
// FIXME: Handle children
|
||||
InventoryFolder trash = (InventoryFolder)invObj;
|
||||
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, true);
|
||||
server.Scene.ObjectRemove(this, obj.Prim.LocalID);
|
||||
|
||||
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 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 (server.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;
|
||||
|
||||
server.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;
|
||||
server.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 (server.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;
|
||||
|
||||
server.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class ParcelManager : IExtension<Simian>, IParcelProvider
|
||||
{
|
||||
Simian server;
|
||||
Dictionary<int, Parcel> parcels = new Dictionary<int, Parcel>();
|
||||
/// <summary>X,Y ordered 2D array of the parcelIDs for each sq. meter of a simulator</summary>
|
||||
int[] parcelOverlay = new int[64 * 64];
|
||||
|
||||
public ParcelManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
lock (parcels)
|
||||
parcels.Clear();
|
||||
|
||||
// Create a default parcel
|
||||
Parcel parcel = new Parcel(0);
|
||||
parcel.Desc = "Simian Parcel";
|
||||
parcel.Flags = Parcel.ParcelFlags.AllowAPrimitiveEntry | Parcel.ParcelFlags.AllowFly | Parcel.ParcelFlags.AllowGroupScripts |
|
||||
Parcel.ParcelFlags.AllowLandmark | Parcel.ParcelFlags.AllowOtherScripts | Parcel.ParcelFlags.AllowTerraform |
|
||||
Parcel.ParcelFlags.AllowVoiceChat | Parcel.ParcelFlags.CreateGroupObjects | Parcel.ParcelFlags.CreateObjects |
|
||||
Parcel.ParcelFlags.UseBanList;
|
||||
parcel.Landing = Parcel.LandingType.Direct;
|
||||
parcel.MaxPrims = 20000;
|
||||
parcel.Name = "Simian";
|
||||
parcel.Status = Parcel.ParcelStatus.Leased;
|
||||
parcel.UserLocation = (parcel.AABBMin + parcel.AABBMax) * 0.5f;
|
||||
|
||||
parcel.Bitmap = new byte[512];
|
||||
for (int i = 0; i < 512; i++)
|
||||
parcel.Bitmap[i] = Byte.MaxValue;
|
||||
|
||||
// The parcelOverlay defaults to all zeroes, and the LocalID of this parcel is zero,
|
||||
// so we don't need to modify parcelOverlay before calling this
|
||||
UpdateParcelSize(ref parcel);
|
||||
|
||||
// Add the default parcel to the list
|
||||
parcels[parcel.LocalID] = parcel;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public void SendParcelOverlay(Agent agent)
|
||||
{
|
||||
const int LAND_BLOCKS_PER_PACKET = 1024;
|
||||
|
||||
byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET];
|
||||
int byteArrayCount = 0;
|
||||
int sequenceID = 0;
|
||||
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 64; x++)
|
||||
{
|
||||
byte tempByte = 0; // The flags for the current 4x4m parcel square
|
||||
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(parcelOverlay[y * 64 + x], out parcel))
|
||||
{
|
||||
// Set the ownership/sale flag
|
||||
if (parcel.OwnerID == agent.ID)
|
||||
tempByte = (byte)ParcelOverlayType.OwnedBySelf;
|
||||
else if (parcel.AuctionID != 0)
|
||||
tempByte = (byte)ParcelOverlayType.Auction;
|
||||
else if (parcel.SalePrice > 0 && (parcel.AuthBuyerID == UUID.Zero || parcel.AuthBuyerID == agent.ID))
|
||||
tempByte = (byte)ParcelOverlayType.ForSale;
|
||||
else if (parcel.GroupID != UUID.Zero)
|
||||
tempByte = (byte)ParcelOverlayType.OwnedByGroup;
|
||||
else if (parcel.OwnerID != UUID.Zero)
|
||||
tempByte = (byte)ParcelOverlayType.OwnedByOther;
|
||||
else
|
||||
tempByte = (byte)ParcelOverlayType.Public;
|
||||
|
||||
// Set the border flags
|
||||
if (x == 0)
|
||||
tempByte |= (byte)ParcelOverlayType.BorderWest;
|
||||
else if (parcelOverlay[y * 64 + (x - 1)] != parcel.LocalID)
|
||||
// Parcel to the west is different from the current parcel
|
||||
tempByte |= (byte)ParcelOverlayType.BorderWest;
|
||||
|
||||
if (y == 0)
|
||||
tempByte |= (byte)ParcelOverlayType.BorderSouth;
|
||||
else if (parcelOverlay[(y - 1) * 64 + x] != parcel.LocalID)
|
||||
// Parcel to the south is different from the current parcel
|
||||
tempByte |= (byte)ParcelOverlayType.BorderSouth;
|
||||
|
||||
byteArray[byteArrayCount] = tempByte;
|
||||
++byteArrayCount;
|
||||
if (byteArrayCount >= LAND_BLOCKS_PER_PACKET)
|
||||
{
|
||||
// Send a ParcelOverlay packet
|
||||
ParcelOverlayPacket overlay = new ParcelOverlayPacket();
|
||||
overlay.ParcelData.SequenceID = sequenceID;
|
||||
overlay.ParcelData.Data = byteArray;
|
||||
server.UDP.SendPacket(agent.ID, overlay, PacketCategory.State);
|
||||
|
||||
byteArrayCount = 0;
|
||||
++sequenceID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Parcel overlay references missing parcel " + parcelOverlay[y * 64 + x],
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateParcelSize(ref Parcel parcel)
|
||||
{
|
||||
int minX = 64;
|
||||
int minY = 64;
|
||||
int maxX = 0;
|
||||
int maxY = 0;
|
||||
int area = 0;
|
||||
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 64; x++)
|
||||
{
|
||||
if (parcelOverlay[y * 64 + x] == parcel.LocalID)
|
||||
{
|
||||
int x4 = x * 4;
|
||||
int y4 = y * 4;
|
||||
|
||||
if (minX > x4) minX = x4;
|
||||
if (minY > y4) minY = y4;
|
||||
if (maxX < x4) maxX = x4;
|
||||
if (maxX < y4) maxY = y4;
|
||||
area += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parcel.AABBMin.X = minX;
|
||||
parcel.AABBMin.Y = minY;
|
||||
parcel.AABBMax.X = maxX;
|
||||
parcel.AABBMax.Y = maxY;
|
||||
parcel.Area = area;
|
||||
}
|
||||
|
||||
void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result,
|
||||
Agent agent)
|
||||
{
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(parcelID, out parcel))
|
||||
{
|
||||
ParcelPropertiesPacket properties = new ParcelPropertiesPacket();
|
||||
properties.AgeVerificationBlock.RegionDenyAgeUnverified = false;
|
||||
properties.ParcelData.AABBMax = parcel.AABBMax;
|
||||
properties.ParcelData.AABBMin = parcel.AABBMin;
|
||||
properties.ParcelData.Area = parcel.Area;
|
||||
properties.ParcelData.AuctionID = parcel.AuctionID;
|
||||
properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID;
|
||||
properties.ParcelData.Bitmap = parcel.Bitmap;
|
||||
properties.ParcelData.Category = (byte)parcel.Category;
|
||||
properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate);
|
||||
properties.ParcelData.ClaimPrice = parcel.ClaimPrice;
|
||||
properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc);
|
||||
properties.ParcelData.GroupID = parcel.GroupID;
|
||||
properties.ParcelData.GroupPrims = parcel.GroupPrims;
|
||||
properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned;
|
||||
properties.ParcelData.LandingType = (byte)parcel.Landing;
|
||||
properties.ParcelData.LocalID = parcel.LocalID;
|
||||
properties.ParcelData.MaxPrims = parcel.MaxPrims;
|
||||
properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale;
|
||||
properties.ParcelData.MediaID = parcel.Media.MediaID;
|
||||
properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL);
|
||||
properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL);
|
||||
properties.ParcelData.Name = Utils.StringToBytes(parcel.Name);
|
||||
properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime;
|
||||
properties.ParcelData.OtherCount = parcel.OtherCount;
|
||||
properties.ParcelData.OtherPrims = parcel.OtherPrims;
|
||||
properties.ParcelData.OwnerID = parcel.OwnerID;
|
||||
properties.ParcelData.OwnerPrims = parcel.OwnerPrims;
|
||||
properties.ParcelData.ParcelFlags = (uint)parcel.Flags;
|
||||
properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus;
|
||||
properties.ParcelData.PassHours = parcel.PassHours;
|
||||
properties.ParcelData.PassPrice = parcel.PassPrice;
|
||||
properties.ParcelData.PublicCount = parcel.PublicCount;
|
||||
properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous;
|
||||
properties.ParcelData.RegionDenyIdentified = false; // Deprecated
|
||||
properties.ParcelData.RegionDenyTransacted = false; // Deprecated
|
||||
properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride;
|
||||
properties.ParcelData.RentPrice = parcel.RentPrice;
|
||||
properties.ParcelData.RequestResult = (int)result;
|
||||
properties.ParcelData.SalePrice = parcel.SalePrice;
|
||||
properties.ParcelData.SelectedPrims = 0; // TODO:
|
||||
properties.ParcelData.SelfCount = parcel.SelfCount;
|
||||
properties.ParcelData.SequenceID = sequenceID;
|
||||
properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims;
|
||||
properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims;
|
||||
properties.ParcelData.SnapSelection = snapSelection;
|
||||
properties.ParcelData.SnapshotID = parcel.SnapshotID;
|
||||
properties.ParcelData.Status = (byte)parcel.Status;
|
||||
properties.ParcelData.TotalPrims = parcel.TotalPrims;
|
||||
properties.ParcelData.UserLocation = parcel.UserLocation;
|
||||
properties.ParcelData.UserLookAt = parcel.UserLookAt;
|
||||
|
||||
// HACK: Make everyone think they are the owner of this parcel
|
||||
properties.ParcelData.OwnerID = agent.ID;
|
||||
|
||||
server.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,569 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class Periscope : IExtension<Simian>
|
||||
{
|
||||
// Change this to login to a different grid
|
||||
const string PERISCOPE_LOGIN_URI = Settings.AGNI_LOGIN_SERVER;
|
||||
|
||||
public Agent MasterAgent = null;
|
||||
|
||||
Simian server;
|
||||
GridClient client;
|
||||
PeriscopeImageDelivery imageDelivery;
|
||||
PeriscopeMovement movement;
|
||||
PeriscopeTransferManager transferManager;
|
||||
bool ignoreObjectKill = false;
|
||||
object loginLock = new object();
|
||||
|
||||
public Periscope()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
client = new GridClient();
|
||||
Settings.LOG_LEVEL = Helpers.LogLevel.Info;
|
||||
client.Settings.MULTIPLE_SIMS = false;
|
||||
client.Settings.SEND_AGENT_UPDATES = false;
|
||||
|
||||
client.Network.OnCurrentSimChanged += Network_OnCurrentSimChanged;
|
||||
client.Objects.OnNewPrim += Objects_OnNewPrim;
|
||||
client.Objects.OnNewAvatar += Objects_OnNewAvatar;
|
||||
client.Objects.OnNewAttachment += Objects_OnNewAttachment;
|
||||
client.Objects.OnObjectKilled += Objects_OnObjectKilled;
|
||||
client.Objects.OnObjectUpdated += Objects_OnObjectUpdated;
|
||||
client.Avatars.OnAvatarAppearance += Avatars_OnAvatarAppearance;
|
||||
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
|
||||
client.Self.OnChat += new AgentManager.ChatCallback(Self_OnChat);
|
||||
client.Self.OnTeleport += new AgentManager.TeleportCallback(Self_OnTeleport);
|
||||
client.Network.RegisterCallback(PacketType.AvatarAnimation, AvatarAnimationHandler);
|
||||
client.Network.RegisterCallback(PacketType.RegionHandshake, RegionHandshakeHandler);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectGrab, ObjectGrabHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectGrabUpdate, ObjectGrabUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectDeGrab, ObjectDeGrabHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
|
||||
|
||||
imageDelivery = new PeriscopeImageDelivery(server, client);
|
||||
movement = new PeriscopeMovement(server, this);
|
||||
transferManager = new PeriscopeTransferManager(server, client);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
transferManager.Stop();
|
||||
movement.Stop();
|
||||
imageDelivery.Stop();
|
||||
|
||||
if (client.Network.Connected)
|
||||
client.Network.Logout();
|
||||
}
|
||||
|
||||
void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, server);
|
||||
if (MasterAgent != null)
|
||||
simObj.Prim.OwnerID = MasterAgent.ID;
|
||||
server.Scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void Objects_OnNewAttachment(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, server);
|
||||
server.Scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
ulong localRegionHandle = Utils.UIntsToLong(256 * server.Scene.RegionX, 256 * server.Scene.RegionY);
|
||||
|
||||
// Add the avatar to both the agents list and the scene objects
|
||||
SimulationObject obj = new SimulationObject(avatar, server);
|
||||
Agent agent = new Agent(obj);
|
||||
agent.Avatar.Prim.ID = avatar.ID;
|
||||
agent.CurrentRegionHandle = localRegionHandle;
|
||||
agent.FirstName = avatar.FirstName;
|
||||
agent.LastName = avatar.LastName;
|
||||
|
||||
server.Scene.AgentAdd(this, agent, avatar.Flags);
|
||||
server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
|
||||
void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject obj;
|
||||
UpdateFlags flags = UpdateFlags.Acceleration | UpdateFlags.AngularVelocity | UpdateFlags.Position |
|
||||
UpdateFlags.Rotation | UpdateFlags.Velocity;
|
||||
|
||||
if (update.Avatar) flags |= UpdateFlags.CollisionPlane;
|
||||
if (update.Textures != null) flags |= UpdateFlags.Textures;
|
||||
|
||||
if (server.Scene.TryGetObject(update.LocalID, out obj))
|
||||
{
|
||||
obj.Prim.Acceleration = update.Acceleration;
|
||||
obj.Prim.AngularVelocity = update.AngularVelocity;
|
||||
obj.Prim.Position = update.Position;
|
||||
obj.Prim.Rotation = update.Rotation;
|
||||
obj.Prim.Velocity = update.Velocity;
|
||||
if (update.Avatar) obj.Prim.CollisionPlane = update.CollisionPlane;
|
||||
if (update.Textures != null) obj.Prim.Textures = update.Textures;
|
||||
|
||||
server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, flags);
|
||||
}
|
||||
|
||||
if (update.LocalID == client.Self.LocalID)
|
||||
{
|
||||
MasterAgent.Avatar.Prim.Acceleration = update.Acceleration;
|
||||
MasterAgent.Avatar.Prim.AngularVelocity = update.AngularVelocity;
|
||||
MasterAgent.Avatar.Prim.Position = update.Position;
|
||||
MasterAgent.Avatar.Prim.Rotation = update.Rotation;
|
||||
MasterAgent.Avatar.Prim.Velocity = update.Velocity;
|
||||
if (update.Avatar) MasterAgent.Avatar.Prim.CollisionPlane = update.CollisionPlane;
|
||||
if (update.Textures != null) MasterAgent.Avatar.Prim.Textures = update.Textures;
|
||||
|
||||
server.Scene.ObjectAddOrUpdate(this, MasterAgent.Avatar, MasterAgent.ID, 0, PrimFlags.None, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void Objects_OnObjectKilled(Simulator simulator, uint objectID)
|
||||
{
|
||||
if (!ignoreObjectKill)
|
||||
server.Scene.ObjectRemove(this, objectID);
|
||||
}
|
||||
|
||||
void Avatars_OnAvatarAppearance(UUID avatarID, bool isTrial, Primitive.TextureEntryFace defaultTexture,
|
||||
Primitive.TextureEntryFace[] faceTextures, List<byte> visualParams)
|
||||
{
|
||||
Primitive.TextureEntry te = new Primitive.TextureEntry(defaultTexture);
|
||||
te.FaceTextures = faceTextures;
|
||||
byte[] vp = (visualParams != null && visualParams.Count > 1 ? visualParams.ToArray() : null);
|
||||
|
||||
Agent agent;
|
||||
if (server.Scene.TryGetAgent(avatarID, out agent))
|
||||
{
|
||||
Logger.Log("[Periscope] Updating foreign avatar appearance for " + agent.FirstName + " " + agent.LastName, Helpers.LogLevel.Info);
|
||||
server.Scene.AgentAppearance(this, agent, te, vp);
|
||||
}
|
||||
|
||||
if (avatarID == client.Self.AgentID)
|
||||
{
|
||||
Logger.Log("[Periscope] Updating foreign avatar appearance for the MasterAgent", Helpers.LogLevel.Info);
|
||||
server.Scene.AgentAppearance(this, MasterAgent, te, vp);
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
|
||||
{
|
||||
float[,] patchData = new float[16, 16];
|
||||
for (int py = 0; py < 16; py++)
|
||||
{
|
||||
for (int px = 0; px < 16; px++)
|
||||
{
|
||||
patchData[py, px] = data[py * 16 + px];
|
||||
}
|
||||
}
|
||||
|
||||
server.Scene.SetTerrainPatch(this, (uint)x, (uint)y, patchData);
|
||||
}
|
||||
|
||||
void Self_OnChat(string message, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
|
||||
string fromName, UUID id, UUID ownerid, Vector3 position)
|
||||
{
|
||||
server.Scene.ObjectChat(this, ownerid, id, audible, type, sourceType, fromName, position, 0, message);
|
||||
}
|
||||
|
||||
void Self_OnTeleport(string message, TeleportStatus status, TeleportFlags flags)
|
||||
{
|
||||
if (status == TeleportStatus.Finished)
|
||||
{
|
||||
ulong localRegionHandle = server.Scene.RegionHandle;
|
||||
|
||||
server.Scene.RemoveAllAgents(
|
||||
delegate(Agent agent)
|
||||
{ return agent.Avatar.Prim.RegionHandle != client.Network.CurrentSim.Handle && agent.Avatar.Prim.RegionHandle != localRegionHandle; });
|
||||
|
||||
server.Scene.RemoveAllObjects(
|
||||
delegate(SimulationObject obj)
|
||||
{ return obj.Prim.RegionHandle != client.Network.CurrentSim.Handle && obj.Prim.RegionHandle != localRegionHandle; });
|
||||
}
|
||||
}
|
||||
|
||||
void Network_OnCurrentSimChanged(Simulator PreviousSimulator)
|
||||
{
|
||||
Logger.Log("[Periscope] Sending bot appearance", Helpers.LogLevel.Info);
|
||||
client.Appearance.SetPreviousAppearance(false);
|
||||
}
|
||||
|
||||
void AvatarAnimationHandler(Packet packet, Simulator simulator)
|
||||
{
|
||||
AvatarAnimationPacket animations = (AvatarAnimationPacket)packet;
|
||||
|
||||
Agent agent;
|
||||
if (server.Scene.TryGetAgent(animations.Sender.ID, out agent))
|
||||
{
|
||||
agent.Animations.Clear();
|
||||
|
||||
for (int i = 0; i < animations.AnimationList.Length; i++)
|
||||
{
|
||||
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
|
||||
agent.Animations.Add(block.AnimID, block.AnimSequenceID);
|
||||
}
|
||||
|
||||
server.Avatars.SendAnimations(agent);
|
||||
}
|
||||
|
||||
if (animations.Sender.ID == client.Self.AgentID)
|
||||
{
|
||||
MasterAgent.Animations.Clear();
|
||||
|
||||
for (int i = 0; i < animations.AnimationList.Length; i++)
|
||||
{
|
||||
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
|
||||
MasterAgent.Animations.Add(block.AnimID, block.AnimSequenceID);
|
||||
}
|
||||
|
||||
server.Avatars.SendAnimations(MasterAgent);
|
||||
}
|
||||
}
|
||||
|
||||
void RegionHandshakeHandler(Packet packet, Simulator simulator)
|
||||
{
|
||||
RegionHandshakePacket handshake = (RegionHandshakePacket)packet;
|
||||
|
||||
handshake.RegionInfo.SimOwner = (MasterAgent != null ? MasterAgent.ID : UUID.Zero);
|
||||
handshake.RegionInfo.RegionFlags &= ~(uint)RegionFlags.NoFly;
|
||||
handshake.RegionInfo2.RegionID = server.Scene.RegionID;
|
||||
|
||||
// TODO: Need more methods to manipulate the scene so we can apply these properties.
|
||||
// Right now this only gets sent out to people who are logged in when the master avatar
|
||||
// is already logged in
|
||||
server.UDP.BroadcastPacket(handshake, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
#region Simian client packet handlers
|
||||
|
||||
void ChatFromViewerHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ChatFromViewerPacket chat = (ChatFromViewerPacket)packet;
|
||||
|
||||
// Forward chat from the viewer to the foreign simulator
|
||||
string message = Utils.BytesToString(chat.ChatData.Message);
|
||||
|
||||
if (!String.IsNullOrEmpty(message) && message[0] == '/')
|
||||
{
|
||||
string[] messageParts = CommandLineParser.Parse(message);
|
||||
|
||||
switch (messageParts[0])
|
||||
{
|
||||
case "/teleport":
|
||||
{
|
||||
float x, y, z;
|
||||
|
||||
if (messageParts.Length == 5 &&
|
||||
Single.TryParse(messageParts[2], out x) &&
|
||||
Single.TryParse(messageParts[3], out y) &&
|
||||
Single.TryParse(messageParts[4], out z))
|
||||
{
|
||||
server.Avatars.SendAlert(agent, String.Format("Attempting teleport to {0} <{1}, {2}, {3}>",
|
||||
messageParts[1], messageParts[2], messageParts[3], messageParts[4]));
|
||||
client.Self.Teleport(messageParts[1], new Vector3(x, y, z));
|
||||
}
|
||||
else
|
||||
{
|
||||
server.Avatars.SendAlert(agent, "Usage: /teleport \"sim name\" x y z");
|
||||
}
|
||||
return;
|
||||
}
|
||||
case "/stats":
|
||||
server.Avatars.SendAlert(agent, String.Format("Downloading textures: {0}, Queued textures: {1}",
|
||||
imageDelivery.Pipeline.CurrentCount, imageDelivery.Pipeline.QueuedCount));
|
||||
return;
|
||||
case "/objectkill":
|
||||
if (messageParts.Length == 2)
|
||||
{
|
||||
if (messageParts[1] == "off" || messageParts[1] == "0")
|
||||
{
|
||||
ignoreObjectKill = true;
|
||||
server.Avatars.SendAlert(agent, "Ignoring upstream ObjectKill packets");
|
||||
}
|
||||
else
|
||||
{
|
||||
ignoreObjectKill = false;
|
||||
server.Avatars.SendAlert(agent, "Enabling upstream ObjectKill packets");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case "/save":
|
||||
{
|
||||
if (messageParts.Length == 2)
|
||||
{
|
||||
string filename = messageParts[1];
|
||||
string directoryname = Path.GetFileNameWithoutExtension(filename);
|
||||
|
||||
Thread saveThread = new Thread(new ThreadStart(
|
||||
delegate()
|
||||
{
|
||||
Logger.Log(String.Format("Preparing to serialize {0} objects", server.Scene.ObjectCount()), Helpers.LogLevel.Info);
|
||||
OarFile.SavePrims(server, directoryname + "/objects", directoryname + "/assets", Simian.ASSET_CACHE_DIR);
|
||||
|
||||
try { Directory.Delete(directoryname + "/terrains", true); }
|
||||
catch (Exception) { }
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(directoryname + "/terrains");
|
||||
|
||||
using (FileStream stream = new FileStream(directoryname + "/terrains/heightmap.r32", FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
for (int y = 0; y < 256; y++)
|
||||
{
|
||||
for (int x = 0; x < 256; x++)
|
||||
{
|
||||
float t = server.Scene.GetTerrainHeightAt(x, y);
|
||||
stream.Write(BitConverter.GetBytes(t), 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log("Failed saving terrain: " + ex.Message, Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
Logger.Log("Saving " + directoryname, Helpers.LogLevel.Info);
|
||||
OarFile.PackageArchive(directoryname, filename);
|
||||
|
||||
try
|
||||
{ System.IO.Directory.Delete(directoryname, true); }
|
||||
catch (Exception ex)
|
||||
{ Logger.Log("Failed to delete temporary directory " + directoryname + ": " + ex.Message, Helpers.LogLevel.Error); }
|
||||
|
||||
server.Avatars.SendAlert(agent, "Finished OAR export to " + filename);
|
||||
}));
|
||||
|
||||
saveThread.Start();
|
||||
server.Avatars.SendAlert(agent, "Starting OAR export to " + filename);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case "/nudemod":
|
||||
//int count = 0;
|
||||
// FIXME: AvatarAppearance locks the agents dictionary. Need to be able to copy the agents dictionary?
|
||||
/*foreach (Agent curAgent in agents.Values)
|
||||
{
|
||||
if (curAgent != agent && curAgent.VisualParams != null)
|
||||
{
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerBaked] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerJacket] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerPants] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerShoes] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerSocks] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerUnderpants] = null;
|
||||
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperBaked] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperGloves] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperJacket] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperShirt] = null;
|
||||
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperUndershirt] = null;
|
||||
|
||||
server.Scene.AvatarAppearance(this, curAgent, curAgent.Avatar.Textures, curAgent.VisualParams);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
server.Avatars.SendAlert(agent, String.Format("Modified appearances for {0} avatar(s)", count));*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string finalMessage;
|
||||
if (agent.FirstName == client.Self.FirstName && agent.LastName == client.Self.LastName)
|
||||
finalMessage = message;
|
||||
else
|
||||
finalMessage = String.Format("<{0} {1}> {2}", agent.FirstName, agent.LastName, message);
|
||||
|
||||
client.Self.Chat(finalMessage, chat.ChatData.Channel, (ChatType)chat.ChatData.Type);
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
if (MasterAgent == null || (!client.Network.Connected && client.Network.LoginStatusCode == LoginStatus.Failed))
|
||||
{
|
||||
MasterAgent = null;
|
||||
|
||||
lock (loginLock)
|
||||
{
|
||||
// Double-checked locking to avoid hitting the loginLock each time
|
||||
if (MasterAgent == null && server.Scene.TryGetAgent(update.AgentData.AgentID, out MasterAgent))
|
||||
{
|
||||
Logger.Log(String.Format("[Periscope] {0} {1} is the controlling agent",
|
||||
MasterAgent.FirstName, MasterAgent.LastName), Helpers.LogLevel.Info);
|
||||
|
||||
LoginParams login = client.Network.DefaultLoginParams(agent.FirstName, agent.LastName, agent.PasswordHash,
|
||||
"Simian Periscope", "1.0.0");
|
||||
login.URI = PERISCOPE_LOGIN_URI;
|
||||
login.Start = "last";
|
||||
client.Network.Login(login);
|
||||
|
||||
if (client.Network.Connected)
|
||||
{
|
||||
Logger.Log("[Periscope] Connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = String.Format("Failed to connect to the foreign grid: ({0}): {1}", client.Network.LoginErrorKey,
|
||||
client.Network.LoginMessage);
|
||||
Logger.Log("[Periscope] " + error, Helpers.LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MasterAgent == null || update.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
update.AgentData.AgentID = client.Self.AgentID;
|
||||
update.AgentData.SessionID = client.Self.SessionID;
|
||||
client.Network.SendPacket(update);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGrabHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectGrabPacket grab = (ObjectGrabPacket)packet;
|
||||
|
||||
if (MasterAgent == null || grab.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
grab.AgentData.AgentID = client.Self.AgentID;
|
||||
grab.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(grab);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGrabUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectGrabUpdatePacket grabUpdate = (ObjectGrabUpdatePacket)packet;
|
||||
|
||||
if (MasterAgent == null || grabUpdate.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
grabUpdate.AgentData.AgentID = client.Self.AgentID;
|
||||
grabUpdate.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(grabUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDeGrabHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectDeGrabPacket degrab = (ObjectDeGrabPacket)packet;
|
||||
|
||||
if (MasterAgent == null || degrab.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
degrab.AgentData.AgentID = client.Self.AgentID;
|
||||
degrab.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(degrab);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerEffectHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ViewerEffectPacket effect = (ViewerEffectPacket)packet;
|
||||
|
||||
if (MasterAgent == null || effect.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
effect.AgentData.AgentID = client.Self.AgentID;
|
||||
effect.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(effect);
|
||||
}
|
||||
}
|
||||
|
||||
void AgentAnimationHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentAnimationPacket animation = (AgentAnimationPacket)packet;
|
||||
|
||||
if (MasterAgent == null || animation.AgentData.AgentID == MasterAgent.ID)
|
||||
{
|
||||
animation.AgentData.AgentID = client.Self.AgentID;
|
||||
animation.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(animation);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Simian client packet handlers
|
||||
|
||||
static void EraseTexture(Avatar avatar, AppearanceManager.TextureIndex texture)
|
||||
{
|
||||
Primitive.TextureEntryFace face = avatar.Textures.FaceTextures[(int)texture];
|
||||
if (face != null) face.TextureID = UUID.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandLineParser
|
||||
{
|
||||
public static string[] Parse(string str)
|
||||
{
|
||||
if (str == null || !(str.Length > 0)) return new string[0];
|
||||
int idx = str.Trim().IndexOf(" ");
|
||||
if (idx == -1) return new string[] { str };
|
||||
int count = str.Length;
|
||||
List<string> list = new List<string>();
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
if (str[0] == '"')
|
||||
{
|
||||
int temp = str.IndexOf("\"", 1, str.Length - 1);
|
||||
while (str[temp - 1] == '\\')
|
||||
{
|
||||
temp = str.IndexOf("\"", temp + 1, str.Length - temp - 1);
|
||||
}
|
||||
idx = temp + 1;
|
||||
}
|
||||
if (str[0] == '\'')
|
||||
{
|
||||
int temp = str.IndexOf("\'", 1, str.Length - 1);
|
||||
while (str[temp - 1] == '\\')
|
||||
{
|
||||
temp = str.IndexOf("\'", temp + 1, str.Length - temp - 1);
|
||||
}
|
||||
idx = temp + 1;
|
||||
}
|
||||
string s = str.Substring(0, idx);
|
||||
int left = count - idx;
|
||||
str = str.Substring(idx, left).Trim();
|
||||
list.Add(s.Trim('"'));
|
||||
count = str.Length;
|
||||
idx = str.IndexOf(" ");
|
||||
if (idx == -1)
|
||||
{
|
||||
string add = str.Trim('"', ' ');
|
||||
if (add.Length > 0)
|
||||
{
|
||||
list.Add(add);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class PeriscopeImageDelivery
|
||||
{
|
||||
public TexturePipeline Pipeline;
|
||||
|
||||
Simian server;
|
||||
GridClient client;
|
||||
Dictionary<UUID, ImageDownload> currentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public PeriscopeImageDelivery(Simian server, GridClient client)
|
||||
{
|
||||
this.server = server;
|
||||
this.client = client;
|
||||
|
||||
Pipeline = new TexturePipeline(client, 12);
|
||||
Pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Pipeline.Shutdown();
|
||||
}
|
||||
|
||||
void RequestImageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RequestImagePacket request = (RequestImagePacket)packet;
|
||||
|
||||
for (int i = 0; i < request.RequestImage.Length; i++)
|
||||
{
|
||||
RequestImagePacket.RequestImageBlock block = request.RequestImage[i];
|
||||
|
||||
ImageDownload download;
|
||||
bool downloadFound = currentDownloads.TryGetValue(block.Image, out download);
|
||||
|
||||
if (downloadFound)
|
||||
{
|
||||
lock (download)
|
||||
{
|
||||
if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
Logger.DebugLog(String.Format("Image download {0} is aborting", block.Image));
|
||||
|
||||
// Update download
|
||||
download.Update(block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
}
|
||||
}
|
||||
else if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
{
|
||||
// Aborting a download we are not tracking, this may be in the pipeline
|
||||
Pipeline.AbortDownload(block.Image);
|
||||
}
|
||||
else
|
||||
{
|
||||
//bool bake = ((ImageType)block.Type == ImageType.Baked);
|
||||
|
||||
// New download, check if we have this image
|
||||
Asset asset;
|
||||
if (server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
|
||||
{
|
||||
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have this texture, add it to the download queue and see if the bot can get it for us
|
||||
download = new ImageDownload(null, agent, block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
lock (currentDownloads)
|
||||
currentDownloads[block.Image] = download;
|
||||
|
||||
Pipeline.RequestTexture(block.Image, (ImageType)block.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pipeline_OnDownloadFinished(UUID id, bool success)
|
||||
{
|
||||
ImageDownload download;
|
||||
if (currentDownloads.TryGetValue(id, out download))
|
||||
{
|
||||
lock (currentDownloads)
|
||||
currentDownloads.Remove(id);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Set the texture to the downloaded texture data
|
||||
AssetTexture texture = new AssetTexture(id, Pipeline.GetTextureToRender(id).AssetData);
|
||||
download.Texture = texture;
|
||||
|
||||
Pipeline.RemoveFromPipeline(id);
|
||||
|
||||
// Store this texture in the local asset store for later
|
||||
server.Assets.StoreAsset(texture);
|
||||
|
||||
SendTexture(download.Agent, download.Texture, download.DiscardLevel, download.CurrentPacket, download.Priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Failed to download texture " + id.ToString(), Helpers.LogLevel.Warning);
|
||||
|
||||
ImageNotInDatabasePacket notfound = new ImageNotInDatabasePacket();
|
||||
notfound.ImageID.ID = id;
|
||||
server.UDP.SendPacket(download.Agent.ID, notfound, PacketCategory.Texture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Pipeline downloaded a texture we're not tracking, " + id.ToString(), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
|
||||
{
|
||||
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"[Periscope] Starting new texture transfer for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
|
||||
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket, download.TexturePacketCount()));
|
||||
|
||||
// Send initial data
|
||||
ImageDataPacket data = new ImageDataPacket();
|
||||
data.ImageID.Codec = (byte)ImageCodec.J2C;
|
||||
data.ImageID.ID = download.Texture.AssetID;
|
||||
data.ImageID.Packets = (ushort)download.TexturePacketCount();
|
||||
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
|
||||
|
||||
// The first bytes of the image are always sent in the ImageData packet
|
||||
data.ImageData = new ImageDataPacket.ImageDataBlock();
|
||||
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
|
||||
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
|
||||
try
|
||||
{
|
||||
data.ImageData.Data = new byte[imageDataSize];
|
||||
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
|
||||
Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.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);
|
||||
}
|
||||
|
||||
server.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,346 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class PeriscopeMovement
|
||||
{
|
||||
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
|
||||
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
|
||||
const float GRAVITY = 9.8f; //meters/sec
|
||||
const float WALK_SPEED = 3f; //meters/sec
|
||||
const float RUN_SPEED = 5f; //meters/sec
|
||||
const float FLY_SPEED = 10f; //meters/sec
|
||||
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
||||
const float FALL_FORGIVENESS = 0.25f; //fall buffer in meters
|
||||
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
||||
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec (no clue why this is so high)
|
||||
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
||||
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
||||
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
||||
|
||||
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
Simian server;
|
||||
Periscope periscope;
|
||||
Timer updateTimer;
|
||||
long lastTick;
|
||||
|
||||
public int LastTick
|
||||
{
|
||||
get { return (int)Interlocked.Read(ref lastTick); }
|
||||
set { Interlocked.Exchange(ref lastTick, value); }
|
||||
}
|
||||
|
||||
public PeriscopeMovement(Simian server, Periscope periscope)
|
||||
{
|
||||
this.server = server;
|
||||
this.periscope = periscope;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
||||
|
||||
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
||||
LastTick = Environment.TickCount;
|
||||
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTimer_Elapsed(object sender)
|
||||
{
|
||||
int tick = Environment.TickCount;
|
||||
float seconds = (float)((tick - LastTick) / 1000f);
|
||||
LastTick = tick;
|
||||
|
||||
server.Scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
// Don't handle movement for the master agent or foreign agents
|
||||
if (agent != periscope.MasterAgent && agent.SessionID != UUID.Zero)
|
||||
{
|
||||
bool animsChanged = false;
|
||||
|
||||
// Create forward and left vectors from the current avatar rotation
|
||||
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
|
||||
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
||||
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
||||
|
||||
// Check control flags
|
||||
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
||||
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
|
||||
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
|
||||
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
|
||||
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
|
||||
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
|
||||
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
|
||||
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
|
||||
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
||||
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
|
||||
|
||||
// direction in which the avatar is trying to move
|
||||
Vector3 move = Vector3.Zero;
|
||||
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
||||
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
||||
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
||||
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
||||
if (heldUp) { move.Z += 1; }
|
||||
if (heldDown) { move.Z -= 1; }
|
||||
|
||||
// is the avatar trying to move?
|
||||
bool moving = move != Vector3.Zero;
|
||||
bool jumping = agent.TickJump != 0;
|
||||
|
||||
// 2-dimensional speed multipler
|
||||
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
||||
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
||||
speed /= SQRT_TWO;
|
||||
|
||||
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
|
||||
float oldFloor = server.Scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
agentPosition += (move * speed);
|
||||
float newFloor = server.Scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
if (!flying && newFloor != oldFloor)
|
||||
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
||||
|
||||
// least possible distance from avatar to the ground
|
||||
// TODO: calculate to get rid of "bot squat"
|
||||
float lowerLimit = newFloor + agent.Avatar.Prim.Scale.Z / 2;
|
||||
|
||||
// Z acceleration resulting from gravity
|
||||
float gravity = 0f;
|
||||
|
||||
float waterChestHeight = server.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 (server.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (server.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 > server.Scene.WaterHeight)
|
||||
{ //above water
|
||||
|
||||
move = Vector3.Zero; //override controls while drifting
|
||||
agent.Avatar.Prim.Velocity *= 0.95f; //keep most of our inertia
|
||||
|
||||
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
||||
|
||||
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
|
||||
{ //just started falling
|
||||
agent.TickFall = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
|
||||
|
||||
if (!jumping)
|
||||
{ //falling
|
||||
if (fallElapsed > FALL_DELAY)
|
||||
{ //falling long enough to trigger the animation
|
||||
if (server.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 (server.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 (server.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 (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
server.Avatars.SendAnimations(agent);
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
if (moving)
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = move * speed;
|
||||
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = maxVel;
|
||||
}
|
||||
else
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = Vector3.Zero;
|
||||
}
|
||||
|
||||
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
|
||||
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = maxVel;
|
||||
|
||||
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
|
||||
|
||||
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
|
||||
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
|
||||
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
// Don't use the local physics to update the master agent
|
||||
if (agent != periscope.MasterAgent)
|
||||
{
|
||||
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
|
||||
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
|
||||
agent.State = (AgentState)update.AgentData.State;
|
||||
agent.HideTitle = update.AgentData.Flags != 0;
|
||||
}
|
||||
|
||||
SimulationObject obj;
|
||||
if (server.Scene.TryGetObject(update.AgentData.AgentID, out obj))
|
||||
{
|
||||
obj.Prim.Rotation = update.AgentData.BodyRotation;
|
||||
server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAlwaysRunHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
|
||||
|
||||
agent.Running = run.AgentData.AlwaysRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class PeriscopeTransferManager
|
||||
{
|
||||
public const string UPLOAD_DIR = "uploadedAssets";
|
||||
|
||||
Simian server;
|
||||
GridClient client;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
/// <summary>A map from TransactionIDs to AvatarIDs</summary>
|
||||
Dictionary<UUID, KeyValuePair<UUID, UUID>> currentDownloads = new Dictionary<UUID, KeyValuePair<UUID, UUID>>();
|
||||
|
||||
public PeriscopeTransferManager(Simian server, GridClient client)
|
||||
{
|
||||
this.server = server;
|
||||
this.client = client;
|
||||
|
||||
client.Assets.OnAssetReceived += new OpenMetaverse.AssetManager.AssetReceivedCallback(Assets_OnAssetReceived);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
#region Xfer System
|
||||
|
||||
void AssetUploadRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet;
|
||||
UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID);
|
||||
|
||||
// Check if the asset is small enough to fit in a single packet
|
||||
if (request.AssetBlock.AssetData.Length != 0)
|
||||
{
|
||||
// Create a new asset from the completed upload
|
||||
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData);
|
||||
if (asset == null)
|
||||
{
|
||||
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType));
|
||||
|
||||
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
|
||||
|
||||
// Store the asset
|
||||
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;
|
||||
server.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;
|
||||
|
||||
server.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;
|
||||
server.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;
|
||||
server.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);
|
||||
|
||||
server.Assets.StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
server.UDP.SendPacket(agent.ID, complete, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog("Received a SendXferPacket for an unknown upload");
|
||||
}
|
||||
}
|
||||
|
||||
void AbortXferHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AbortXferPacket abort = (AbortXferPacket)packet;
|
||||
|
||||
lock (CurrentUploads)
|
||||
{
|
||||
if (CurrentUploads.ContainsKey(abort.XferID.ID))
|
||||
{
|
||||
Logger.DebugLog(String.Format("Aborting Xfer {0}, result: {1}", abort.XferID.ID,
|
||||
(TransferError)abort.XferID.Result));
|
||||
|
||||
CurrentUploads.Remove(abort.XferID.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugLog(String.Format("Received an AbortXfer for an unknown xfer {0}",
|
||||
abort.XferID.ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Xfer System
|
||||
|
||||
#region Transfer System
|
||||
|
||||
void TransferRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
TransferRequestPacket request = (TransferRequestPacket)packet;
|
||||
|
||||
ChannelType channel = (ChannelType)request.TransferInfo.ChannelType;
|
||||
SourceType source = (SourceType)request.TransferInfo.SourceType;
|
||||
|
||||
if (channel == ChannelType.Asset)
|
||||
{
|
||||
if (source == SourceType.Asset)
|
||||
{
|
||||
// Parse the request
|
||||
UUID assetID = new UUID(request.TransferInfo.Params, 0);
|
||||
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 16);
|
||||
|
||||
// Check if we have this asset
|
||||
Asset asset;
|
||||
if (server.Assets.TryGetAsset(assetID, out asset))
|
||||
{
|
||||
if (asset.AssetType == type)
|
||||
{
|
||||
TransferToClient(asset, agent, request.TransferInfo.TransferID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Request for asset {0} with type {1} does not match actual asset type {2}",
|
||||
assetID, type, asset.AssetType), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the bot to try and request this asset
|
||||
lock (currentDownloads)
|
||||
{
|
||||
currentDownloads.Add(client.Assets.RequestAsset(assetID, type, false),
|
||||
new KeyValuePair<UUID, UUID>(agent.ID, request.TransferInfo.TransferID));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source == SourceType.SimEstate)
|
||||
{
|
||||
//UUID agentID = new UUID(request.TransferInfo.Params, 0);
|
||||
//UUID sessionID = new UUID(request.TransferInfo.Params, 16);
|
||||
//EstateAssetType type = (EstateAssetType)Utils.BytesToInt(request.TransferInfo.Params, 32);
|
||||
|
||||
Logger.Log("Please implement estate asset transfers", Helpers.LogLevel.Warning);
|
||||
}
|
||||
else if (source == SourceType.SimInventoryItem)
|
||||
{
|
||||
//UUID agentID = new UUID(request.TransferInfo.Params, 0);
|
||||
//UUID sessionID = new UUID(request.TransferInfo.Params, 16);
|
||||
//UUID ownerID = new UUID(request.TransferInfo.Params, 32);
|
||||
UUID taskID = new UUID(request.TransferInfo.Params, 48);
|
||||
//UUID itemID = new UUID(request.TransferInfo.Params, 64);
|
||||
//UUID assetID = new UUID(request.TransferInfo.Params, 80);
|
||||
//AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 96);
|
||||
|
||||
if (taskID != UUID.Zero)
|
||||
{
|
||||
// Task (prim) inventory request
|
||||
Logger.Log("Please implement task inventory transfers", Helpers.LogLevel.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Agent inventory request
|
||||
Logger.Log("Please implement agent inventory transfer", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
|
||||
channel, source), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
|
||||
channel, source), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void TransferToClient(Asset asset, Agent agent, UUID transferID)
|
||||
{
|
||||
Logger.Log(String.Format("Transferring asset {0} ({1})", asset.AssetID, asset.AssetType), Helpers.LogLevel.Info);
|
||||
|
||||
TransferInfoPacket response = new TransferInfoPacket();
|
||||
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
|
||||
response.TransferInfo.TransferID = transferID;
|
||||
|
||||
response.TransferInfo.Params = new byte[20];
|
||||
Buffer.BlockCopy(asset.AssetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
|
||||
Buffer.BlockCopy(Utils.IntToBytes((int)asset.AssetType), 0, response.TransferInfo.Params, 16, 4);
|
||||
|
||||
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
|
||||
response.TransferInfo.Size = asset.AssetData.Length;
|
||||
response.TransferInfo.Status = (int)StatusCode.OK;
|
||||
response.TransferInfo.TargetType = (int)TargetType.Unknown; // Doesn't seem to be used by the client
|
||||
|
||||
server.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
|
||||
|
||||
// Transfer system does not wait for ACKs, just sends all of the
|
||||
// packets for this transfer out
|
||||
const int MAX_CHUNK_SIZE = Settings.MAX_PACKET_SIZE - 100;
|
||||
int processedLength = 0;
|
||||
int packetNum = 0;
|
||||
while (processedLength < asset.AssetData.Length)
|
||||
{
|
||||
TransferPacketPacket transfer = new TransferPacketPacket();
|
||||
transfer.TransferData.ChannelType = (int)ChannelType.Asset;
|
||||
transfer.TransferData.TransferID = transferID;
|
||||
transfer.TransferData.Packet = packetNum++;
|
||||
|
||||
int chunkSize = Math.Min(asset.AssetData.Length - processedLength, MAX_CHUNK_SIZE);
|
||||
transfer.TransferData.Data = new byte[chunkSize];
|
||||
Buffer.BlockCopy(asset.AssetData, processedLength, transfer.TransferData.Data, 0, chunkSize);
|
||||
processedLength += chunkSize;
|
||||
|
||||
if (processedLength >= asset.AssetData.Length)
|
||||
transfer.TransferData.Status = (int)StatusCode.Done;
|
||||
else
|
||||
transfer.TransferData.Status = (int)StatusCode.OK;
|
||||
|
||||
server.UDP.SendPacket(agent.ID, transfer, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Transfer System
|
||||
|
||||
void Assets_OnAssetReceived(AssetDownload transfer, Asset asset)
|
||||
{
|
||||
KeyValuePair<UUID, UUID> kvp;
|
||||
Agent agent;
|
||||
if (currentDownloads.TryGetValue(transfer.ID, out kvp))
|
||||
{
|
||||
currentDownloads.Remove(transfer.ID);
|
||||
|
||||
if (server.Scene.TryGetAgent(kvp.Key, out agent))
|
||||
{
|
||||
if (transfer.Success)
|
||||
{
|
||||
server.Assets.StoreAsset(asset);
|
||||
TransferToClient(asset, agent, kvp.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Request for missing asset " + transfer.AssetID.ToString(), Helpers.LogLevel.Warning);
|
||||
|
||||
// Asset not found
|
||||
TransferInfoPacket response = new TransferInfoPacket();
|
||||
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
|
||||
response.TransferInfo.TransferID = kvp.Value;
|
||||
|
||||
response.TransferInfo.Params = new byte[20];
|
||||
Buffer.BlockCopy(transfer.AssetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
|
||||
Buffer.BlockCopy(Utils.IntToBytes((int)transfer.AssetType), 0, response.TransferInfo.Params, 16, 4);
|
||||
|
||||
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
|
||||
response.TransferInfo.Size = 0;
|
||||
response.TransferInfo.Status = (int)StatusCode.UnknownSource;
|
||||
response.TransferInfo.TargetType = (int)TargetType.Unknown;
|
||||
|
||||
server.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Asset transfer finished for an untracked agent, ignoring", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Asset transfer finished for an untracked download, ignoring", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
Asset CreateAsset(AssetType type, UUID assetID, byte[] data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AssetType.Bodypart:
|
||||
return new AssetBodypart(assetID, data);
|
||||
case AssetType.Clothing:
|
||||
return new AssetClothing(assetID, data);
|
||||
case AssetType.LSLBytecode:
|
||||
return new AssetScriptBinary(assetID, data);
|
||||
case AssetType.LSLText:
|
||||
return new AssetScriptText(assetID, data);
|
||||
case AssetType.Notecard:
|
||||
return new AssetNotecard(assetID, data);
|
||||
case AssetType.Texture:
|
||||
return new AssetTexture(assetID, data);
|
||||
case AssetType.Animation:
|
||||
return new AssetAnimation(assetID, data);
|
||||
case AssetType.CallingCard:
|
||||
case AssetType.Folder:
|
||||
case AssetType.Gesture:
|
||||
case AssetType.ImageJPEG:
|
||||
case AssetType.ImageTGA:
|
||||
case AssetType.Landmark:
|
||||
case AssetType.LostAndFoundFolder:
|
||||
case AssetType.Object:
|
||||
case AssetType.RootFolder:
|
||||
case AssetType.Simstate:
|
||||
case AssetType.SnapshotFolder:
|
||||
case AssetType.Sound:
|
||||
return new AssetSound(assetID, data);
|
||||
case AssetType.SoundWAV:
|
||||
case AssetType.TextureTGA:
|
||||
case AssetType.TrashFolder:
|
||||
case AssetType.Unknown:
|
||||
default:
|
||||
Logger.Log("Asset type " + type.ToString() + " not implemented!", Helpers.LogLevel.Warning);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class PermissionsFreeForAll : IExtension<Simian>, IPermissionsProvider
|
||||
{
|
||||
@@ -12,9 +12,10 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
using System;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Rendering;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class PhysicsSimple : IExtension<Simian>, IPhysicsProvider
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public float TimeDilation
|
||||
{
|
||||
get { return 1.0f; }
|
||||
}
|
||||
|
||||
public PhysicsSimple()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj)
|
||||
{
|
||||
Vector3 closestPoint = rayEnd;
|
||||
|
||||
if (rayStart == rayEnd)
|
||||
{
|
||||
Logger.DebugLog("RayStart is equal to RayEnd, returning given location");
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
Vector3 direction = Vector3.Normalize(rayEnd - rayStart);
|
||||
|
||||
// Get the mesh that has been transformed into world-space
|
||||
SimpleMesh mesh = obj.GetWorldMesh(DetailLevel.Low, false, false);
|
||||
if (mesh != null)
|
||||
{
|
||||
// Iterate through all of the triangles in the mesh, doing a ray-triangle intersection
|
||||
|
||||
float closestDistance = Single.MaxValue;
|
||||
for (int i = 0; i < mesh.Indices.Count; i += 3)
|
||||
{
|
||||
Vector3 point0 = mesh.Vertices[mesh.Indices[i + 0]].Position;
|
||||
Vector3 point1 = mesh.Vertices[mesh.Indices[i + 1]].Position;
|
||||
Vector3 point2 = mesh.Vertices[mesh.Indices[i + 2]].Position;
|
||||
|
||||
Vector3 collisionPoint;
|
||||
if (RayTriangleIntersection(rayStart, direction, point0, point1, point2, out collisionPoint))
|
||||
{
|
||||
if ((collisionPoint - rayStart).Length() < closestDistance)
|
||||
closestPoint = collisionPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
public bool TryGetObjectMass(UUID objectID, out float mass)
|
||||
{
|
||||
SimulationObject obj;
|
||||
if (server.Scene.TryGetObject(objectID, out obj))
|
||||
{
|
||||
mass = CalculateMass(obj.Prim);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mass = 0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
|
||||
/// </summary>
|
||||
/// <param name="origin">Origin point of the ray</param>
|
||||
/// <param name="direction">Unit vector representing the direction of the ray</param>
|
||||
/// <param name="vert0">Position of the first triangle corner</param>
|
||||
/// <param name="vert1">Position of the second triangle corner</param>
|
||||
/// <param name="vert2">Position of the third triangle corner</param>
|
||||
/// <param name="collisionPoint">The collision point in the triangle</param>
|
||||
/// <returns>True if the ray passes through the triangle, otherwise false</returns>
|
||||
static bool RayTriangleIntersection(Vector3 origin, Vector3 direction, Vector3 vert0, Vector3 vert1, Vector3 vert2, out Vector3 collisionPoint)
|
||||
{
|
||||
const float EPSILON = 0.00001f;
|
||||
|
||||
Vector3 edge1, edge2, pvec;
|
||||
float determinant, invDeterminant;
|
||||
|
||||
// Find vectors for two edges sharing vert0
|
||||
edge1 = vert1 - vert0;
|
||||
edge2 = vert2 - vert0;
|
||||
|
||||
// Begin calculating the determinant
|
||||
pvec = Vector3.Cross(direction, edge2);
|
||||
|
||||
// If the determinant is near zero, ray lies in plane of triangle
|
||||
determinant = Vector3.Dot(edge1, pvec);
|
||||
|
||||
if (determinant > -EPSILON && determinant < EPSILON)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
invDeterminant = 1f / determinant;
|
||||
|
||||
// Calculate distance from vert0 to ray origin
|
||||
Vector3 tvec = origin - vert0;
|
||||
|
||||
// Calculate U parameter and test bounds
|
||||
float u = Vector3.Dot(tvec, pvec) * invDeterminant;
|
||||
if (u < 0.0f || u > 1.0f)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare to test V parameter
|
||||
Vector3 qvec = Vector3.Cross(tvec, edge1);
|
||||
|
||||
// Calculate V parameter and test bounds
|
||||
float v = Vector3.Dot(direction, qvec) * invDeterminant;
|
||||
if (v < 0.0f || u + v > 1.0f)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
//t = Vector3.Dot(edge2, qvec) * invDeterminant;
|
||||
|
||||
collisionPoint = new Vector3(
|
||||
vert0.X + u * (vert1.X - vert0.X) + v * (vert2.X - vert0.X),
|
||||
vert0.Y + u * (vert1.Y - vert0.Y) + v * (vert2.Y - vert0.Y),
|
||||
vert0.Z + u * (vert1.Z - vert0.Z) + v * (vert2.Z - vert0.Z));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from code written by Teravus for OpenSim
|
||||
/// </summary>
|
||||
/// <param name="prim">Primitive to calculate the mass of</param>
|
||||
/// <returns>Estimated mass of the given primitive</returns>
|
||||
static float CalculateMass(Primitive prim)
|
||||
{
|
||||
const float PRIM_DENSITY = 10.000006836f; // Aluminum g/cm3
|
||||
|
||||
float volume = 0f;
|
||||
float returnMass = 0f;
|
||||
|
||||
// TODO: Use the prim material in mass calculations once our physics
|
||||
// engine supports different materials
|
||||
|
||||
switch (prim.PrimData.ProfileCurve)
|
||||
{
|
||||
case ProfileCurve.Square:
|
||||
// Profile Volume
|
||||
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
|
||||
// If the user has 'hollowed out'
|
||||
if (prim.PrimData.ProfileHollow > 0.0f)
|
||||
{
|
||||
float hollowAmount = prim.PrimData.ProfileHollow;
|
||||
|
||||
// calculate the hollow volume by it's shape compared to the prim shape
|
||||
float hollowVolume = 0;
|
||||
switch (prim.PrimData.ProfileHole)
|
||||
{
|
||||
case HoleType.Square:
|
||||
case HoleType.Same:
|
||||
// Cube Hollow volume calculation
|
||||
float hollowsizex = prim.Scale.X * hollowAmount;
|
||||
float hollowsizey = prim.Scale.Y * hollowAmount;
|
||||
float hollowsizez = prim.Scale.Z * hollowAmount;
|
||||
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
|
||||
break;
|
||||
|
||||
case HoleType.Circle:
|
||||
// Hollow shape is a perfect cyllinder in respect to the cube's scale
|
||||
// Cyllinder hollow volume calculation
|
||||
float hRadius = prim.Scale.X * 0.5f;
|
||||
float hLength = prim.Scale.Z;
|
||||
|
||||
// pi * r2 * h
|
||||
hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
|
||||
break;
|
||||
|
||||
case HoleType.Triangle:
|
||||
// Equilateral Triangular Prism volume hollow calculation
|
||||
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
|
||||
|
||||
float aLength = prim.Scale.Y;
|
||||
// 1/2 abh
|
||||
hollowVolume = (float)((0.5 * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount);
|
||||
break;
|
||||
|
||||
default:
|
||||
hollowVolume = 0;
|
||||
break;
|
||||
}
|
||||
volume = volume - hollowVolume;
|
||||
}
|
||||
|
||||
break;
|
||||
case ProfileCurve.Circle:
|
||||
if (prim.PrimData.PathCurve == PathCurve.Line)
|
||||
{
|
||||
// Cylinder
|
||||
float volume1 = (float)(Math.PI * Math.Pow(prim.Scale.X / 2, 2) * prim.Scale.Z);
|
||||
float volume2 = (float)(Math.PI * Math.Pow(prim.Scale.Y / 2, 2) * prim.Scale.Z);
|
||||
|
||||
// Approximating the cylinder's irregularity.
|
||||
if (volume1 > volume2)
|
||||
{
|
||||
volume = (float)volume1 - (volume1 - volume2);
|
||||
}
|
||||
else if (volume2 > volume1)
|
||||
{
|
||||
volume = (float)volume2 - (volume2 - volume1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular cylinder
|
||||
volume = volume1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know what the shape is yet, so use default
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
}
|
||||
|
||||
// If the user has 'hollowed out'
|
||||
if (prim.PrimData.ProfileHollow > 0.0f)
|
||||
{
|
||||
float hollowAmount = prim.PrimData.ProfileHollow;
|
||||
|
||||
// calculate the hollow volume by it's shape compared to the prim shape
|
||||
float hollowVolume = 0f;
|
||||
switch (prim.PrimData.ProfileHole)
|
||||
{
|
||||
case HoleType.Circle:
|
||||
case HoleType.Same:
|
||||
// Hollow shape is a perfect cyllinder in respect to the cube's scale
|
||||
// Cyllinder hollow volume calculation
|
||||
float hRadius = prim.Scale.X * 0.5f;
|
||||
float hLength = prim.Scale.Z;
|
||||
|
||||
// pi * r2 * h
|
||||
hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
|
||||
break;
|
||||
|
||||
case HoleType.Square:
|
||||
// Cube Hollow volume calculation
|
||||
float hollowsizex = prim.Scale.X * hollowAmount;
|
||||
float hollowsizey = prim.Scale.Y * hollowAmount;
|
||||
float hollowsizez = prim.Scale.Z * hollowAmount;
|
||||
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
|
||||
break;
|
||||
|
||||
case HoleType.Triangle:
|
||||
// Equilateral Triangular Prism volume hollow calculation
|
||||
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
|
||||
|
||||
float aLength = prim.Scale.Y;
|
||||
// 1/2 abh
|
||||
hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount;
|
||||
break;
|
||||
|
||||
default:
|
||||
hollowVolume = 0;
|
||||
break;
|
||||
}
|
||||
volume = volume - hollowVolume;
|
||||
}
|
||||
break;
|
||||
|
||||
case ProfileCurve.HalfCircle:
|
||||
if (prim.PrimData.PathCurve == PathCurve.Circle)
|
||||
{
|
||||
if (prim.Scale.X == prim.Scale.Y && prim.Scale.Y == prim.Scale.Z)
|
||||
{
|
||||
// regular sphere
|
||||
// v = 4/3 * pi * r^3
|
||||
float sradius3 = (float)Math.Pow((prim.Scale.X * 0.5f), 3);
|
||||
volume = (4f / 3f) * (float)Math.PI * sradius3;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we treat this as a box currently
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know what the shape is yet, so use default
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
}
|
||||
break;
|
||||
|
||||
case ProfileCurve.EqualTriangle:
|
||||
float xA = -0.25f * prim.Scale.X;
|
||||
float yA = -0.45f * prim.Scale.Y;
|
||||
|
||||
float xB = 0.5f * prim.Scale.X;
|
||||
float yB = 0;
|
||||
|
||||
float xC = -0.25f * prim.Scale.X;
|
||||
float yC = 0.45f * prim.Scale.Y;
|
||||
|
||||
volume = (float)((Math.Abs((xB * yA - xA * yB) + (xC * yB - xB * yC) + (xA * yC - xC * yA)) / 2) * prim.Scale.Z);
|
||||
|
||||
// If the user has 'hollowed out'
|
||||
// ProfileHollow is one of those 0 to 50000 values :P
|
||||
// we like percentages better.. so turning into a percentage
|
||||
if (prim.PrimData.ProfileHollow > 0.0f)
|
||||
{
|
||||
float hollowAmount = prim.PrimData.ProfileHollow;
|
||||
|
||||
// calculate the hollow volume by it's shape compared to the prim shape
|
||||
float hollowVolume = 0f;
|
||||
|
||||
switch (prim.PrimData.ProfileHole)
|
||||
{
|
||||
case HoleType.Triangle:
|
||||
case HoleType.Same:
|
||||
// Equilateral Triangular Prism volume hollow calculation
|
||||
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
|
||||
|
||||
float aLength = prim.Scale.Y;
|
||||
// 1/2 abh
|
||||
hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount;
|
||||
break;
|
||||
|
||||
case HoleType.Square:
|
||||
// Cube Hollow volume calculation
|
||||
float hollowsizex = prim.Scale.X * hollowAmount;
|
||||
float hollowsizey = prim.Scale.Y * hollowAmount;
|
||||
float hollowsizez = prim.Scale.Z * hollowAmount;
|
||||
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
|
||||
break;
|
||||
|
||||
case HoleType.Circle:
|
||||
// Hollow shape is a perfect cyllinder in respect to the cube's scale
|
||||
// Cyllinder hollow volume calculation
|
||||
float hRadius = prim.Scale.X * 0.5f;
|
||||
float hLength = prim.Scale.Z;
|
||||
|
||||
// pi * r2 * h
|
||||
hollowVolume = ((float)((Math.PI * Math.Pow(hRadius, 2) * hLength) / 2) * hollowAmount);
|
||||
break;
|
||||
|
||||
default:
|
||||
hollowVolume = 0;
|
||||
break;
|
||||
}
|
||||
volume = volume - hollowVolume;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// we don't have all of the volume formulas yet so
|
||||
// use the common volume formula for all
|
||||
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate Path cut effect on volume
|
||||
// Not exact, in the triangle hollow example
|
||||
// They should never be zero or less then zero..
|
||||
// we'll ignore it if it's less then zero
|
||||
|
||||
if (prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd > 0.0f)
|
||||
{
|
||||
float pathCutAmount = prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd;
|
||||
|
||||
// Check the return amount for sanity
|
||||
if (pathCutAmount >= 0.99f)
|
||||
pathCutAmount = 0.99f;
|
||||
|
||||
volume = volume - (volume * pathCutAmount);
|
||||
}
|
||||
|
||||
// Mass = density * volume
|
||||
if (prim.PrimData.PathTaperX != 1f)
|
||||
volume *= (prim.PrimData.PathTaperX / 3f) + 0.001f;
|
||||
if (prim.PrimData.PathTaperY != 1f)
|
||||
volume *= (prim.PrimData.PathTaperY / 3f) + 0.001f;
|
||||
|
||||
returnMass = PRIM_DENSITY * volume;
|
||||
|
||||
if (returnMass <= 0f)
|
||||
returnMass = 0.0001f; //ckrinke: Mass must be greater then zero.
|
||||
|
||||
return returnMass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Rendering;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class RenderingPluginMesher : IExtension<Simian>, IMeshingProvider
|
||||
{
|
||||
@@ -15,7 +15,7 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
@@ -35,11 +35,12 @@ namespace Simian.Extensions
|
||||
else
|
||||
{
|
||||
Logger.Log("No suitable OpenMetaverse.Rendering plugins found", Helpers.LogLevel.Error);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Renderer = RenderingLoader.LoadRenderer(renderer);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
@@ -14,7 +15,7 @@ using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Http;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
class EventQueueServerCap
|
||||
{
|
||||
@@ -28,20 +29,17 @@ namespace Simian.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
public class SceneManager : IExtension<Simian>, ISceneProvider
|
||||
public class SceneManager : ISceneProvider
|
||||
{
|
||||
Simian server;
|
||||
// Contains all scene objects, including prims and avatars
|
||||
DoubleDictionary<uint, UUID, SimulationObject> sceneObjects = new DoubleDictionary<uint, UUID, SimulationObject>();
|
||||
// A duplicate of the avatar information stored in sceneObjects, improves operations such as iterating over all agents
|
||||
Dictionary<UUID, Agent> sceneAgents = new Dictionary<UUID, Agent>();
|
||||
// Event queues for each avatar in the scene
|
||||
Dictionary<UUID, EventQueueServerCap> eventQueues = new Dictionary<UUID, EventQueueServerCap>();
|
||||
int currentLocalID = 1;
|
||||
ulong regionHandle;
|
||||
UUID regionID = UUID.Random();
|
||||
TerrainPatch[,] heightmap = new TerrainPatch[16, 16];
|
||||
Vector2[,] windSpeeds = new Vector2[16, 16];
|
||||
// Interfaces. Although no other classes will access these interfaces directly
|
||||
// (getters are used instead), they must be marked public so ExtensionLoader
|
||||
// can automatically assign them
|
||||
public IAvatarProvider avatars;
|
||||
public IParcelProvider parcels;
|
||||
public IPhysicsProvider physics;
|
||||
public IScriptEngine scriptEngine;
|
||||
public ITaskInventoryProvider taskInventory;
|
||||
public IUDPProvider udp;
|
||||
|
||||
public event ObjectAddOrUpdateCallback OnObjectAddOrUpdate;
|
||||
public event ObjectRemoveCallback OnObjectRemove;
|
||||
@@ -61,12 +59,39 @@ namespace Simian.Extensions
|
||||
public event TerrainUpdateCallback OnTerrainUpdate;
|
||||
public event WindUpdateCallback OnWindUpdate;
|
||||
|
||||
public uint RegionX { get { return 7777; } }
|
||||
public uint RegionY { get { return 7777; } }
|
||||
public Simian Server { get { return server; } }
|
||||
public IAvatarProvider Avatars { get { return avatars; } }
|
||||
public IParcelProvider Parcels { get { return parcels; } }
|
||||
public IPhysicsProvider Physics { get { return physics; } }
|
||||
public IScriptEngine ScriptEngine { get { return scriptEngine; } }
|
||||
public ITaskInventoryProvider TaskInventory { get { return taskInventory; } }
|
||||
public IUDPProvider UDP { get { return udp; } }
|
||||
|
||||
public uint RegionX
|
||||
{
|
||||
get { return regionX; }
|
||||
set
|
||||
{
|
||||
regionX = value;
|
||||
regionHandle = Utils.UIntsToLong(regionX * 256, regionY * 256);
|
||||
}
|
||||
}
|
||||
public uint RegionY
|
||||
{
|
||||
get { return regionY; }
|
||||
set
|
||||
{
|
||||
regionY = value;
|
||||
regionHandle = Utils.UIntsToLong(regionX * 256, regionY * 256);
|
||||
}
|
||||
}
|
||||
public ulong RegionHandle { get { return regionHandle; } }
|
||||
public UUID RegionID { get { return regionID; } }
|
||||
public string RegionName { get { return "Simian"; } }
|
||||
public string RegionName { get { return regionName; } set { regionName = value; } }
|
||||
public RegionFlags RegionFlags { get { return RegionFlags.None; } }
|
||||
public IPEndPoint IPAndPort { get { return endpoint; } set { endpoint = value; } }
|
||||
public Vector3 DefaultPosition { get { return defaultPosition; } }
|
||||
public Vector3 DefaultLookAt { get { return defaultLookAt; } }
|
||||
|
||||
public float WaterHeight { get { return 20f; } }
|
||||
|
||||
@@ -75,21 +100,91 @@ namespace Simian.Extensions
|
||||
public uint TerrainPatchCountWidth { get { return 16; } }
|
||||
public uint TerrainPatchCountHeight { get { return 16; } }
|
||||
|
||||
Simian server;
|
||||
// Contains all scene objects, including prims and avatars
|
||||
DoubleDictionary<uint, UUID, SimulationObject> sceneObjects = new DoubleDictionary<uint, UUID, SimulationObject>();
|
||||
// A duplicate of the avatar information stored in sceneObjects, improves operations such as iterating over all agents
|
||||
Dictionary<UUID, Agent> sceneAgents = new Dictionary<UUID, Agent>();
|
||||
// Event queues for each avatar in the scene
|
||||
Dictionary<UUID, EventQueueServerCap> eventQueues = new Dictionary<UUID, EventQueueServerCap>();
|
||||
int currentLocalID = 1;
|
||||
ulong regionHandle;
|
||||
UUID regionID = UUID.Random();
|
||||
TerrainPatch[,] heightmap = new TerrainPatch[16, 16];
|
||||
Vector2[,] windSpeeds = new Vector2[16, 16];
|
||||
ExtensionLoader<ISceneProvider> extensions = new ExtensionLoader<ISceneProvider>();
|
||||
IPEndPoint endpoint;
|
||||
uint regionX;
|
||||
uint regionY;
|
||||
string regionName;
|
||||
Vector3 defaultPosition = new Vector3(128f, 128f, 30f);
|
||||
Vector3 defaultLookAt = Vector3.UnitX;
|
||||
|
||||
public SceneManager()
|
||||
{
|
||||
regionHandle = Utils.UIntsToLong(RegionX * 256, RegionY * 256);
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server, string name, IPEndPoint endpoint, uint regionX, uint regionY,
|
||||
string defaultTerrainFile, int staticObjects, int physicalObjects)
|
||||
{
|
||||
this.server = server;
|
||||
this.regionName = name;
|
||||
this.endpoint = endpoint;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.CompleteAgentMovement, new PacketCallback(CompleteAgentMovementHandler));
|
||||
LoadTerrain(Simian.DATA_DIR + "heightmap.tga");
|
||||
// Set the properties because this will automatically update the regionHandle
|
||||
RegionX = regionX;
|
||||
RegionY = regionY;
|
||||
|
||||
#region ISceneProvider Extension Loading
|
||||
|
||||
try
|
||||
{
|
||||
// Create a list of references for .cs extensions that are compiled at runtime
|
||||
List<string> references = new List<string>();
|
||||
references.Add("OpenMetaverseTypes.dll");
|
||||
references.Add("OpenMetaverse.dll");
|
||||
references.Add("Simian.exe");
|
||||
|
||||
// Load extensions from the current executing assembly, Simian.*.dll assemblies on disk, and
|
||||
// Simian.*.cs source files on disk.
|
||||
extensions.LoadAllExtensions(Assembly.GetExecutingAssembly(),
|
||||
AppDomain.CurrentDomain.BaseDirectory, server.ExtensionList, references,
|
||||
"Simian.*.dll", "Simian.*.cs");
|
||||
|
||||
// Automatically assign extensions that implement interfaces to the list of interface
|
||||
// variables in "assignables"
|
||||
extensions.AssignExtensions(this, extensions.GetInterfaces(this));
|
||||
|
||||
// Start all of the extensions
|
||||
foreach (IExtension<ISceneProvider> extension in extensions.Extensions)
|
||||
{
|
||||
Logger.Log("Starting Scene extension " + extension.GetType().Name, Helpers.LogLevel.Info);
|
||||
extension.Start(this);
|
||||
}
|
||||
}
|
||||
catch (ExtensionException ex)
|
||||
{
|
||||
Logger.Log("SceneManager extension loading failed, shutting down: " + ex.Message, Helpers.LogLevel.Error);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion ISceneProvider Extension Loading
|
||||
|
||||
udp.RegisterPacketCallback(PacketType.CompleteAgentMovement, new PacketCallback(CompleteAgentMovementHandler));
|
||||
|
||||
if (!String.IsNullOrEmpty(defaultTerrainFile))
|
||||
LoadTerrain(Simian.DATA_DIR + defaultTerrainFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Logger.Log("Stopping region " + regionName, Helpers.LogLevel.Info);
|
||||
|
||||
// Remove all of the agents from the scene. This will shutdown UDP connections and event queues to
|
||||
// each of the agents as well
|
||||
lock (sceneAgents)
|
||||
{
|
||||
List<Agent> agents = new List<Agent>(sceneAgents.Values);
|
||||
@@ -97,7 +192,14 @@ namespace Simian.Extensions
|
||||
ObjectRemove(this, agents[i].ID);
|
||||
}
|
||||
|
||||
Logger.DebugLog("SceneManager is stopped");
|
||||
// Stop ISceneProvider extensions
|
||||
foreach (IExtension<ISceneProvider> extension in extensions.Extensions)
|
||||
{
|
||||
Logger.Log("Stopping Scene extension " + extension.GetType().Name, Helpers.LogLevel.Info);
|
||||
extension.Stop();
|
||||
}
|
||||
|
||||
Logger.Log("Region " + regionName + " is stopped", Helpers.LogLevel.Info);
|
||||
}
|
||||
|
||||
#region Object Interfaces
|
||||
@@ -136,7 +238,7 @@ namespace Simian.Extensions
|
||||
|
||||
// Set the RegionHandle if no RegionHandle is set
|
||||
if (obj.Prim.RegionHandle == 0)
|
||||
obj.Prim.RegionHandle = server.Scene.RegionHandle;
|
||||
obj.Prim.RegionHandle = regionHandle;
|
||||
|
||||
// Make sure this object has properties
|
||||
if (obj.Prim.Properties == null)
|
||||
@@ -236,7 +338,7 @@ namespace Simian.Extensions
|
||||
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
|
||||
kill.ObjectData[0].ID = obj.Prim.LocalID;
|
||||
|
||||
server.UDP.BroadcastPacket(kill, PacketCategory.State);
|
||||
udp.BroadcastPacket(kill, PacketCategory.State);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -263,7 +365,7 @@ namespace Simian.Extensions
|
||||
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
|
||||
kill.ObjectData[0].ID = obj.Prim.LocalID;
|
||||
|
||||
server.UDP.BroadcastPacket(kill, PacketCategory.State);
|
||||
udp.BroadcastPacket(kill, PacketCategory.State);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -332,7 +434,7 @@ namespace Simian.Extensions
|
||||
sendAnim.AnimationList[i].AnimSequenceID = animations[i].SequenceID;
|
||||
}
|
||||
|
||||
server.UDP.BroadcastPacket(sendAnim, PacketCategory.State);
|
||||
udp.BroadcastPacket(sendAnim, PacketCategory.State);
|
||||
}
|
||||
|
||||
public void ObjectChat(object sender, UUID ownerID, UUID sourceID, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
|
||||
@@ -356,7 +458,7 @@ namespace Simian.Extensions
|
||||
chat.ChatData.FromName = Utils.StringToBytes(fromName);
|
||||
chat.ChatData.Message = Utils.StringToBytes(message);
|
||||
|
||||
server.UDP.BroadcastPacket(chat, PacketCategory.Messaging);
|
||||
udp.BroadcastPacket(chat, PacketCategory.Messaging);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +478,7 @@ namespace Simian.Extensions
|
||||
obj.Prim = prim;
|
||||
|
||||
// Inform clients
|
||||
server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -401,7 +503,7 @@ namespace Simian.Extensions
|
||||
obj.Prim = prim;
|
||||
|
||||
// Inform clients
|
||||
server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -458,7 +560,7 @@ namespace Simian.Extensions
|
||||
}
|
||||
|
||||
SoundTriggerPacket sound = new SoundTriggerPacket();
|
||||
sound.SoundData.Handle = server.Scene.RegionHandle;
|
||||
sound.SoundData.Handle = regionHandle;
|
||||
sound.SoundData.ObjectID = objectID;
|
||||
sound.SoundData.ParentID = parentID;
|
||||
sound.SoundData.OwnerID = ownerID;
|
||||
@@ -466,7 +568,7 @@ namespace Simian.Extensions
|
||||
sound.SoundData.SoundID = soundID;
|
||||
sound.SoundData.Gain = gain;
|
||||
|
||||
server.UDP.BroadcastPacket(sound, PacketCategory.State);
|
||||
udp.BroadcastPacket(sound, PacketCategory.State);
|
||||
}
|
||||
|
||||
public void TriggerEffects(object sender, ViewerEffect[] effects)
|
||||
@@ -497,7 +599,7 @@ namespace Simian.Extensions
|
||||
effect.Effect[i] = block;
|
||||
}
|
||||
|
||||
server.UDP.BroadcastPacket(effect, PacketCategory.State);
|
||||
udp.BroadcastPacket(effect, PacketCategory.State);
|
||||
}
|
||||
|
||||
#endregion Object Interfaces
|
||||
@@ -506,6 +608,9 @@ namespace Simian.Extensions
|
||||
|
||||
public bool AgentAdd(object sender, Agent agent, PrimFlags creatorFlags)
|
||||
{
|
||||
// Sanity check, since this should have already been done
|
||||
agent.Avatar.Prim.ID = agent.Info.ID;
|
||||
|
||||
// Check if the agent already exists in the scene
|
||||
lock (sceneAgents)
|
||||
{
|
||||
@@ -513,6 +618,9 @@ namespace Simian.Extensions
|
||||
sceneAgents.Remove(agent.ID);
|
||||
}
|
||||
|
||||
// Update the current region handle
|
||||
agent.Avatar.Prim.RegionHandle = regionHandle;
|
||||
|
||||
// Avatars always have physics
|
||||
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
|
||||
|
||||
@@ -527,17 +635,17 @@ namespace Simian.Extensions
|
||||
// Set the avatar name
|
||||
NameValue[] name = new NameValue[2];
|
||||
name[0] = new NameValue("FirstName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite,
|
||||
NameValue.SendtoType.SimViewer, agent.FirstName);
|
||||
NameValue.SendtoType.SimViewer, agent.Info.FirstName);
|
||||
name[1] = new NameValue("LastName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite,
|
||||
NameValue.SendtoType.SimViewer, agent.LastName);
|
||||
NameValue.SendtoType.SimViewer, agent.Info.LastName);
|
||||
agent.Avatar.Prim.NameValues = name;
|
||||
|
||||
// Give testers a provisionary balance of 1000L
|
||||
agent.Balance = 1000;
|
||||
agent.Info.Balance = 1000;
|
||||
|
||||
// Some default avatar prim properties
|
||||
agent.Avatar.Prim.Properties = new Primitive.ObjectProperties();
|
||||
agent.Avatar.Prim.Properties.CreationDate = Utils.UnixTimeToDateTime(agent.CreationTime);
|
||||
agent.Avatar.Prim.Properties.CreationDate = Utils.UnixTimeToDateTime(agent.Info.CreationTime);
|
||||
agent.Avatar.Prim.Properties.Name = agent.FullName;
|
||||
agent.Avatar.Prim.Properties.ObjectID = agent.ID;
|
||||
|
||||
@@ -570,14 +678,14 @@ namespace Simian.Extensions
|
||||
RemoveEventQueue(agent.ID);
|
||||
|
||||
// Remove the UDP client
|
||||
server.UDP.RemoveClient(agent);
|
||||
udp.RemoveClient(agent);
|
||||
|
||||
// Notify everyone in the scene that this agent has gone offline
|
||||
OfflineNotificationPacket offline = new OfflineNotificationPacket();
|
||||
offline.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[1];
|
||||
offline.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
|
||||
offline.AgentBlock[0].AgentID = agent.ID;
|
||||
server.UDP.BroadcastPacket(offline, PacketCategory.State);
|
||||
udp.BroadcastPacket(offline, PacketCategory.State);
|
||||
}
|
||||
|
||||
public void AgentAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams)
|
||||
@@ -591,14 +699,14 @@ namespace Simian.Extensions
|
||||
// TODO: Is this necessary here?
|
||||
//ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar,
|
||||
// regionHandle, agent.Flags);
|
||||
//server.UDP.BroadcastPacket(update, PacketCategory.State);
|
||||
//scene.UDP.BroadcastPacket(update, PacketCategory.State);
|
||||
|
||||
// Update the avatar
|
||||
agent.Avatar.Prim.Textures = textures;
|
||||
if (visualParams != null && visualParams.Length > 1)
|
||||
agent.VisualParams = visualParams;
|
||||
agent.Info.VisualParams = visualParams;
|
||||
|
||||
if (agent.VisualParams != null)
|
||||
if (agent.Info.VisualParams != null)
|
||||
{
|
||||
// Send the appearance packet to all other clients
|
||||
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
|
||||
@@ -606,7 +714,7 @@ namespace Simian.Extensions
|
||||
delegate(Agent recipient)
|
||||
{
|
||||
if (recipient != agent)
|
||||
server.UDP.SendPacket(recipient.ID, appearance, PacketCategory.State);
|
||||
udp.SendPacket(recipient.ID, appearance, PacketCategory.State);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -745,7 +853,7 @@ namespace Simian.Extensions
|
||||
heightmap[y, x].Height = copy;
|
||||
|
||||
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap[y, x].Height, (int)x, (int)y);
|
||||
server.UDP.BroadcastPacket(layer, PacketCategory.Terrain);
|
||||
udp.BroadcastPacket(layer, PacketCategory.Terrain);
|
||||
}
|
||||
|
||||
public Vector2 GetWindSpeedAt(float fx, float fy)
|
||||
@@ -901,7 +1009,7 @@ namespace Simian.Extensions
|
||||
complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now);
|
||||
complete.SimData.ChannelVersion = Utils.StringToBytes("Simian");
|
||||
|
||||
server.UDP.SendPacket(agent.ID, complete, PacketCategory.Transaction);
|
||||
udp.SendPacket(agent.ID, complete, PacketCategory.Transaction);
|
||||
|
||||
// Send updates and appearances for every avatar to this new avatar
|
||||
SynchronizeStateTo(agent);
|
||||
@@ -911,7 +1019,7 @@ namespace Simian.Extensions
|
||||
online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1];
|
||||
online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
|
||||
online.AgentBlock[0].AgentID = agent.ID;
|
||||
server.UDP.BroadcastPacket(online, PacketCategory.State);
|
||||
udp.BroadcastPacket(online, PacketCategory.State);
|
||||
}
|
||||
|
||||
#endregion Callback Handlers
|
||||
@@ -920,18 +1028,18 @@ namespace Simian.Extensions
|
||||
void SynchronizeStateTo(Agent agent)
|
||||
{
|
||||
// Send the parcel overlay
|
||||
server.Parcels.SendParcelOverlay(agent);
|
||||
parcels.SendParcelOverlay(agent);
|
||||
|
||||
// Send object updates for objects and avatars
|
||||
sceneObjects.ForEach(delegate(SimulationObject obj)
|
||||
{
|
||||
ObjectUpdatePacket update = new ObjectUpdatePacket();
|
||||
update.RegionData.RegionHandle = regionHandle;
|
||||
update.RegionData.TimeDilation = (ushort)(server.Physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
update.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1];
|
||||
update.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC);
|
||||
|
||||
server.UDP.SendPacket(agent.ID, update, PacketCategory.State);
|
||||
udp.SendPacket(agent.ID, update, PacketCategory.State);
|
||||
});
|
||||
|
||||
// Send appearances for all avatars
|
||||
@@ -942,7 +1050,7 @@ namespace Simian.Extensions
|
||||
{
|
||||
// Send appearances for this avatar
|
||||
AvatarAppearancePacket appearance = BuildAppearancePacket(otherAgent);
|
||||
server.UDP.SendPacket(agent.ID, appearance, PacketCategory.State);
|
||||
udp.SendPacket(agent.ID, appearance, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1026,7 +1134,7 @@ namespace Simian.Extensions
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap[y, x].Height, x, y);
|
||||
server.UDP.SendPacket(agent.ID, layer, PacketCategory.Terrain);
|
||||
udp.SendPacket(agent.ID, layer, PacketCategory.Terrain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1044,12 +1152,12 @@ namespace Simian.Extensions
|
||||
// Send an update out to the creator
|
||||
ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket();
|
||||
updateToOwner.RegionData.RegionHandle = regionHandle;
|
||||
updateToOwner.RegionData.TimeDilation = (ushort)(server.Physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
updateToOwner.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
updateToOwner.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1];
|
||||
updateToOwner.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim,
|
||||
obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner, obj.CRC);
|
||||
|
||||
server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
|
||||
udp.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
|
||||
}
|
||||
|
||||
// Send an update out to everyone else
|
||||
@@ -1060,11 +1168,11 @@ namespace Simian.Extensions
|
||||
updateToOthers.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim,
|
||||
obj.Prim.Flags, obj.CRC);
|
||||
|
||||
server.Scene.ForEachAgent(
|
||||
ForEachAgent(
|
||||
delegate(Agent recipient)
|
||||
{
|
||||
if (recipient.ID != obj.Prim.OwnerID)
|
||||
server.UDP.SendPacket(recipient.ID, updateToOthers, PacketCategory.State);
|
||||
udp.SendPacket(recipient.ID, updateToOthers, PacketCategory.State);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1307,13 +1415,13 @@ namespace Simian.Extensions
|
||||
// Send an update out to the creator
|
||||
ObjectUpdateCompressedPacket updateToOwner = new ObjectUpdateCompressedPacket();
|
||||
updateToOwner.RegionData.RegionHandle = regionHandle;
|
||||
updateToOwner.RegionData.TimeDilation = (ushort)(server.Physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
updateToOwner.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
updateToOwner.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[1];
|
||||
updateToOwner.ObjectData[0] = new ObjectUpdateCompressedPacket.ObjectDataBlock();
|
||||
updateToOwner.ObjectData[0].UpdateFlags = (uint)(obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner);
|
||||
updateToOwner.ObjectData[0].Data = data;
|
||||
|
||||
server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
|
||||
udp.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
|
||||
}
|
||||
|
||||
// Send an update out to everyone else
|
||||
@@ -1325,11 +1433,11 @@ namespace Simian.Extensions
|
||||
updateToOthers.ObjectData[0].UpdateFlags = (uint)obj.Prim.Flags;
|
||||
updateToOthers.ObjectData[0].Data = data;
|
||||
|
||||
server.Scene.ForEachAgent(
|
||||
ForEachAgent(
|
||||
delegate(Agent recipient)
|
||||
{
|
||||
if (recipient.ID != obj.Prim.OwnerID)
|
||||
server.UDP.SendPacket(recipient.ID, updateToOthers, PacketCategory.State);
|
||||
udp.SendPacket(recipient.ID, updateToOthers, PacketCategory.State);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1385,7 +1493,7 @@ namespace Simian.Extensions
|
||||
|
||||
ImprovedTerseObjectUpdatePacket update = new ImprovedTerseObjectUpdatePacket();
|
||||
update.RegionData.RegionHandle = RegionHandle;
|
||||
update.RegionData.TimeDilation = (ushort)(server.Physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
update.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue);
|
||||
update.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
|
||||
update.ObjectData[0] = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock();
|
||||
update.ObjectData[0].Data = data;
|
||||
@@ -1407,7 +1515,7 @@ namespace Simian.Extensions
|
||||
update.ObjectData[0].TextureEntry = Utils.EmptyBytes;
|
||||
}
|
||||
|
||||
server.UDP.BroadcastPacket(update, PacketCategory.State);
|
||||
udp.BroadcastPacket(update, PacketCategory.State);
|
||||
|
||||
#endregion ImprovedTerseObjectUpdate
|
||||
}
|
||||
@@ -1420,16 +1528,17 @@ namespace Simian.Extensions
|
||||
appearance.Sender.ID = agent.ID;
|
||||
appearance.Sender.IsTrial = false;
|
||||
|
||||
appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[agent.VisualParams.Length];
|
||||
for (int i = 0; i < agent.VisualParams.Length; i++)
|
||||
int count = agent.Info.VisualParams != null ? agent.Info.VisualParams.Length : 0;
|
||||
|
||||
appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock();
|
||||
appearance.VisualParam[i].ParamValue = agent.VisualParams[i];
|
||||
appearance.VisualParam[i].ParamValue = agent.Info.VisualParams[i];
|
||||
}
|
||||
|
||||
if (agent.VisualParams.Length != 218)
|
||||
Logger.Log("Built an appearance packet with VisualParams.Length=" + agent.VisualParams.Length,
|
||||
Helpers.LogLevel.Warning);
|
||||
if (count != 218)
|
||||
Logger.Log("Built an odd appearance packet with VisualParams.Length=" + count, Helpers.LogLevel.Warning);
|
||||
|
||||
return appearance;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,228 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class ScriptFunction
|
||||
{
|
||||
public MethodInfo Method;
|
||||
public string Name;
|
||||
public List<Type> Parameters;
|
||||
public Type Return;
|
||||
}
|
||||
|
||||
public class ScriptConsole : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
IScriptApi api;
|
||||
Dictionary<string, ScriptFunction> functions;
|
||||
|
||||
public ScriptConsole()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
// Create a single local scripting API instance that we will manage
|
||||
api = new ScriptApi();
|
||||
api.Start(server, null, UUID.Zero, false, false);
|
||||
|
||||
// Create a dictionary of all of the scripting functions
|
||||
functions = new Dictionary<string, ScriptFunction>();
|
||||
Type apiType = typeof(IScriptApi);
|
||||
MethodInfo[] apiMethods = apiType.GetMethods();
|
||||
for (int i = 0; i < apiMethods.Length; i++)
|
||||
{
|
||||
MethodInfo method = apiMethods[i];
|
||||
|
||||
if (!method.IsConstructor && method.Name != "Start" && method.Name != "Stop")
|
||||
{
|
||||
ScriptFunction function = new ScriptFunction();
|
||||
function.Method = method;
|
||||
function.Name = method.Name;
|
||||
function.Return = method.ReturnParameter.ParameterType.UnderlyingSystemType;
|
||||
function.Parameters = new List<Type>();
|
||||
|
||||
ParameterInfo[] parms = method.GetParameters();
|
||||
for (int j = 0; j < parms.Length; j++)
|
||||
function.Parameters.Add(parms[j].ParameterType.UnderlyingSystemType);
|
||||
|
||||
functions.Add(function.Name, function);
|
||||
}
|
||||
}
|
||||
|
||||
server.Scene.OnObjectChat += new ObjectChatCallback(Scene_OnObjectChat);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (api)
|
||||
api.Stop();
|
||||
}
|
||||
|
||||
void PrintFunctionUsage(ScriptFunction function)
|
||||
{
|
||||
string message = "Usage: " + function.Return.Name + " " + function.Name + "(";
|
||||
for (int i = 0; i < function.Parameters.Count; i++)
|
||||
{
|
||||
message += function.Parameters[i].Name;
|
||||
if (i != function.Parameters.Count - 1)
|
||||
message += ", ";
|
||||
}
|
||||
message += ")";
|
||||
|
||||
server.Scene.ObjectChat(this, UUID.Zero, UUID.Zero, ChatAudibleLevel.Fully, ChatType.OwnerSay, ChatSourceType.Object,
|
||||
"Script Console", Vector3.Zero, 0, message);
|
||||
}
|
||||
|
||||
List<string> ParseParameters(string paramsString)
|
||||
{
|
||||
List<string> parameters = new List<string>();
|
||||
int i = 0;
|
||||
|
||||
while (i < paramsString.Length)
|
||||
{
|
||||
int commaPos = paramsString.IndexOf(',', i);
|
||||
int bracketPos = paramsString.IndexOf('<', i);
|
||||
int endBracketPos = paramsString.IndexOf('>', i);
|
||||
string param;
|
||||
|
||||
if (bracketPos > 0 && bracketPos < commaPos)
|
||||
{
|
||||
if (endBracketPos > bracketPos)
|
||||
commaPos = paramsString.IndexOf(',', endBracketPos);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (commaPos > 0)
|
||||
{
|
||||
// ...,
|
||||
param = paramsString.Substring(i, commaPos - i);
|
||||
i = commaPos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...
|
||||
param = paramsString.Substring(i);
|
||||
i = paramsString.Length;
|
||||
}
|
||||
|
||||
parameters.Add(param);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
object[] ConvertParameters(ScriptFunction function, List<string> parameters)
|
||||
{
|
||||
object[] objParameters = new object[function.Parameters.Count];
|
||||
|
||||
for (int i = 0; i < objParameters.Length; i++)
|
||||
{
|
||||
Type paramType = function.Parameters[i];
|
||||
|
||||
if (paramType == typeof(int))
|
||||
{
|
||||
int value;
|
||||
if (Int32.TryParse(parameters[i], out value))
|
||||
objParameters[i] = value;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if (paramType == typeof(double))
|
||||
{
|
||||
double value;
|
||||
if (Double.TryParse(parameters[i], out value))
|
||||
objParameters[i] = value;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if (paramType == typeof(ScriptTypes.LSL_Vector))
|
||||
{
|
||||
ScriptTypes.LSL_Vector value;
|
||||
value = (ScriptTypes.LSL_Vector)parameters[i];
|
||||
objParameters[i] = value;
|
||||
}
|
||||
else if (paramType == typeof(ScriptTypes.LSL_Rotation))
|
||||
{
|
||||
ScriptTypes.LSL_Rotation value;
|
||||
value = (ScriptTypes.LSL_Rotation)parameters[i];
|
||||
objParameters[i] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// String value, or something that can (hopefully) be implicitly converted
|
||||
objParameters[i] = parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
return objParameters;
|
||||
}
|
||||
|
||||
void Scene_OnObjectChat(object sender, UUID ownerID, UUID sourceID, ChatAudibleLevel audible, ChatType type,
|
||||
ChatSourceType sourceType, string fromName, Vector3 position, int channel, string message)
|
||||
{
|
||||
if (sourceType == ChatSourceType.Agent && type == ChatType.Normal)
|
||||
{
|
||||
if (message.StartsWith("ll") && message.Contains("("))
|
||||
{
|
||||
int startParam = message.IndexOf('(');
|
||||
int endParam = message.IndexOf(')');
|
||||
|
||||
if (startParam > 2 && endParam > startParam)
|
||||
{
|
||||
// Try and parse this into a function call
|
||||
string name = message.Substring(0, startParam);
|
||||
|
||||
ScriptFunction function;
|
||||
if (functions.TryGetValue(name, out function))
|
||||
{
|
||||
// Parse the parameters
|
||||
++startParam;
|
||||
List<string> parameters = ParseParameters(message.Substring(startParam, endParam - startParam));
|
||||
|
||||
// Parameters sanity check
|
||||
if (parameters != null && parameters.Count == function.Parameters.Count)
|
||||
{
|
||||
// Convert the parameters into the required types
|
||||
object[] objParameters = ConvertParameters(function, parameters);
|
||||
|
||||
if (objParameters != null)
|
||||
{
|
||||
// Find the avatar that sent this chat
|
||||
SimulationObject avatar;
|
||||
if (server.Scene.TryGetObject(ownerID, out avatar))
|
||||
{
|
||||
// Lock the API, setup the member values, and invoke the function
|
||||
lock (api)
|
||||
{
|
||||
api.Start(server, avatar, UUID.Random(), false, false);
|
||||
|
||||
object ret = function.Method.Invoke(api, objParameters);
|
||||
|
||||
if (ret != null)
|
||||
{
|
||||
server.Scene.ObjectChat(this, UUID.Zero, UUID.Zero, ChatAudibleLevel.Fully, ChatType.OwnerSay,
|
||||
ChatSourceType.Object, "Script Console", Vector3.Zero, 0, ret.ToString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintFunctionUsage(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
// FIXME: Implement this class
|
||||
class TaskInventoryManager : IExtension<Simian>, ITaskInventoryProvider
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public TaskInventoryManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public UUID CreateItem(UUID agentID, UUID containerObjectID, string name, string description, InventoryType invType,
|
||||
AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask,
|
||||
UUID ownerID, UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket)
|
||||
{
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
public bool RemoveItem(UUID agentID, UUID containerObjectID, UUID itemID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetAsset(UUID containerObjectID, UUID assetID, out Asset asset)
|
||||
{
|
||||
asset = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForEachItem(UUID containerObjectID, Action<InventoryTaskItem> action)
|
||||
{
|
||||
}
|
||||
|
||||
public InventoryTaskItem FindItem(UUID containerObjectID, Predicate<InventoryTaskItem> match)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<InventoryTaskItem> FindAllItems(UUID containerObjectID, Predicate<InventoryTaskItem> match)
|
||||
{
|
||||
return new List<InventoryTaskItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class TransferManager : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
|
||||
public TransferManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#region Xfer System
|
||||
|
||||
void AssetUploadRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet;
|
||||
UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID);
|
||||
|
||||
// Check if the asset is small enough to fit in a single packet
|
||||
if (request.AssetBlock.AssetData.Length != 0)
|
||||
{
|
||||
// Create a new asset from the completed upload
|
||||
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData);
|
||||
if (asset == null)
|
||||
{
|
||||
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType));
|
||||
|
||||
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
|
||||
|
||||
// Store the asset
|
||||
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;
|
||||
server.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;
|
||||
|
||||
server.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;
|
||||
server.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;
|
||||
server.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);
|
||||
|
||||
server.Assets.StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
server.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 (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
|
||||
|
||||
server.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;
|
||||
|
||||
server.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;
|
||||
|
||||
server.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,635 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public struct IncomingPacket
|
||||
{
|
||||
public UDPClient Client;
|
||||
public Packet Packet;
|
||||
}
|
||||
|
||||
public class OutgoingPacket
|
||||
{
|
||||
public Packet Packet;
|
||||
/// <summary>Number of times this packet has been resent</summary>
|
||||
public int ResendCount;
|
||||
/// <summary>Environment.TickCount when this packet was last sent over the wire</summary>
|
||||
public int TickCount;
|
||||
/// <summary>Category this packet belongs to</summary>
|
||||
public PacketCategory Category;
|
||||
|
||||
public OutgoingPacket(Packet packet, PacketCategory category)
|
||||
{
|
||||
Packet = packet;
|
||||
Category = category;
|
||||
}
|
||||
}
|
||||
|
||||
public class UDPClient
|
||||
{
|
||||
/// <summary></summary>
|
||||
public Agent Agent;
|
||||
/// <summary></summary>
|
||||
public IPEndPoint Address;
|
||||
/// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
|
||||
public Queue<uint> PacketArchive = new Queue<uint>();
|
||||
/// <summary>Packets we have sent that need to be ACKed by the client</summary>
|
||||
public Dictionary<uint, OutgoingPacket> NeedAcks = new Dictionary<uint, OutgoingPacket>();
|
||||
/// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
|
||||
public SortedList<uint, uint> PendingAcks = new SortedList<uint, uint>();
|
||||
/// <summary>Current packet sequence number</summary>
|
||||
public int CurrentSequence = 0;
|
||||
|
||||
Timer ackTimer;
|
||||
UDPServer udpServer;
|
||||
|
||||
public UDPClient(UDPServer server, Agent agent, IPEndPoint address)
|
||||
{
|
||||
udpServer = server;
|
||||
Agent = agent;
|
||||
Address = address;
|
||||
ackTimer = new Timer(new TimerCallback(AckTimer_Elapsed), null, Settings.NETWORK_TICK_INTERVAL,
|
||||
Settings.NETWORK_TICK_INTERVAL);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
ackTimer.Dispose();
|
||||
}
|
||||
|
||||
private void AckTimer_Elapsed(object obj)
|
||||
{
|
||||
udpServer.SendAcks(this);
|
||||
udpServer.ResendUnacked(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class UDPManager : IExtension<Simian>, IUDPProvider
|
||||
{
|
||||
Simian server;
|
||||
UDPServer udpServer;
|
||||
|
||||
public event OutgoingPacketCallback OnOutgoingPacket;
|
||||
|
||||
public UDPManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
udpServer = new UDPServer(server.UDPPort, server);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (udpServer != null)
|
||||
udpServer.Stop();
|
||||
}
|
||||
|
||||
public void AddClient(Agent agent, IPEndPoint endpoint)
|
||||
{
|
||||
udpServer.AddClient(agent, endpoint);
|
||||
}
|
||||
|
||||
public bool RemoveClient(Agent agent)
|
||||
{
|
||||
return udpServer.RemoveClient(agent);
|
||||
}
|
||||
|
||||
public uint CreateCircuit(Agent agent)
|
||||
{
|
||||
return udpServer.CreateCircuit(agent);
|
||||
}
|
||||
|
||||
public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
|
||||
{
|
||||
if (OnOutgoingPacket == null || OnOutgoingPacket(packet, agentID, category))
|
||||
udpServer.SendPacket(agentID, packet, category);
|
||||
}
|
||||
|
||||
public void BroadcastPacket(Packet packet, PacketCategory category)
|
||||
{
|
||||
if (OnOutgoingPacket == null || OnOutgoingPacket(packet, UUID.Zero, category))
|
||||
udpServer.BroadcastPacket(packet, category);
|
||||
}
|
||||
|
||||
public void RegisterPacketCallback(PacketType type, PacketCallback callback)
|
||||
{
|
||||
udpServer.RegisterPacketCallback(type, callback);
|
||||
}
|
||||
}
|
||||
|
||||
public class UDPServer : UDPBase
|
||||
{
|
||||
/// <summary>This is only used to fetch unassociated agents, which will
|
||||
/// be exposed through a login interface at some point</summary>
|
||||
Simian server;
|
||||
/// <summary>Handlers for incoming packets</summary>
|
||||
PacketEventDictionary packetEvents = new PacketEventDictionary();
|
||||
/// <summary>Incoming packets that are awaiting handling</summary>
|
||||
BlockingQueue<IncomingPacket> packetInbox = new BlockingQueue<IncomingPacket>(Settings.PACKET_INBOX_SIZE);
|
||||
/// <summary></summary>
|
||||
DoubleDictionary<UUID, IPEndPoint, UDPClient> clients = new DoubleDictionary<UUID, IPEndPoint, UDPClient>();
|
||||
/// <summary></summary>
|
||||
Dictionary<uint, Agent> unassociatedAgents = new Dictionary<uint, Agent>();
|
||||
/// <summary></summary>
|
||||
int currentCircuitCode = 0;
|
||||
|
||||
public UDPServer(int port, Simian server)
|
||||
: base(port)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
Start();
|
||||
|
||||
// Start the incoming packet processing thread
|
||||
Thread incomingThread = new Thread(new ThreadStart(IncomingPacketHandler));
|
||||
incomingThread.Start();
|
||||
}
|
||||
|
||||
public void RegisterPacketCallback(PacketType type, PacketCallback callback)
|
||||
{
|
||||
packetEvents.RegisterEvent(type, callback);
|
||||
}
|
||||
|
||||
public void AddClient(Agent agent, IPEndPoint endpoint)
|
||||
{
|
||||
UDPClient client = new UDPClient(this, agent, endpoint);
|
||||
clients.Add(agent.ID, endpoint, client);
|
||||
}
|
||||
|
||||
public bool RemoveClient(Agent agent)
|
||||
{
|
||||
UDPClient client;
|
||||
if (clients.TryGetValue(agent.ID, out client))
|
||||
{
|
||||
client.Shutdown();
|
||||
return clients.Remove(agent.ID, client.Address);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint CreateCircuit(Agent agent)
|
||||
{
|
||||
uint circuitCode = (uint)Interlocked.Increment(ref currentCircuitCode);
|
||||
|
||||
// Put this client in the list of clients that have not been associated with an IPEndPoint yet
|
||||
lock (unassociatedAgents)
|
||||
unassociatedAgents[circuitCode] = agent;
|
||||
|
||||
Logger.Log("Created circuit " + circuitCode + " for " + agent.FirstName, Helpers.LogLevel.Info);
|
||||
|
||||
return circuitCode;
|
||||
}
|
||||
|
||||
public void BroadcastPacket(Packet packet, PacketCategory category)
|
||||
{
|
||||
clients.ForEach(
|
||||
delegate(UDPClient client) { SendPacket(client, new OutgoingPacket(packet, category)); });
|
||||
}
|
||||
|
||||
public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
|
||||
{
|
||||
// Look up the UDPClient this is going to
|
||||
UDPClient client;
|
||||
if (clients.TryGetValue(agentID, out client))
|
||||
SendPacket(client, new OutgoingPacket(packet, category));
|
||||
}
|
||||
|
||||
void SendPacket(UDPClient client, OutgoingPacket outgoingPacket)
|
||||
{
|
||||
Packet packet = outgoingPacket.Packet;
|
||||
byte[] buffer;
|
||||
int bytes;
|
||||
|
||||
// Update the sent time for this packet
|
||||
outgoingPacket.TickCount = Environment.TickCount;
|
||||
|
||||
if (!packet.Header.Resent)
|
||||
{
|
||||
// Reset to zero if we've hit the upper sequence number limit
|
||||
Interlocked.CompareExchange(ref client.CurrentSequence, 0, 0xFFFFFF);
|
||||
// Increment and fetch the current sequence number
|
||||
uint sequence = (uint)Interlocked.Increment(ref client.CurrentSequence);
|
||||
packet.Header.Sequence = sequence;
|
||||
|
||||
if (packet.Header.Reliable)
|
||||
{
|
||||
// Add this packet to the list of ACK responses we are waiting on from this client
|
||||
lock (client.NeedAcks)
|
||||
client.NeedAcks[sequence] = outgoingPacket;
|
||||
|
||||
// This packet is reliable and not a resend, check if the conditions are favorable
|
||||
// to ACK appending
|
||||
if (packet.Type != PacketType.PacketAck)
|
||||
{
|
||||
lock (client.PendingAcks)
|
||||
{
|
||||
int count = client.PendingAcks.Count;
|
||||
|
||||
if (count > 0 && count < 10)
|
||||
{
|
||||
// Append all of the queued up outgoing ACKs to this packet
|
||||
packet.Header.AckList = new uint[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
packet.Header.AckList[i] = client.PendingAcks.Values[i];
|
||||
|
||||
client.PendingAcks.Clear();
|
||||
packet.Header.AppendedAcks = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This packet has already been sent out once, strip any appended ACKs
|
||||
// off it and reinsert them into the outgoing ACK queue under the
|
||||
// assumption that this packet will continually be rejected from the
|
||||
// client or that the appended ACKs are possibly making the delivery fail
|
||||
if (packet.Header.AckList.Length > 0)
|
||||
{
|
||||
Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.",
|
||||
packet.Header.Sequence, packet.GetType()));
|
||||
|
||||
lock (client.PendingAcks)
|
||||
{
|
||||
foreach (uint ack in packet.Header.AckList)
|
||||
{
|
||||
if (!client.PendingAcks.ContainsKey(ack))
|
||||
client.PendingAcks[ack] = ack;
|
||||
}
|
||||
}
|
||||
|
||||
packet.Header.AppendedAcks = false;
|
||||
packet.Header.AckList = new uint[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the packet
|
||||
buffer = packet.ToBytes();
|
||||
bytes = buffer.Length;
|
||||
//Stats.SentBytes += (ulong)bytes;
|
||||
//++Stats.SentPackets;
|
||||
|
||||
UDPPacketBuffer buf = new UDPPacketBuffer(client.Address);
|
||||
|
||||
// Zerocode if needed
|
||||
if (packet.Header.Zerocoded)
|
||||
bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data);
|
||||
else
|
||||
Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes);
|
||||
|
||||
buf.DataLength = bytes;
|
||||
|
||||
AsyncBeginSend(buf);
|
||||
}
|
||||
|
||||
void QueueAck(UDPClient client, uint ack)
|
||||
{
|
||||
// Add this packet to the list of ACKs that need to be sent out
|
||||
lock (client.PendingAcks)
|
||||
client.PendingAcks[ack] = ack;
|
||||
|
||||
// Send out ACKs if we have a lot of them
|
||||
if (client.PendingAcks.Count >= 10)
|
||||
SendAcks(client);
|
||||
}
|
||||
|
||||
void ProcessAcks(UDPClient client, List<uint> acks)
|
||||
{
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
foreach (uint ack in acks)
|
||||
client.NeedAcks.Remove(ack);
|
||||
}
|
||||
}
|
||||
|
||||
void SendAck(UDPClient client, uint ack)
|
||||
{
|
||||
PacketAckPacket acks = new PacketAckPacket();
|
||||
acks.Header.Reliable = false;
|
||||
acks.Packets = new PacketAckPacket.PacketsBlock[1];
|
||||
acks.Packets[0] = new PacketAckPacket.PacketsBlock();
|
||||
acks.Packets[0].ID = ack;
|
||||
|
||||
SendPacket(client, new OutgoingPacket(acks, PacketCategory.Overhead));
|
||||
}
|
||||
|
||||
public void SendAcks(UDPClient client)
|
||||
{
|
||||
PacketAckPacket acks = null;
|
||||
|
||||
lock (client.PendingAcks)
|
||||
{
|
||||
int count = client.PendingAcks.Count;
|
||||
|
||||
if (count > 250)
|
||||
{
|
||||
Logger.Log("Too many ACKs queued up!", Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
else if (count > 0)
|
||||
{
|
||||
acks = new PacketAckPacket();
|
||||
acks.Header.Reliable = false;
|
||||
acks.Packets = new PacketAckPacket.PacketsBlock[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
acks.Packets[i] = new PacketAckPacket.PacketsBlock();
|
||||
acks.Packets[i].ID = client.PendingAcks.Values[i];
|
||||
}
|
||||
|
||||
client.PendingAcks.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (acks != null)
|
||||
SendPacket(client, new OutgoingPacket(acks, PacketCategory.Overhead));
|
||||
}
|
||||
|
||||
public void ResendUnacked(UDPClient client)
|
||||
{
|
||||
if (client.NeedAcks.Count > 0)
|
||||
{
|
||||
OutgoingPacket[] array;
|
||||
int now = Environment.TickCount;
|
||||
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
// Create a temporary copy of the outgoing packets array to iterate over
|
||||
array = new OutgoingPacket[client.NeedAcks.Count];
|
||||
client.NeedAcks.Values.CopyTo(array, 0);
|
||||
}
|
||||
|
||||
// Resend packets
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
OutgoingPacket outgoing = array[i];
|
||||
|
||||
// FIXME: Make 4000 and 3 .ini settings
|
||||
if (outgoing.TickCount != 0 && now - outgoing.TickCount > 4000)
|
||||
{
|
||||
if (outgoing.ResendCount < 3)
|
||||
{
|
||||
//Logger.DebugLog(String.Format("Resending packet #{0} ({1}), {2}ms have passed",
|
||||
// outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), now - outgoing.TickCount));
|
||||
|
||||
// The TickCount will be set to the current time when the packet
|
||||
// is actually sent out again
|
||||
outgoing.TickCount = 0;
|
||||
outgoing.Packet.Header.Resent = true;
|
||||
++outgoing.ResendCount;
|
||||
|
||||
//++Stats.ResentPackets;
|
||||
|
||||
try
|
||||
{
|
||||
SendPacket(client, outgoing);
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
// Programming error elsewhere can put bad packets in the outgoing queue.
|
||||
// These will never successfully serialize, so just drop them
|
||||
Logger.Log(String.Format("Dropping bad packet #{0} ({1}) from the outgoing queue",
|
||||
outgoing.Packet.Header.Sequence, outgoing.Packet.GetType()),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
lock (client.NeedAcks) client.NeedAcks.Remove(outgoing.Packet.Header.Sequence);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format("Dropping packet #{0} ({1}) after {2} failed attempts",
|
||||
outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), outgoing.ResendCount),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
lock (client.NeedAcks) client.NeedAcks.Remove(outgoing.Packet.Header.Sequence);
|
||||
|
||||
//Disconnect an agent if no packets are received for some time
|
||||
//FIXME: Make 60000 an .ini setting
|
||||
if (Environment.TickCount - client.Agent.TickLastPacketReceived > 60000)
|
||||
{
|
||||
Logger.Log(String.Format("Ack timeout for {0}, disconnecting", client.Agent.FullName),
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
server.Scene.ObjectRemove(this, client.Agent.ID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PacketReceived(UDPPacketBuffer buffer)
|
||||
{
|
||||
UDPClient client = null;
|
||||
Packet packet = null;
|
||||
int packetEnd = buffer.DataLength - 1;
|
||||
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
|
||||
|
||||
// Decoding
|
||||
try
|
||||
{
|
||||
packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData);
|
||||
}
|
||||
catch (MalformedDataException)
|
||||
{
|
||||
Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}",
|
||||
Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
// Fail-safe check
|
||||
if (packet == null)
|
||||
{
|
||||
Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
//Stats.RecvBytes += (ulong)buffer.DataLength;
|
||||
//++Stats.RecvPackets;
|
||||
|
||||
if (packet.Type == PacketType.UseCircuitCode)
|
||||
{
|
||||
UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
|
||||
|
||||
Agent agent;
|
||||
if (CompleteAgentConnection(useCircuitCode.CircuitCode.Code, out agent))
|
||||
{
|
||||
// Sanity check that the agent isn't already logged in
|
||||
if (clients.ContainsKey(agent.ID))
|
||||
{
|
||||
Logger.Log("Client UDP reference already exists for " + agent.ID.ToString() + ", removing",
|
||||
Helpers.LogLevel.Warning);
|
||||
server.Scene.ObjectRemove(this, agent.ID);
|
||||
clients.Remove(agent.ID);
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Adding client {0} from {1}", agent.FullName, address));
|
||||
AddClient(agent, address);
|
||||
if (clients.TryGetValue(agent.ID, out client))
|
||||
{
|
||||
Logger.Log("Activated UDP circuit " + useCircuitCode.CircuitCode.Code, Helpers.LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Failed to locate newly created UDPClient", Helpers.LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Received a UseCircuitCode packet for an unrecognized circuit: " +
|
||||
useCircuitCode.CircuitCode.Code.ToString(), Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine which agent this packet came from
|
||||
if (!clients.TryGetValue(address, out client))
|
||||
{
|
||||
Logger.Log("Received UDP packet from an unrecognized source: " + address.ToString(),
|
||||
Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
client.Agent.TickLastPacketReceived = Environment.TickCount;
|
||||
|
||||
// Reliable handling
|
||||
if (packet.Header.Reliable)
|
||||
{
|
||||
// Queue up this sequence number for acknowledgement
|
||||
QueueAck(client, (uint)packet.Header.Sequence);
|
||||
//if (packet.Header.Resent) ++Stats.ReceivedResends;
|
||||
}
|
||||
|
||||
// Inbox insertion
|
||||
IncomingPacket incomingPacket;
|
||||
incomingPacket.Client = client;
|
||||
incomingPacket.Packet = packet;
|
||||
|
||||
// TODO: Prioritize the queue
|
||||
packetInbox.Enqueue(incomingPacket);
|
||||
}
|
||||
|
||||
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
|
||||
{
|
||||
}
|
||||
|
||||
void IncomingPacketHandler()
|
||||
{
|
||||
IncomingPacket incomingPacket = new IncomingPacket();
|
||||
Packet packet = null;
|
||||
UDPClient client = null;
|
||||
|
||||
while (IsRunning)
|
||||
{
|
||||
// Reset packet to null for the check below
|
||||
packet = null;
|
||||
|
||||
if (packetInbox.Dequeue(100, ref incomingPacket))
|
||||
{
|
||||
packet = incomingPacket.Packet;
|
||||
client = incomingPacket.Client;
|
||||
|
||||
if (packet != null)
|
||||
{
|
||||
#region ACK accounting
|
||||
|
||||
// Check the archives to see whether we already received this packet
|
||||
lock (client.PacketArchive)
|
||||
{
|
||||
if (client.PacketArchive.Contains(packet.Header.Sequence))
|
||||
{
|
||||
if (packet.Header.Resent)
|
||||
{
|
||||
Logger.DebugLog("Received resent packet #" + packet.Header.Sequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log(String.Format("Received a duplicate of packet #{0}, current type: {1}",
|
||||
packet.Header.Sequence, packet.Type), Helpers.LogLevel.Warning);
|
||||
}
|
||||
|
||||
// Avoid firing a callback twice for the same packet
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep the PacketArchive size within a certain capacity
|
||||
while (client.PacketArchive.Count >= Settings.PACKET_ARCHIVE_SIZE)
|
||||
{
|
||||
client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
|
||||
client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
|
||||
}
|
||||
|
||||
client.PacketArchive.Enqueue(packet.Header.Sequence);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion ACK accounting
|
||||
|
||||
#region ACK handling
|
||||
|
||||
// Handle appended ACKs
|
||||
if (packet.Header.AppendedAcks)
|
||||
{
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
for (int i = 0; i < packet.Header.AckList.Length; i++)
|
||||
client.NeedAcks.Remove(packet.Header.AckList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle PacketAck packets
|
||||
if (packet.Type == PacketType.PacketAck)
|
||||
{
|
||||
PacketAckPacket ackPacket = (PacketAckPacket)packet;
|
||||
|
||||
lock (client.NeedAcks)
|
||||
{
|
||||
for (int i = 0; i < ackPacket.Packets.Length; i++)
|
||||
client.NeedAcks.Remove(ackPacket.Packets[i].ID);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion ACK handling
|
||||
|
||||
packetEvents.BeginRaiseEvent(packet.Type, packet, client.Agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CompleteAgentConnection(uint circuitCode, out Agent agent)
|
||||
{
|
||||
lock (unassociatedAgents)
|
||||
{
|
||||
if (unassociatedAgents.TryGetValue(circuitCode, out agent))
|
||||
{
|
||||
unassociatedAgents.Remove(circuitCode);
|
||||
server.Scene.AgentAdd(this, agent, PrimFlags.None);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
||||
namespace Simian.Extensions
|
||||
namespace Simian
|
||||
{
|
||||
public class XMLPersistence : IExtension<Simian>, IPersistenceProvider
|
||||
{
|
||||
@@ -16,7 +16,7 @@ namespace Simian.Extensions
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
public bool Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
@@ -30,12 +30,13 @@ namespace Simian.Extensions
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log("Failed to load saved data: " + ex.Message, Helpers.LogLevel.Error);
|
||||
return;
|
||||
Logger.Log("Failed to load saved data. You may have to move or delete simiandata.xml: " +
|
||||
ex.Message, Helpers.LogLevel.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (osd is OSDMap)
|
||||
@@ -58,6 +59,8 @@ namespace Simian.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
// FIXME: Implement this class
|
||||
class XScriptEngine : IExtension<Simian>, IScriptEngine
|
||||
{
|
||||
Simian server;
|
||||
|
||||
public XScriptEngine()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public bool PostScriptEvent(UUID scriptID, EventParams parms)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PostObjectEvent(UUID hostObjectID, EventParams parms)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetTimerEvent(UUID scriptID, double seconds)
|
||||
{
|
||||
}
|
||||
|
||||
public DetectParams GetDetectParams(UUID scriptID, int detectIndex)
|
||||
{
|
||||
DetectParams parms = new DetectParams();
|
||||
return parms;
|
||||
}
|
||||
|
||||
public bool GetScriptState(UUID scriptID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetScriptState(UUID scriptID, bool state)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetStartParameter(UUID scriptID, int startParam)
|
||||
{
|
||||
}
|
||||
|
||||
public int GetStartParameter(UUID scriptID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void SetScriptMinEventDelay(UUID scriptID, double minDelay)
|
||||
{
|
||||
}
|
||||
|
||||
public void TriggerState(UUID scriptID, string newState)
|
||||
{
|
||||
}
|
||||
|
||||
public void ApiResetScript(UUID scriptID)
|
||||
{
|
||||
}
|
||||
|
||||
public void ResetScript(UUID scriptID)
|
||||
{
|
||||
}
|
||||
|
||||
public int AddListener(UUID scriptID, UUID hostObjectID, int channel, string name, UUID keyID, string message)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void RemoveListener(UUID scriptID, int handle)
|
||||
{
|
||||
}
|
||||
|
||||
public void RemoveListeners(UUID scriptID)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetListenerState(UUID scriptID, int handle, bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
public void SensorOnce(UUID scriptID, UUID hostObjectID, string name, UUID keyID, int type, double range, double arc)
|
||||
{
|
||||
}
|
||||
|
||||
public void SensorRepeat(UUID scriptID, UUID hostObjectID, string name, UUID keyID, int type, double range, double arc, double rate)
|
||||
{
|
||||
}
|
||||
|
||||
public void SensorRemove(UUID scriptID)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user