[Simian]
* Created an LLUDP folder to hold extensions that are purely LLUDP packet handlers. This is not a complete abstraction away from transport protocols, but it's a start * Moved physics code from Movement.cs into PhysicsSimple.cs, and moved the physics loop into a thread in SceneManager * Simian.ini cleanup git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2490 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
70
Programs/Simian/CapsMessages.cs
Normal file
70
Programs/Simian/CapsMessages.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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</summary>
|
||||
Dictionary<UUID, Uri> enableClientCompleteCallbacks = new Dictionary<UUID, Uri>();
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ namespace Simian
|
||||
{
|
||||
public interface IPhysicsProvider
|
||||
{
|
||||
float TimeDilation { get; }
|
||||
/// <summary>
|
||||
/// Runs a single physics frame
|
||||
/// </summary>
|
||||
/// <param name="elapsedTime">The time since Update was called last</param>
|
||||
void Update(float elapsedTime);
|
||||
|
||||
Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj);
|
||||
bool TryGetObjectMass(UUID objectID, out float mass);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -6,12 +6,12 @@ using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class TransferManager : IExtension<ISceneProvider>
|
||||
public class LLAssets : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
|
||||
public TransferManager()
|
||||
public LLAssets()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ConnectionManagement : IExtension<ISceneProvider>
|
||||
public class LLConnections : IExtension<ISceneProvider>
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
140
Programs/Simian/LLUDP/LLMovement.cs
Normal file
140
Programs/Simian/LLUDP/LLMovement.cs
Normal file
@@ -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<ISceneProvider>
|
||||
{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,11 @@ using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class ObjectManager : IExtension<ISceneProvider>
|
||||
public class LLObjects : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
|
||||
public ObjectManager()
|
||||
|
||||
public LLObjects()
|
||||
{
|
||||
}
|
||||
|
||||
177
Programs/Simian/LLUDP/LLParcels.cs
Normal file
177
Programs/Simian/LLUDP/LLParcels.cs
Normal file
@@ -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>
|
||||
{
|
||||
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<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 = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,12 +102,12 @@ namespace Simian
|
||||
}
|
||||
}
|
||||
|
||||
public class ImageDelivery : IExtension<ISceneProvider>
|
||||
public class LLTextures : IExtension<ISceneProvider>
|
||||
{
|
||||
ISceneProvider scene;
|
||||
Dictionary<UUID, ImageDownload> CurrentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public ImageDelivery()
|
||||
public LLTextures()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,518 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.Rendering;
|
||||
|
||||
namespace Simian
|
||||
{
|
||||
public class Movement : IExtension<ISceneProvider>
|
||||
{
|
||||
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
|
||||
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
|
||||
const float GRAVITY = 9.8f; //meters/sec
|
||||
const float WALK_SPEED = 3f; //meters/sec
|
||||
const float RUN_SPEED = 5f; //meters/sec
|
||||
const float FLY_SPEED = 10f; //meters/sec
|
||||
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
||||
const float FALL_FORGIVENESS = .25f; //fall buffer in meters
|
||||
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
||||
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec
|
||||
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
||||
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
||||
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
||||
|
||||
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
|
||||
static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f);
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
ISceneProvider scene;
|
||||
Timer updateTimer;
|
||||
long lastTick;
|
||||
|
||||
public int LastTick
|
||||
{
|
||||
get { return (int) Interlocked.Read(ref lastTick); }
|
||||
set { Interlocked.Exchange(ref lastTick, value); }
|
||||
}
|
||||
|
||||
public Movement()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
||||
|
||||
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
||||
LastTick = Environment.TickCount;
|
||||
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
|
||||
{
|
||||
bool forceMeshing = false;
|
||||
bool forceTransform = false;
|
||||
|
||||
if ((update & UpdateFlags.Scale) != 0 ||
|
||||
(update & UpdateFlags.Position) != 0 ||
|
||||
(update & UpdateFlags.Rotation) != 0)
|
||||
{
|
||||
forceTransform = true;
|
||||
}
|
||||
|
||||
if ((update & UpdateFlags.PrimData) != 0)
|
||||
{
|
||||
forceMeshing = true;
|
||||
}
|
||||
|
||||
// TODO: This doesn't update children prims when their parents move
|
||||
obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform);
|
||||
}
|
||||
|
||||
void UpdateTimer_Elapsed(object sender)
|
||||
{
|
||||
int tick = Environment.TickCount;
|
||||
float seconds = (float)((tick - LastTick) / 1000f);
|
||||
LastTick = tick;
|
||||
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
|
||||
return;
|
||||
|
||||
bool animsChanged = false;
|
||||
|
||||
// Create forward and left vectors from the current avatar rotation
|
||||
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
|
||||
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
||||
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
||||
|
||||
// Check control flags
|
||||
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
||||
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
|
||||
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
|
||||
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
|
||||
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
|
||||
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
|
||||
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
|
||||
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
|
||||
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
||||
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
|
||||
|
||||
// direction in which the avatar is trying to move
|
||||
Vector3 move = Vector3.Zero;
|
||||
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
||||
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
||||
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
||||
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
||||
if (heldUp) { move.Z += 1; }
|
||||
if (heldDown) { move.Z -= 1; }
|
||||
|
||||
// is the avatar trying to move?
|
||||
bool moving = move != Vector3.Zero;
|
||||
bool jumping = agent.TickJump != 0;
|
||||
|
||||
// 2-dimensional speed multipler
|
||||
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
||||
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
||||
speed /= SQRT_TWO;
|
||||
|
||||
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
|
||||
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
agentPosition += (move * speed);
|
||||
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
if (!flying && newFloor != oldFloor)
|
||||
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
||||
|
||||
//HACK: distance from avatar center to the bottom of its feet
|
||||
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
|
||||
|
||||
float lowerLimit = newFloor + distanceFromFloor;
|
||||
|
||||
//"bridge" physics
|
||||
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
|
||||
{
|
||||
//start ray at our feet
|
||||
Vector3 rayStart = new Vector3(
|
||||
agent.Avatar.Prim.Position.X,
|
||||
agent.Avatar.Prim.Position.Y,
|
||||
agent.Avatar.Prim.Position.Z - distanceFromFloor
|
||||
);
|
||||
|
||||
//end ray at 0.01m below our feet
|
||||
Vector3 rayEnd = new Vector3(
|
||||
rayStart.X,
|
||||
rayStart.Y,
|
||||
rayStart.Z - 0.01f
|
||||
);
|
||||
|
||||
scene.ForEachObject(delegate(SimulationObject obj)
|
||||
{
|
||||
//HACK: check nearby objects (what did you expect, octree?)
|
||||
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
|
||||
{
|
||||
Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
|
||||
|
||||
if (collision != rayEnd) //we collided!
|
||||
{
|
||||
//check if we are any higher than before
|
||||
float height = collision.Z + distanceFromFloor;
|
||||
if (height > lowerLimit) lowerLimit = height;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Z acceleration resulting from gravity
|
||||
float gravity = 0f;
|
||||
|
||||
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
|
||||
|
||||
if (flying)
|
||||
{
|
||||
agent.TickFall = 0;
|
||||
agent.TickJump = 0;
|
||||
|
||||
//velocity falloff while flying
|
||||
agent.Avatar.Prim.Velocity.X *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Z *= 0.33f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z == lowerLimit)
|
||||
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //flying horizontally
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
|
||||
{ //falling, floating, or landing from a jump
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
|
||||
{ //above water
|
||||
|
||||
//override controls while drifting
|
||||
move = Vector3.Zero;
|
||||
|
||||
//keep most of our horizontal inertia
|
||||
agent.Avatar.Prim.Velocity.X *= 0.975f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.975f;
|
||||
|
||||
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
||||
|
||||
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
|
||||
{ //just started falling
|
||||
agent.TickFall = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
|
||||
|
||||
if (!jumping)
|
||||
{ //falling
|
||||
if (fallElapsed > FALL_DELAY)
|
||||
{ //falling long enough to trigger the animation
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
|
||||
{ //at the water line
|
||||
|
||||
gravity = 0f;
|
||||
agent.Avatar.Prim.Velocity *= 0.5f;
|
||||
agent.Avatar.Prim.Velocity.Z = 0f;
|
||||
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
|
||||
|
||||
if (move.Z > 0)
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.X != 0 || move.Y != 0)
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //underwater
|
||||
|
||||
gravity = 0f; //buoyant
|
||||
agent.Avatar.Prim.Velocity *= 0.5f * seconds;
|
||||
agent.Avatar.Prim.Velocity.Z += 0.75f * seconds;
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //on the ground
|
||||
|
||||
agent.TickFall = 0;
|
||||
|
||||
//friction
|
||||
agent.Avatar.Prim.Acceleration *= 0.2f;
|
||||
agent.Avatar.Prim.Velocity *= 0.2f;
|
||||
|
||||
agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
|
||||
if (move.Z > 0)
|
||||
{ //jumping
|
||||
if (!jumping)
|
||||
{ //begin prejump
|
||||
move.Z = 0; //override Z control
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.TickJump = Environment.TickCount;
|
||||
}
|
||||
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
|
||||
{ //start actual jump
|
||||
|
||||
if (agent.TickJump == -1)
|
||||
{
|
||||
//already jumping! end current jump
|
||||
agent.TickJump = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
|
||||
|
||||
agent.TickJump = -1; //flag that we are currently jumping
|
||||
}
|
||||
else move.Z = 0; //override Z control
|
||||
}
|
||||
|
||||
else
|
||||
{ //not jumping
|
||||
|
||||
agent.TickJump = 0;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //not walking
|
||||
|
||||
if (move.Z < 0)
|
||||
{ //crouchwalking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
if (moving)
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = move * speed;
|
||||
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = maxVel;
|
||||
}
|
||||
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
|
||||
|
||||
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
|
||||
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = maxVel;
|
||||
|
||||
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
|
||||
|
||||
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
|
||||
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
|
||||
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void AgentRequestSitHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentRequestSitPacket request = (AgentRequestSitPacket)packet;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(request.TargetObject.TargetID, out obj))
|
||||
{
|
||||
agent.RequestedSitTarget = request.TargetObject.TargetID;
|
||||
agent.RequestedSitOffset = request.TargetObject.Offset;
|
||||
|
||||
AvatarSitResponsePacket response = new AvatarSitResponsePacket();
|
||||
response.SitObject.ID = request.TargetObject.TargetID;
|
||||
response.SitTransform.AutoPilot = true;
|
||||
response.SitTransform.CameraAtOffset = Vector3.Zero;
|
||||
response.SitTransform.CameraEyeOffset = Vector3.Zero;
|
||||
response.SitTransform.ForceMouselook = false;
|
||||
response.SitTransform.SitPosition = request.TargetObject.Offset;
|
||||
response.SitTransform.SitRotation = obj.SitRotation;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, response, PacketCategory.State);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: send error
|
||||
}
|
||||
}
|
||||
|
||||
void AgentSitHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentSitPacket sit = (AgentSitPacket)packet;
|
||||
|
||||
if (agent.RequestedSitTarget != UUID.Zero)
|
||||
{
|
||||
SimulationObject obj;
|
||||
SimulationObject avObj;
|
||||
if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj))
|
||||
{
|
||||
agent.Avatar.Prim.Flags &= ~PrimFlags.Physics;
|
||||
agent.Avatar.Prim.ParentID = obj.Prim.LocalID;
|
||||
agent.Avatar.Prim.Position = new Vector3(
|
||||
obj.Prim.Scale.X * 0.5f,
|
||||
obj.Prim.Scale.Z * 0.5f,
|
||||
agent.Avatar.Prim.Scale.Z * 0.33f);
|
||||
|
||||
scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
|
||||
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
|
||||
scene.Avatars.SetDefaultAnimation(agent, Animations.SIT);
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: send error
|
||||
}
|
||||
|
||||
agent.RequestedSitTarget = UUID.Zero;
|
||||
agent.RequestedSitOffset = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
SimulationObject obj;
|
||||
if (scene.TryGetObject(agent.ID, out obj))
|
||||
{
|
||||
if (agent.Avatar.Prim.ParentID == 0)
|
||||
agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation;
|
||||
|
||||
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
|
||||
agent.State = (AgentState)update.AgentData.State;
|
||||
agent.HideTitle = update.AgentData.Flags != 0;
|
||||
|
||||
// Check for standing up
|
||||
SimulationObject parent;
|
||||
if (scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) &&
|
||||
agent.Avatar.Prim.ParentID > 0 &&
|
||||
(agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP)
|
||||
{
|
||||
agent.Avatar.Prim.Position = parent.Prim.Position
|
||||
+ Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation))
|
||||
+ Vector3.UnitZ;
|
||||
|
||||
agent.Avatar.Prim.ParentID = 0;
|
||||
|
||||
scene.Avatars.SetDefaultAnimation(agent, Animations.STAND);
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
|
||||
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
|
||||
}
|
||||
|
||||
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAlwaysRunHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
|
||||
|
||||
agent.Running = run.AgentData.AlwaysRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +48,6 @@ namespace Simian
|
||||
// Add the default parcel to the list
|
||||
parcels[parcel.LocalID] = parcel;
|
||||
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler);
|
||||
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -124,6 +122,21 @@ namespace Simian
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateParcel(Parcel parcel)
|
||||
{
|
||||
lock (parcels) parcels[parcel.LocalID] = parcel;
|
||||
}
|
||||
|
||||
public int GetParcelID(int x, int y)
|
||||
{
|
||||
return parcelOverlay[y * 64 + x];
|
||||
}
|
||||
|
||||
public bool TryGetParcel(int parcelID, out Parcel parcel)
|
||||
{
|
||||
return parcels.TryGetValue(parcelID, out parcel);
|
||||
}
|
||||
|
||||
void UpdateParcelSize(ref Parcel parcel)
|
||||
{
|
||||
int minX = 64;
|
||||
@@ -156,152 +169,5 @@ namespace Simian
|
||||
parcel.AABBMax.Y = maxY;
|
||||
parcel.Area = area;
|
||||
}
|
||||
|
||||
void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result,
|
||||
Agent agent)
|
||||
{
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(parcelID, out parcel))
|
||||
{
|
||||
ParcelPropertiesPacket properties = new ParcelPropertiesPacket();
|
||||
properties.AgeVerificationBlock.RegionDenyAgeUnverified = false;
|
||||
properties.ParcelData.AABBMax = parcel.AABBMax;
|
||||
properties.ParcelData.AABBMin = parcel.AABBMin;
|
||||
properties.ParcelData.Area = parcel.Area;
|
||||
properties.ParcelData.AuctionID = parcel.AuctionID;
|
||||
properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID;
|
||||
properties.ParcelData.Bitmap = parcel.Bitmap;
|
||||
properties.ParcelData.Category = (byte)parcel.Category;
|
||||
properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate);
|
||||
properties.ParcelData.ClaimPrice = parcel.ClaimPrice;
|
||||
properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc);
|
||||
properties.ParcelData.GroupID = parcel.GroupID;
|
||||
properties.ParcelData.GroupPrims = parcel.GroupPrims;
|
||||
properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned;
|
||||
properties.ParcelData.LandingType = (byte)parcel.Landing;
|
||||
properties.ParcelData.LocalID = parcel.LocalID;
|
||||
properties.ParcelData.MaxPrims = parcel.MaxPrims;
|
||||
properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale;
|
||||
properties.ParcelData.MediaID = parcel.Media.MediaID;
|
||||
properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL);
|
||||
properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL);
|
||||
properties.ParcelData.Name = Utils.StringToBytes(parcel.Name);
|
||||
properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime;
|
||||
properties.ParcelData.OtherCount = parcel.OtherCount;
|
||||
properties.ParcelData.OtherPrims = parcel.OtherPrims;
|
||||
properties.ParcelData.OwnerID = parcel.OwnerID;
|
||||
properties.ParcelData.OwnerPrims = parcel.OwnerPrims;
|
||||
properties.ParcelData.ParcelFlags = (uint)parcel.Flags;
|
||||
properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus;
|
||||
properties.ParcelData.PassHours = parcel.PassHours;
|
||||
properties.ParcelData.PassPrice = parcel.PassPrice;
|
||||
properties.ParcelData.PublicCount = parcel.PublicCount;
|
||||
properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous;
|
||||
properties.ParcelData.RegionDenyIdentified = false; // Deprecated
|
||||
properties.ParcelData.RegionDenyTransacted = false; // Deprecated
|
||||
properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride;
|
||||
properties.ParcelData.RentPrice = parcel.RentPrice;
|
||||
properties.ParcelData.RequestResult = (int)result;
|
||||
properties.ParcelData.SalePrice = parcel.SalePrice;
|
||||
properties.ParcelData.SelectedPrims = 0; // TODO:
|
||||
properties.ParcelData.SelfCount = parcel.SelfCount;
|
||||
properties.ParcelData.SequenceID = sequenceID;
|
||||
properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims;
|
||||
properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims;
|
||||
properties.ParcelData.SnapSelection = snapSelection;
|
||||
properties.ParcelData.SnapshotID = parcel.SnapshotID;
|
||||
properties.ParcelData.Status = (byte)parcel.Status;
|
||||
properties.ParcelData.TotalPrims = parcel.TotalPrims;
|
||||
properties.ParcelData.UserLocation = parcel.UserLocation;
|
||||
properties.ParcelData.UserLookAt = parcel.UserLookAt;
|
||||
|
||||
// HACK: Make everyone think they are the owner of this parcel
|
||||
properties.ParcelData.OwnerID = agent.ID;
|
||||
|
||||
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void ParcelPropertiesRequestHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet;
|
||||
|
||||
// TODO: Replace with HashSet when we switch to .NET 3.5
|
||||
List<int> parcels = new List<int>();
|
||||
|
||||
// Convert the boundaries to integers
|
||||
int north = (int)Math.Round(request.ParcelData.North) / 4;
|
||||
int east = (int)Math.Round(request.ParcelData.East) / 4;
|
||||
int south = (int)Math.Round(request.ParcelData.South) / 4;
|
||||
int west = (int)Math.Round(request.ParcelData.West) / 4;
|
||||
|
||||
// Find all of the parcels within the given boundaries
|
||||
int xLen = east - west;
|
||||
int yLen = north - south;
|
||||
|
||||
for (int x = 0; x < xLen; x++)
|
||||
{
|
||||
for (int y = 0; y < yLen; y++)
|
||||
{
|
||||
if (west + x < 64 && south + y < 64)
|
||||
{
|
||||
int currentParcelID = parcelOverlay[(south + y) * 64 + (west + x)];
|
||||
if (!parcels.Contains(currentParcelID))
|
||||
parcels.Add(currentParcelID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParcelResult result = ParcelResult.NoData;
|
||||
if (parcels.Count == 1)
|
||||
result = ParcelResult.Single;
|
||||
else if (parcels.Count > 1)
|
||||
result = ParcelResult.Multiple;
|
||||
|
||||
for (int i = 0; i < parcels.Count; i++)
|
||||
SendParcelProperties(parcels[i], request.ParcelData.SequenceID, request.ParcelData.SnapSelection, result, agent);
|
||||
}
|
||||
|
||||
void ParcelPropertiesUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ParcelPropertiesUpdatePacket update = (ParcelPropertiesUpdatePacket)packet;
|
||||
|
||||
Parcel parcel;
|
||||
if (parcels.TryGetValue(update.ParcelData.LocalID, out parcel))
|
||||
{
|
||||
parcel.AuthBuyerID = update.ParcelData.AuthBuyerID;
|
||||
parcel.Category = (Parcel.ParcelCategory)update.ParcelData.Category;
|
||||
parcel.Desc = Utils.BytesToString(update.ParcelData.Desc);
|
||||
parcel.Flags = (Parcel.ParcelFlags)update.ParcelData.ParcelFlags;
|
||||
parcel.GroupID = update.ParcelData.GroupID;
|
||||
parcel.Landing = (Parcel.LandingType)update.ParcelData.LandingType;
|
||||
parcel.Media.MediaAutoScale = update.ParcelData.MediaAutoScale;
|
||||
parcel.Media.MediaID = update.ParcelData.MediaID;
|
||||
parcel.Media.MediaURL = Utils.BytesToString(update.ParcelData.MediaURL);
|
||||
parcel.MusicURL = Utils.BytesToString(update.ParcelData.MusicURL);
|
||||
parcel.Name = Utils.BytesToString(update.ParcelData.Name);
|
||||
parcel.PassHours = update.ParcelData.PassHours;
|
||||
parcel.PassPrice = update.ParcelData.PassPrice;
|
||||
parcel.SalePrice = update.ParcelData.SalePrice;
|
||||
parcel.SnapshotID = update.ParcelData.SnapshotID;
|
||||
parcel.UserLocation = update.ParcelData.UserLocation;
|
||||
parcel.UserLookAt = update.ParcelData.UserLookAt;
|
||||
|
||||
lock (parcels)
|
||||
parcels[parcel.LocalID] = parcel;
|
||||
|
||||
if (update.ParcelData.Flags != 0)
|
||||
SendParcelProperties(parcel.LocalID, 0, false, ParcelResult.Single, agent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Got a ParcelPropertiesUpdate for an unknown parcel " + update.ParcelData.LocalID,
|
||||
Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace Simian
|
||||
{
|
||||
public class PeriscopeTransferManager
|
||||
{
|
||||
public const string UPLOAD_DIR = "uploadedAssets";
|
||||
|
||||
ISceneProvider scene;
|
||||
GridClient client;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
|
||||
@@ -7,12 +7,25 @@ namespace Simian
|
||||
{
|
||||
public class PhysicsSimple : IExtension<ISceneProvider>, IPhysicsProvider
|
||||
{
|
||||
ISceneProvider scene;
|
||||
// Run our own frames per second limiter on top of the limiting done by ISceneProvider
|
||||
const int FRAMES_PER_SECOND = 10;
|
||||
|
||||
public float TimeDilation
|
||||
{
|
||||
get { return 1.0f; }
|
||||
}
|
||||
const float GRAVITY = 9.8f; //meters/sec
|
||||
const float WALK_SPEED = 3f; //meters/sec
|
||||
const float RUN_SPEED = 5f; //meters/sec
|
||||
const float FLY_SPEED = 10f; //meters/sec
|
||||
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
||||
const float FALL_FORGIVENESS = .25f; //fall buffer in meters
|
||||
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
||||
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec
|
||||
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
||||
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
||||
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
ISceneProvider scene;
|
||||
float elapsedSinceUpdate;
|
||||
|
||||
public PhysicsSimple()
|
||||
{
|
||||
@@ -21,6 +34,8 @@ namespace Simian
|
||||
public bool Start(ISceneProvider scene)
|
||||
{
|
||||
this.scene = scene;
|
||||
|
||||
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,6 +43,335 @@ namespace Simian
|
||||
{
|
||||
}
|
||||
|
||||
public void Update(float elapsedTime)
|
||||
{
|
||||
if (elapsedSinceUpdate >= 1f / (float)FRAMES_PER_SECOND)
|
||||
{
|
||||
elapsedTime = elapsedSinceUpdate;
|
||||
elapsedSinceUpdate = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsedSinceUpdate += elapsedTime;
|
||||
return;
|
||||
}
|
||||
|
||||
scene.ForEachAgent(
|
||||
delegate(Agent agent)
|
||||
{
|
||||
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
|
||||
return;
|
||||
|
||||
bool animsChanged = false;
|
||||
|
||||
// Create forward and left vectors from the current avatar rotation
|
||||
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.Rotation);
|
||||
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
||||
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
||||
|
||||
// Check control flags
|
||||
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
||||
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
|
||||
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
|
||||
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
|
||||
//bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
|
||||
//bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
|
||||
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
|
||||
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
|
||||
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
||||
//bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
|
||||
|
||||
// direction in which the avatar is trying to move
|
||||
Vector3 move = Vector3.Zero;
|
||||
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
||||
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
||||
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
||||
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
||||
if (heldUp) { move.Z += 1; }
|
||||
if (heldDown) { move.Z -= 1; }
|
||||
|
||||
// is the avatar trying to move?
|
||||
bool moving = move != Vector3.Zero;
|
||||
bool jumping = agent.TickJump != 0;
|
||||
|
||||
// 2-dimensional speed multipler
|
||||
float speed = elapsedTime * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
||||
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
||||
speed /= SQRT_TWO;
|
||||
|
||||
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
|
||||
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
agentPosition += (move * speed);
|
||||
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
|
||||
|
||||
if (!flying && newFloor != oldFloor)
|
||||
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
||||
|
||||
//HACK: distance from avatar center to the bottom of its feet
|
||||
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
|
||||
|
||||
float lowerLimit = newFloor + distanceFromFloor;
|
||||
|
||||
//"bridge" physics
|
||||
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
|
||||
{
|
||||
//start ray at our feet
|
||||
Vector3 rayStart = new Vector3(
|
||||
agent.Avatar.Prim.Position.X,
|
||||
agent.Avatar.Prim.Position.Y,
|
||||
agent.Avatar.Prim.Position.Z - distanceFromFloor
|
||||
);
|
||||
|
||||
//end ray at 0.01m below our feet
|
||||
Vector3 rayEnd = new Vector3(
|
||||
rayStart.X,
|
||||
rayStart.Y,
|
||||
rayStart.Z - 0.01f
|
||||
);
|
||||
|
||||
scene.ForEachObject(delegate(SimulationObject obj)
|
||||
{
|
||||
//HACK: check nearby objects (what did you expect, octree?)
|
||||
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
|
||||
{
|
||||
Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
|
||||
|
||||
if (collision != rayEnd) //we collided!
|
||||
{
|
||||
//check if we are any higher than before
|
||||
float height = collision.Z + distanceFromFloor;
|
||||
if (height > lowerLimit) lowerLimit = height;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Z acceleration resulting from gravity
|
||||
float gravity = 0f;
|
||||
|
||||
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
|
||||
|
||||
if (flying)
|
||||
{
|
||||
agent.TickFall = 0;
|
||||
agent.TickJump = 0;
|
||||
|
||||
//velocity falloff while flying
|
||||
agent.Avatar.Prim.Velocity.X *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.66f;
|
||||
agent.Avatar.Prim.Velocity.Z *= 0.33f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z == lowerLimit)
|
||||
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //flying horizontally
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
|
||||
{ //falling, floating, or landing from a jump
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
|
||||
{ //above water
|
||||
|
||||
//override controls while drifting
|
||||
move = Vector3.Zero;
|
||||
|
||||
//keep most of our horizontal inertia
|
||||
agent.Avatar.Prim.Velocity.X *= 0.975f;
|
||||
agent.Avatar.Prim.Velocity.Y *= 0.975f;
|
||||
|
||||
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
||||
|
||||
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.Velocity.Z >= 0f))
|
||||
{ //just started falling
|
||||
agent.TickFall = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
gravity = GRAVITY * fallElapsed * elapsedTime; //normal gravity
|
||||
|
||||
if (!jumping)
|
||||
{ //falling
|
||||
if (fallElapsed > FALL_DELAY)
|
||||
{ //falling long enough to trigger the animation
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
|
||||
{ //at the water line
|
||||
|
||||
gravity = 0f;
|
||||
agent.Avatar.Prim.Velocity *= 0.5f;
|
||||
agent.Avatar.Prim.Velocity.Z = 0f;
|
||||
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
|
||||
|
||||
if (move.Z > 0)
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.X != 0 || move.Y != 0)
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //underwater
|
||||
|
||||
gravity = 0f; //buoyant
|
||||
agent.Avatar.Prim.Velocity *= 0.5f * elapsedTime;
|
||||
agent.Avatar.Prim.Velocity.Z += 0.75f * elapsedTime;
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //on the ground
|
||||
|
||||
agent.TickFall = 0;
|
||||
|
||||
//friction
|
||||
agent.Avatar.Prim.Acceleration *= 0.2f;
|
||||
agent.Avatar.Prim.Velocity *= 0.2f;
|
||||
|
||||
agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
|
||||
if (move.Z > 0)
|
||||
{ //jumping
|
||||
if (!jumping)
|
||||
{ //begin prejump
|
||||
move.Z = 0; //override Z control
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.TickJump = Environment.TickCount;
|
||||
}
|
||||
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
|
||||
{ //start actual jump
|
||||
|
||||
if (agent.TickJump == -1)
|
||||
{
|
||||
//already jumping! end current jump
|
||||
agent.TickJump = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Prim.Velocity.Z = JUMP_IMPULSE_VERTICAL * elapsedTime;
|
||||
|
||||
agent.TickJump = -1; //flag that we are currently jumping
|
||||
}
|
||||
else move.Z = 0; //override Z control
|
||||
}
|
||||
|
||||
else
|
||||
{ //not jumping
|
||||
|
||||
agent.TickJump = 0;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //not walking
|
||||
|
||||
if (move.Z < 0)
|
||||
{ //crouchwalking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
scene.Avatars.SendAnimations(agent);
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * elapsedTime;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
if (moving)
|
||||
{
|
||||
agent.Avatar.Prim.Acceleration = move * speed;
|
||||
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
|
||||
agent.Avatar.Prim.Acceleration.Z = maxVel;
|
||||
}
|
||||
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
|
||||
|
||||
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
|
||||
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = -maxVel;
|
||||
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
|
||||
agent.Avatar.Prim.Velocity.Z = maxVel;
|
||||
|
||||
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
|
||||
|
||||
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
|
||||
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
|
||||
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj)
|
||||
{
|
||||
Vector3 closestPoint = rayEnd;
|
||||
@@ -80,6 +424,28 @@ namespace Simian
|
||||
}
|
||||
}
|
||||
|
||||
void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update)
|
||||
{
|
||||
// Recompute meshes for
|
||||
bool forceMeshing = false;
|
||||
bool forceTransform = false;
|
||||
|
||||
if ((update & UpdateFlags.Scale) != 0 ||
|
||||
(update & UpdateFlags.Position) != 0 ||
|
||||
(update & UpdateFlags.Rotation) != 0)
|
||||
{
|
||||
forceTransform = true;
|
||||
}
|
||||
|
||||
if ((update & UpdateFlags.PrimData) != 0)
|
||||
{
|
||||
forceMeshing = true;
|
||||
}
|
||||
|
||||
// TODO: This doesn't update children prims when their parents move
|
||||
obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user