From 241b48032093bfeabaaf72ebdbfdc4aea8cb9045 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 17 Mar 2009 22:33:22 +0000 Subject: [PATCH] [Simian] * Created an LLUDP folder to hold extensions that are purely LLUDP packet handlers. This is not a complete abstraction away from transport protocols, but it's a start * Moved physics code from Movement.cs into PhysicsSimple.cs, and moved the physics loop into a thread in SceneManager * Simian.ini cleanup git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2490 52acb1d6-8a22-11de-b505-999d5b087335 --- Programs/Simian/CapsMessages.cs | 70 +++ Programs/Simian/Extensions/SceneManager.cs | 96 ++-- Programs/Simian/Interfaces/IParcelProvider.cs | 3 + .../Simian/Interfaces/IPhysicsProvider.cs | 6 +- Programs/Simian/Interfaces/ISceneProvider.cs | 7 +- .../{SceneExtensions => LLUDP}/LLAgents.cs | 0 .../TransferManager.cs => LLUDP/LLAssets.cs} | 4 +- .../LLConnections.cs} | 6 +- .../{SceneExtensions => LLUDP}/LLInventory.cs | 0 .../{SceneExtensions => LLUDP}/LLMap.cs | 19 +- .../{SceneExtensions => LLUDP}/LLMessaging.cs | 0 .../{SceneExtensions => LLUDP}/LLMoney.cs | 0 Programs/Simian/LLUDP/LLMovement.cs | 140 +++++ .../ObjectManager.cs => LLUDP/LLObjects.cs} | 6 +- Programs/Simian/LLUDP/LLParcels.cs | 177 ++++++ .../ImageDelivery.cs => LLUDP/LLTextures.cs} | 4 +- Programs/Simian/SceneExtensions/Movement.cs | 518 ------------------ .../Simian/SceneExtensions/ParcelManager.cs | 164 +----- .../PeriscopeTransferManager.cs | 2 - .../Simian/SceneExtensions/PhysicsSimple.cs | 376 ++++++++++++- .../Simian/{SceneExtensions => }/ScriptApi.cs | 0 bin/SimianData/Simian.ini | 276 +++++----- 22 files changed, 997 insertions(+), 877 deletions(-) create mode 100644 Programs/Simian/CapsMessages.cs rename Programs/Simian/{SceneExtensions => LLUDP}/LLAgents.cs (100%) rename Programs/Simian/{SceneExtensions/TransferManager.cs => LLUDP/LLAssets.cs} (97%) rename Programs/Simian/{SceneExtensions/ConnectionManagement.cs => LLUDP/LLConnections.cs} (95%) rename Programs/Simian/{SceneExtensions => LLUDP}/LLInventory.cs (100%) rename Programs/Simian/{SceneExtensions => LLUDP}/LLMap.cs (94%) rename Programs/Simian/{SceneExtensions => LLUDP}/LLMessaging.cs (100%) rename Programs/Simian/{SceneExtensions => LLUDP}/LLMoney.cs (100%) create mode 100644 Programs/Simian/LLUDP/LLMovement.cs rename Programs/Simian/{SceneExtensions/ObjectManager.cs => LLUDP/LLObjects.cs} (97%) create mode 100644 Programs/Simian/LLUDP/LLParcels.cs rename Programs/Simian/{SceneExtensions/ImageDelivery.cs => LLUDP/LLTextures.cs} (96%) delete mode 100644 Programs/Simian/SceneExtensions/Movement.cs rename Programs/Simian/{SceneExtensions => }/ScriptApi.cs (100%) diff --git a/Programs/Simian/CapsMessages.cs b/Programs/Simian/CapsMessages.cs new file mode 100644 index 00000000..aa55260b --- /dev/null +++ b/Programs/Simian/CapsMessages.cs @@ -0,0 +1,70 @@ +using System; +using System.Net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace Simian +{ + public static class CapsMessages + { + public static OSDMap TeleportFinish(UUID agentID, int locationID, ulong regionHandle, Uri seedCap, SimAccess simAccess, + IPAddress simIP, int simPort, TeleportFlags teleportFlags) + { + OSDMap info = new OSDMap(8); + info.Add("AgentID", OSD.FromUUID(agentID)); + info.Add("LocationID", OSD.FromInteger(locationID)); // Unused by the client + info.Add("RegionHandle", OSD.FromULong(regionHandle)); + info.Add("SeedCapability", OSD.FromUri(seedCap)); + info.Add("SimAccess", OSD.FromInteger((byte)simAccess)); + info.Add("SimIP", OSD.FromBinary(simIP.GetAddressBytes())); + info.Add("SimPort", OSD.FromInteger(simPort)); + info.Add("TeleportFlags", OSD.FromUInteger((uint)teleportFlags)); + + OSDArray infoArray = new OSDArray(1); + infoArray.Add(info); + + OSDMap teleport = new OSDMap(1); + teleport.Add("Info", infoArray); + + return teleport; + } + + public static OSDMap EnableSimulator(ulong regionHandle, IPAddress ip, int port) + { + OSDMap llsdSimInfo = new OSDMap(3); + + llsdSimInfo.Add("Handle", OSD.FromULong(regionHandle)); + llsdSimInfo.Add("IP", OSD.FromBinary(ip.GetAddressBytes())); + llsdSimInfo.Add("Port", OSD.FromInteger(port)); + + OSDArray arr = new OSDArray(1); + arr.Add(llsdSimInfo); + + OSDMap llsdBody = new OSDMap(1); + llsdBody.Add("SimulatorInfo", arr); + + return llsdBody; + } + + public static OSDMap EnableClient(UUID agentID, UUID sessionID, UUID secureSessionID, int circuitCode, string firstName, string lastName, Uri callbackUri) + { + OSDMap map = new OSDMap(7); + map["agent_id"] = OSD.FromUUID(agentID); + map["session_id"] = OSD.FromUUID(sessionID); + map["secure_session_id"] = OSD.FromUUID(secureSessionID); + map["circuit_code"] = OSD.FromInteger(circuitCode); + map["first_name"] = OSD.FromString(firstName); + map["last_name"] = OSD.FromString(lastName); + map["callback_uri"] = OSD.FromUri(callbackUri); + + return map; + } + + public static OSDMap EnableClientComplete(UUID agentID) + { + OSDMap map = new OSDMap(1); + map["agent_id"] = OSD.FromUUID(agentID); + return map; + } + } +} diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index d16d84b6..0f8e15f7 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -32,6 +32,8 @@ namespace Simian public class SceneManager : ISceneProvider { + const int TARGET_FRAMES_PER_SECOND = 45; + // 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 @@ -100,6 +102,7 @@ namespace Simian public uint TerrainPatchHeight { get { return 16; } } public uint TerrainPatchCountWidth { get { return 16; } } public uint TerrainPatchCountHeight { get { return 16; } } + public float TimeDilation { get { return TimeDilation; } } Simian server; // Contains all scene objects, including prims and avatars @@ -130,6 +133,8 @@ namespace Simian /// is established for a client in this dictionary, an enable_client_complete message will be /// sent to the associated URI Dictionary enableClientCompleteCallbacks = new Dictionary(); + float timeDilation; + bool running; public SceneManager() { @@ -137,6 +142,8 @@ namespace Simian public bool Start(Simian server, RegionInfo regionInfo, X509Certificate2 regionCert, string defaultTerrainFile, int staticObjects, int physicalObjects) { + running = true; + this.server = server; this.regionName = regionInfo.Name; this.endpoint = regionInfo.IPAndPort; @@ -193,11 +200,16 @@ namespace Simian server.Grid.OnRegionUpdate += Grid_OnRegionUpdate; udp.OnAgentConnection += udp_OnAgentConnection; udp.RegisterPacketCallback(PacketType.CompleteAgentMovement, CompleteAgentMovementHandler); - udp.RegisterPacketCallback(PacketType.ChatFromViewer, ChatHandler); + // Load the default terrain for this sim if (!String.IsNullOrEmpty(defaultTerrainFile)) LoadTerrain(Simian.DATA_DIR + defaultTerrainFile); + // Start the physics thread + Thread physicsThread = new Thread(new ThreadStart(PhysicsThread)); + physicsThread.Name = "Physics"; + physicsThread.Start(); + Logger.Log(String.Format("Region {0} online at ({1},{2}) listening on {3}", regionName, regionX, regionY, endpoint), Helpers.LogLevel.Info); @@ -208,18 +220,12 @@ namespace Simian return true; } - void ChatHandler(Packet packet, Agent agent) - { - ChatFromViewerPacket chat = (ChatFromViewerPacket)packet; - string message = Utils.BytesToString(chat.ChatData.Message); - if (message == "lol") - SendEvent(agent, "lol", new OSDMap()); - } - public void Stop() { Logger.Log("Stopping region " + regionName, Helpers.LogLevel.Info); + running = false; + // 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) @@ -990,14 +996,8 @@ namespace Simian // Create a callback for enable_client_complete Uri callbackUri = server.Capabilities.CreateCapability(EnableClientCompleteCapHandler, false, null); - OSDMap map = new OSDMap(); - map["agent_id"] = OSD.FromUUID(agent.ID); - map["session_id"] = OSD.FromUUID(agent.SessionID); - map["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID); - map["circuit_code"] = OSD.FromInteger((int)agent.CircuitCode); - map["first_name"] = OSD.FromString(agent.Info.FirstName); - map["last_name"] = OSD.FromString(agent.Info.LastName); - map["callback_uri"] = OSD.FromUri(callbackUri); + OSDMap enableClient = CapsMessages.EnableClient(agent.ID, agent.SessionID, agent.SecureSessionID, + (int)agent.CircuitCode, agent.Info.FirstName, agent.Info.LastName, callbackUri); AutoResetEvent waitEvent = new AutoResetEvent(false); @@ -1014,24 +1014,15 @@ namespace Simian if (success) { // Send the EnableSimulator capability to clients - OSDMap llsdSimInfo = new OSDMap(3); + RegionInfo neighbor = neighbors[i]; + OSDMap enableSimulator = CapsMessages.EnableSimulator(neighbor.Handle, neighbor.IPAndPort.Address, neighbor.IPAndPort.Port); - llsdSimInfo.Add("Handle", OSD.FromULong(neighbors[i].Handle)); - llsdSimInfo.Add("IP", OSD.FromBinary(neighbors[i].IPAndPort.Address.GetAddressBytes())); - llsdSimInfo.Add("Port", OSD.FromInteger(neighbors[i].IPAndPort.Port)); - - OSDArray arr = new OSDArray(1); - arr.Add(llsdSimInfo); - - OSDMap llsdBody = new OSDMap(1); - llsdBody.Add("SimulatorInfo", arr); - - SendEvent(agent, "EnableSimulator", llsdBody); + SendEvent(agent, "EnableSimulator", enableSimulator); } } waitEvent.Set(); }; - request.StartRequest(map); + request.StartRequest(enableClient); if (!waitEvent.WaitOne(30 * 1000, false)) Logger.Log("enable_client request timed out", Helpers.LogLevel.Warning); @@ -1229,8 +1220,7 @@ namespace Simian Logger.Log("Sending enable_client_complete callback to " + callbackUri.ToString(), Helpers.LogLevel.Info); - OSDMap map = new OSDMap(1); - map["agent_id"] = OSD.FromUUID(agent.ID); + OSDMap enableClientComplete = CapsMessages.EnableClientComplete(agent.ID); AutoResetEvent waitEvent = new AutoResetEvent(false); @@ -1251,7 +1241,7 @@ namespace Simian } waitEvent.Set(); }; - request.StartRequest(map); + request.StartRequest(enableClientComplete); if (!waitEvent.WaitOne(30 * 1000, false)) Logger.Log("enable_client_complete request timed out", Helpers.LogLevel.Warning); @@ -1285,6 +1275,40 @@ namespace Simian #endregion Callback Handlers + void PhysicsThread() + { + const float TARGET_FRAME_TIME = 1f / (float)TARGET_FRAMES_PER_SECOND; + + System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); + float elapsedTime = 0f; + int sleepMS; + + while (running) + { + stopwatch.Start(); + + physics.Update(elapsedTime); + + // Measure the duration of this frame + stopwatch.Stop(); + elapsedTime = (float)stopwatch.Elapsed.TotalSeconds; + stopwatch.Reset(); + + // Calculate time dilation and decide if we need to sleep to limit FPS + if (elapsedTime < TARGET_FRAME_TIME) + { + timeDilation = (1f / elapsedTime) / (float)TARGET_FRAMES_PER_SECOND; + sleepMS = (int)((TARGET_FRAME_TIME - elapsedTime) * 1000f); + Thread.Sleep(sleepMS); + elapsedTime = TARGET_FRAME_TIME; + } + else + { + timeDilation = 1f; + } + } + } + void LoadTerrain(string mapFile) { byte[] rgbValues = new byte[256 * 256 * 3]; @@ -1366,7 +1390,7 @@ namespace Simian // Send an update out to the creator ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket(); updateToOwner.RegionData.RegionHandle = regionHandle; - updateToOwner.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue); + updateToOwner.RegionData.TimeDilation = (ushort)(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); @@ -1629,7 +1653,7 @@ namespace Simian // Send an update out to the creator ObjectUpdateCompressedPacket updateToOwner = new ObjectUpdateCompressedPacket(); updateToOwner.RegionData.RegionHandle = regionHandle; - updateToOwner.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue); + updateToOwner.RegionData.TimeDilation = (ushort)(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); @@ -1707,7 +1731,7 @@ namespace Simian ImprovedTerseObjectUpdatePacket update = new ImprovedTerseObjectUpdatePacket(); update.RegionData.RegionHandle = RegionHandle; - update.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue); + update.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); update.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; update.ObjectData[0] = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); update.ObjectData[0].Data = data; diff --git a/Programs/Simian/Interfaces/IParcelProvider.cs b/Programs/Simian/Interfaces/IParcelProvider.cs index 27f0f07f..ee558fe1 100644 --- a/Programs/Simian/Interfaces/IParcelProvider.cs +++ b/Programs/Simian/Interfaces/IParcelProvider.cs @@ -6,5 +6,8 @@ namespace Simian public interface IParcelProvider { void SendParcelOverlay(Agent agent); + void UpdateParcel(Parcel parcel); + int GetParcelID(int x, int y); + bool TryGetParcel(int parcelID, out Parcel parcel); } } diff --git a/Programs/Simian/Interfaces/IPhysicsProvider.cs b/Programs/Simian/Interfaces/IPhysicsProvider.cs index 804d701d..6504f9a0 100644 --- a/Programs/Simian/Interfaces/IPhysicsProvider.cs +++ b/Programs/Simian/Interfaces/IPhysicsProvider.cs @@ -5,7 +5,11 @@ namespace Simian { public interface IPhysicsProvider { - float TimeDilation { get; } + /// + /// Runs a single physics frame + /// + /// The time since Update was called last + void Update(float elapsedTime); Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj); bool TryGetObjectMass(UUID objectID, out float mass); diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs index 41a21743..55fdb8f2 100644 --- a/Programs/Simian/Interfaces/ISceneProvider.cs +++ b/Programs/Simian/Interfaces/ISceneProvider.cs @@ -87,6 +87,8 @@ namespace Simian #endregion Scene related classes + #region Scene delegates + public delegate void ObjectAddOrUpdateCallback(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags updateFlags); public delegate void ObjectRemoveCallback(object sender, SimulationObject obj); public delegate void ObjectSetRotationAxisCallback(object sender, SimulationObject obj, Vector3 rotationAxis); @@ -105,6 +107,8 @@ namespace Simian public delegate void TerrainUpdateCallback(object sender, uint x, uint y, float[,] patchData); public delegate void WindUpdateCallback(object sender, uint x, uint y, Vector2 windSpeed); + #endregion Scene delegates + public interface ISceneProvider { event ObjectAddOrUpdateCallback OnObjectAddOrUpdate; @@ -144,13 +148,12 @@ namespace Simian Vector3 DefaultLookAt { get; } Vector3 DefaultPosition { get; } IPEndPoint IPAndPort { get; set; } - float WaterHeight { get; } - uint TerrainPatchWidth { get; } uint TerrainPatchHeight { get; } uint TerrainPatchCountWidth { get; } uint TerrainPatchCountHeight { get; } + float TimeDilation { get; } bool Start(Simian server, RegionInfo regionInfo, X509Certificate2 regionCert, string defaultTerrainFile, int staticObjects, int physicalObjects); void Stop(); diff --git a/Programs/Simian/SceneExtensions/LLAgents.cs b/Programs/Simian/LLUDP/LLAgents.cs similarity index 100% rename from Programs/Simian/SceneExtensions/LLAgents.cs rename to Programs/Simian/LLUDP/LLAgents.cs diff --git a/Programs/Simian/SceneExtensions/TransferManager.cs b/Programs/Simian/LLUDP/LLAssets.cs similarity index 97% rename from Programs/Simian/SceneExtensions/TransferManager.cs rename to Programs/Simian/LLUDP/LLAssets.cs index 42541bbf..acae35c6 100644 --- a/Programs/Simian/SceneExtensions/TransferManager.cs +++ b/Programs/Simian/LLUDP/LLAssets.cs @@ -6,12 +6,12 @@ using OpenMetaverse.Packets; namespace Simian { - public class TransferManager : IExtension + public class LLAssets : IExtension { ISceneProvider scene; Dictionary CurrentUploads = new Dictionary(); - public TransferManager() + public LLAssets() { } diff --git a/Programs/Simian/SceneExtensions/ConnectionManagement.cs b/Programs/Simian/LLUDP/LLConnections.cs similarity index 95% rename from Programs/Simian/SceneExtensions/ConnectionManagement.cs rename to Programs/Simian/LLUDP/LLConnections.cs index 476971db..ed77d6d8 100644 --- a/Programs/Simian/SceneExtensions/ConnectionManagement.cs +++ b/Programs/Simian/LLUDP/LLConnections.cs @@ -5,11 +5,11 @@ using OpenMetaverse.Packets; namespace Simian { - public class ConnectionManagement : IExtension + public class LLConnections : IExtension { ISceneProvider scene; - public ConnectionManagement() + public LLConnections() { } @@ -114,7 +114,7 @@ namespace Simian { ObjectUpdatePacket update = new ObjectUpdatePacket(); update.RegionData.RegionHandle = scene.RegionHandle; - update.RegionData.TimeDilation = (ushort)(scene.Physics.TimeDilation * (float)UInt16.MaxValue); + update.RegionData.TimeDilation = (ushort)(scene.TimeDilation * (float)UInt16.MaxValue); update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; update.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC); diff --git a/Programs/Simian/SceneExtensions/LLInventory.cs b/Programs/Simian/LLUDP/LLInventory.cs similarity index 100% rename from Programs/Simian/SceneExtensions/LLInventory.cs rename to Programs/Simian/LLUDP/LLInventory.cs diff --git a/Programs/Simian/SceneExtensions/LLMap.cs b/Programs/Simian/LLUDP/LLMap.cs similarity index 94% rename from Programs/Simian/SceneExtensions/LLMap.cs rename to Programs/Simian/LLUDP/LLMap.cs index 8876b032..da034ce7 100644 --- a/Programs/Simian/SceneExtensions/LLMap.cs +++ b/Programs/Simian/LLUDP/LLMap.cs @@ -241,7 +241,7 @@ namespace Simian scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction); } - // FIXME: Add XML config support for HyperGrid destinations + // FIXME: Add .ini config support for HyperGrid destinations /*else if (request.Info.RegionHandle == Utils.UIntsToLong((scene.RegionX + 1) * 256, scene.RegionY * 256)) { // Special case: adjacent simulator is the HyperGrid portal @@ -296,21 +296,8 @@ namespace Simian Logger.Log(String.Format("HyperGrid teleporting to {0} ({1}, {2}) @ {3}", link.RegionName, x, y, destination), Helpers.LogLevel.Info); - OSDMap info = new OSDMap(); - info.Add("AgentID", OSD.FromUUID(agent.ID)); - info.Add("LocationID", OSD.FromInteger(4)); // Unused by the client - info.Add("RegionHandle", OSD.FromULong(link.RegionHandle)); - info.Add("SeedCapability", OSD.FromUri(seedCap)); - info.Add("SimAccess", OSD.FromInteger((byte)SimAccess.Min)); - info.Add("SimIP", OSD.FromBinary(entry.AddressList[0].GetAddressBytes())); - info.Add("SimPort", OSD.FromInteger(link.UDPPort)); - info.Add("TeleportFlags", OSD.FromUInteger((uint)TeleportFlags.ViaLocation)); - - OSDArray infoArray = new OSDArray(1); - infoArray.Add(info); - - OSDMap teleport = new OSDMap(); - teleport.Add("Info", infoArray); + OSDMap teleport = CapsMessages.TeleportFinish(agent.ID, 4, link.RegionHandle, seedCap, (byte)SimAccess.Min, + entry.AddressList[0], link.UDPPort, TeleportFlags.ViaLocation); scene.SendEvent(agent, "TeleportFinish", teleport); } diff --git a/Programs/Simian/SceneExtensions/LLMessaging.cs b/Programs/Simian/LLUDP/LLMessaging.cs similarity index 100% rename from Programs/Simian/SceneExtensions/LLMessaging.cs rename to Programs/Simian/LLUDP/LLMessaging.cs diff --git a/Programs/Simian/SceneExtensions/LLMoney.cs b/Programs/Simian/LLUDP/LLMoney.cs similarity index 100% rename from Programs/Simian/SceneExtensions/LLMoney.cs rename to Programs/Simian/LLUDP/LLMoney.cs diff --git a/Programs/Simian/LLUDP/LLMovement.cs b/Programs/Simian/LLUDP/LLMovement.cs new file mode 100644 index 00000000..a5aaadaa --- /dev/null +++ b/Programs/Simian/LLUDP/LLMovement.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using ExtensionLoader; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenMetaverse.Rendering; + +namespace Simian +{ + public class LLMovement : IExtension + { + //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); + + ISceneProvider scene; + + public LLMovement() + { + } + + public bool Start(ISceneProvider scene) + { + this.scene = scene; + + scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler); + scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler); + scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler); + scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler); + + return true; + } + + public void Stop() + { + } + + void AgentRequestSitHandler(Packet packet, Agent agent) + { + AgentRequestSitPacket request = (AgentRequestSitPacket)packet; + + SimulationObject obj; + if (scene.TryGetObject(request.TargetObject.TargetID, out obj)) + { + agent.RequestedSitTarget = request.TargetObject.TargetID; + agent.RequestedSitOffset = request.TargetObject.Offset; + + AvatarSitResponsePacket response = new AvatarSitResponsePacket(); + response.SitObject.ID = request.TargetObject.TargetID; + response.SitTransform.AutoPilot = true; + response.SitTransform.CameraAtOffset = Vector3.Zero; + response.SitTransform.CameraEyeOffset = Vector3.Zero; + response.SitTransform.ForceMouselook = false; + response.SitTransform.SitPosition = request.TargetObject.Offset; + response.SitTransform.SitRotation = obj.SitRotation; + + scene.UDP.SendPacket(agent.ID, response, PacketCategory.State); + } + else + { + //TODO: send error + } + } + + void AgentSitHandler(Packet packet, Agent agent) + { + AgentSitPacket sit = (AgentSitPacket)packet; + + if (agent.RequestedSitTarget != UUID.Zero) + { + SimulationObject obj; + SimulationObject avObj; + if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj)) + { + agent.Avatar.Prim.Flags &= ~PrimFlags.Physics; + agent.Avatar.Prim.ParentID = obj.Prim.LocalID; + agent.Avatar.Prim.Position = new Vector3( + obj.Prim.Scale.X * 0.5f, + obj.Prim.Scale.Z * 0.5f, + agent.Avatar.Prim.Scale.Z * 0.33f); + + scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None, + UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position); + scene.Avatars.SetDefaultAnimation(agent, Animations.SIT); + scene.Avatars.SendAnimations(agent); + } + else + { + //TODO: send error + } + + agent.RequestedSitTarget = UUID.Zero; + agent.RequestedSitOffset = Vector3.Zero; + } + } + + void AgentUpdateHandler(Packet packet, Agent agent) + { + AgentUpdatePacket update = (AgentUpdatePacket)packet; + + SimulationObject obj; + if (scene.TryGetObject(agent.ID, out obj)) + { + if (agent.Avatar.Prim.ParentID == 0) + agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation; + + agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags; + agent.State = (AgentState)update.AgentData.State; + agent.HideTitle = update.AgentData.Flags != 0; + + // Check for standing up + SimulationObject parent; + if (scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) && + agent.Avatar.Prim.ParentID > 0 && + (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) + { + agent.Avatar.Prim.Position = parent.Prim.Position + + Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation)) + + Vector3.UnitZ; + + agent.Avatar.Prim.ParentID = 0; + + scene.Avatars.SetDefaultAnimation(agent, Animations.STAND); + scene.Avatars.SendAnimations(agent); + + agent.Avatar.Prim.Flags |= PrimFlags.Physics; + } + + scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation); + } + } + + void SetAlwaysRunHandler(Packet packet, Agent agent) + { + SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet; + + agent.Running = run.AgentData.AlwaysRun; + } + } +} diff --git a/Programs/Simian/SceneExtensions/ObjectManager.cs b/Programs/Simian/LLUDP/LLObjects.cs similarity index 97% rename from Programs/Simian/SceneExtensions/ObjectManager.cs rename to Programs/Simian/LLUDP/LLObjects.cs index c14918b4..c88f33f2 100644 --- a/Programs/Simian/SceneExtensions/ObjectManager.cs +++ b/Programs/Simian/LLUDP/LLObjects.cs @@ -8,11 +8,11 @@ using OpenMetaverse.Packets; namespace Simian { - public class ObjectManager : IExtension + public class LLObjects : IExtension { ISceneProvider scene; - - public ObjectManager() + + public LLObjects() { } diff --git a/Programs/Simian/LLUDP/LLParcels.cs b/Programs/Simian/LLUDP/LLParcels.cs new file mode 100644 index 00000000..87c9a833 --- /dev/null +++ b/Programs/Simian/LLUDP/LLParcels.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using ExtensionLoader; +using OpenMetaverse; +using OpenMetaverse.Packets; + +namespace Simian +{ + public class LLParcels : IExtension + { + ISceneProvider scene; + + public LLParcels() + { + } + + public bool Start(ISceneProvider scene) + { + this.scene = scene; + + scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler); + scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler); + return true; + } + + public void Stop() + { + } + + void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result, + Agent agent) + { + Parcel parcel; + if (scene.Parcels.TryGetParcel(parcelID, out parcel)) + { + ParcelPropertiesPacket properties = new ParcelPropertiesPacket(); + properties.AgeVerificationBlock.RegionDenyAgeUnverified = false; + properties.ParcelData.AABBMax = parcel.AABBMax; + properties.ParcelData.AABBMin = parcel.AABBMin; + properties.ParcelData.Area = parcel.Area; + properties.ParcelData.AuctionID = parcel.AuctionID; + properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID; + properties.ParcelData.Bitmap = parcel.Bitmap; + properties.ParcelData.Category = (byte)parcel.Category; + properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate); + properties.ParcelData.ClaimPrice = parcel.ClaimPrice; + properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc); + properties.ParcelData.GroupID = parcel.GroupID; + properties.ParcelData.GroupPrims = parcel.GroupPrims; + properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned; + properties.ParcelData.LandingType = (byte)parcel.Landing; + properties.ParcelData.LocalID = parcel.LocalID; + properties.ParcelData.MaxPrims = parcel.MaxPrims; + properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale; + properties.ParcelData.MediaID = parcel.Media.MediaID; + properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL); + properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL); + properties.ParcelData.Name = Utils.StringToBytes(parcel.Name); + properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime; + properties.ParcelData.OtherCount = parcel.OtherCount; + properties.ParcelData.OtherPrims = parcel.OtherPrims; + properties.ParcelData.OwnerID = parcel.OwnerID; + properties.ParcelData.OwnerPrims = parcel.OwnerPrims; + properties.ParcelData.ParcelFlags = (uint)parcel.Flags; + properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus; + properties.ParcelData.PassHours = parcel.PassHours; + properties.ParcelData.PassPrice = parcel.PassPrice; + properties.ParcelData.PublicCount = parcel.PublicCount; + properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous; + properties.ParcelData.RegionDenyIdentified = false; // Deprecated + properties.ParcelData.RegionDenyTransacted = false; // Deprecated + properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride; + properties.ParcelData.RentPrice = parcel.RentPrice; + properties.ParcelData.RequestResult = (int)result; + properties.ParcelData.SalePrice = parcel.SalePrice; + properties.ParcelData.SelectedPrims = 0; // TODO: + properties.ParcelData.SelfCount = parcel.SelfCount; + properties.ParcelData.SequenceID = sequenceID; + properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims; + properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims; + properties.ParcelData.SnapSelection = snapSelection; + properties.ParcelData.SnapshotID = parcel.SnapshotID; + properties.ParcelData.Status = (byte)parcel.Status; + properties.ParcelData.TotalPrims = parcel.TotalPrims; + properties.ParcelData.UserLocation = parcel.UserLocation; + properties.ParcelData.UserLookAt = parcel.UserLookAt; + + // HACK: Make everyone think they are the owner of this parcel + properties.ParcelData.OwnerID = agent.ID; + + scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction); + } + else + { + Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning); + } + } + + void ParcelPropertiesRequestHandler(Packet packet, Agent agent) + { + ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet; + + // TODO: Replace with HashSet when we switch to .NET 3.5 + List parcels = new List(); + + // 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 = scene.Parcels.GetParcelID(west + x, south + y); + 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 (scene.Parcels.TryGetParcel(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; + + scene.Parcels.UpdateParcel(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); + } + } + } +} diff --git a/Programs/Simian/SceneExtensions/ImageDelivery.cs b/Programs/Simian/LLUDP/LLTextures.cs similarity index 96% rename from Programs/Simian/SceneExtensions/ImageDelivery.cs rename to Programs/Simian/LLUDP/LLTextures.cs index 27d35a66..4496d511 100644 --- a/Programs/Simian/SceneExtensions/ImageDelivery.cs +++ b/Programs/Simian/LLUDP/LLTextures.cs @@ -102,12 +102,12 @@ namespace Simian } } - public class ImageDelivery : IExtension + public class LLTextures : IExtension { ISceneProvider scene; Dictionary CurrentDownloads = new Dictionary(); - public ImageDelivery() + public LLTextures() { } diff --git a/Programs/Simian/SceneExtensions/Movement.cs b/Programs/Simian/SceneExtensions/Movement.cs deleted file mode 100644 index da62bbf8..00000000 --- a/Programs/Simian/SceneExtensions/Movement.cs +++ /dev/null @@ -1,518 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using ExtensionLoader; -using OpenMetaverse; -using OpenMetaverse.Packets; -using OpenMetaverse.Rendering; - -namespace Simian -{ - public class Movement : IExtension - { - const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate - const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc - const float GRAVITY = 9.8f; //meters/sec - const float WALK_SPEED = 3f; //meters/sec - const float RUN_SPEED = 5f; //meters/sec - const float FLY_SPEED = 10f; //meters/sec - const float FALL_DELAY = 0.33f; //seconds before starting animation - const float FALL_FORGIVENESS = .25f; //fall buffer in meters - const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec - const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec - const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec - const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping - const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph - - static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015"); - static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f); - - const float SQRT_TWO = 1.41421356f; - - ISceneProvider scene; - Timer updateTimer; - long lastTick; - - public int LastTick - { - get { return (int) Interlocked.Read(ref lastTick); } - set { Interlocked.Exchange(ref lastTick, value); } - } - - public Movement() - { - } - - public bool Start(ISceneProvider scene) - { - this.scene = scene; - - scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate; - - scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler); - scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler); - scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler); - scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler); - - updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed)); - LastTick = Environment.TickCount; - updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION); - return true; - } - - public void Stop() - { - if (updateTimer != null) - { - updateTimer.Dispose(); - updateTimer = null; - } - } - - void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update) - { - bool forceMeshing = false; - bool forceTransform = false; - - if ((update & UpdateFlags.Scale) != 0 || - (update & UpdateFlags.Position) != 0 || - (update & UpdateFlags.Rotation) != 0) - { - forceTransform = true; - } - - if ((update & UpdateFlags.PrimData) != 0) - { - forceMeshing = true; - } - - // TODO: This doesn't update children prims when their parents move - obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform); - } - - void UpdateTimer_Elapsed(object sender) - { - int tick = Environment.TickCount; - float seconds = (float)((tick - LastTick) / 1000f); - LastTick = tick; - - scene.ForEachAgent( - delegate(Agent agent) - { - if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0) - return; - - bool animsChanged = false; - - // Create forward and left vectors from the current avatar rotation - Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation); - Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix); - Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix); - - // Check control flags - bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS; - bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG; - bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS; - bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG; - //bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT; - //bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT; - bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS; - bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG; - bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY; - //bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK; - - // direction in which the avatar is trying to move - Vector3 move = Vector3.Zero; - if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; } - if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; } - if (heldLeft) { move.X += left.X; move.Y += left.Y; } - if (heldRight) { move.X -= left.X; move.Y -= left.Y; } - if (heldUp) { move.Z += 1; } - if (heldDown) { move.Z -= 1; } - - // is the avatar trying to move? - bool moving = move != Vector3.Zero; - bool jumping = agent.TickJump != 0; - - // 2-dimensional speed multipler - float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED); - if ((heldForward || heldBack) && (heldLeft || heldRight)) - speed /= SQRT_TWO; - - Vector3 agentPosition = agent.Avatar.GetSimulatorPosition(); - float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y); - - agentPosition += (move * speed); - float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y); - - if (!flying && newFloor != oldFloor) - speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor))); - - //HACK: distance from avatar center to the bottom of its feet - float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f; - - float lowerLimit = newFloor + distanceFromFloor; - - //"bridge" physics - if (agent.Avatar.Prim.Velocity != Vector3.Zero) - { - //start ray at our feet - Vector3 rayStart = new Vector3( - agent.Avatar.Prim.Position.X, - agent.Avatar.Prim.Position.Y, - agent.Avatar.Prim.Position.Z - distanceFromFloor - ); - - //end ray at 0.01m below our feet - Vector3 rayEnd = new Vector3( - rayStart.X, - rayStart.Y, - rayStart.Z - 0.01f - ); - - scene.ForEachObject(delegate(SimulationObject obj) - { - //HACK: check nearby objects (what did you expect, octree?) - if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f) - { - Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj); - - if (collision != rayEnd) //we collided! - { - //check if we are any higher than before - float height = collision.Z + distanceFromFloor; - if (height > lowerLimit) lowerLimit = height; - } - } - }); - } - - // Z acceleration resulting from gravity - float gravity = 0f; - - float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f); - - if (flying) - { - agent.TickFall = 0; - agent.TickJump = 0; - - //velocity falloff while flying - agent.Avatar.Prim.Velocity.X *= 0.66f; - agent.Avatar.Prim.Velocity.Y *= 0.66f; - agent.Avatar.Prim.Velocity.Z *= 0.33f; - - if (agent.Avatar.Prim.Position.Z == lowerLimit) - agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE; - - if (move.X != 0 || move.Y != 0) - { //flying horizontally - if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY)) - animsChanged = true; - } - else if (move.Z > 0) - { //flying straight up - if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP)) - animsChanged = true; - } - else if (move.Z < 0) - { //flying straight down - if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN)) - animsChanged = true; - } - else - { //hovering in the air - if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER)) - animsChanged = true; - } - } - - else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight) - { //falling, floating, or landing from a jump - - if (agent.Avatar.Prim.Position.Z > scene.WaterHeight) - { //above water - - //override controls while drifting - move = Vector3.Zero; - - //keep most of our horizontal inertia - agent.Avatar.Prim.Velocity.X *= 0.975f; - agent.Avatar.Prim.Velocity.Y *= 0.975f; - - float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f; - - if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f)) - { //just started falling - agent.TickFall = Environment.TickCount; - } - else - { - gravity = GRAVITY * fallElapsed * seconds; //normal gravity - - if (!jumping) - { //falling - if (fallElapsed > FALL_DELAY) - { //falling long enough to trigger the animation - if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN)) - animsChanged = true; - } - } - } - } - else if (agent.Avatar.Prim.Position.Z >= waterChestHeight) - { //at the water line - - gravity = 0f; - agent.Avatar.Prim.Velocity *= 0.5f; - agent.Avatar.Prim.Velocity.Z = 0f; - if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight; - - if (move.Z > 0) - { - if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP)) - animsChanged = true; - } - else if (move.X != 0 || move.Y != 0) - { - if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW)) - animsChanged = true; - } - else - { - if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER)) - animsChanged = true; - } - } - else - { //underwater - - gravity = 0f; //buoyant - agent.Avatar.Prim.Velocity *= 0.5f * seconds; - agent.Avatar.Prim.Velocity.Z += 0.75f * seconds; - - if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN)) - animsChanged = true; - } - } - else - { //on the ground - - agent.TickFall = 0; - - //friction - agent.Avatar.Prim.Acceleration *= 0.2f; - agent.Avatar.Prim.Velocity *= 0.2f; - - agent.Avatar.Prim.Position.Z = lowerLimit; - - if (move.Z > 0) - { //jumping - if (!jumping) - { //begin prejump - move.Z = 0; //override Z control - if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP)) - animsChanged = true; - - agent.TickJump = Environment.TickCount; - } - else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000) - { //start actual jump - - if (agent.TickJump == -1) - { - //already jumping! end current jump - agent.TickJump = 0; - return; - } - - if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP)) - animsChanged = true; - - agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL; - agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL; - agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds; - - agent.TickJump = -1; //flag that we are currently jumping - } - else move.Z = 0; //override Z control - } - - else - { //not jumping - - agent.TickJump = 0; - - if (move.X != 0 || move.Y != 0) - { //not walking - - if (move.Z < 0) - { //crouchwalking - if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK)) - animsChanged = true; - } - else if (agent.Running) - { //running - if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN)) - animsChanged = true; - } - else - { //walking - if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK)) - animsChanged = true; - } - } - else - { //walking - if (move.Z < 0) - { //crouching - if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH)) - animsChanged = true; - } - else - { //standing - if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND)) - animsChanged = true; - } - } - } - } - - if (animsChanged) - scene.Avatars.SendAnimations(agent); - - float maxVel = AVATAR_TERMINAL_VELOCITY * seconds; - - // static acceleration when any control is held, otherwise none - if (moving) - { - agent.Avatar.Prim.Acceleration = move * speed; - if (agent.Avatar.Prim.Acceleration.Z < -maxVel) - agent.Avatar.Prim.Acceleration.Z = -maxVel; - else if (agent.Avatar.Prim.Acceleration.Z > maxVel) - agent.Avatar.Prim.Acceleration.Z = maxVel; - } - else agent.Avatar.Prim.Acceleration = Vector3.Zero; - - agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity); - if (agent.Avatar.Prim.Velocity.Z < -maxVel) - agent.Avatar.Prim.Velocity.Z = -maxVel; - else if (agent.Avatar.Prim.Velocity.Z > maxVel) - agent.Avatar.Prim.Velocity.Z = maxVel; - - agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity; - - if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f; - else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f; - - if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f; - else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f; - - if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit; - } - ); - } - - void AgentRequestSitHandler(Packet packet, Agent agent) - { - AgentRequestSitPacket request = (AgentRequestSitPacket)packet; - - SimulationObject obj; - if (scene.TryGetObject(request.TargetObject.TargetID, out obj)) - { - agent.RequestedSitTarget = request.TargetObject.TargetID; - agent.RequestedSitOffset = request.TargetObject.Offset; - - AvatarSitResponsePacket response = new AvatarSitResponsePacket(); - response.SitObject.ID = request.TargetObject.TargetID; - response.SitTransform.AutoPilot = true; - response.SitTransform.CameraAtOffset = Vector3.Zero; - response.SitTransform.CameraEyeOffset = Vector3.Zero; - response.SitTransform.ForceMouselook = false; - response.SitTransform.SitPosition = request.TargetObject.Offset; - response.SitTransform.SitRotation = obj.SitRotation; - - scene.UDP.SendPacket(agent.ID, response, PacketCategory.State); - } - else - { - //TODO: send error - } - } - - void AgentSitHandler(Packet packet, Agent agent) - { - AgentSitPacket sit = (AgentSitPacket)packet; - - if (agent.RequestedSitTarget != UUID.Zero) - { - SimulationObject obj; - SimulationObject avObj; - if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj)) - { - agent.Avatar.Prim.Flags &= ~PrimFlags.Physics; - agent.Avatar.Prim.ParentID = obj.Prim.LocalID; - agent.Avatar.Prim.Position = new Vector3( - obj.Prim.Scale.X * 0.5f, - obj.Prim.Scale.Z * 0.5f, - agent.Avatar.Prim.Scale.Z * 0.33f); - - scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None, - UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position); - scene.Avatars.SetDefaultAnimation(agent, Animations.SIT); - scene.Avatars.SendAnimations(agent); - } - else - { - //TODO: send error - } - - agent.RequestedSitTarget = UUID.Zero; - agent.RequestedSitOffset = Vector3.Zero; - } - } - - void AgentUpdateHandler(Packet packet, Agent agent) - { - AgentUpdatePacket update = (AgentUpdatePacket)packet; - - SimulationObject obj; - if (scene.TryGetObject(agent.ID, out obj)) - { - if (agent.Avatar.Prim.ParentID == 0) - agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation; - - agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags; - agent.State = (AgentState)update.AgentData.State; - agent.HideTitle = update.AgentData.Flags != 0; - - // Check for standing up - SimulationObject parent; - if (scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) && - agent.Avatar.Prim.ParentID > 0 && - (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) - { - agent.Avatar.Prim.Position = parent.Prim.Position - + Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation)) - + Vector3.UnitZ; - - agent.Avatar.Prim.ParentID = 0; - - scene.Avatars.SetDefaultAnimation(agent, Animations.STAND); - scene.Avatars.SendAnimations(agent); - - agent.Avatar.Prim.Flags |= PrimFlags.Physics; - } - - scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation); - } - } - - void SetAlwaysRunHandler(Packet packet, Agent agent) - { - SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet; - - agent.Running = run.AgentData.AlwaysRun; - } - } -} diff --git a/Programs/Simian/SceneExtensions/ParcelManager.cs b/Programs/Simian/SceneExtensions/ParcelManager.cs index 043915f4..7b936d14 100644 --- a/Programs/Simian/SceneExtensions/ParcelManager.cs +++ b/Programs/Simian/SceneExtensions/ParcelManager.cs @@ -48,8 +48,6 @@ namespace Simian // Add the default parcel to the list parcels[parcel.LocalID] = parcel; - scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler); - scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler); return true; } @@ -124,6 +122,21 @@ namespace Simian } } + public void UpdateParcel(Parcel parcel) + { + lock (parcels) parcels[parcel.LocalID] = parcel; + } + + public int GetParcelID(int x, int y) + { + return parcelOverlay[y * 64 + x]; + } + + public bool TryGetParcel(int parcelID, out Parcel parcel) + { + return parcels.TryGetValue(parcelID, out parcel); + } + void UpdateParcelSize(ref Parcel parcel) { int minX = 64; @@ -156,152 +169,5 @@ namespace Simian parcel.AABBMax.Y = maxY; parcel.Area = area; } - - void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result, - Agent agent) - { - Parcel parcel; - if (parcels.TryGetValue(parcelID, out parcel)) - { - ParcelPropertiesPacket properties = new ParcelPropertiesPacket(); - properties.AgeVerificationBlock.RegionDenyAgeUnverified = false; - properties.ParcelData.AABBMax = parcel.AABBMax; - properties.ParcelData.AABBMin = parcel.AABBMin; - properties.ParcelData.Area = parcel.Area; - properties.ParcelData.AuctionID = parcel.AuctionID; - properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID; - properties.ParcelData.Bitmap = parcel.Bitmap; - properties.ParcelData.Category = (byte)parcel.Category; - properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate); - properties.ParcelData.ClaimPrice = parcel.ClaimPrice; - properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc); - properties.ParcelData.GroupID = parcel.GroupID; - properties.ParcelData.GroupPrims = parcel.GroupPrims; - properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned; - properties.ParcelData.LandingType = (byte)parcel.Landing; - properties.ParcelData.LocalID = parcel.LocalID; - properties.ParcelData.MaxPrims = parcel.MaxPrims; - properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale; - properties.ParcelData.MediaID = parcel.Media.MediaID; - properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL); - properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL); - properties.ParcelData.Name = Utils.StringToBytes(parcel.Name); - properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime; - properties.ParcelData.OtherCount = parcel.OtherCount; - properties.ParcelData.OtherPrims = parcel.OtherPrims; - properties.ParcelData.OwnerID = parcel.OwnerID; - properties.ParcelData.OwnerPrims = parcel.OwnerPrims; - properties.ParcelData.ParcelFlags = (uint)parcel.Flags; - properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus; - properties.ParcelData.PassHours = parcel.PassHours; - properties.ParcelData.PassPrice = parcel.PassPrice; - properties.ParcelData.PublicCount = parcel.PublicCount; - properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous; - properties.ParcelData.RegionDenyIdentified = false; // Deprecated - properties.ParcelData.RegionDenyTransacted = false; // Deprecated - properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride; - properties.ParcelData.RentPrice = parcel.RentPrice; - properties.ParcelData.RequestResult = (int)result; - properties.ParcelData.SalePrice = parcel.SalePrice; - properties.ParcelData.SelectedPrims = 0; // TODO: - properties.ParcelData.SelfCount = parcel.SelfCount; - properties.ParcelData.SequenceID = sequenceID; - properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims; - properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims; - properties.ParcelData.SnapSelection = snapSelection; - properties.ParcelData.SnapshotID = parcel.SnapshotID; - properties.ParcelData.Status = (byte)parcel.Status; - properties.ParcelData.TotalPrims = parcel.TotalPrims; - properties.ParcelData.UserLocation = parcel.UserLocation; - properties.ParcelData.UserLookAt = parcel.UserLookAt; - - // HACK: Make everyone think they are the owner of this parcel - properties.ParcelData.OwnerID = agent.ID; - - scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction); - } - else - { - Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning); - } - } - - void ParcelPropertiesRequestHandler(Packet packet, Agent agent) - { - ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet; - - // TODO: Replace with HashSet when we switch to .NET 3.5 - List parcels = new List(); - - // 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); - } - } } } diff --git a/Programs/Simian/SceneExtensions/PeriscopeTransferManager.cs b/Programs/Simian/SceneExtensions/PeriscopeTransferManager.cs index 65bf5ef1..a8ad09a1 100644 --- a/Programs/Simian/SceneExtensions/PeriscopeTransferManager.cs +++ b/Programs/Simian/SceneExtensions/PeriscopeTransferManager.cs @@ -10,8 +10,6 @@ namespace Simian { public class PeriscopeTransferManager { - public const string UPLOAD_DIR = "uploadedAssets"; - ISceneProvider scene; GridClient client; Dictionary CurrentUploads = new Dictionary(); diff --git a/Programs/Simian/SceneExtensions/PhysicsSimple.cs b/Programs/Simian/SceneExtensions/PhysicsSimple.cs index 61103181..a4973750 100644 --- a/Programs/Simian/SceneExtensions/PhysicsSimple.cs +++ b/Programs/Simian/SceneExtensions/PhysicsSimple.cs @@ -7,12 +7,25 @@ namespace Simian { public class PhysicsSimple : IExtension, IPhysicsProvider { - ISceneProvider scene; + // Run our own frames per second limiter on top of the limiting done by ISceneProvider + const int FRAMES_PER_SECOND = 10; - public float TimeDilation - { - get { return 1.0f; } - } + const float GRAVITY = 9.8f; //meters/sec + const float WALK_SPEED = 3f; //meters/sec + const float RUN_SPEED = 5f; //meters/sec + const float FLY_SPEED = 10f; //meters/sec + const float FALL_DELAY = 0.33f; //seconds before starting animation + const float FALL_FORGIVENESS = .25f; //fall buffer in meters + const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec + const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec + const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec + const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping + const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph + + const float SQRT_TWO = 1.41421356f; + + ISceneProvider scene; + float elapsedSinceUpdate; public PhysicsSimple() { @@ -21,6 +34,8 @@ namespace Simian public bool Start(ISceneProvider scene) { this.scene = scene; + + scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate; return true; } @@ -28,6 +43,335 @@ namespace Simian { } + public void Update(float elapsedTime) + { + if (elapsedSinceUpdate >= 1f / (float)FRAMES_PER_SECOND) + { + elapsedTime = elapsedSinceUpdate; + elapsedSinceUpdate = 0f; + } + else + { + elapsedSinceUpdate += elapsedTime; + return; + } + + scene.ForEachAgent( + delegate(Agent agent) + { + if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0) + return; + + bool animsChanged = false; + + // Create forward and left vectors from the current avatar rotation + Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation); + Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix); + Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix); + + // Check control flags + bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS; + bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG; + bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS; + bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG; + //bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT; + //bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT; + bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS; + bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG; + bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY; + //bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK; + + // direction in which the avatar is trying to move + Vector3 move = Vector3.Zero; + if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; } + if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; } + if (heldLeft) { move.X += left.X; move.Y += left.Y; } + if (heldRight) { move.X -= left.X; move.Y -= left.Y; } + if (heldUp) { move.Z += 1; } + if (heldDown) { move.Z -= 1; } + + // is the avatar trying to move? + bool moving = move != Vector3.Zero; + bool jumping = agent.TickJump != 0; + + // 2-dimensional speed multipler + float speed = elapsedTime * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED); + if ((heldForward || heldBack) && (heldLeft || heldRight)) + speed /= SQRT_TWO; + + Vector3 agentPosition = agent.Avatar.GetSimulatorPosition(); + float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y); + + agentPosition += (move * speed); + float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y); + + if (!flying && newFloor != oldFloor) + speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor))); + + //HACK: distance from avatar center to the bottom of its feet + float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f; + + float lowerLimit = newFloor + distanceFromFloor; + + //"bridge" physics + if (agent.Avatar.Prim.Velocity != Vector3.Zero) + { + //start ray at our feet + Vector3 rayStart = new Vector3( + agent.Avatar.Prim.Position.X, + agent.Avatar.Prim.Position.Y, + agent.Avatar.Prim.Position.Z - distanceFromFloor + ); + + //end ray at 0.01m below our feet + Vector3 rayEnd = new Vector3( + rayStart.X, + rayStart.Y, + rayStart.Z - 0.01f + ); + + scene.ForEachObject(delegate(SimulationObject obj) + { + //HACK: check nearby objects (what did you expect, octree?) + if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f) + { + Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj); + + if (collision != rayEnd) //we collided! + { + //check if we are any higher than before + float height = collision.Z + distanceFromFloor; + if (height > lowerLimit) lowerLimit = height; + } + } + }); + } + + // Z acceleration resulting from gravity + float gravity = 0f; + + float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f); + + if (flying) + { + agent.TickFall = 0; + agent.TickJump = 0; + + //velocity falloff while flying + agent.Avatar.Prim.Velocity.X *= 0.66f; + agent.Avatar.Prim.Velocity.Y *= 0.66f; + agent.Avatar.Prim.Velocity.Z *= 0.33f; + + if (agent.Avatar.Prim.Position.Z == lowerLimit) + agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE; + + if (move.X != 0 || move.Y != 0) + { //flying horizontally + if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY)) + animsChanged = true; + } + else if (move.Z > 0) + { //flying straight up + if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP)) + animsChanged = true; + } + else if (move.Z < 0) + { //flying straight down + if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN)) + animsChanged = true; + } + else + { //hovering in the air + if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER)) + animsChanged = true; + } + } + else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight) + { //falling, floating, or landing from a jump + + if (agent.Avatar.Prim.Position.Z > scene.WaterHeight) + { //above water + + //override controls while drifting + move = Vector3.Zero; + + //keep most of our horizontal inertia + agent.Avatar.Prim.Velocity.X *= 0.975f; + agent.Avatar.Prim.Velocity.Y *= 0.975f; + + float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f; + + if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f)) + { //just started falling + agent.TickFall = Environment.TickCount; + } + else + { + gravity = GRAVITY * fallElapsed * elapsedTime; //normal gravity + + if (!jumping) + { //falling + if (fallElapsed > FALL_DELAY) + { //falling long enough to trigger the animation + if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN)) + animsChanged = true; + } + } + } + } + else if (agent.Avatar.Prim.Position.Z >= waterChestHeight) + { //at the water line + + gravity = 0f; + agent.Avatar.Prim.Velocity *= 0.5f; + agent.Avatar.Prim.Velocity.Z = 0f; + if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight; + + if (move.Z > 0) + { + if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP)) + animsChanged = true; + } + else if (move.X != 0 || move.Y != 0) + { + if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW)) + animsChanged = true; + } + else + { + if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER)) + animsChanged = true; + } + } + else + { //underwater + + gravity = 0f; //buoyant + agent.Avatar.Prim.Velocity *= 0.5f * elapsedTime; + agent.Avatar.Prim.Velocity.Z += 0.75f * elapsedTime; + + if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN)) + animsChanged = true; + } + } + else + { //on the ground + + agent.TickFall = 0; + + //friction + agent.Avatar.Prim.Acceleration *= 0.2f; + agent.Avatar.Prim.Velocity *= 0.2f; + + agent.Avatar.Prim.Position.Z = lowerLimit; + + if (move.Z > 0) + { //jumping + if (!jumping) + { //begin prejump + move.Z = 0; //override Z control + if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP)) + animsChanged = true; + + agent.TickJump = Environment.TickCount; + } + else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000) + { //start actual jump + + if (agent.TickJump == -1) + { + //already jumping! end current jump + agent.TickJump = 0; + return; + } + + if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP)) + animsChanged = true; + + agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL; + agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL; + agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * elapsedTime; + + agent.TickJump = -1; //flag that we are currently jumping + } + else move.Z = 0; //override Z control + } + + else + { //not jumping + + agent.TickJump = 0; + + if (move.X != 0 || move.Y != 0) + { //not walking + + if (move.Z < 0) + { //crouchwalking + if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK)) + animsChanged = true; + } + else if (agent.Running) + { //running + if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN)) + animsChanged = true; + } + else + { //walking + if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK)) + animsChanged = true; + } + } + else + { //walking + if (move.Z < 0) + { //crouching + if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH)) + animsChanged = true; + } + else + { //standing + if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND)) + animsChanged = true; + } + } + } + } + + if (animsChanged) + scene.Avatars.SendAnimations(agent); + + float maxVel = AVATAR_TERMINAL_VELOCITY * elapsedTime; + + // static acceleration when any control is held, otherwise none + if (moving) + { + agent.Avatar.Prim.Acceleration = move * speed; + if (agent.Avatar.Prim.Acceleration.Z < -maxVel) + agent.Avatar.Prim.Acceleration.Z = -maxVel; + else if (agent.Avatar.Prim.Acceleration.Z > maxVel) + agent.Avatar.Prim.Acceleration.Z = maxVel; + } + else agent.Avatar.Prim.Acceleration = Vector3.Zero; + + agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity); + if (agent.Avatar.Prim.Velocity.Z < -maxVel) + agent.Avatar.Prim.Velocity.Z = -maxVel; + else if (agent.Avatar.Prim.Velocity.Z > maxVel) + agent.Avatar.Prim.Velocity.Z = maxVel; + + agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity; + + if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f; + else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f; + + if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f; + else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f; + + if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit; + } + ); + } + public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj) { Vector3 closestPoint = rayEnd; @@ -80,6 +424,28 @@ namespace Simian } } + void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update) + { + // Recompute meshes for + bool forceMeshing = false; + bool forceTransform = false; + + if ((update & UpdateFlags.Scale) != 0 || + (update & UpdateFlags.Position) != 0 || + (update & UpdateFlags.Rotation) != 0) + { + forceTransform = true; + } + + if ((update & UpdateFlags.PrimData) != 0) + { + forceMeshing = true; + } + + // TODO: This doesn't update children prims when their parents move + obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform); + } + /// /// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf /// diff --git a/Programs/Simian/SceneExtensions/ScriptApi.cs b/Programs/Simian/ScriptApi.cs similarity index 100% rename from Programs/Simian/SceneExtensions/ScriptApi.cs rename to Programs/Simian/ScriptApi.cs diff --git a/bin/SimianData/Simian.ini b/bin/SimianData/Simian.ini index 61b0d3f6..cee3de43 100644 --- a/bin/SimianData/Simian.ini +++ b/bin/SimianData/Simian.ini @@ -13,144 +13,6 @@ ListenPort = 8002 ; file is given the server will run in HTTPS mode. ;SSLCertFile = server.p12 -[Extensions] - -; Handles sending and receiving packets in the LLUDP format -UDPManager - -; LLUDP connection management. Allows agents to connect and disconnect from a -; simulator, as well as keeping the UDP connection alive. -ConnectionManagement - -; Main scene graph engine. All spatial events are processed through here. -SceneManager - -; Creates an account for anyone who logs in. The account will be registered -; with the account provider so persistence between sessions is possible, but -; no form of authentication or authorization is done -AuthFreeForAll - -; A permissions module that grants full access for everything. Only use this if -; all of the system users are fully trusted -PermissionsFreeForAll - -; Implements the login server for Linden-based clients directly into Simian -LindenLogin - -; -; ---Local Simulator Extensions--- -; -; The following extensions may use persistence, but do not communicate with a -; remote server or other simulators. These extensions are only useful for a -; single simulator running in standalone mode, and will have unexpected results -; if the simulator is part of a larger grid. - -; A local account store -AccountManager - -; A local asset store using the filesystem to store assets -AssetManager - -; A local inventory store -InventoryManager - -; A local grid service provider -GridLocal - -; A simulator-local messaging layer for instant messages and script e-mails -MessagingLocal - -; -; ---End Local Simulator Extensions--- -; - -; A task inventory store. Handles inventory for objects in a simulator -TaskInventoryManager - -; Manages creation and deletion of capabilities (see -; http://wiki.secondlife.com/wiki/Capabilities for more information). -CapsManager - -; Various avatar-related functions including appearance, animations and sounds -AvatarManager - -; Other asset downloads -TransferManager - -; Converts prims into 3D geometry data. This is only used if a physics engine -; is present that makes use of prim mesh data. -RenderingPluginMesher - -; A simple physics engine with good (but slow) implementations of the -; interfaces. -PhysicsSimple - -; Physics engine provided by the Open Dynamics Engine library. Work in progress, -; not functional yet. -;PhysicsODE - -; Object creation, editing, deleting, etc. Processes packets and passes events -; to the scene provider. -ObjectManager - -; Parcel management -ParcelManager - -; An scripting engine implementation adapted from OpenSim -XScriptEngine - -; Allows LSL functions to be called through chat -ScriptConsole - -; Texture downloads -ImageDelivery - -; A simple physics engine for avatar movement. Supports walking, flying, and -; swimming as well as avatar-avatar collisions. Does not support avatar-prim -; or prim-prim collisions. -Movement - -; Periscope allows you to proxy a foreign grid simulator into the local Simian -; using a libOpenMetaverse bot. The first person to login to Simian will become -; the master agent, who's movement is tethered to the bot's movement. Any other -; agents that login can move around freely, but will only see what the master -; agent is seeing through the periscope. If you enable this extension, disable -; ImageDelivery and Movement as Periscope has its own implementations of those -; extensions -;Periscope - -; -; ---Linden Client Extensions--- -; - -; Packet handling for miscellaneous agent and avatar functions -LLAgents - -; Packet handling for inventory -LLInventory - -; Packet handling for money packets -LLMoney - -; Packet handling for chat and instant messaging -LLMessaging - -; Packet handling for world map display -LLMap - -; -; ---End Linden Client Extensions--- -; - -; -; ---Persistence Providers--- -; -; The following extensions allow extensions that use persistence to save -; state between simulator runs. Only one persistence provider should be -; active. - -; Persistence provider using LLSD XML serialization -XMLPersistence [Meshers] @@ -160,3 +22,141 @@ XMLPersistence OpenMetaverse.Rendering.GPL OpenMetaverse.Rendering.Meshmerizer OpenMetaverse.Rendering.Simple + + +[Extensions] + +; +; ---Server Extensions--- +; + +; A local account store +AccountManager + +; A local asset store using the filesystem to store assets +AssetManager + +; Creates an account for anyone who logs in. The account will be registered +; with the account provider so persistence between sessions is possible, but +; no form of authentication or authorization is done +AuthFreeForAll + +; Manages creation and deletion of capabilities (see +; http://wiki.secondlife.com/wiki/Capabilities for more information). +CapsManager + +; A local grid service provider +GridLocal + +; A local inventory store +InventoryManager + +; Implements the login server for Linden-based clients directly into Simian +LindenLogin + +; A simulator-local messaging layer for instant messages and script e-mails +MessagingLocal + +; A permissions module that grants full access for everything. Only use this if +; all of the system users are fully trusted +PermissionsFreeForAll + +; Converts prims into 3D geometry data. This is only used if a physics engine +; is present that makes use of prim mesh data. +RenderingPluginMesher + +; Main scene graph engine. All spatial events are processed through here. +SceneManager + +; Allows other extensions to persist data using OSD serialization +XMLPersistence + +; +; ---End Server Extensions--- +; + +; +; ---Scene Extensions--- +; + +; Various avatar-related functions including appearance, animations and sounds +AvatarManager + +; Track parcels and parcel information for a scene +ParcelManager + +; A simple physics engine with accurate implementations of the interfaces and +; reasonable avatar-only physics with no external dependencies. Good performance +; for lightweight scenes, but scales poorly in complex scenes. +PhysicsSimple + +; Physics engine provided by the Open Dynamics Engine library. Work in progress, +; not functional yet. +;PhysicsODE + +; Allows LSL functions to be called through chat +ScriptConsole + +; A task inventory store. Handles inventory for objects in a simulator +TaskInventoryManager + +; Handles sending and receiving packets in the LLUDP format +UDPManager + +; An scripting engine implementation adapted from OpenSim +XScriptEngine + +; +; ---End Scene Extensions--- +; + +; +; ---Linden Client Extensions--- +; + +; Packet handling for miscellaneous agent and avatar functions +LLAgents + +; Packet handling for asset upload and download +LLAssets + +; Connection management such as creating the UDP circuit, logout, and throttle +; control +LLConnections + +; Packet handling for inventory +LLInventory + +; Packet handling for world map display +LLMap + +; Packet handling for chat and instant messaging +LLMessaging + +; Packet handling for money packets +LLMoney + +; Packet handling for avatar/camera movement +LLMovement + +; Packet handling for object creation, editing, deleting, etc. +LLObjects + +; Packet handling for parcel information +LLParcels + +; Packet handling for texture downloads +LLTextures + +; +; ---End Linden Client Extensions--- +; + +; Periscope allows you to proxy a foreign grid simulator into the local Simian +; using a libOpenMetaverse bot. The first person to login to Simian will become +; the master agent, who's movement is tethered to the bot's movement. Any other +; agents that login can move around freely, but will only see what the master +; agent is seeing through the periscope. If you enable this extension, disable +; LLAssets, LLMovement, and LLTextures as Periscope has its own implementations +; of those extensions +;Periscope