2008-08-18 04:40:05 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2008-08-19 10:36:58 +00:00
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Runtime.InteropServices;
|
2008-08-18 04:40:05 +00:00
|
|
|
using System.Threading;
|
|
|
|
|
using OpenMetaverse;
|
2008-08-19 10:36:58 +00:00
|
|
|
using OpenMetaverse.Imaging;
|
2008-08-18 04:40:05 +00:00
|
|
|
using OpenMetaverse.Packets;
|
|
|
|
|
|
2008-08-21 05:14:16 +00:00
|
|
|
namespace Simian.Extensions
|
2008-08-18 04:40:05 +00:00
|
|
|
{
|
2008-09-04 21:09:44 +00:00
|
|
|
public class SceneManager : ISimianExtension, ISceneProvider
|
2008-08-18 04:40:05 +00:00
|
|
|
{
|
|
|
|
|
Simian server;
|
2008-09-04 21:09:44 +00:00
|
|
|
DoubleDictionary<uint, UUID, SimulationObject> sceneObjects = new DoubleDictionary<uint, UUID, SimulationObject>();
|
|
|
|
|
int currentLocalID = 1;
|
2008-09-06 18:02:38 +00:00
|
|
|
float[] heightmap = new float[256 * 256];
|
2008-09-04 21:09:44 +00:00
|
|
|
|
2008-09-08 18:23:16 +00:00
|
|
|
public event ObjectAddCallback OnObjectAdd;
|
|
|
|
|
public event ObjectRemoveCallback OnObjectRemove;
|
|
|
|
|
public event ObjectTransformCallback OnObjectTransform;
|
|
|
|
|
public event ObjectFlagsCallback OnObjectFlags;
|
|
|
|
|
public event ObjectModifyCallback OnObjectModify;
|
2008-09-06 18:02:38 +00:00
|
|
|
public event TerrainUpdatedCallback OnTerrainUpdated;
|
|
|
|
|
|
|
|
|
|
public float[] Heightmap
|
|
|
|
|
{
|
|
|
|
|
get { return heightmap; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value.Length != (256 * 256))
|
|
|
|
|
throw new ArgumentException("Heightmap must be 256x256");
|
|
|
|
|
heightmap = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-08-18 04:40:05 +00:00
|
|
|
|
2008-09-22 16:33:42 +00:00
|
|
|
public float WaterHeight { get { return 35f; } }
|
|
|
|
|
|
2008-08-18 04:40:05 +00:00
|
|
|
public SceneManager(Simian server)
|
|
|
|
|
{
|
|
|
|
|
this.server = server;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
2008-08-28 22:11:47 +00:00
|
|
|
server.UDP.RegisterPacketCallback(PacketType.CompleteAgentMovement, new PacketCallback(CompleteAgentMovementHandler));
|
2008-08-21 06:46:36 +00:00
|
|
|
LoadTerrain(server.DataDir + "heightmap.tga");
|
2008-08-18 04:40:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-14 08:12:11 +00:00
|
|
|
public bool ObjectAdd(object sender, Agent creator, SimulationObject obj, PrimFlags creatorFlags)
|
2008-09-04 21:09:44 +00:00
|
|
|
{
|
2008-09-28 21:28:10 +00:00
|
|
|
// Check if the object already exists in the scene
|
|
|
|
|
if (sceneObjects.ContainsKey(obj.Prim.ID))
|
|
|
|
|
{
|
|
|
|
|
Logger.Log(String.Format("Attempting to add duplicate object {0} to the scene",
|
|
|
|
|
obj.Prim.ID), Helpers.LogLevel.Warning);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-04 21:09:44 +00:00
|
|
|
// Assign a unique LocalID to this object
|
|
|
|
|
obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
|
|
|
|
|
|
2008-09-08 18:23:16 +00:00
|
|
|
if (OnObjectAdd != null)
|
|
|
|
|
{
|
2008-09-14 08:12:11 +00:00
|
|
|
OnObjectAdd(sender, creator, obj, creatorFlags);
|
2008-09-08 18:23:16 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-04 21:09:44 +00:00
|
|
|
// Add the object to the scene dictionary
|
|
|
|
|
sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj);
|
|
|
|
|
|
|
|
|
|
// Send an update out to the creator
|
|
|
|
|
ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle, 0,
|
2008-09-14 08:12:11 +00:00
|
|
|
obj.Prim.Flags | creatorFlags);
|
2008-09-04 21:09:44 +00:00
|
|
|
server.UDP.SendPacket(creator.AgentID, updateToOwner, PacketCategory.State);
|
|
|
|
|
|
|
|
|
|
// Send an update out to everyone else
|
|
|
|
|
ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle, 0,
|
|
|
|
|
obj.Prim.Flags);
|
|
|
|
|
lock (server.Agents)
|
|
|
|
|
{
|
|
|
|
|
foreach (Agent recipient in server.Agents.Values)
|
|
|
|
|
{
|
|
|
|
|
if (recipient != creator)
|
|
|
|
|
server.UDP.SendPacket(recipient.AgentID, updateToOthers, PacketCategory.State);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-09-06 18:02:38 +00:00
|
|
|
|
2008-09-08 18:23:16 +00:00
|
|
|
return true;
|
2008-09-04 21:09:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-08 18:23:16 +00:00
|
|
|
public bool ObjectRemove(object sender, SimulationObject obj)
|
2008-09-04 21:09:44 +00:00
|
|
|
{
|
2008-09-08 18:23:16 +00:00
|
|
|
if (OnObjectRemove != null)
|
|
|
|
|
{
|
|
|
|
|
OnObjectRemove(sender, obj);
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-04 21:09:44 +00:00
|
|
|
sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID);
|
|
|
|
|
|
|
|
|
|
KillObjectPacket kill = new KillObjectPacket();
|
|
|
|
|
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
|
|
|
|
|
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
|
|
|
|
|
kill.ObjectData[0].ID = obj.Prim.LocalID;
|
|
|
|
|
|
|
|
|
|
server.UDP.BroadcastPacket(kill, PacketCategory.State);
|
2008-09-06 18:02:38 +00:00
|
|
|
|
2008-09-08 18:23:16 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ObjectTransform(object sender, SimulationObject obj, Vector3 position,
|
|
|
|
|
Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity,
|
|
|
|
|
Vector3 scale)
|
|
|
|
|
{
|
|
|
|
|
if (OnObjectTransform != null)
|
|
|
|
|
{
|
|
|
|
|
OnObjectTransform(sender, obj, position, rotation, velocity,
|
|
|
|
|
acceleration, angularVelocity, scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the object
|
|
|
|
|
obj.Prim.Position = position;
|
|
|
|
|
obj.Prim.Rotation = rotation;
|
|
|
|
|
obj.Prim.Velocity = velocity;
|
|
|
|
|
obj.Prim.Acceleration = acceleration;
|
|
|
|
|
obj.Prim.AngularVelocity = angularVelocity;
|
|
|
|
|
obj.Prim.Scale = scale;
|
|
|
|
|
|
|
|
|
|
// Inform clients
|
|
|
|
|
BroadcastObjectUpdate(obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags)
|
|
|
|
|
{
|
|
|
|
|
if (OnObjectFlags != null)
|
2008-09-06 18:02:38 +00:00
|
|
|
{
|
2008-09-08 18:23:16 +00:00
|
|
|
OnObjectFlags(sender, obj, flags);
|
2008-09-06 18:02:38 +00:00
|
|
|
}
|
2008-09-08 18:23:16 +00:00
|
|
|
|
|
|
|
|
// Update the object
|
|
|
|
|
obj.Prim.Flags = flags;
|
|
|
|
|
|
|
|
|
|
// Inform clients
|
|
|
|
|
BroadcastObjectUpdate(obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data)
|
|
|
|
|
{
|
|
|
|
|
if (OnObjectModify != null)
|
|
|
|
|
{
|
|
|
|
|
OnObjectModify(sender, obj, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the object
|
|
|
|
|
obj.Prim.PrimData = data;
|
|
|
|
|
|
|
|
|
|
// Inform clients
|
|
|
|
|
BroadcastObjectUpdate(obj);
|
2008-09-04 21:09:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool TryGetObject(uint localID, out SimulationObject obj)
|
|
|
|
|
{
|
|
|
|
|
return sceneObjects.TryGetValue(localID, out obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool TryGetObject(UUID id, out SimulationObject obj)
|
|
|
|
|
{
|
|
|
|
|
return sceneObjects.TryGetValue(id, out obj);
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-08 18:23:16 +00:00
|
|
|
void BroadcastObjectUpdate(SimulationObject obj)
|
2008-09-04 21:09:44 +00:00
|
|
|
{
|
|
|
|
|
ObjectUpdatePacket update =
|
2008-09-08 18:23:16 +00:00
|
|
|
SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle, 0, obj.Prim.Flags);
|
2008-09-04 21:09:44 +00:00
|
|
|
|
|
|
|
|
server.UDP.BroadcastPacket(update, PacketCategory.State);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-18 04:40:05 +00:00
|
|
|
void CompleteAgentMovementHandler(Packet packet, Agent agent)
|
|
|
|
|
{
|
|
|
|
|
CompleteAgentMovementPacket request = (CompleteAgentMovementPacket)packet;
|
|
|
|
|
|
|
|
|
|
// Create a representation for this agent
|
|
|
|
|
Avatar avatar = new Avatar();
|
|
|
|
|
avatar.ID = agent.AgentID;
|
|
|
|
|
avatar.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
|
|
|
|
|
avatar.Position = new Vector3(128f, 128f, 25f);
|
|
|
|
|
avatar.Rotation = Quaternion.Identity;
|
2008-08-21 05:14:16 +00:00
|
|
|
avatar.Scale = new Vector3(0.45f, 0.6f, 1.9f);
|
2008-08-24 05:06:51 +00:00
|
|
|
avatar.PrimData.Material = Material.Flesh;
|
|
|
|
|
avatar.PrimData.PCode = PCode.Avatar;
|
2008-08-21 05:14:16 +00:00
|
|
|
|
|
|
|
|
// Create a default outfit for the avatar
|
2008-08-24 05:06:51 +00:00
|
|
|
Primitive.TextureEntry te = new Primitive.TextureEntry(new UUID("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"));
|
2008-08-21 05:14:16 +00:00
|
|
|
avatar.Textures = te;
|
2008-08-18 04:40:05 +00:00
|
|
|
|
2008-08-19 18:38:29 +00:00
|
|
|
// Set the avatar name
|
2008-08-19 01:29:22 +00:00
|
|
|
NameValue[] name = new NameValue[2];
|
2008-08-19 18:38:29 +00:00
|
|
|
name[0] = new NameValue("FirstName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite,
|
|
|
|
|
NameValue.SendtoType.SimViewer, agent.FirstName);
|
|
|
|
|
name[1] = new NameValue("LastName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite,
|
|
|
|
|
NameValue.SendtoType.SimViewer, agent.LastName);
|
2008-08-19 01:29:22 +00:00
|
|
|
avatar.NameValues = name;
|
|
|
|
|
|
2008-08-18 05:11:13 +00:00
|
|
|
// Link this avatar up with the corresponding agent
|
|
|
|
|
agent.Avatar = avatar;
|
2008-08-18 04:40:05 +00:00
|
|
|
|
2008-08-19 08:23:25 +00:00
|
|
|
// Give testers a provisionary balance of 1000L
|
|
|
|
|
agent.Balance = 1000;
|
|
|
|
|
|
2008-09-15 23:17:21 +00:00
|
|
|
// Add this avatar as an object in the scene
|
2008-09-28 21:28:10 +00:00
|
|
|
if (ObjectAdd(this, agent, new SimulationObject(agent.Avatar, server), PrimFlags.None))
|
|
|
|
|
{
|
|
|
|
|
// Send a response back to the client
|
|
|
|
|
AgentMovementCompletePacket complete = new AgentMovementCompletePacket();
|
|
|
|
|
complete.AgentData.AgentID = agent.AgentID;
|
|
|
|
|
complete.AgentData.SessionID = agent.SessionID;
|
|
|
|
|
complete.Data.LookAt = Vector3.UnitX;
|
|
|
|
|
complete.Data.Position = avatar.Position;
|
|
|
|
|
complete.Data.RegionHandle = server.RegionHandle;
|
|
|
|
|
complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now);
|
|
|
|
|
complete.SimData.ChannelVersion = Utils.StringToBytes("Simian");
|
|
|
|
|
|
|
|
|
|
server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Transaction);
|
|
|
|
|
|
|
|
|
|
// Send updates and appearances for every avatar to this new avatar
|
|
|
|
|
SynchronizeStateTo(agent);
|
|
|
|
|
|
|
|
|
|
//HACK: Notify everyone when someone logs on to the simulator
|
|
|
|
|
OnlineNotificationPacket online = new OnlineNotificationPacket();
|
|
|
|
|
online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1];
|
|
|
|
|
online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
|
|
|
|
|
online.AgentBlock[0].AgentID = agent.AgentID;
|
|
|
|
|
server.UDP.BroadcastPacket(online, PacketCategory.State);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger.Log("Received a CompleteAgentMovement from an avatar already in the scene, " +
|
|
|
|
|
agent.FullName, Helpers.LogLevel.Warning);
|
|
|
|
|
}
|
2008-09-04 21:09:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HACK: The reduction provider will deprecate this at some point
|
|
|
|
|
void SynchronizeStateTo(Agent agent)
|
|
|
|
|
{
|
2008-09-22 15:11:39 +00:00
|
|
|
// Send the parcel overlay
|
|
|
|
|
server.Parcels.SendParcelOverlay(agent);
|
|
|
|
|
|
2008-09-15 23:17:21 +00:00
|
|
|
// Send object updates for objects and avatars
|
2008-09-04 21:09:44 +00:00
|
|
|
sceneObjects.ForEach(delegate(SimulationObject obj)
|
|
|
|
|
{
|
|
|
|
|
ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(obj.Prim,
|
|
|
|
|
obj.Prim.RegionHandle, 0, obj.Prim.Flags);
|
|
|
|
|
server.UDP.SendPacket(agent.AgentID, update, PacketCategory.State);
|
|
|
|
|
});
|
|
|
|
|
|
2008-09-15 23:17:21 +00:00
|
|
|
// Send appearances for all avatars
|
|
|
|
|
lock (server.Agents)
|
|
|
|
|
{
|
|
|
|
|
foreach (Agent otherAgent in server.Agents.Values)
|
|
|
|
|
{
|
|
|
|
|
if (otherAgent != agent)
|
|
|
|
|
{
|
|
|
|
|
// Send appearances for this avatar
|
|
|
|
|
AvatarAppearancePacket appearance = AvatarManager.BuildAppearancePacket(otherAgent);
|
|
|
|
|
server.UDP.SendPacket(agent.AgentID, appearance, PacketCategory.State);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 21:00:51 +00:00
|
|
|
// Send terrain data
|
2008-08-19 08:23:25 +00:00
|
|
|
SendLayerData(agent);
|
2008-08-18 04:40:05 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-19 10:36:58 +00:00
|
|
|
void LoadTerrain(string mapFile)
|
2008-08-19 08:23:25 +00:00
|
|
|
{
|
2008-08-19 10:36:58 +00:00
|
|
|
if (File.Exists(mapFile))
|
|
|
|
|
{
|
2008-09-06 18:02:38 +00:00
|
|
|
lock (heightmap)
|
2008-08-19 11:03:05 +00:00
|
|
|
{
|
|
|
|
|
Bitmap bmp = LoadTGAClass.LoadTGA(mapFile);
|
|
|
|
|
|
|
|
|
|
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
|
|
|
|
BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
|
|
|
|
|
IntPtr ptr = bmpData.Scan0;
|
|
|
|
|
int bytes = bmpData.Stride * bmp.Height;
|
|
|
|
|
byte[] rgbValues = new byte[bytes];
|
|
|
|
|
Marshal.Copy(ptr, rgbValues, 0, bytes);
|
|
|
|
|
bmp.UnlockBits(bmpData);
|
|
|
|
|
|
2008-09-06 18:02:38 +00:00
|
|
|
for (int i = 1, pos = 0; i < heightmap.Length; i++, pos += 3)
|
|
|
|
|
heightmap[i] = (float)rgbValues[pos];
|
|
|
|
|
|
|
|
|
|
if (OnTerrainUpdated != null)
|
|
|
|
|
OnTerrainUpdated(this);
|
2008-08-19 11:03:05 +00:00
|
|
|
}
|
2008-08-19 10:36:58 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger.Log("Map file " + mapFile + " not found, defaulting to 25m", Helpers.LogLevel.Info);
|
2008-08-19 08:23:25 +00:00
|
|
|
|
2008-09-06 18:02:38 +00:00
|
|
|
server.Scene.Heightmap = new float[65536];
|
|
|
|
|
for (int i = 0; i < server.Scene.Heightmap.Length; i++)
|
|
|
|
|
server.Scene.Heightmap[i] = 25f;
|
2008-08-19 10:36:58 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SendLayerData(Agent agent)
|
|
|
|
|
{
|
2008-09-06 18:02:38 +00:00
|
|
|
lock (heightmap)
|
2008-08-19 08:23:25 +00:00
|
|
|
{
|
2008-08-19 11:03:05 +00:00
|
|
|
for (int y = 0; y < 16; y++)
|
2008-08-19 08:23:25 +00:00
|
|
|
{
|
2008-08-19 11:03:05 +00:00
|
|
|
for (int x = 0; x < 16; x++)
|
|
|
|
|
{
|
|
|
|
|
int[] patches = new int[1];
|
|
|
|
|
patches[0] = (y * 16) + x;
|
2008-09-06 18:02:38 +00:00
|
|
|
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap, patches);
|
2008-08-28 22:11:47 +00:00
|
|
|
server.UDP.SendPacket(agent.AgentID, layer, PacketCategory.Terrain);
|
2008-08-19 11:03:05 +00:00
|
|
|
}
|
2008-08-19 08:23:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-08-18 04:40:05 +00:00
|
|
|
}
|
|
|
|
|
}
|