From 0232f01a58a3c0a88e95c22589efec21f502f081 Mon Sep 17 00:00:00 2001 From: mingchen Date: Wed, 27 Jun 2007 19:43:46 +0000 Subject: *Moved all the classes into their own file from LLSDHelpers.cs *Some folder renaming to follow project Name *Updated prebuild.xml --- OpenSim/Region/Environment/Scenes/Scene.cs | 795 +++++++++++++++++++++++++++++ 1 file changed, 795 insertions(+) create mode 100644 OpenSim/Region/Environment/Scenes/Scene.cs (limited to 'OpenSim/Region/Environment/Scenes/Scene.cs') diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs new file mode 100644 index 0000000..ff54efa --- /dev/null +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -0,0 +1,795 @@ +/* +* Copyright (c) Contributors, http://www.openmetaverse.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ +using System; +using libsecondlife; +using libsecondlife.Packets; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.IO; +using System.Threading; +using System.Timers; +using OpenSim.Physics.Manager; +using OpenSim.Framework.Interfaces; +using OpenSim.Framework.Types; +using OpenSim.Framework.Inventory; +using OpenSim.Framework; +using OpenSim.Region.Environment.Scripting; +using OpenSim.Region.Terrain; +using OpenSim.Framework.Communications; +using OpenSim.Region.Caches; +using OpenSim.Region.Environment; +using OpenSim.Framework.Servers; + +namespace OpenSim.Region.Environment.Scenes +{ + public delegate bool FilterAvatarList(ScenePresence avatar); + + public partial class Scene : SceneBase, ILocalStorageReceiver, IScriptAPI + { + protected System.Timers.Timer m_heartbeatTimer = new System.Timers.Timer(); + protected Dictionary Avatars; + protected Dictionary Prims; + private PhysicsScene phyScene; + private float timeStep = 0.1f; + private Random Rand = new Random(); + private uint _primCount = 702000; + private int storageCount; + private Dictionary m_scriptHandlers; + private Dictionary m_scripts; + private Mutex updateLock; + + protected AuthenticateSessionsBase authenticateHandler; + protected RegionCommsListener regionCommsHost; + protected CommunicationsManager commsManager; + + protected Dictionary capsHandlers = new Dictionary(); + protected BaseHttpServer httpListener; + + public ParcelManager parcelManager; + public EstateManager estateManager; + public EventManager eventManager; + + #region Properties + /// + /// + /// + public PhysicsScene PhysScene + { + set + { + this.phyScene = value; + } + get + { + return (this.phyScene); + } + } + + #endregion + + #region Constructors + /// + /// Creates a new World class, and a region to go with it. + /// + /// Dictionary to contain client threads + /// Region Handle for this region + /// Region Name for this region + public Scene(Dictionary clientThreads, RegionInfo regInfo, AuthenticateSessionsBase authen, CommunicationsManager commsMan, AssetCache assetCach, BaseHttpServer httpServer) + { + try + { + updateLock = new Mutex(false); + this.authenticateHandler = authen; + this.commsManager = commsMan; + this.assetCache = assetCach; + m_clientThreads = clientThreads; + m_regInfo = regInfo; + m_regionHandle = m_regInfo.RegionHandle; + m_regionName = m_regInfo.RegionName; + this.m_datastore = m_regInfo.DataStore; + this.RegisterRegionWithComms(); + + parcelManager = new ParcelManager(this, this.m_regInfo); + estateManager = new EstateManager(this, this.m_regInfo); + + eventManager = new EventManager(); + + m_scriptHandlers = new Dictionary(); + m_scripts = new Dictionary(); + + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs - creating new entitities instance"); + Entities = new Dictionary(); + Avatars = new Dictionary(); + Prims = new Dictionary(); + + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs - creating LandMap"); + Terrain = new TerrainEngine(); + + ScenePresence.LoadAnims(); + this.httpListener = httpServer; + + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Error( "World.cs: Constructor failed with exception " + e.ToString()); + } + } + #endregion + + /// + /// + /// + public void StartTimer() + { + m_heartbeatTimer.Enabled = true; + m_heartbeatTimer.Interval = 100; + m_heartbeatTimer.Elapsed += new ElapsedEventHandler(this.Heartbeat); + } + + + #region Update Methods + + + /// + /// Performs per-frame updates regularly + /// + /// + /// + void Heartbeat(object sender, System.EventArgs e) + { + this.Update(); + } + + /// + /// Performs per-frame updates on the world, this should be the central world loop + /// + public override void Update() + { + updateLock.WaitOne(); + try + { + if (this.phyScene.IsThreaded) + { + this.phyScene.GetResults(); + + } + + foreach (libsecondlife.LLUUID UUID in Entities.Keys) + { + Entities[UUID].updateMovement(); + } + + lock (this.m_syncRoot) + { + this.phyScene.Simulate(timeStep); + } + + foreach (libsecondlife.LLUUID UUID in Entities.Keys) + { + Entities[UUID].update(); + } + + // New + eventManager.TriggerOnFrame(); + + // TODO: Obsolete - Phase out + foreach (ScriptHandler scriptHandler in m_scriptHandlers.Values) + { + scriptHandler.OnFrame(); + } + foreach (IScriptEngine scripteng in this.scriptEngines.Values) + { + scripteng.OnFrame(); + } + + //backup world data + this.storageCount++; + if (storageCount > 1200) //set to how often you want to backup + { + this.Backup(); + storageCount = 0; + } + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: Update() - Failed with exception " + e.ToString()); + } + updateLock.ReleaseMutex(); + + } + + /// + /// + /// + /// + public bool Backup() + { + /* + try + { + // Terrain backup routines + if (Terrain.tainted > 0) + { + Terrain.tainted = 0; + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs: Backup() - Terrain tainted, saving."); + localStorage.SaveMap(Terrain.getHeights1D()); + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs: Backup() - Terrain saved, informing Physics."); + lock (this.m_syncRoot) + { + phyScene.SetTerrain(Terrain.getHeights1D()); + } + } + + // Primitive backup routines + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs: Backup() - Backing up Primitives"); + foreach (libsecondlife.LLUUID UUID in Entities.Keys) + { + Entities[UUID].BackUp(); + } + + //Parcel backup routines + ParcelData[] parcels = new ParcelData[parcelManager.parcelList.Count]; + int i = 0; + foreach (OpenSim.Region.Environment.Parcel parcel in parcelManager.parcelList.Values) + { + parcels[i] = parcel.parcelData; + i++; + } + localStorage.SaveParcels(parcels); + + // Backup successful + return true; + } + catch (Exception e) + { + // Backup failed + OpenSim.Framework.Console.MainLog.Instance.WriteLine(OpenSim.Framework.Console.LogPriority.HIGH, "World.cs: Backup() - Backup Failed with exception " + e.ToString()); + return false; + } + */ + return true; + } + #endregion + + #region Regenerate Terrain + + /// + /// Rebuilds the terrain using a procedural algorithm + /// + public void RegenerateTerrain() + { + try + { + Terrain.hills(); + + lock (this.m_syncRoot) + { + this.phyScene.SetTerrain(Terrain.getHeights1D()); + } + this.localStorage.SaveMap(this.Terrain.getHeights1D()); + + foreach (IClientAPI client in m_clientThreads.Values) + { + this.SendLayerData(client); + } + + foreach (libsecondlife.LLUUID UUID in Entities.Keys) + { + Entities[UUID].LandRenegerated(); + } + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: RegenerateTerrain() - Failed with exception " + e.ToString()); + } + } + + /// + /// Rebuilds the terrain using a 2D float array + /// + /// 256,256 float array containing heights + public void RegenerateTerrain(float[,] newMap) + { + try + { + this.Terrain.setHeights2D(newMap); + lock (this.m_syncRoot) + { + this.phyScene.SetTerrain(this.Terrain.getHeights1D()); + } + this.localStorage.SaveMap(this.Terrain.getHeights1D()); + + foreach (IClientAPI client in m_clientThreads.Values) + { + this.SendLayerData(client); + } + + foreach (libsecondlife.LLUUID UUID in Entities.Keys) + { + Entities[UUID].LandRenegerated(); + } + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: RegenerateTerrain() - Failed with exception " + e.ToString()); + } + } + + /// + /// Rebuilds the terrain assuming changes occured at a specified point[?] + /// + /// ??? + /// ??? + /// ??? + public void RegenerateTerrain(bool changes, int pointx, int pointy) + { + try + { + if (changes) + { + /* Dont save here, rely on tainting system instead */ + + foreach (IClientAPI client in m_clientThreads.Values) + { + this.SendLayerData(pointx, pointy, client); + } + } + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: RegenerateTerrain() - Failed with exception " + e.ToString()); + } + } + + #endregion + + #region Load Terrain + /// + /// Loads the World heightmap + /// + /// + public override void LoadWorldMap() + { + try + { + float[] map = this.localStorage.LoadWorld(); + if (map == null) + { + if (string.IsNullOrEmpty(this.m_regInfo.estateSettings.terrainFile)) + { + Console.WriteLine("No default terrain, procedurally generating..."); + this.Terrain.hills(); + + this.localStorage.SaveMap(this.Terrain.getHeights1D()); + } + else + { + try + { + this.Terrain.loadFromFileF32(this.m_regInfo.estateSettings.terrainFile); + this.Terrain *= this.m_regInfo.estateSettings.terrainMultiplier; + } + catch + { + Console.WriteLine("Unable to load default terrain, procedurally generating instead..."); + Terrain.hills(); + } + this.localStorage.SaveMap(this.Terrain.getHeights1D()); + } + } + else + { + this.Terrain.setHeights1D(map); + } + + CreateTerrainTexture(); + + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: LoadWorldMap() - Failed with exception " + e.ToString()); + } + } + + /// + /// + /// + private void CreateTerrainTexture() + { + //create a texture asset of the terrain + byte[] data = this.Terrain.exportJpegImage("defaultstripe.png"); + this.m_regInfo.estateSettings.terrainImageID = LLUUID.Random(); + AssetBase asset = new AssetBase(); + asset.FullID = this.m_regInfo.estateSettings.terrainImageID; + asset.Data = data; + asset.Name = "terrainImage"; + asset.Type = 0; + this.assetCache.AddAsset(asset); + } + #endregion + + #region Primitives Methods + + + /// + /// Loads the World's objects + /// + public void LoadPrimsFromStorage() + { + try + { + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs: LoadPrimsFromStorage() - Loading primitives"); + this.localStorage.LoadPrimitives(this); + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: LoadPrimsFromStorage() - Failed with exception " + e.ToString()); + } + } + + /// + /// Loads a specific object from storage + /// + /// The object to load + public void PrimFromStorage(PrimData prim) + { + + } + + /// + /// + /// + /// + /// + public void AddNewPrim(Packet addPacket, IClientAPI agentClient) + { + AddNewPrim((ObjectAddPacket)addPacket, agentClient.AgentId); + } + + /// + /// + /// + /// + /// + public void AddNewPrim(ObjectAddPacket addPacket, LLUUID ownerID) + { + try + { + Primitive prim = new Primitive(m_regionHandle, this, addPacket, ownerID, this._primCount); + + this.Entities.Add(prim.uuid, prim); + this._primCount++; + + // Trigger event for listeners + eventManager.TriggerOnNewPrimitive(prim); + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: AddNewPrim() - Failed with exception " + e.ToString()); + } + } + + #endregion + + #region Add/Remove Avatar Methods + + /// + /// + /// + /// + /// + public override void AddNewClient(IClientAPI remoteClient, LLUUID agentID, bool child) + { + remoteClient.OnRegionHandShakeReply += this.SendLayerData; + //remoteClient.OnRequestWearables += new GenericCall(this.GetInitialPrims); + remoteClient.OnChatFromViewer += this.SimChat; + remoteClient.OnRequestWearables += this.InformClientOfNeighbours; + remoteClient.OnAddPrim += this.AddNewPrim; + remoteClient.OnUpdatePrimPosition += this.UpdatePrimPosition; + remoteClient.OnRequestMapBlocks += this.RequestMapBlocks; + remoteClient.OnTeleportLocationRequest += this.RequestTeleportLocation; + //remoteClient.OnObjectSelect += this.SelectPrim; + remoteClient.OnGrapUpdate += this.MoveObject; + + /* remoteClient.OnParcelPropertiesRequest += new ParcelPropertiesRequest(parcelManager.handleParcelPropertiesRequest); + remoteClient.OnParcelDivideRequest += new ParcelDivideRequest(parcelManager.handleParcelDivideRequest); + remoteClient.OnParcelJoinRequest += new ParcelJoinRequest(parcelManager.handleParcelJoinRequest); + remoteClient.OnParcelPropertiesUpdateRequest += new ParcelPropertiesUpdateRequest(parcelManager.handleParcelPropertiesUpdateRequest); + remoteClient.OnEstateOwnerMessage += new EstateOwnerMessageRequest(estateManager.handleEstateOwnerMessage); + */ + + ScenePresence newAvatar = null; + try + { + + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs:AddViewerAgent() - Creating new avatar for remote viewer agent"); + newAvatar = new ScenePresence(remoteClient, this, this.m_regInfo); + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs:AddViewerAgent() - Adding new avatar to world"); + OpenSim.Framework.Console.MainLog.Instance.Verbose( "World.cs:AddViewerAgent() - Starting RegionHandshake "); + + //newAvatar.SendRegionHandshake(); + this.estateManager.sendRegionHandshake(remoteClient); + + PhysicsVector pVec = new PhysicsVector(newAvatar.Pos.X, newAvatar.Pos.Y, newAvatar.Pos.Z); + lock (this.m_syncRoot) + { + newAvatar.PhysActor = this.phyScene.AddAvatar(pVec); + } + + lock (Entities) + { + if (!Entities.ContainsKey(agentID)) + { + this.Entities.Add(agentID, newAvatar); + } + else + { + Entities[agentID] = newAvatar; + } + } + lock (Avatars) + { + if (Avatars.ContainsKey(agentID)) + { + Avatars[agentID] = newAvatar; + } + else + { + this.Avatars.Add(agentID, newAvatar); + } + } + } + catch (Exception e) + { + OpenSim.Framework.Console.MainLog.Instance.Warn("World.cs: AddViewerAgent() - Failed with exception " + e.ToString()); + } + return; + } + + + + /// + /// + /// + /// + public override void RemoveClient(LLUUID agentID) + { + eventManager.TriggerOnRemovePresence(agentID); + + return; + } + #endregion + + #region Request Avatars List Methods + //The idea is to have a group of method that return a list of avatars meeting some requirement + // ie it could be all Avatars within a certain range of the calling prim/avatar. + + /// + /// Request a List of all Avatars in this World + /// + /// + public List RequestAvatarList() + { + List result = new List(); + + foreach (ScenePresence avatar in Avatars.Values) + { + result.Add(avatar); + } + + return result; + } + + /// + /// Request a filtered list of Avatars in this World + /// + /// + public List RequestAvatarList(FilterAvatarList filter) + { + List result = new List(); + + foreach (ScenePresence avatar in Avatars.Values) + { + if (filter(avatar)) + { + result.Add(avatar); + } + } + + return result; + } + + /// + /// Request a Avatar by UUID + /// + /// + /// + public ScenePresence RequestAvatar(LLUUID avatarID) + { + if (this.Avatars.ContainsKey(avatarID)) + { + return Avatars[avatarID]; + } + return null; + } + #endregion + + + #region RegionCommsHost + + /// + /// + /// + public void RegisterRegionWithComms() + { + GridInfo gridSettings = new GridInfo(); + this.regionCommsHost = this.commsManager.GridServer.RegisterRegion(this.m_regInfo,gridSettings); + if (this.regionCommsHost != null) + { + this.regionCommsHost.OnExpectUser += new ExpectUserDelegate(this.NewUserConnection); + this.regionCommsHost.OnAvatarCrossingIntoRegion += new AgentCrossing(this.AgentCrossing); + } + } + + /// + /// + /// + /// + /// + public void NewUserConnection(ulong regionHandle, AgentCircuitData agent) + { + // Console.WriteLine("World.cs - add new user connection"); + //should just check that its meant for this region + if (regionHandle == this.m_regInfo.RegionHandle) + { + if (agent.CapsPath != "") + { + //Console.WriteLine("new user, so creating caps handler for it"); + Capabilities.Caps cap = new Capabilities.Caps(this.assetCache, httpListener, this.m_regInfo.CommsIPListenAddr, 9000, agent.CapsPath, agent.AgentID); + cap.RegisterHandlers(); + this.capsHandlers.Add(agent.AgentID, cap); + } + this.authenticateHandler.AddNewCircuit(agent.circuitcode, agent); + } + } + + public void AgentCrossing(ulong regionHandle, libsecondlife.LLUUID agentID, libsecondlife.LLVector3 position) + { + if (regionHandle == this.m_regInfo.RegionHandle) + { + if (this.Avatars.ContainsKey(agentID)) + { + this.Avatars[agentID].MakeAvatar(position); + } + } + } + + /// + /// + /// + public void InformClientOfNeighbours(IClientAPI remoteClient) + { + // Console.WriteLine("informing client of neighbouring regions"); + List neighbours = this.commsManager.GridServer.RequestNeighbours(this.m_regInfo); + + //Console.WriteLine("we have " + neighbours.Count + " neighbouring regions"); + if (neighbours != null) + { + for (int i = 0; i < neighbours.Count; i++) + { + // Console.WriteLine("sending neighbours data"); + AgentCircuitData agent = remoteClient.RequestClientInfo(); + agent.BaseFolder = LLUUID.Zero; + agent.InventoryFolder = LLUUID.Zero; + agent.startpos = new LLVector3(128, 128, 70); + agent.child = true; + this.commsManager.InterRegion.InformRegionOfChildAgent(neighbours[i].RegionHandle, agent); + remoteClient.InformClientOfNeighbour(neighbours[i].RegionHandle, System.Net.IPAddress.Parse(neighbours[i].CommsIPListenAddr), (ushort)neighbours[i].CommsIPListenPort); + //this.capsHandlers[remoteClient.AgentId].CreateEstablishAgentComms("", System.Net.IPAddress.Parse(neighbours[i].CommsIPListenAddr) + ":" + neighbours[i].CommsIPListenPort); + } + } + } + + /// + /// + /// + /// + /// + public RegionInfo RequestNeighbouringRegionInfo(ulong regionHandle) + { + return this.commsManager.GridServer.RequestNeighbourInfo(regionHandle); + } + + /// + /// + /// + /// + /// + /// + /// + public void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY) + { + List mapBlocks; + mapBlocks = this.commsManager.GridServer.RequestNeighbourMapBlocks(minX, minY, maxX, maxY); + remoteClient.SendMapBlock(mapBlocks); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void RequestTeleportLocation(IClientAPI remoteClient, ulong regionHandle, LLVector3 position, LLVector3 lookAt, uint flags) + { + if (regionHandle == this.m_regionHandle) + { + if (this.Avatars.ContainsKey(remoteClient.AgentId)) + { + remoteClient.SendTeleportLocationStart(); + remoteClient.SendLocalTeleport(position, lookAt, flags); + this.Avatars[remoteClient.AgentId].Teleport(position); + } + } + else + { + RegionInfo reg = this.RequestNeighbouringRegionInfo(regionHandle); + if (reg != null) + { + remoteClient.SendTeleportLocationStart(); + AgentCircuitData agent = remoteClient.RequestClientInfo(); + agent.BaseFolder = LLUUID.Zero; + agent.InventoryFolder = LLUUID.Zero; + agent.startpos = new LLVector3(128, 128, 70); + agent.child = true; + this.commsManager.InterRegion.InformRegionOfChildAgent(regionHandle, agent); + this.commsManager.InterRegion.ExpectAvatarCrossing(regionHandle, remoteClient.AgentId, position); + remoteClient.SendRegionTeleport(regionHandle, 13, reg.CommsIPListenAddr, (ushort)reg.CommsIPListenPort, 4, (1 << 4)); + } + //remoteClient.SendTeleportCancel(); + } + } + + /// + /// + /// + /// + /// + /// + public bool InformNeighbourOfCrossing(ulong regionhandle, LLUUID agentID, LLVector3 position) + { + return this.commsManager.InterRegion.ExpectAvatarCrossing(regionhandle, agentID, position); + } + + #endregion + } +} -- cgit v1.1