From 180be7de07014aa33bc6066f12a0819b731c1c9d Mon Sep 17 00:00:00 2001 From: Dr Scofield Date: Tue, 10 Feb 2009 13:10:57 +0000 Subject: this is step 2 of 2 of the OpenSim.Region.Environment refactor. NOTHING has been deleted or moved off to forge at this point. what has happened is that OpenSim.Region.Environment.Modules has been split in two: - OpenSim.Region.CoreModules: all those modules that are either directly or indirectly referenced from other OpenSim packages, or that provide functionality that the OpenSim developer community considers core functionality: CoreModules/Agent/AssetTransaction CoreModules/Agent/Capabilities CoreModules/Agent/TextureDownload CoreModules/Agent/TextureSender CoreModules/Agent/TextureSender/Tests CoreModules/Agent/Xfer CoreModules/Avatar/AvatarFactory CoreModules/Avatar/Chat/ChatModule CoreModules/Avatar/Combat CoreModules/Avatar/Currency/SampleMoney CoreModules/Avatar/Dialog CoreModules/Avatar/Friends CoreModules/Avatar/Gestures CoreModules/Avatar/Groups CoreModules/Avatar/InstantMessage CoreModules/Avatar/Inventory CoreModules/Avatar/Inventory/Archiver CoreModules/Avatar/Inventory/Transfer CoreModules/Avatar/Lure CoreModules/Avatar/ObjectCaps CoreModules/Avatar/Profiles CoreModules/Communications/Local CoreModules/Communications/REST CoreModules/Framework/EventQueue CoreModules/Framework/InterfaceCommander CoreModules/Hypergrid CoreModules/InterGrid CoreModules/Scripting/DynamicTexture CoreModules/Scripting/EMailModules CoreModules/Scripting/HttpRequest CoreModules/Scripting/LoadImageURL CoreModules/Scripting/VectorRender CoreModules/Scripting/WorldComm CoreModules/Scripting/XMLRPC CoreModules/World/Archiver CoreModules/World/Archiver/Tests CoreModules/World/Estate CoreModules/World/Land CoreModules/World/Permissions CoreModules/World/Serialiser CoreModules/World/Sound CoreModules/World/Sun CoreModules/World/Terrain CoreModules/World/Terrain/DefaultEffects CoreModules/World/Terrain/DefaultEffects/bin CoreModules/World/Terrain/DefaultEffects/bin/Debug CoreModules/World/Terrain/Effects CoreModules/World/Terrain/FileLoaders CoreModules/World/Terrain/FloodBrushes CoreModules/World/Terrain/PaintBrushes CoreModules/World/Terrain/Tests CoreModules/World/Vegetation CoreModules/World/Wind CoreModules/World/WorldMap - OpenSim.Region.OptionalModules: all those modules that are not core modules: OptionalModules/Avatar/Chat/IRC-stuff OptionalModules/Avatar/Concierge OptionalModules/Avatar/Voice/AsterixVoice OptionalModules/Avatar/Voice/SIPVoice OptionalModules/ContentManagementSystem OptionalModules/Grid/Interregion OptionalModules/Python OptionalModules/SvnSerialiser OptionalModules/World/NPC OptionalModules/World/TreePopulator --- .../OptionalModules/Avatar/Chat/ChannelState.cs | 628 ++++++++++++ .../OptionalModules/Avatar/Chat/IRCBridgeModule.cs | 219 ++++ .../OptionalModules/Avatar/Chat/IRCConnector.cs | 887 ++++++++++++++++ .../OptionalModules/Avatar/Chat/RegionState.cs | 424 ++++++++ .../Avatar/Concierge/ConciergeModule.cs | 604 +++++++++++ .../Avatar/Concierge/ConciergeServer.py | 130 +++ .../Voice/AsterixVoice/AsteriskVoiceModule.cs | 292 ++++++ .../Avatar/Voice/SIPVoice/SIPVoiceModule.cs | 202 ++++ .../ContentManagementSystem/AuraMetaEntity.cs | 161 +++ .../ContentManagementSystem/BeamMetaEntity.cs | 139 +++ .../ContentManagementSystem/CMController.cs | 757 ++++++++++++++ .../ContentManagementSystem/CMEntityCollection.cs | 193 ++++ .../ContentManagementSystem/CMModel.cs | 362 +++++++ .../ContentManagementSystem/CMView.cs | 206 ++++ .../ContentManagementEntity.cs | 383 +++++++ .../ContentManagementModule.cs | 163 +++ .../ContentManagementSystem/FileSystemDatabase.cs | 317 ++++++ .../ContentManagementSystem/GitDatabase.cs | 167 +++ .../ContentManagementSystem/IContentDatabase.cs | 94 ++ .../ContentManagementSystem/MetaEntity.cs | 274 +++++ .../ContentManagementSystem/PointMetaEntity.cs | 116 +++ .../OptionalModules/ContentManagementSystem/README | 52 + .../SceneObjectGroupDiff.cs | 218 ++++ .../Grid/Interregion/IInterregionModule.cs | 43 + .../Grid/Interregion/InterregionModule.cs | 199 ++++ .../Grid/Interregion/RemotingObject.cs | 77 ++ .../OptionalModules/Python/PythonAPI/Console.cs | 48 + .../Region/OptionalModules/Python/PythonModule.cs | 71 ++ OpenSim/Region/OptionalModules/README | 9 + .../SvnSerialiser/Properties/AssemblyInfo.cs | 62 ++ .../SvnSerialiser/SvnBackupModule.cs | 396 ++++++++ .../Region/OptionalModules/World/NPC/NPCAvatar.cs | 1060 ++++++++++++++++++++ .../Region/OptionalModules/World/NPC/NPCModule.cs | 68 ++ .../World/TreePopulator/TreePopulatorModule.cs | 304 ++++++ 34 files changed, 9325 insertions(+) create mode 100644 OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/Chat/IRCBridgeModule.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs create mode 100755 OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py create mode 100644 OpenSim/Region/OptionalModules/Avatar/Voice/AsterixVoice/AsteriskVoiceModule.cs create mode 100644 OpenSim/Region/OptionalModules/Avatar/Voice/SIPVoice/SIPVoiceModule.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/AuraMetaEntity.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/BeamMetaEntity.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/CMEntityCollection.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/CMModel.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/CMView.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementEntity.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementModule.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/FileSystemDatabase.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/GitDatabase.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/IContentDatabase.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/PointMetaEntity.cs create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/README create mode 100644 OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs create mode 100644 OpenSim/Region/OptionalModules/Grid/Interregion/IInterregionModule.cs create mode 100644 OpenSim/Region/OptionalModules/Grid/Interregion/InterregionModule.cs create mode 100644 OpenSim/Region/OptionalModules/Grid/Interregion/RemotingObject.cs create mode 100644 OpenSim/Region/OptionalModules/Python/PythonAPI/Console.cs create mode 100644 OpenSim/Region/OptionalModules/Python/PythonModule.cs create mode 100644 OpenSim/Region/OptionalModules/README create mode 100644 OpenSim/Region/OptionalModules/SvnSerialiser/Properties/AssemblyInfo.cs create mode 100644 OpenSim/Region/OptionalModules/SvnSerialiser/SvnBackupModule.cs create mode 100644 OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs create mode 100644 OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs create mode 100644 OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs (limited to 'OpenSim/Region/OptionalModules') diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs new file mode 100644 index 0000000..167f0cc --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs @@ -0,0 +1,628 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.Avatar.Chat +{ + + // An instance of this class exists for each unique combination of + // IRC chat interface characteristics, as determined by the supplied + // configuration file. + + internal class ChannelState + { + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static Regex arg = new Regex(@"\[[^\[\]]*\]"); + private static int _idk_ = 0; + private static int DEBUG_CHANNEL = 2147483647; + + // These are the IRC Connector configurable parameters with hard-wired + // default values (retained for compatability). + + internal string Server = null; + internal string Password = null; + internal string IrcChannel = null; + internal string BaseNickname = "OSimBot"; + internal uint Port = 6667; + internal string User = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot"; + + internal bool ClientReporting = true; + internal bool RelayChat = true; + internal bool RelayPrivateChannels = false; + internal int RelayChannel = 1; + internal List ValidInWorldChannels = new List(); + + // Connector agnostic parameters. These values are NOT shared with the + // connector and do not differentiate at an IRC level + + internal string PrivateMessageFormat = "PRIVMSG {0} :<{2}> {1} {3}"; + internal string NoticeMessageFormat = "PRIVMSG {0} :<{2}> {3}"; + internal int RelayChannelOut = -1; + internal bool RandomizeNickname = true; + internal bool CommandsEnabled = false; + internal int CommandChannel = -1; + internal int ConnectDelay = 10; + internal int PingDelay = 15; + internal string DefaultZone = "Sim"; + + internal string _accessPassword = String.Empty; + internal Regex AccessPasswordRegex = null; + internal string AccessPassword + { + get { return _accessPassword; } + set + { + _accessPassword = value; + AccessPasswordRegex = new Regex(String.Format(@"^{0},\s*(?[^,]+),\s*(?.+)$", _accessPassword), + RegexOptions.Compiled); + } + } + + + + // IRC connector reference + + internal IRCConnector irc = null; + + internal int idn = _idk_++; + + // List of regions dependent upon this connection + + internal List clientregions = new List(); + + // Needed by OpenChannel + + internal ChannelState() + { + } + + // This constructor is used by the Update* methods. A copy of the + // existing channel state is created, and distinguishing characteristics + // are copied across. + + internal ChannelState(ChannelState model) + { + Server = model.Server; + Password = model.Password; + IrcChannel = model.IrcChannel; + Port = model.Port; + BaseNickname = model.BaseNickname; + RandomizeNickname = model.RandomizeNickname; + User = model.User; + CommandsEnabled = model.CommandsEnabled; + CommandChannel = model.CommandChannel; + RelayChat = model.RelayChat; + RelayPrivateChannels = model.RelayPrivateChannels; + RelayChannelOut = model.RelayChannelOut; + RelayChannel = model.RelayChannel; + ValidInWorldChannels = model.ValidInWorldChannels; + PrivateMessageFormat = model.PrivateMessageFormat; + NoticeMessageFormat = model.NoticeMessageFormat; + ClientReporting = model.ClientReporting; + AccessPassword = model.AccessPassword; + DefaultZone = model.DefaultZone; + ConnectDelay = model.ConnectDelay; + PingDelay = model.PingDelay; + } + + // Read the configuration file, performing variable substitution and any + // necessary aliasing. See accompanying documentation for how this works. + // If you don't need variables, then this works exactly as before. + // If either channel or server are not specified, the request fails. + + internal static void OpenChannel(RegionState rs, IConfig config) + { + + // Create a new instance of a channel. This may not actually + // get used if an equivalent channel already exists. + + ChannelState cs = new ChannelState(); + + // Read in the configuration file and filter everything for variable + // subsititution. + + m_log.DebugFormat("[IRC-Channel-{0}] Initial request by Region {1} to connect to IRC", cs.idn, rs.Region); + + cs.Server = Substitute(rs, config.GetString("server", null)); + m_log.DebugFormat("[IRC-Channel-{0}] Server : <{1}>", cs.idn, cs.Server); + cs.Password = Substitute(rs, config.GetString("password", null)); + // probably not a good idea to put a password in the log file + cs.IrcChannel = Substitute(rs, config.GetString("channel", null)); + m_log.DebugFormat("[IRC-Channel-{0}] IrcChannel : <{1}>", cs.idn, cs.IrcChannel); + cs.Port = Convert.ToUInt32(Substitute(rs, config.GetString("port", Convert.ToString(cs.Port)))); + m_log.DebugFormat("[IRC-Channel-{0}] Port : <{1}>", cs.idn, cs.Port); + cs.BaseNickname = Substitute(rs, config.GetString("nick", cs.BaseNickname)); + m_log.DebugFormat("[IRC-Channel-{0}] BaseNickname : <{1}>", cs.idn, cs.BaseNickname); + cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("randomize_nick", Convert.ToString(cs.RandomizeNickname)))); + m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname); + cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("nicknum", Convert.ToString(cs.RandomizeNickname)))); + m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname); + cs.User = Substitute(rs, config.GetString("username", cs.User)); + m_log.DebugFormat("[IRC-Channel-{0}] User : <{1}>", cs.idn, cs.User); + cs.CommandsEnabled = Convert.ToBoolean(Substitute(rs, config.GetString("commands_enabled", Convert.ToString(cs.CommandsEnabled)))); + m_log.DebugFormat("[IRC-Channel-{0}] CommandsEnabled : <{1}>", cs.idn, cs.CommandsEnabled); + cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("commandchannel", Convert.ToString(cs.CommandChannel)))); + m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel); + cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("command_channel", Convert.ToString(cs.CommandChannel)))); + m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel); + cs.RelayChat = Convert.ToBoolean(Substitute(rs, config.GetString("relay_chat", Convert.ToString(cs.RelayChat)))); + m_log.DebugFormat("[IRC-Channel-{0}] RelayChat : <{1}>", cs.idn, cs.RelayChat); + cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("relay_private_channels", Convert.ToString(cs.RelayPrivateChannels)))); + m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels); + cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("useworldcomm", Convert.ToString(cs.RelayPrivateChannels)))); + m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels); + cs.RelayChannelOut = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_out", Convert.ToString(cs.RelayChannelOut)))); + m_log.DebugFormat("[IRC-Channel-{0}] RelayChannelOut : <{1}>", cs.idn, cs.RelayChannelOut); + cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_in", Convert.ToString(cs.RelayChannel)))); + m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel); + cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("inchannel", Convert.ToString(cs.RelayChannel)))); + m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel); + cs.PrivateMessageFormat = Substitute(rs, config.GetString("msgformat", cs.PrivateMessageFormat)); + m_log.DebugFormat("[IRC-Channel-{0}] PrivateMessageFormat : <{1}>", cs.idn, cs.PrivateMessageFormat); + cs.NoticeMessageFormat = Substitute(rs, config.GetString("noticeformat", cs.NoticeMessageFormat)); + m_log.DebugFormat("[IRC-Channel-{0}] NoticeMessageFormat : <{1}>", cs.idn, cs.NoticeMessageFormat); + cs.ClientReporting = Convert.ToInt32(Substitute(rs, config.GetString("verbosity", cs.ClientReporting?"1":"0"))) > 0; + m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting); + cs.ClientReporting = Convert.ToBoolean(Substitute(rs, config.GetString("report_clients", Convert.ToString(cs.ClientReporting)))); + m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting); + cs.DefaultZone = Substitute(rs, config.GetString("fallback_region", cs.DefaultZone)); + m_log.DebugFormat("[IRC-Channel-{0}] DefaultZone : <{1}>", cs.idn, cs.DefaultZone); + cs.ConnectDelay = Convert.ToInt32(Substitute(rs, config.GetString("connect_delay", Convert.ToString(cs.ConnectDelay)))); + m_log.DebugFormat("[IRC-Channel-{0}] ConnectDelay : <{1}>", cs.idn, cs.ConnectDelay); + cs.PingDelay = Convert.ToInt32(Substitute(rs, config.GetString("ping_delay", Convert.ToString(cs.PingDelay)))); + m_log.DebugFormat("[IRC-Channel-{0}] PingDelay : <{1}>", cs.idn, cs.PingDelay); + cs.AccessPassword = Substitute(rs, config.GetString("access_password", cs.AccessPassword)); + m_log.DebugFormat("[IRC-Channel-{0}] AccessPassword : <{1}>", cs.idn, cs.AccessPassword); + + + // Fail if fundamental information is still missing + + if (cs.Server == null || cs.IrcChannel == null || cs.BaseNickname == null || cs.User == null) + throw new Exception(String.Format("[IRC-Channel-{0}] Invalid configuration for region {1}", cs.idn, rs.Region)); + + m_log.InfoFormat("[IRC-Channel-{0}] Configuration for Region {1} is valid", cs.idn, rs.Region); + m_log.InfoFormat("[IRC-Channel-{0}] Server = {1}", cs.idn, cs.Server); + m_log.InfoFormat("[IRC-Channel-{0}] Channel = {1}", cs.idn, cs.IrcChannel); + m_log.InfoFormat("[IRC-Channel-{0}] Port = {1}", cs.idn, cs.Port); + m_log.InfoFormat("[IRC-Channel-{0}] Nickname = {1}", cs.idn, cs.BaseNickname); + m_log.InfoFormat("[IRC-Channel-{0}] User = {1}", cs.idn, cs.User); + + // Set the channel state for this region + + if (cs.RelayChat) + { + cs.ValidInWorldChannels.Add(0); + cs.ValidInWorldChannels.Add(DEBUG_CHANNEL); + } + + if (cs.RelayPrivateChannels) + cs.ValidInWorldChannels.Add(cs.RelayChannelOut); + + rs.cs = Integrate(rs, cs); + + } + + // An initialized channel state instance is passed in. If an identical + // channel state instance already exists, then the existing instance + // is used to replace the supplied value. + // If the instance matches with respect to IRC, then the underlying + // IRCConnector is assigned to the supplied channel state and the + // updated value is returned. + // If there is no match, then the supplied instance is completed by + // creating and assigning an instance of an IRC connector. + + private static ChannelState Integrate(RegionState rs, ChannelState p_cs) + { + + ChannelState cs = p_cs; + + // Check to see if we have an existing server/channel setup that can be used + // In the absence of variable substitution this will always resolve to the + // same ChannelState instance, and the table will only contains a single + // entry, so the performance considerations for the existing behavior are + // zero. Only the IRC connector is shared, the ChannelState still contains + // values that, while independent of the IRC connetion, do still distinguish + // this region's behavior. + + lock (IRCBridgeModule.m_channels) + { + + foreach (ChannelState xcs in IRCBridgeModule.m_channels) + { + if (cs.IsAPerfectMatchFor(xcs)) + { + m_log.DebugFormat("[IRC-Channel-{0}] Channel state matched", cs.idn); + cs = xcs; + break; + } + if (cs.IsAConnectionMatchFor(xcs)) + { + m_log.DebugFormat("[IRC-Channel-{0}] Channel matched", cs.idn); + cs.irc = xcs.irc; + break; + } + } + + } + + // No entry was found, so this is going to be a new entry. + + if (cs.irc == null) + { + + m_log.DebugFormat("[IRC-Channel-{0}] New channel required", cs.idn); + + if ((cs.irc = new IRCConnector(cs)) != null) + { + + IRCBridgeModule.m_channels.Add(cs); + + m_log.InfoFormat("[IRC-Channel-{0}] New channel initialized for {1}, nick: {2}, commands {3}, private channels {4}", + cs.idn, rs.Region, cs.DefaultZone, + cs.CommandsEnabled ? "enabled" : "not enabled", + cs.RelayPrivateChannels ? "relayed" : "not relayed"); + } + else + { + string txt = String.Format("[IRC-Channel-{0}] Region {1} failed to connect to channel {2} on server {3}:{4}", + cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port); + m_log.Error(txt); + throw new Exception(txt); + } + } + else + { + m_log.InfoFormat("[IRC-Channel-{0}] Region {1} reusing existing connection to channel {2} on server {3}:{4}", + cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port); + } + + m_log.InfoFormat("[IRC-Channel-{0}] Region {1} associated with channel {2} on server {3}:{4}", + cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port); + + // We're finally ready to commit ourselves + + + return cs; + + } + + // These routines allow differentiating changes to + // the underlying channel state. If necessary, a + // new channel state will be created. + + internal ChannelState UpdateServer(RegionState rs, string server) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.Server = server; + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + internal ChannelState UpdatePort(RegionState rs, string port) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.Port = Convert.ToUInt32(port); + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + internal ChannelState UpdateChannel(RegionState rs, string channel) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.IrcChannel = channel; + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + internal ChannelState UpdateNickname(RegionState rs, string nickname) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.BaseNickname = nickname; + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + internal ChannelState UpdateClientReporting(RegionState rs, string cr) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.ClientReporting = Convert.ToBoolean(cr); + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + internal ChannelState UpdateRelayIn(RegionState rs, string channel) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.RelayChannel = Convert.ToInt32(channel); + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + internal ChannelState UpdateRelayOut(RegionState rs, string channel) + { + RemoveRegion(rs); + ChannelState cs = new ChannelState(this); + cs.RelayChannelOut = Convert.ToInt32(channel); + cs = Integrate(rs, cs); + cs.AddRegion(rs); + return cs; + } + + // Determine whether or not this is a 'new' channel. Only those + // attributes that uniquely distinguish an IRC connection should + // be included here (and only those attributes should really be + // in the ChannelState structure) + + private bool IsAConnectionMatchFor(ChannelState cs) + { + return ( + Server == cs.Server && + IrcChannel == cs.IrcChannel && + Port == cs.Port && + BaseNickname == cs.BaseNickname && + User == cs.User + ); + } + + // This level of obsessive matching allows us to produce + // a minimal overhead int he case of a server which does + // need to differentiate IRC at a region level. + + private bool IsAPerfectMatchFor(ChannelState cs) + { + return ( IsAConnectionMatchFor(cs) && + RelayChannelOut == cs.RelayChannelOut && + PrivateMessageFormat == cs.PrivateMessageFormat && + NoticeMessageFormat == cs.NoticeMessageFormat && + RandomizeNickname == cs.RandomizeNickname && + AccessPassword == cs.AccessPassword && + CommandsEnabled == cs.CommandsEnabled && + CommandChannel == cs.CommandChannel && + DefaultZone == cs.DefaultZone && + RelayPrivateChannels == cs.RelayPrivateChannels && + RelayChannel == cs.RelayChannel && + RelayChat == cs.RelayChat && + ClientReporting == cs.ClientReporting + ); + } + + // This function implements the variable substitution mechanism + // for the configuration values. Each string read from the + // configuration file is scanned for '[...]' enclosures. Each + // one that is found is replaced by either a runtime variable + // (%xxx) or an existing configuration key. When no further + // substitution is possible, the remaining string is returned + // to the caller. This allows for arbitrarily nested + // enclosures. + + private static string Substitute(RegionState rs, string instr) + { + + string result = instr; + + if (result == null || result.Length == 0) + return result; + + // Repeatedly scan the string until all possible + // substitutions have been performed. + + // m_log.DebugFormat("[IRC-Channel] Parse[1]: {0}", result); + + while (arg.IsMatch(result)) + { + + string vvar = arg.Match(result).ToString(); + string var = vvar.Substring(1,vvar.Length-2).Trim(); + + switch (var.ToLower()) + { + case "%region" : + result = result.Replace(vvar, rs.Region); + break; + case "%host" : + result = result.Replace(vvar, rs.Host); + break; + case "%master1" : + result = result.Replace(vvar, rs.MA1); + break; + case "%master2" : + result = result.Replace(vvar, rs.MA2); + break; + case "%locx" : + result = result.Replace(vvar, rs.LocX); + break; + case "%locy" : + result = result.Replace(vvar, rs.LocY); + break; + case "%k" : + result = result.Replace(vvar, rs.IDK); + break; + default : + result = result.Replace(vvar, rs.config.GetString(var,var)); + break; + } + // m_log.DebugFormat("[IRC-Channel] Parse[2]: {0}", result); + } + + // m_log.DebugFormat("[IRC-Channel] Parse[3]: {0}", result); + return result; + + } + + public void Close() + { + m_log.InfoFormat("[IRC-Channel-{0}] Closing channel <{1}> to server <{2}:{3}>", + idn, IrcChannel, Server, Port); + m_log.InfoFormat("[IRC-Channel-{0}] There are {1} active clients", + idn, clientregions.Count); + irc.Close(); + } + + public void Open() + { + m_log.InfoFormat("[IRC-Channel-{0}] Opening channel <{1}> to server <{2}:{3}>", + idn, IrcChannel, Server, Port); + + irc.Open(); + + } + + // These are called by each region that attaches to this channel. The call affects + // only the relationship of the region with the channel. Not the channel to IRC + // relationship (unless it is closed and we want it open). + + public void Open(RegionState rs) + { + AddRegion(rs); + Open(); + } + + // Close is called to ensure that the IRC session is terminated if this is the + // only client. + + public void Close(RegionState rs) + { + RemoveRegion(rs); + lock (IRCBridgeModule.m_channels) + { + if (clientregions.Count == 0) + { + Close(); + IRCBridgeModule.m_channels.Remove(this); + m_log.InfoFormat("[IRC-Channel-{0}] Region {1} is last user of channel <{2}> to server <{3}:{4}>", + idn, rs.Region, IrcChannel, Server, Port); + m_log.InfoFormat("[IRC-Channel-{0}] Removed", idn); + } + } + } + + // Add a client region to this channel if it is not already known + + public void AddRegion(RegionState rs) + { + m_log.InfoFormat("[IRC-Channel-{0}] Adding region {1} to channel <{2}> to server <{3}:{4}>", + idn, rs.Region, IrcChannel, Server, Port); + if (!clientregions.Contains(rs)) + { + clientregions.Add(rs); + lock (irc) irc.depends++; + } + } + + // Remove a client region from the channel. If this is the last + // region, then clean up the channel. The connector will clean itself + // up if it finds itself about to be GC'd. + + public void RemoveRegion(RegionState rs) + { + + m_log.InfoFormat("[IRC-Channel-{0}] Removing region {1} from channel <{2} to server <{3}:{4}>", + idn, rs.Region, IrcChannel, Server, Port); + + if (clientregions.Contains(rs)) + { + clientregions.Remove(rs); + lock (irc) irc.depends--; + } + + } + + // This function is lifted from the IRCConnector because it + // contains information that is not differentiating from an + // IRC point-of-view. + + public static void OSChat(IRCConnector p_irc, OSChatMessage c, bool cmsg) + { + + // m_log.DebugFormat("[IRC-OSCHAT] from {0}:{1}", p_irc.Server, p_irc.IrcChannel); + + try + { + + // Scan through the set of unique channel configuration for those + // that belong to this connector. And then forward the message to + // all regions known to those channels. + // Note that this code is responsible for completing some of the + // settings for the inbound OSChatMessage + + lock (IRCBridgeModule.m_channels) + { + foreach (ChannelState cs in IRCBridgeModule.m_channels) + { + if ( p_irc == cs.irc) + { + + // This non-IRC differentiator moved to here + + if (cmsg && !cs.ClientReporting) + continue; + + // This non-IRC differentiator moved to here + + c.Channel = (cs.RelayPrivateChannels ? cs.RelayChannel : 0); + + foreach (RegionState region in cs.clientregions) + { + region.OSChat(cs.irc, c); + } + + } + } + } + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRC-OSCHAT]: BroadcastSim Exception: {0}", ex.Message); + m_log.Debug(ex); + } + } + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCBridgeModule.cs new file mode 100644 index 0000000..0facc14 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCBridgeModule.cs @@ -0,0 +1,219 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.Avatar.Chat +{ + public class IRCBridgeModule : IRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + internal static bool configured = false; + internal static bool enabled = false; + internal static IConfig m_config = null; + + internal static List m_channels = new List(); + internal static List m_regions = new List(); + + internal static string password = String.Empty; + + internal RegionState region = null; + + #region IRegionModule Members + + public string Name + { + get { return "IRCBridgeModule"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + public void Initialise(Scene scene, IConfigSource config) + { + // Do a once-only scan of the configuration file to make + // sure it's basically intact. + + if (!configured) + { + configured = true; + + try + { + if ((m_config = config.Configs["IRC"]) == null) + { + m_log.InfoFormat("[IRC-Bridge] module not configured"); + return; + } + + if (!m_config.GetBoolean("enabled", false)) + { + m_log.InfoFormat("[IRC-Bridge] module disabled in configuration"); + return; + } + } + catch (Exception e) + { + m_log.ErrorFormat("[IRC-Bridge] configuration failed : {0}", e.Message); + return; + } + + enabled = true; + + if (config.Configs["RemoteAdmin"] != null) + { + password = config.Configs["RemoteAdmin"].GetString("access_password", password); + scene.CommsManager.HttpServer.AddXmlRPCHandler("irc_admin", XmlRpcAdminMethod, false); + } + } + + // Iff the IRC bridge is enabled, then each new region may be + // connected to IRC. But it should NOT be obligatory (and it + // is not). + // We have to do ALL of the startup here because PostInitialize + // is not called when a region gets created in-flight from the + // command line. + + if (enabled) + { + try + { + m_log.InfoFormat("[IRC-Bridge] Connecting region {0}", scene.RegionInfo.RegionName); + region = new RegionState(scene, m_config); + lock (m_regions) m_regions.Add(region); + region.Open(); + } + catch (Exception e) + { + m_log.WarnFormat("[IRC-Bridge] Region {0} not connected to IRC : {1}", scene.RegionInfo.RegionName, e.Message); + m_log.Debug(e); + } + } + else + { + m_log.WarnFormat("[IRC-Bridge] Not enabled. Connect for region {0} ignored", scene.RegionInfo.RegionName); + } + } + + // This module can be called in-flight in which case PostInitialize + // is not called following Initialize. So no use is made of this + // call. + + public void PostInitialise() + { + } + + // Called immediately before the region module is unloaded. Cleanup + // the region. + + public void Close() + { + if (!enabled) + return; + + region.Close(); + lock (m_regions) m_regions.Remove(region); + } + + #endregion + + public static XmlRpcResponse XmlRpcAdminMethod(XmlRpcRequest request) + { + m_log.Info("[IRC-Bridge]: XML RPC Admin Entry"); + + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable responseData = new Hashtable(); + + try + { + Hashtable requestData = (Hashtable)request.Params[0]; + bool found = false; + string region = String.Empty; + + if (password != String.Empty) + { + if (!requestData.ContainsKey("password")) + throw new Exception("Invalid request"); + if ((string)requestData["password"] != password) + throw new Exception("Invalid request"); + } + + if (!requestData.ContainsKey("region")) + throw new Exception("No region name specified"); + region = (string)requestData["region"]; + + foreach (RegionState rs in m_regions) + { + if (rs.Region == region) + { + responseData["server"] = rs.cs.Server; + responseData["port"] = (int)rs.cs.Port; + responseData["user"] = rs.cs.User; + responseData["channel"] = rs.cs.IrcChannel; + responseData["enabled"] = rs.cs.irc.Enabled; + responseData["connected"] = rs.cs.irc.Connected; + responseData["nickname"] = rs.cs.irc.Nick; + found = true; + break; + } + } + + if (!found) throw new Exception(String.Format("Region <{0}> not found", region)); + + responseData["success"] = true; + } + catch (Exception e) + { + m_log.InfoFormat("[IRC-Bridge] XML RPC Admin request failed : {0}", e.Message); + + responseData["success"] = "false"; + responseData["error"] = e.Message; + } + finally + { + response.Value = responseData; + } + + m_log.Debug("[IRC-Bridge]: XML RPC Admin Exit"); + + return response; + } + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs new file mode 100644 index 0000000..f5c324d --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs @@ -0,0 +1,887 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Timers; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using OpenMetaverse; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.Avatar.Chat +{ + public class IRCConnector + { + + #region Global (static) state + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Local constants + + private static readonly Vector3 CenterOfRegion = new Vector3(128, 128, 20); + private static readonly char[] CS_SPACE = { ' ' }; + + private const int WD_INTERVAL = 1000; // base watchdog interval + private static int PING_PERIOD = 15; // WD intervals per PING + private static int ICCD_PERIOD = 10; // WD intervals between Connects + private static int L_TIMEOUT = 25; // Login time out interval + + private static int _idk_ = 0; // core connector identifier + private static int _pdk_ = 0; // ping interval counter + private static int _icc_ = 0; // IRC connect counter + + // List of configured connectors + + private static List m_connectors = new List(); + + // Watchdog state + + private static System.Timers.Timer m_watchdog = null; + + static IRCConnector() + { + m_log.DebugFormat("[IRC-Connector]: Static initialization started"); + m_watchdog = new System.Timers.Timer(WD_INTERVAL); + m_watchdog.Elapsed += new ElapsedEventHandler(WatchdogHandler); + m_watchdog.AutoReset = true; + m_watchdog.Start(); + m_log.DebugFormat("[IRC-Connector]: Static initialization complete"); + } + + #endregion + + #region Instance state + + // Connector identity + + internal int idn = _idk_++; + + // How many regions depend upon this connection + // This count is updated by the ChannelState object and reflects the sum + // of the region clients associated with the set of associated channel + // state instances. That's why it cannot be managed here. + + internal int depends = 0; + + // Working threads + + private Thread m_listener = null; + + private Object msyncConnect = new Object(); + + internal bool m_randomizeNick = true; // add random suffix + internal string m_baseNick = null; // base name for randomizing + internal string m_nick = null; // effective nickname + + public string Nick // Public property + { + get { return m_nick; } + set { m_nick = value; } + } + + private bool m_enabled = false; // connector enablement + public bool Enabled + { + get { return m_enabled; } + } + + private bool m_connected = false; // connection status + private bool m_pending = false; // login disposition + private int m_timeout = L_TIMEOUT; // login timeout counter + public bool Connected + { + get { return m_connected; } + } + + private string m_ircChannel; // associated channel id + public string IrcChannel + { + get { return m_ircChannel; } + set { m_ircChannel = value; } + } + + private uint m_port = 6667; // session port + public uint Port + { + get { return m_port; } + set { m_port = value; } + } + + private string m_server = null; // IRC server name + public string Server + { + get { return m_server; } + set { m_server = value; } + } + private string m_password = null; + public string Password + { + get { return m_password; } + set { m_password = value; } + } + + private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot"; + public string User + { + get { return m_user; } + } + + // Network interface + + private TcpClient m_tcp; + private NetworkStream m_stream = null; + private StreamReader m_reader; + private StreamWriter m_writer; + + // Channel characteristic info (if available) + + internal string usermod = String.Empty; + internal string chanmod = String.Empty; + internal string version = String.Empty; + internal bool motd = false; + + #endregion + + #region connector instance management + + internal IRCConnector(ChannelState cs) + { + + // Prepare network interface + + m_tcp = null; + m_writer = null; + m_reader = null; + + // Setup IRC session parameters + + m_server = cs.Server; + m_password = cs.Password; + m_baseNick = cs.BaseNickname; + m_randomizeNick = cs.RandomizeNickname; + m_ircChannel = cs.IrcChannel; + m_port = cs.Port; + m_user = cs.User; + + if (m_watchdog == null) + { + // Non-differentiating + + ICCD_PERIOD = cs.ConnectDelay; + PING_PERIOD = cs.PingDelay; + + // Smaller values are not reasonable + + if (ICCD_PERIOD < 5) + ICCD_PERIOD = 5; + + if (PING_PERIOD < 5) + PING_PERIOD = 5; + + _icc_ = ICCD_PERIOD; // get started right away! + + } + + // The last line of defense + + if (m_server == null || m_baseNick == null || m_ircChannel == null || m_user == null) + throw new Exception("Invalid connector configuration"); + + // Generate an initial nickname if randomizing is enabled + + if (m_randomizeNick) + { + m_nick = m_baseNick + Util.RandomClass.Next(1, 99); + } + + // Add the newly created connector to the known connectors list + + m_connectors.Add(this); + + m_log.InfoFormat("[IRC-Connector-{0}]: Initialization complete", idn); + + } + + ~IRCConnector() + { + m_watchdog.Stop(); + Close(); + } + + // Mark the connector as connectable. Harmless if already enabled. + + public void Open() + { + if (!m_enabled) + { + + m_connectors.Add(this); + m_enabled = true; + + if (!Connected) + { + Connect(); + } + + } + } + + // Only close the connector if the dependency count is zero. + + public void Close() + { + + m_log.InfoFormat("[IRC-Connector-{0}] Closing", idn); + + lock (msyncConnect) + { + + if ((depends == 0) && Enabled) + { + + m_enabled = false; + + if (Connected) + { + m_log.DebugFormat("[IRC-Connector-{0}] Closing interface", idn); + + // Cleanup the IRC session + + try + { + m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing", + m_nick, m_ircChannel, m_server)); + m_writer.Flush(); + } + catch (Exception) {} + + + m_connected = false; + + try { m_writer.Close(); } catch (Exception) {} + try { m_reader.Close(); } catch (Exception) {} + try { m_stream.Close(); } catch (Exception) {} + try { m_tcp.Close(); } catch (Exception) {} + + } + + m_connectors.Remove(this); + + } + } + + m_log.InfoFormat("[IRC-Connector-{0}] Closed", idn); + + } + + #endregion + + #region session management + + // Connect to the IRC server. A connector should always be connected, once enabled + + public void Connect() + { + + if (!m_enabled) + return; + + // Delay until next WD cycle if this is too close to the last start attempt + + while (_icc_ < ICCD_PERIOD) + return; + + m_log.DebugFormat("[IRC-Connector-{0}]: Connection request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel); + + lock (msyncConnect) + { + + _icc_ = 0; + + try + { + if (m_connected) return; + + m_connected = true; + m_pending = true; + m_timeout = L_TIMEOUT; + + m_tcp = new TcpClient(m_server, (int)m_port); + m_stream = m_tcp.GetStream(); + m_reader = new StreamReader(m_stream); + m_writer = new StreamWriter(m_stream); + + m_log.InfoFormat("[IRC-Connector-{0}]: Connected to {1}:{2}", idn, m_server, m_port); + + m_listener = new Thread(new ThreadStart(ListenerRun)); + m_listener.Name = "IRCConnectorListenerThread"; + m_listener.IsBackground = true; + m_listener.Start(); + ThreadTracker.Add(m_listener); + + // This is the message order recommended by RFC 2812 + if (m_password != null) + m_writer.WriteLine(String.Format("PASS {0}", m_password)); + m_writer.WriteLine(String.Format("NICK {0}", m_nick)); + m_writer.Flush(); + m_writer.WriteLine(m_user); + m_writer.Flush(); + m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel)); + m_writer.Flush(); + + m_log.InfoFormat("[IRC-Connector-{0}]: {1} has asked to join {2}", idn, m_nick, m_ircChannel); + + } + catch (Exception e) + { + m_log.ErrorFormat("[IRC-Connector-{0}] cannot connect {1} to {2}:{3}: {4}", + idn, m_nick, m_server, m_port, e.Message); + m_connected = false; + m_pending = false; + } + + } + + return; + + } + + // Reconnect is used to force a re-cycle of the IRC connection. Should generally + // be a transparent event + + public void Reconnect() + { + m_log.DebugFormat("[IRC-Connector-{0}]: Reconnect request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel); + + // Don't do this if a Connect is in progress... + + lock (msyncConnect) + { + + if (m_connected) + { + m_log.InfoFormat("[IRC-Connector-{0}] Resetting connector", idn); + + // Mark as disconnected. This will allow the listener thread + // to exit if still in-flight. + + + // The listener thread is not aborted - it *might* actually be + // the thread that is running the Reconnect! Instead just close + // the socket and it will disappear of its own accord, once this + // processing is completed. + + try { m_writer.Close(); } catch (Exception) {} + try { m_reader.Close(); } catch (Exception) {} + try { m_tcp.Close(); } catch (Exception) {} + + m_connected = false; + m_pending = false; + + } + + } + + Connect(); + + } + + #endregion + + #region Outbound (to-IRC) message handlers + + public void PrivMsg(string pattern, string from, string region, string msg) + { + + m_log.DebugFormat("[IRC-Connector-{0}] PrivMsg to IRC from {1}: <{2}>", idn, from, + String.Format(pattern, m_ircChannel, from, region, msg)); + + // One message to the IRC server + + try + { + m_writer.WriteLine(pattern, m_ircChannel, from, region, msg); + m_writer.Flush(); + m_log.DebugFormat("[IRC-Connector-{0}]: PrivMsg from {1} in {2}: {3}", idn, from, region, msg); + } + catch (IOException) + { + m_log.ErrorFormat("[IRC-Connector-{0}]: PrivMsg I/O Error: disconnected from IRC server", idn); + Reconnect(); + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRC-Connector-{0}]: PrivMsg exception : {1}", idn, ex.Message); + m_log.Debug(ex); + } + + } + + public void Send(string msg) + { + + m_log.DebugFormat("[IRC-Connector-{0}] Send to IRC : <{1}>", idn, msg); + + try + { + m_writer.WriteLine(msg); + m_writer.Flush(); + m_log.DebugFormat("[IRC-Connector-{0}] Sent command string: {1}", idn, msg); + } + catch (IOException) + { + m_log.ErrorFormat("[IRC-Connector-{0}] Disconnected from IRC server.(Send)", idn); + Reconnect(); + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRC-Connector-{0}] Send exception trap: {0}", idn, ex.Message); + m_log.Debug(ex); + } + + } + + #endregion + + public void ListenerRun() + { + string inputLine; + + try + { + while (m_enabled && m_connected) + { + + if ((inputLine = m_reader.ReadLine()) == null) + throw new Exception("Listener input socket closed"); + + // m_log.Info("[IRCConnector]: " + inputLine); + + if (inputLine.Contains("PRIVMSG")) + { + + Dictionary data = ExtractMsg(inputLine); + + // Any chat ??? + if (data != null) + { + + OSChatMessage c = new OSChatMessage(); + c.Message = data["msg"]; + c.Type = ChatTypeEnum.Region; + c.Position = CenterOfRegion; + c.From = data["nick"]; + c.Sender = null; + c.SenderUUID = UUID.Zero; + + // Is message "\001ACTION foo bar\001"? + // Then change to: "/me foo bar" + + if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION")) + c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9)); + + ChannelState.OSChat(this, c, false); + + } + + } + else + { + ProcessIRCCommand(inputLine); + } + } + } + catch (Exception /*e*/) + { + // m_log.ErrorFormat("[IRC-Connector-{0}]: ListenerRun exception trap: {1}", idn, e.Message); + // m_log.Debug(e); + } + + // This is potentially circular, but harmless if so. + // The connection is marked as not connected the first time + // through reconnect. + + if (m_enabled) Reconnect(); + + } + + private Regex RE = new Regex(@":(?[\w-]*)!(?\S*) PRIVMSG (?\S+) :(?.*)", + RegexOptions.Multiline); + + private Dictionary ExtractMsg(string input) + { + //examines IRC commands and extracts any private messages + // which will then be reboadcast in the Sim + + // m_log.InfoFormat("[IRC-Connector-{0}]: ExtractMsg: {1}", idn, input); + + Dictionary result = null; + MatchCollection matches = RE.Matches(input); + + // Get some direct matches $1 $4 is a + if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5)) + { + // m_log.Info("[IRCConnector]: Number of matches: " + matches.Count); + // if (matches.Count > 0) + // { + // m_log.Info("[IRCConnector]: Number of groups: " + matches[0].Groups.Count); + // } + return null; + } + + result = new Dictionary(); + result.Add("nick", matches[0].Groups[1].Value); + result.Add("user", matches[0].Groups[2].Value); + result.Add("channel", matches[0].Groups[3].Value); + result.Add("msg", matches[0].Groups[4].Value); + + return result; + } + + public void BroadcastSim(string sender, string format, params string[] args) + { + try + { + OSChatMessage c = new OSChatMessage(); + c.From = sender; + c.Message = String.Format(format, args); + c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say; + c.Position = CenterOfRegion; + c.Sender = null; + c.SenderUUID = UUID.Zero; + + ChannelState.OSChat(this, c, true); + + } + catch (Exception ex) // IRC gate should not crash Sim + { + m_log.ErrorFormat("[IRC-Connector-{0}]: BroadcastSim Exception Trap: {1}\n{2}", idn, ex.Message, ex.StackTrace); + } + } + + #region IRC Command Handlers + + public void ProcessIRCCommand(string command) + { + + string[] commArgs; + string c_server = m_server; + + string pfx = String.Empty; + string cmd = String.Empty; + string parms = String.Empty; + + // ":" indicates that a prefix is present + // There are NEVER more than 17 real + // fields. A parameter that starts with + // ":" indicates that the remainder of the + // line is a single parameter value. + + commArgs = command.Split(CS_SPACE,2); + + if (commArgs[0].StartsWith(":")) + { + pfx = commArgs[0].Substring(1); + commArgs = commArgs[1].Split(CS_SPACE,2); + } + + cmd = commArgs[0]; + parms = commArgs[1]; + + // m_log.DebugFormat("[IRC-Connector-{0}] prefix = <{1}> cmd = <{2}>", idn, pfx, cmd); + + switch (cmd) + { + + // Messages 001-004 are always sent + // following signon. + + case "001" : // Welcome ... + case "002" : // Server information + case "003" : // Welcome ... + break; + case "004" : // Server information + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + commArgs = parms.Split(CS_SPACE); + c_server = commArgs[1]; + m_server = c_server; + version = commArgs[2]; + usermod = commArgs[3]; + chanmod = commArgs[4]; + break; + case "005" : // Server information + break; + case "042" : + case "250" : + case "251" : + case "252" : + case "254" : + case "255" : + case "265" : + case "266" : + case "332" : // Subject + case "333" : // Subject owner (?) + case "353" : // Name list + case "366" : // End-of-Name list marker + case "372" : // MOTD body + case "375" : // MOTD start + m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); + break; + case "376" : // MOTD end + m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); + motd = true; + break; + case "451" : // Not registered + break; + case "433" : // Nickname in use + // Gen a new name + m_nick = m_baseNick + Util.RandomClass.Next(1, 99); + m_log.ErrorFormat("[IRC-Connector-{0}]: [{1}] IRC SERVER reports NicknameInUse, trying {2}", idn, cmd, m_nick); + // Retry + m_writer.WriteLine(String.Format("NICK {0}", m_nick)); + m_writer.Flush(); + m_writer.WriteLine(m_user); + m_writer.Flush(); + m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel)); + m_writer.Flush(); + break; + case "479" : // Bad channel name, etc. This will never work, so disable the connection + m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); + m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd); + m_enabled = false; + m_connected = false; + m_pending = false; + break; + case "NOTICE" : + m_log.WarnFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); + break; + case "ERROR" : + m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); + if (parms.Contains("reconnect too fast")) + ICCD_PERIOD++; + m_pending = false; + Reconnect(); + break; + case "PING" : + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + m_writer.WriteLine(String.Format("PONG {0}", parms)); + m_writer.Flush(); + break; + case "PONG" : + break; + case "JOIN": + if (m_pending) + { + m_log.InfoFormat("[IRC-Connector-{0}] [{1}] Connected", idn, cmd); + m_pending = false; + } + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + eventIrcJoin(pfx, cmd, parms); + break; + case "PART": + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + eventIrcPart(pfx, cmd, parms); + break; + case "MODE": + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + eventIrcMode(pfx, cmd, parms); + break; + case "NICK": + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + eventIrcNickChange(pfx, cmd, parms); + break; + case "KICK": + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + eventIrcKick(pfx, cmd, parms); + break; + case "QUIT": + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); + eventIrcQuit(pfx, cmd, parms); + break; + default : + m_log.DebugFormat("[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms); + break; + } + + // m_log.DebugFormat("[IRC-Connector-{0}] prefix = <{1}> cmd = <{2}> complete", idn, pfx, cmd); + + } + + public void eventIrcJoin(string prefix, string command, string parms) + { + string[] args = parms.Split(CS_SPACE,2); + string IrcUser = prefix.Split('!')[0]; + string IrcChannel = args[0]; + + if (IrcChannel.StartsWith(":")) + IrcChannel = IrcChannel.Substring(1); + + m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCJoin {1}:{2}", idn, m_server, m_ircChannel); + BroadcastSim(IrcUser, "/me joins {0}", IrcChannel); + } + + public void eventIrcPart(string prefix, string command, string parms) + { + string[] args = parms.Split(CS_SPACE,2); + string IrcUser = prefix.Split('!')[0]; + string IrcChannel = args[0]; + + m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCPart {1}:{2}", idn, m_server, m_ircChannel); + BroadcastSim(IrcUser, "/me parts {0}", IrcChannel); + } + + public void eventIrcMode(string prefix, string command, string parms) + { + string[] args = parms.Split(CS_SPACE,2); + string UserMode = args[1]; + + m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCMode {1}:{2}", idn, m_server, m_ircChannel); + if (UserMode.Substring(0, 1) == ":") + { + UserMode = UserMode.Remove(0, 1); + } + } + + public void eventIrcNickChange(string prefix, string command, string parms) + { + string[] args = parms.Split(CS_SPACE,2); + string UserOldNick = prefix.Split('!')[0]; + string UserNewNick = args[0].Remove(0, 1); + + m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCNickChange {1}:{2}", idn, m_server, m_ircChannel); + BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick); + } + + public void eventIrcKick(string prefix, string command, string parms) + { + string[] args = parms.Split(CS_SPACE,3); + string UserKicker = prefix.Split('!')[0]; + string IrcChannel = args[0]; + string UserKicked = args[1]; + string KickMessage = args[2]; + + m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCKick {1}:{2}", idn, m_server, m_ircChannel); + BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage); + + if (UserKicked == m_nick) + { + BroadcastSim(m_nick, "Hey, that was me!!!"); + } + + } + + public void eventIrcQuit(string prefix, string command, string parms) + { + string IrcUser = prefix.Split('!')[0]; + string QuitMessage = parms; + + m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCQuit {1}:{2}", idn, m_server, m_ircChannel); + BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage); + } + + #endregion + + #region Connector Watch Dog + + // A single watch dog monitors extant connectors and makes sure that they + // are re-connected as necessary. If a connector IS connected, then it is + // pinged, but only if a PING period has elapsed. + + protected static void WatchdogHandler(Object source, ElapsedEventArgs args) + { + + // m_log.InfoFormat("[IRC-Watchdog] Status scan"); + + _pdk_ = (_pdk_+1)%PING_PERIOD; // cycle the ping trigger + _icc_++; // increment the inter-consecutive-connect-delay counter + + foreach (IRCConnector connector in m_connectors) + { + if (connector.Enabled) + { + if (!connector.Connected) + { + try + { + // m_log.DebugFormat("[IRC-Watchdog] Connecting {1}:{2}", connector.idn, connector.m_server, connector.m_ircChannel); + connector.Connect(); + } + catch (Exception e) + { + m_log.ErrorFormat("[IRC-Watchdog] Exception on connector {0}: {1} ", connector.idn, e.Message); + } + } + else + { + + if (connector.m_pending) + { + if (connector.m_timeout == 0) + { + m_log.ErrorFormat("[IRC-Watchdog] Login timed-out for connector {0}, reconnecting", connector.idn); + connector.Reconnect(); + } + else + connector.m_timeout--; + } + + if (_pdk_ == 0) + { + try + { + connector.m_writer.WriteLine(String.Format("PING :{0}", connector.m_server)); + connector.m_writer.Flush(); + } + catch (Exception /*e*/) + { + // m_log.ErrorFormat("[IRC-PingRun] Exception on connector {0}: {1} ", connector.idn, e.Message); + // m_log.Debug(e); + connector.Reconnect(); + } + } + + } + } + } + + // m_log.InfoFormat("[IRC-Watchdog] Status scan completed"); + + } + + #endregion + + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs new file mode 100644 index 0000000..6733120 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs @@ -0,0 +1,424 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.Avatar.Chat +{ + // An instance of this class exists for every active region + + internal class RegionState + { + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(128, 128, 20); + private const int DEBUG_CHANNEL = 2147483647; + + private static int _idk_ = 0; + + // Runtime variables; these values are assigned when the + // IrcState is created and remain constant thereafter. + + internal string Region = String.Empty; + internal string Host = String.Empty; + internal string LocX = String.Empty; + internal string LocY = String.Empty; + internal string MA1 = String.Empty; + internal string MA2 = String.Empty; + internal string IDK = String.Empty; + + // System values - used only be the IRC classes themselves + + internal ChannelState cs = null; // associated IRC configuration + internal Scene scene = null; // associated scene + internal IConfig config = null; // configuration file reference + internal bool enabled = true; + + // This list is used to keep track of who is here, and by + // implication, who is not. + + internal List clients = new List(); + + // Setup runtime variable values + + public RegionState(Scene p_scene, IConfig p_config) + { + + scene = p_scene; + config = p_config; + + Region = scene.RegionInfo.RegionName; + Host = scene.RegionInfo.ExternalHostName; + LocX = Convert.ToString(scene.RegionInfo.RegionLocX); + LocY = Convert.ToString(scene.RegionInfo.RegionLocY); + MA1 = scene.RegionInfo.MasterAvatarFirstName; + MA2 = scene.RegionInfo.MasterAvatarLastName; + IDK = Convert.ToString(_idk_++); + + // OpenChannel conditionally establishes a connection to the + // IRC server. The request will either succeed, or it will + // throw an exception. + + ChannelState.OpenChannel(this, config); + + // Connect channel to world events + + scene.EventManager.OnChatFromWorld += OnSimChat; + scene.EventManager.OnChatFromClient += OnSimChat; + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.EventManager.OnMakeChildAgent += OnMakeChildAgent; + + m_log.InfoFormat("[IRC-Region {0}] Initialization complete", Region); + + } + + // Auto cleanup when abandoned + + ~RegionState() + { + if (cs != null) + cs.RemoveRegion(this); + } + + // Called by PostInitialize after all regions have been created + + public void Open() + { + cs.Open(this); + enabled = true; + } + + // Called by IRCBridgeModule.Close immediately prior to unload + // of the module for this region. This happens when the region + // is being removed or the server is terminating. The IRC + // BridgeModule will remove the region from the region list + // when control returns. + + public void Close() + { + enabled = false; + cs.Close(this); + } + + // The agent has disconnected, cleanup associated resources + + private void OnClientLoggedOut(IClientAPI client) + { + try + { + if (clients.Contains(client)) + { + if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting)) + { + m_log.InfoFormat("[IRC-Region {0}]: {1} has left", Region, client.Name); + cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", client.Name)); + } + client.OnLogout -= OnClientLoggedOut; + client.OnConnectionClosed -= OnClientLoggedOut; + clients.Remove(client); + } + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRC-Region {0}]: ClientLoggedOut exception: {1}", Region, ex.Message); + m_log.Debug(ex); + } + } + + // This event indicates that the agent has left the building. We should treat that the same + // as if the agent has logged out (we don't want cross-region noise - or do we?) + + private void OnMakeChildAgent(ScenePresence presence) + { + + IClientAPI client = presence.ControllingClient; + + try + { + if (clients.Contains(client)) + { + if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting)) + { + string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); + m_log.DebugFormat("[IRC-Region {0}] {1} has left", Region, clientName); + cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", clientName)); + } + client.OnLogout -= OnClientLoggedOut; + client.OnConnectionClosed -= OnClientLoggedOut; + clients.Remove(client); + } + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRC-Region {0}]: MakeChildAgent exception: {1}", Region, ex.Message); + m_log.Debug(ex); + } + + } + + // An agent has entered the region (from another region). Add the client to the locally + // known clients list + + private void OnMakeRootAgent(ScenePresence presence) + { + + IClientAPI client = presence.ControllingClient; + + try + { + if (!clients.Contains(client)) + { + client.OnLogout += OnClientLoggedOut; + client.OnConnectionClosed += OnClientLoggedOut; + clients.Add(client); + if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting)) + { + string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); + m_log.DebugFormat("[IRC-Region {0}] {1} has arrived", Region, clientName); + cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has arrived", clientName)); + } + } + } + catch (Exception ex) + { + m_log.ErrorFormat("[IRC-Region {0}]: MakeRootAgent exception: {1}", Region, ex.Message); + m_log.Debug(ex); + } + + } + + // This handler detects chat events int he virtual world. + + public void OnSimChat(Object sender, OSChatMessage msg) + { + + // early return if this comes from the IRC forwarder + + if (cs.irc.Equals(sender)) return; + + // early return if nothing to forward + + if (msg.Message.Length == 0) return; + + // check for commands coming from avatars or in-world + // object (if commands are enabled) + + if (cs.CommandsEnabled && msg.Channel == cs.CommandChannel) + { + + m_log.DebugFormat("[IRC-Region {0}] command on channel {1}: {2}", Region, msg.Channel, msg.Message); + + string[] messages = msg.Message.Split(' '); + string command = messages[0].ToLower(); + + try + { + switch (command) + { + + // These commands potentially require a change in the + // underlying ChannelState. + + case "server": + cs.Close(this); + cs = cs.UpdateServer(this, messages[1]); + cs.Open(this); + break; + case "port": + cs.Close(this); + cs = cs.UpdatePort(this, messages[1]); + cs.Open(this); + break; + case "channel": + cs.Close(this); + cs = cs.UpdateChannel(this, messages[1]); + cs.Open(this); + break; + case "nick": + cs.Close(this); + cs = cs.UpdateNickname(this, messages[1]); + cs.Open(this); + break; + + // These may also (but are less likely) to require a + // change in ChannelState. + + case "client-reporting": + cs = cs.UpdateClientReporting(this, messages[1]); + break; + case "in-channel": + cs = cs.UpdateRelayIn(this, messages[1]); + break; + case "out-channel": + cs = cs.UpdateRelayOut(this, messages[1]); + break; + + // These are all taken to be temporary changes in state + // so the underlying connector remains intact. But note + // that with regions sharing a connector, there could + // be interference. + + case "close": + enabled = false; + cs.Close(this); + break; + + case "connect": + enabled = true; + cs.Open(this); + break; + + case "reconnect": + enabled = true; + cs.Close(this); + cs.Open(this); + break; + + // This one is harmless as far as we can judge from here. + // If it is not, then the complaints will eventually make + // that evident. + + default: + m_log.DebugFormat("[IRC-Region {0}] Forwarding unrecognized command to IRC : {1}", + Region, msg.Message); + cs.irc.Send(msg.Message); + break; + } + } + catch (Exception ex) + { + m_log.WarnFormat("[IRC-Region {0}] error processing in-world command channel input: {1}", + Region, ex.Message); + m_log.Debug(ex); + } + + return; + + } + + // The command channel remains enabled, even if we have otherwise disabled the IRC + // interface. + + if (!enabled) + return; + + // drop messages unless they are on a valid in-world + // channel as configured in the ChannelState + + if (!cs.ValidInWorldChannels.Contains(msg.Channel)) + { + m_log.DebugFormat("[IRC-Region {0}] dropping message {1} on channel {2}", Region, msg, msg.Channel); + return; + } + + ScenePresence avatar = null; + string fromName = msg.From; + + if (msg.Sender != null) + { + avatar = scene.GetScenePresence(msg.Sender.AgentId); + if (avatar != null) fromName = avatar.Name; + } + + if (!cs.irc.Connected) + { + m_log.WarnFormat("[IRC-Region {0}] IRCConnector not connected: dropping message from {1}", Region, fromName); + return; + } + + m_log.DebugFormat("[IRC-Region {0}] heard on channel {1} : {2}", Region, msg.Channel, msg.Message); + + if (null != avatar && cs.RelayChat && (msg.Channel == 0 || msg.Channel == DEBUG_CHANNEL)) + { + string txt = msg.Message; + if (txt.StartsWith("/me ")) + txt = String.Format("{0} {1}", fromName, msg.Message.Substring(4)); + + cs.irc.PrivMsg(cs.PrivateMessageFormat, fromName, Region, txt); + return; + } + + if (null == avatar && cs.RelayPrivateChannels && null != cs.AccessPassword && + msg.Channel == cs.RelayChannelOut) + { + Match m = cs.AccessPasswordRegex.Match(msg.Message); + if (null != m) + { + m_log.DebugFormat("[IRC] relaying message from {0}: {1}", m.Groups["avatar"].ToString(), + m.Groups["message"].ToString()); + cs.irc.PrivMsg(cs.PrivateMessageFormat, m.Groups["avatar"].ToString(), + scene.RegionInfo.RegionName, m.Groups["message"].ToString()); + } + } + } + + // This method gives the region an opportunity to interfere with + // message delivery. For now we just enforce the enable/disable + // flag. + + internal void OSChat(Object irc, OSChatMessage msg) + { + if (enabled) + { + // m_log.DebugFormat("[IRC-OSCHAT] Region {0} being sent message", region.Region); + msg.Scene = scene; + scene.EventManager.TriggerOnChatBroadcast(irc, msg); + } + } + + // This supports any local message traffic that might be needed in + // support of command processing. At present there is none. + + internal void LocalChat(string msg) + { + if (enabled) + { + OSChatMessage osm = new OSChatMessage(); + osm.From = "IRC Agent"; + osm.Message = msg; + osm.Type = ChatTypeEnum.Region; + osm.Position = CenterOfRegion; + osm.Sender = null; + osm.SenderUUID = OpenMetaverse.UUID.Zero; // Hmph! Still? + osm.Channel = 0; + OSChat(this, osm); + } + } + + } + +} diff --git a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs new file mode 100644 index 0000000..bb46b11 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs @@ -0,0 +1,604 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.CoreModules.Avatar.Chat; + +namespace OpenSim.Region.OptionalModules.Avatar.Concierge +{ + public class ConciergeModule : ChatModule, IRegionModule + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const int DEBUG_CHANNEL = 2147483647; + + private List _scenes = new List(); + private List _conciergedScenes = new List(); + private Dictionary> _sceneAttendees = + new Dictionary>(); + private Dictionary _attendeeNames = + new Dictionary(); + + private bool _replacingChatModule = false; + + private IConfig _config; + + private string _whoami = "conferencier"; + private Regex _regions = null; + private string _welcomes = null; + private int _conciergeChannel = 42; + private string _announceEntering = "{0} enters {1} (now {2} visitors in this region)"; + private string _announceLeaving = "{0} leaves {1} (back to {2} visitors in this region)"; + private string _xmlRpcPassword = String.Empty; + private string _brokerURI = String.Empty; + + internal object _syncy = new object(); + + #region IRegionModule Members + public override void Initialise(Scene scene, IConfigSource config) + { + try + { + if ((_config = config.Configs["Concierge"]) == null) + { + //_log.InfoFormat("[Concierge]: no configuration section [Concierge] in OpenSim.ini: module not configured"); + return; + } + + if (!_config.GetBoolean("enabled", false)) + { + //_log.InfoFormat("[Concierge]: module disabled by OpenSim.ini configuration"); + return; + } + } + catch (Exception) + { + _log.Info("[Concierge]: module not configured"); + return; + } + + // check whether ChatModule has been disabled: if yes, + // then we'll "stand in" + try + { + if (config.Configs["Chat"] == null) + { + _replacingChatModule = false; + } + else + { + _replacingChatModule = !config.Configs["Chat"].GetBoolean("enabled", true); + } + } + catch (Exception) + { + _replacingChatModule = false; + } + _log.InfoFormat("[Concierge] {0} ChatModule", _replacingChatModule ? "replacing" : "not replacing"); + + + // take note of concierge channel and of identity + _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel); + _whoami = _config.GetString("whoami", "conferencier"); + _welcomes = _config.GetString("welcomes", _welcomes); + _announceEntering = _config.GetString("announce_entering", _announceEntering); + _announceLeaving = _config.GetString("announce_leaving", _announceLeaving); + _xmlRpcPassword = _config.GetString("password", _xmlRpcPassword); + _brokerURI = _config.GetString("broker", _brokerURI); + + _log.InfoFormat("[Concierge] reporting as \"{0}\" to our users", _whoami); + + // calculate regions Regex + if (_regions == null) + { + string regions = _config.GetString("regions", String.Empty); + if (!String.IsNullOrEmpty(regions)) + { + _regions = new Regex(@regions, RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + } + + scene.CommsManager.HttpServer.AddXmlRPCHandler("concierge_update_welcome", XmlRpcUpdateWelcomeMethod, false); + + lock (_syncy) + { + if (!_scenes.Contains(scene)) + { + _scenes.Add(scene); + + if (_regions == null || _regions.IsMatch(scene.RegionInfo.RegionName)) + _conciergedScenes.Add(scene); + + // subscribe to NewClient events + scene.EventManager.OnNewClient += OnNewClient; + + // subscribe to *Chat events + scene.EventManager.OnChatFromWorld += OnChatFromWorld; + if (!_replacingChatModule) + scene.EventManager.OnChatFromClient += OnChatFromClient; + scene.EventManager.OnChatBroadcast += OnChatBroadcast; + + // subscribe to agent change events + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.EventManager.OnMakeChildAgent += OnMakeChildAgent; + } + } + _log.InfoFormat("[Concierge]: initialized for {0}", scene.RegionInfo.RegionName); + } + + public override void PostInitialise() + { + } + + public override void Close() + { + } + + public override string Name + { + get { return "ConciergeModule"; } + } + + public override bool IsSharedModule + { + get { return true; } + } + + #endregion + + #region ISimChat Members + public override void OnChatBroadcast(Object sender, OSChatMessage c) + { + if (_replacingChatModule) + { + // distribute chat message to each and every avatar in + // the region + base.OnChatBroadcast(sender, c); + } + + // TODO: capture logic + return; + } + + public override void OnChatFromClient(Object sender, OSChatMessage c) + { + if (_replacingChatModule) + { + // replacing ChatModule: need to redistribute + // ChatFromClient to interested subscribers + c = FixPositionOfChatMessage(c); + + Scene scene = (Scene)c.Scene; + scene.EventManager.TriggerOnChatFromClient(sender, c); + + if (_conciergedScenes.Contains(c.Scene)) + { + // when we are replacing ChatModule, we treat + // OnChatFromClient like OnChatBroadcast for + // concierged regions, effectively extending the + // range of chat to cover the whole + // region. however, we don't do this for whisper + // (got to have some privacy) + if (c.Type != ChatTypeEnum.Whisper) + { + base.OnChatBroadcast(sender, c); + return; + } + } + + // redistribution will be done by base class + base.OnChatFromClient(sender, c); + } + + // TODO: capture chat + return; + } + + public override void OnChatFromWorld(Object sender, OSChatMessage c) + { + if (_replacingChatModule) + { + if (_conciergedScenes.Contains(c.Scene)) + { + // when we are replacing ChatModule, we treat + // OnChatFromClient like OnChatBroadcast for + // concierged regions, effectively extending the + // range of chat to cover the whole + // region. however, we don't do this for whisper + // (got to have some privacy) + if (c.Type != ChatTypeEnum.Whisper) + { + base.OnChatBroadcast(sender, c); + return; + } + } + + base.OnChatFromWorld(sender, c); + } + return; + } + #endregion + + + public override void OnNewClient(IClientAPI client) + { + client.OnLogout += OnClientLoggedOut; + + if (_replacingChatModule) + client.OnChatFromClient += OnChatFromClient; + } + + + + public void OnClientLoggedOut(IClientAPI client) + { + client.OnLogout -= OnClientLoggedOut; + client.OnConnectionClosed -= OnClientLoggedOut; + + if (_conciergedScenes.Contains(client.Scene)) + { + _log.DebugFormat("[Concierge]: {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); + RemoveFromAttendeeList(client.AgentId, client.Name, client.Scene); + AnnounceToAgentsRegion(client.Scene, String.Format(_announceLeaving, client.Name, client.Scene.RegionInfo.RegionName, + _sceneAttendees[client.Scene].Count)); + UpdateBroker(client.Scene); + } + } + + + public void OnMakeRootAgent(ScenePresence agent) + { + if (_conciergedScenes.Contains(agent.Scene)) + { + _log.DebugFormat("[Concierge]: {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); + AddToAttendeeList(agent.UUID, agent.Name, agent.Scene); + WelcomeAvatar(agent, agent.Scene); + AnnounceToAgentsRegion(agent.Scene, String.Format(_announceEntering, agent.Name, agent.Scene.RegionInfo.RegionName, + _sceneAttendees[agent.Scene].Count)); + UpdateBroker(agent.Scene); + } + } + + + public void OnMakeChildAgent(ScenePresence agent) + { + if (_conciergedScenes.Contains(agent.Scene)) + { + _log.DebugFormat("[Concierge]: {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); + RemoveFromAttendeeList(agent.UUID, agent.Name, agent.Scene); + AnnounceToAgentsRegion(agent.Scene, String.Format(_announceLeaving, agent.Name, agent.Scene.RegionInfo.RegionName, + _sceneAttendees[agent.Scene].Count)); + UpdateBroker(agent.Scene); + } + } + + protected void AddToAttendeeList(UUID agentID, string name, Scene scene) + { + lock (_sceneAttendees) + { + if (!_sceneAttendees.ContainsKey(scene)) + _sceneAttendees[scene] = new List(); + + List attendees = _sceneAttendees[scene]; + if (!attendees.Contains(agentID)) + { + attendees.Add(agentID); + _attendeeNames[agentID] = name; + } + } + } + + protected void RemoveFromAttendeeList(UUID agentID, String name, IScene scene) + { + lock (_sceneAttendees) + { + if (!_sceneAttendees.ContainsKey(scene)) + { + _log.WarnFormat("[Concierge]: attendee list missing for region {0}", scene.RegionInfo.RegionName); + return; + } + + List attendees = _sceneAttendees[scene]; + if (!attendees.Contains(agentID)) + { + _log.WarnFormat("[Concierge]: avatar {0} must have sneaked in to region {1} earlier", + name, scene.RegionInfo.RegionName); + return; + } + + attendees.Remove(agentID); + _attendeeNames.Remove(agentID); + } + } + + protected void UpdateBroker(IScene scene) + { + if (String.IsNullOrEmpty(_brokerURI)) + return; + + string uri = String.Format(_brokerURI, scene.RegionInfo.RegionName, scene.RegionInfo.RegionID); + + // get attendee list for the scene + List attendees; + lock (_sceneAttendees) + { + if (!_sceneAttendees.ContainsKey(scene)) + { + _log.DebugFormat("[Concierge]: attendee list missing for region {0}", scene.RegionInfo.RegionName); + return; + } + + attendees = _sceneAttendees[scene]; + } + + // create XML sniplet + StringBuilder list = new StringBuilder(); + if (0 == attendees.Count) + { + list.Append(String.Format("", + scene.RegionInfo.RegionName, scene.RegionInfo.RegionID, + DateTime.UtcNow.ToString("s"))); + } + else + { + list.Append(String.Format("\n", + attendees.Count, scene.RegionInfo.RegionName, + scene.RegionInfo.RegionID, + DateTime.UtcNow.ToString("s"))); + foreach (UUID uuid in attendees) + { + string name = _attendeeNames[uuid]; + list.Append(String.Format(" \n", name, uuid)); + } + list.Append(""); + } + string payload = list.ToString(); + + // post via REST to broker + HttpWebRequest updatePost = WebRequest.Create(uri) as HttpWebRequest; + updatePost.Method = "POST"; + updatePost.ContentType = "text/xml"; + updatePost.ContentLength = payload.Length; + updatePost.UserAgent = "OpenSim.Concierge"; + + try + { + StreamWriter payloadStream = new StreamWriter(updatePost.GetRequestStream()); + payloadStream.Write(payload); + payloadStream.Close(); + + updatePost.BeginGetResponse(UpdateBrokerDone, updatePost); + _log.DebugFormat("[Concierge] async broker POST to {0} started", uri); + } + catch (WebException we) + { + _log.ErrorFormat("[Concierge] async broker POST to {0} failed: {1}", uri, we.Status); + } + } + + private void UpdateBrokerDone(IAsyncResult result) + { + HttpWebRequest updatePost = null; + try + { + updatePost = result.AsyncState as HttpWebRequest; + using (HttpWebResponse response = updatePost.EndGetResponse(result) as HttpWebResponse) + { + _log.DebugFormat("[Concierge] broker update: status {0}", response.StatusCode); + } + } + catch (WebException we) + { + string uri = updatePost.RequestUri.OriginalString; + _log.ErrorFormat("[Concierge] broker update to {0} failed with status {1}", uri, we.Status); + if (null != we.Response) + { + using (HttpWebResponse resp = we.Response as HttpWebResponse) + { + _log.ErrorFormat("[Concierge] response from {0} status code: {1}", uri, resp.StatusCode); + _log.ErrorFormat("[Concierge] response from {0} status desc: {1}", uri, resp.StatusDescription); + _log.ErrorFormat("[Concierge] response from {0} server: {1}", uri, resp.Server); + + if (resp.ContentLength > 0) + { + StreamReader content = new StreamReader(resp.GetResponseStream()); + _log.ErrorFormat("[Concierge] response from {0} content: {1}", uri, content.ReadToEnd()); + content.Close(); + } + } + } + } + } + + protected void WelcomeAvatar(ScenePresence agent, Scene scene) + { + // welcome mechanics: check whether we have a welcomes + // directory set and wether there is a region specific + // welcome file there: if yes, send it to the agent + if (!String.IsNullOrEmpty(_welcomes)) + { + string[] welcomes = new string[] { + Path.Combine(_welcomes, agent.Scene.RegionInfo.RegionName), + Path.Combine(_welcomes, "DEFAULT")}; + foreach (string welcome in welcomes) + { + if (File.Exists(welcome)) + { + try + { + string[] welcomeLines = File.ReadAllLines(welcome); + foreach (string l in welcomeLines) + { + AnnounceToAgent(agent, String.Format(l, agent.Name, scene.RegionInfo.RegionName, _whoami)); + } + } + catch (IOException ioe) + { + _log.ErrorFormat("[Concierge]: run into trouble reading welcome file {0} for region {1} for avatar {2}: {3}", + welcome, scene.RegionInfo.RegionName, agent.Name, ioe); + } + catch (FormatException fe) + { + _log.ErrorFormat("[Concierge]: welcome file {0} is malformed: {1}", welcome, fe); + } + } + return; + } + _log.DebugFormat("[Concierge]: no welcome message for region {0}", scene.RegionInfo.RegionName); + } + } + + static private Vector3 PosOfGod = new Vector3(128, 128, 9999); + + // protected void AnnounceToAgentsRegion(Scene scene, string msg) + // { + // ScenePresence agent = null; + // if ((client.Scene is Scene) && (client.Scene as Scene).TryGetAvatar(client.AgentId, out agent)) + // AnnounceToAgentsRegion(agent, msg); + // else + // _log.DebugFormat("[Concierge]: could not find an agent for client {0}", client.Name); + // } + + protected void AnnounceToAgentsRegion(IScene scene, string msg) + { + OSChatMessage c = new OSChatMessage(); + c.Message = msg; + c.Type = ChatTypeEnum.Say; + c.Channel = 0; + c.Position = PosOfGod; + c.From = _whoami; + c.Sender = null; + c.SenderUUID = UUID.Zero; + c.Scene = scene; + + if (scene is Scene) + (scene as Scene).EventManager.TriggerOnChatBroadcast(this, c); + } + + protected void AnnounceToAgent(ScenePresence agent, string msg) + { + OSChatMessage c = new OSChatMessage(); + c.Message = msg; + c.Type = ChatTypeEnum.Say; + c.Channel = 0; + c.Position = PosOfGod; + c.From = _whoami; + c.Sender = null; + c.SenderUUID = UUID.Zero; + c.Scene = agent.Scene; + + agent.ControllingClient.SendChatMessage(msg, (byte) ChatTypeEnum.Say, PosOfGod, _whoami, UUID.Zero, + (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully); + } + + private static void checkStringParameters(XmlRpcRequest request, string[] param) + { + Hashtable requestData = (Hashtable) request.Params[0]; + foreach (string p in param) + { + if (!requestData.Contains(p)) + throw new Exception(String.Format("missing string parameter {0}", p)); + if (String.IsNullOrEmpty((string)requestData[p])) + throw new Exception(String.Format("parameter {0} is empty", p)); + } + } + + public XmlRpcResponse XmlRpcUpdateWelcomeMethod(XmlRpcRequest request) + { + _log.Info("[Concierge]: processing UpdateWelcome request"); + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable responseData = new Hashtable(); + + try + { + Hashtable requestData = (Hashtable)request.Params[0]; + checkStringParameters(request, new string[] { "password", "region", "welcome" }); + + // check password + if (!String.IsNullOrEmpty(_xmlRpcPassword) && + (string)requestData["password"] != _xmlRpcPassword) throw new Exception("wrong password"); + + if (String.IsNullOrEmpty(_welcomes)) + throw new Exception("welcome templates are not enabled, ask your OpenSim operator to set the \"welcomes\" option in the [Concierge] section of OpenSim.ini"); + + string msg = (string)requestData["welcome"]; + if (String.IsNullOrEmpty(msg)) + throw new Exception("empty parameter \"welcome\""); + + string regionName = (string)requestData["region"]; + IScene scene = _scenes.Find(delegate(IScene s) { return s.RegionInfo.RegionName == regionName; }); + if (scene == null) + throw new Exception(String.Format("unknown region \"{0}\"", regionName)); + + if (!_conciergedScenes.Contains(scene)) + throw new Exception(String.Format("region \"{0}\" is not a concierged region.", regionName)); + + string welcome = Path.Combine(_welcomes, regionName); + if (File.Exists(welcome)) + { + _log.InfoFormat("[Concierge]: UpdateWelcome: updating existing template \"{0}\"", welcome); + string welcomeBackup = String.Format("{0}~", welcome); + if (File.Exists(welcomeBackup)) + File.Delete(welcomeBackup); + File.Move(welcome, welcomeBackup); + } + File.WriteAllText(welcome, msg); + + responseData["success"] = "true"; + response.Value = responseData; + } + catch (Exception e) + { + _log.InfoFormat("[Concierge]: UpdateWelcome failed: {0}", e.Message); + + responseData["success"] = "false"; + responseData["error"] = e.Message; + + response.Value = responseData; + } + _log.Debug("[Concierge]: done processing UpdateWelcome request"); + return response; + } + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py new file mode 100755 index 0000000..1c088fb --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# Copyright (c) Contributors, http://opensimulator.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. +# + +import logging +import BaseHTTPServer +import optparse +import xml.etree.ElementTree as ET +import xml.parsers.expat + + +# enable debug level logging +logging.basicConfig(level = logging.DEBUG, + format='%(asctime)s %(levelname)s %(message)s') + +options = None + +# subclassed HTTPRequestHandler +class ConciergeHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def logRequest(self): + logging.info('[ConciergeHandler] %(command)s request: %(host)s:%(port)d --- %(path)s', + dict(command = self.command, + host = self.client_address[0], + port = self.client_address[1], + path = self.path)) + + def logResponse(self, status): + logging.info('[ConciergeHandler] %(command)s returned %(status)d', + dict(command = self.command, + status = status)) + + + def do_HEAD(self): + self.logRequest() + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + self.logResponse(200) + + def dumpXml(self, xml): + logging.debug('[ConciergeHandler] %s', xml.tag) + for attr in xml.attrib: + logging.debug('[ConciergeHandler] %s [%s] %s', xml.tag, attr, xml.attrib[attr]) + for kid in xml.getchildren(): + self.dumpXml(kid) + + def do_POST(self): + self.logRequest() + hdrs = {} + for hdr in self.headers.headers: + logging.debug('[ConciergeHandler] POST: header: %s', hdr.rstrip()) + + length = int(self.headers.getheader('Content-Length')) + content = self.rfile.read(length) + self.rfile.close() + + logging.debug('[ConciergeHandler] POST: content: %s', content) + try: + postXml = ET.fromstring(content) + self.dumpXml(postXml) + except xml.parsers.expat.ExpatError, xmlError: + logging.error('[ConciergeHandler] POST illformed:%s', xmlError) + self.send_response(500) + return + + if not options.fail: + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.send_header('Content-Length', len('')) + self.end_headers() + self.logResponse(200) + self.wfile.write('') + self.wfile.close() + else: + self.send_response(500) + self.send_header('Content-Type', 'text/html') + self.send_header('Content-Length', len('gotcha!')) + self.end_headers() + self.wfile.write('gotcha!') + self.wfile.close() + + self.logResponse(500) + + def log_request(code, size): + pass + +if __name__ == '__main__': + + logging.info('[ConciergeServer] Concierge Broker Test Server starting') + + parser = optparse.OptionParser() + parser.add_option('-p', '--port', dest = 'port', help = 'port to listen on', metavar = 'PORT') + parser.add_option('-f', '--fail', dest = 'fail', action = 'store_true', help = 'always fail POST requests') + + (options, args) = parser.parse_args() + + httpServer = BaseHTTPServer.HTTPServer(('', 8080), ConciergeHandler) + try: + httpServer.serve_forever() + except KeyboardInterrupt: + logging.info('[ConciergeServer] terminating') + + httpServer.server_close() diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/AsterixVoice/AsteriskVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/AsterixVoice/AsteriskVoiceModule.cs new file mode 100644 index 0000000..c827214 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/AsterixVoice/AsteriskVoiceModule.cs @@ -0,0 +1,292 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections; +using System.Reflection; +using OpenMetaverse; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Communications.Capabilities; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Caps=OpenSim.Framework.Communications.Capabilities.Caps; + +namespace OpenSim.Region.OptionalModules.Avatar.Voice.AsterixVoice +{ + public class AsteriskVoiceModule : IRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly string m_parcelVoiceInfoRequestPath = "0007/"; + private static readonly string m_provisionVoiceAccountRequestPath = "0008/"; + + private string m_asterisk; + private string m_asterisk_password; + private string m_asterisk_salt; + private int m_asterisk_timeout; + private string m_confDomain; + private IConfig m_config; + private Scene m_scene; + private string m_sipDomain; + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource config) + { + m_scene = scene; + m_config = config.Configs["AsteriskVoice"]; + + if (null == m_config) + { + m_log.Info("[ASTERISKVOICE] no config found, plugin disabled"); + return; + } + + if (!m_config.GetBoolean("enabled", false)) + { + m_log.Info("[ASTERISKVOICE] plugin disabled by configuration"); + return; + } + m_log.Info("[ASTERISKVOICE] plugin enabled"); + + try + { + m_sipDomain = m_config.GetString("sip_domain", String.Empty); + m_log.InfoFormat("[ASTERISKVOICE] using SIP domain {0}", m_sipDomain); + + m_confDomain = m_config.GetString("conf_domain", String.Empty); + m_log.InfoFormat("[ASTERISKVOICE] using conf domain {0}", m_confDomain); + + m_asterisk = m_config.GetString("asterisk_frontend", String.Empty); + m_asterisk_password = m_config.GetString("asterisk_password", String.Empty); + m_asterisk_timeout = m_config.GetInt("asterisk_timeout", 3000); + m_asterisk_salt = m_config.GetString("asterisk_salt", "Wuffwuff"); + if (String.IsNullOrEmpty(m_asterisk)) throw new Exception("missing asterisk_frontend config parameter"); + if (String.IsNullOrEmpty(m_asterisk_password)) throw new Exception("missing asterisk_password config parameter"); + m_log.InfoFormat("[ASTERISKVOICE] using asterisk front end {0}", m_asterisk); + + scene.EventManager.OnRegisterCaps += OnRegisterCaps; + } + catch (Exception e) + { + m_log.ErrorFormat("[ASTERISKVOICE] plugin initialization failed: {0}", e.Message); + m_log.DebugFormat("[ASTERISKVOICE] plugin initialization failed: {0}", e.ToString()); + return; + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "AsteriskVoiceModule"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + #endregion + + public void OnRegisterCaps(UUID agentID, Caps caps) + { + m_log.DebugFormat("[ASTERISKVOICE] OnRegisterCaps: agentID {0} caps {1}", agentID, caps); + string capsBase = "/CAPS/" + caps.CapsObjectPath; + caps.RegisterHandler("ParcelVoiceInfoRequest", + new RestStreamHandler("POST", capsBase + m_parcelVoiceInfoRequestPath, + delegate(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + return ParcelVoiceInfoRequest(request, path, param, + agentID, caps); + })); + caps.RegisterHandler("ProvisionVoiceAccountRequest", + new RestStreamHandler("POST", capsBase + m_provisionVoiceAccountRequestPath, + delegate(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + return ProvisionVoiceAccountRequest(request, path, param, + agentID, caps); + })); + } + + /// + /// Callback for a client request for ParcelVoiceInfo + /// + /// + /// + /// + /// + /// + /// + public string ParcelVoiceInfoRequest(string request, string path, string param, + UUID agentID, Caps caps) + { + // we need to do: + // - send channel_uri: as "sip:regionID@m_sipDomain" + try + { + m_log.DebugFormat("[ASTERISKVOICE][PARCELVOICE]: request: {0}, path: {1}, param: {2}", + request, path, param); + + + // setup response to client + Hashtable creds = new Hashtable(); + creds["channel_uri"] = String.Format("sip:{0}@{1}", + m_scene.RegionInfo.RegionID, m_sipDomain); + + string regionName = m_scene.RegionInfo.RegionName; + ScenePresence avatar = m_scene.GetScenePresence(agentID); + if (null == m_scene.LandChannel) throw new Exception("land data not yet available"); + LandData land = m_scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); + + LLSDParcelVoiceInfoResponse parcelVoiceInfo = + new LLSDParcelVoiceInfoResponse(regionName, land.LocalID, creds); + + string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo); + + + // update region on asterisk-opensim frontend + Hashtable requestData = new Hashtable(); + requestData["admin_password"] = m_asterisk_password; + requestData["region"] = m_scene.RegionInfo.RegionID.ToString(); + if (!String.IsNullOrEmpty(m_confDomain)) + { + requestData["region"] += String.Format("@{0}", m_confDomain); + } + + ArrayList SendParams = new ArrayList(); + SendParams.Add(requestData); + XmlRpcRequest updateAccountRequest = new XmlRpcRequest("region_update", SendParams); + XmlRpcResponse updateAccountResponse = updateAccountRequest.Send(m_asterisk, m_asterisk_timeout); + Hashtable responseData = (Hashtable) updateAccountResponse.Value; + + if (!responseData.ContainsKey("success")) throw new Exception("region_update call failed"); + + bool success = Convert.ToBoolean((string) responseData["success"]); + if (!success) throw new Exception("region_update failed"); + + + m_log.DebugFormat("[ASTERISKVOICE][PARCELVOICE]: {0}", r); + return r; + } + catch (Exception e) + { + m_log.ErrorFormat("[ASTERISKVOICE][CAPS][PARCELVOICE]: {0}, retry later", e.Message); + m_log.DebugFormat("[ASTERISKVOICE][CAPS][PARCELVOICE]: {0} failed", e.ToString()); + + return "undef"; + } + } + + /// + /// Callback for a client request for Voice Account Details + /// + /// + /// + /// + /// + /// + /// + public string ProvisionVoiceAccountRequest(string request, string path, string param, + UUID agentID, Caps caps) + { + // we need to + // - get user data from UserProfileCacheService + // - generate nonce for user voice account password + // - issue XmlRpc request to asterisk opensim front end: + // + user: base 64 encoded user name (otherwise SL + // client is unhappy) + // + password: nonce + // - the XmlRpc call to asteris-opensim was successful: + // send account details back to client + try + { + m_log.DebugFormat("[ASTERISKVOICE][PROVISIONVOICE]: request: {0}, path: {1}, param: {2}", + request, path, param); + + // get user data & prepare voice account response + string voiceUser = "x" + Convert.ToBase64String(agentID.GetBytes()); + voiceUser = voiceUser.Replace('+', '-').Replace('/', '_'); + + CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(agentID); + if (null == userInfo) throw new Exception("cannot get user details"); + + // we generate a nonce everytime + string voicePassword = "$1$" + Util.Md5Hash(DateTime.UtcNow.ToLongTimeString() + m_asterisk_salt); + LLSDVoiceAccountResponse voiceAccountResponse = + new LLSDVoiceAccountResponse(voiceUser, voicePassword); + string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse); + m_log.DebugFormat("[CAPS][PROVISIONVOICE]: {0}", r); + + + // update user account on asterisk frontend + Hashtable requestData = new Hashtable(); + requestData["admin_password"] = m_asterisk_password; + requestData["username"] = voiceUser; + if (!String.IsNullOrEmpty(m_sipDomain)) + { + requestData["username"] += String.Format("@{0}", m_sipDomain); + } + requestData["password"] = voicePassword; + + ArrayList SendParams = new ArrayList(); + SendParams.Add(requestData); + XmlRpcRequest updateAccountRequest = new XmlRpcRequest("account_update", SendParams); + XmlRpcResponse updateAccountResponse = updateAccountRequest.Send(m_asterisk, m_asterisk_timeout); + Hashtable responseData = (Hashtable) updateAccountResponse.Value; + + if (!responseData.ContainsKey("success")) throw new Exception("account_update call failed"); + + bool success = Convert.ToBoolean((string) responseData["success"]); + if (!success) throw new Exception("account_update failed"); + + return r; + } + catch (Exception e) + { + m_log.ErrorFormat("[ASTERISKVOICE][CAPS][PROVISIONVOICE]: {0}, retry later", e.Message); + m_log.DebugFormat("[ASTERISKVOICE][CAPS][PROVISIONVOICE]: {0} failed", e.ToString()); + + return "undef"; + } + } + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/SIPVoice/SIPVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/SIPVoice/SIPVoiceModule.cs new file mode 100644 index 0000000..3e8a433 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/SIPVoice/SIPVoiceModule.cs @@ -0,0 +1,202 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections; +using System.Reflection; +using OpenMetaverse; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Communications.Capabilities; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Caps=OpenSim.Framework.Communications.Capabilities.Caps; + +namespace OpenSim.Region.OptionalModules.Avatar.Voice.SIPVoice +{ + public class SIPVoiceModule : IRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly string m_parcelVoiceInfoRequestPath = "0007/"; + private static readonly string m_provisionVoiceAccountRequestPath = "0008/"; + private IConfig m_config; + private Scene m_scene; + private string m_sipDomain; + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource config) + { + m_scene = scene; + m_config = config.Configs["Voice"]; + + if (null == m_config || !m_config.GetBoolean("enabled", false)) + { + m_log.Info("[VOICE] plugin disabled"); + return; + } + m_log.Info("[VOICE] plugin enabled"); + + m_sipDomain = m_config.GetString("sip_domain", String.Empty); + if (String.IsNullOrEmpty(m_sipDomain)) + { + m_log.Error("[VOICE] plugin mis-configured: missing sip_domain configuration"); + m_log.Info("[VOICE] plugin disabled"); + return; + } + m_log.InfoFormat("[VOICE] using SIP domain {0}", m_sipDomain); + + scene.EventManager.OnRegisterCaps += OnRegisterCaps; + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "VoiceModule"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + #endregion + + public void OnRegisterCaps(UUID agentID, Caps caps) + { + m_log.DebugFormat("[VOICE] OnRegisterCaps: agentID {0} caps {1}", agentID, caps); + string capsBase = "/CAPS/" + caps.CapsObjectPath; + caps.RegisterHandler("ParcelVoiceInfoRequest", + new RestStreamHandler("POST", capsBase + m_parcelVoiceInfoRequestPath, + delegate(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + return ParcelVoiceInfoRequest(request, path, param, + agentID, caps); + })); + caps.RegisterHandler("ProvisionVoiceAccountRequest", + new RestStreamHandler("POST", capsBase + m_provisionVoiceAccountRequestPath, + delegate(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + return ProvisionVoiceAccountRequest(request, path, param, + agentID, caps); + })); + } + + /// + /// Callback for a client request for ParcelVoiceInfo + /// + /// + /// + /// + /// + /// + /// + public string ParcelVoiceInfoRequest(string request, string path, string param, + UUID agentID, Caps caps) + { + try + { + m_log.DebugFormat("[VOICE][PARCELVOICE]: request: {0}, path: {1}, param: {2}", request, path, param); + + // FIXME: get the creds from region file or from config + Hashtable creds = new Hashtable(); + + creds["channel_uri"] = String.Format("sip:{0}@{1}", agentID, m_sipDomain); + + string regionName = m_scene.RegionInfo.RegionName; + ScenePresence avatar = m_scene.GetScenePresence(agentID); + if (null == m_scene.LandChannel) throw new Exception("land data not yet available"); + LandData land = m_scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); + + LLSDParcelVoiceInfoResponse parcelVoiceInfo = + new LLSDParcelVoiceInfoResponse(regionName, land.LocalID, creds); + + string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo); + m_log.DebugFormat("[VOICE][PARCELVOICE]: {0}", r); + + return r; + } + catch (Exception e) + { + m_log.ErrorFormat("[CAPS]: {0}, try again later", e.ToString()); + } + + return null; + } + + /// + /// Callback for a client request for Voice Account Details + /// + /// + /// + /// + /// + /// + /// + public string ProvisionVoiceAccountRequest(string request, string path, string param, + UUID agentID, Caps caps) + { + try + { + m_log.DebugFormat("[VOICE][PROVISIONVOICE]: request: {0}, path: {1}, param: {2}", + request, path, param); + + string voiceUser = "x" + Convert.ToBase64String(agentID.GetBytes()); + voiceUser = voiceUser.Replace('+', '-').Replace('/', '_'); + + CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(agentID); + if (null == userInfo) throw new Exception("cannot get user details"); + + LLSDVoiceAccountResponse voiceAccountResponse = + new LLSDVoiceAccountResponse(voiceUser, "$1$" + userInfo.UserProfile.PasswordHash); + string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse); + m_log.DebugFormat("[CAPS][PROVISIONVOICE]: {0}", r); + return r; + } + catch (Exception e) + { + m_log.ErrorFormat("[CAPS][PROVISIONVOICE]: {0}, retry later", e.Message); + } + + return null; + } + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/AuraMetaEntity.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/AuraMetaEntity.cs new file mode 100644 index 0000000..d4acc34 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/AuraMetaEntity.cs @@ -0,0 +1,161 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// AuraMetaEntity.cs created with MonoDevelop +// User: bongiojp at 3:03 PM 8/6/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Drawing; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class AuraMetaEntity : PointMetaEntity + { + #region Constructors + + //transparency of root part, NOT particle system. Should probably add support for changing particle system transparency. + public AuraMetaEntity(Scene scene, Vector3 groupPos, float transparency, Vector3 color, Vector3 scale) + : base(scene, groupPos, transparency) + { + SetAura(color, scale); + } + + public AuraMetaEntity(Scene scene, UUID uuid, Vector3 groupPos, float transparency, Vector3 color, Vector3 scale) + : base(scene, uuid, groupPos, transparency) + { + SetAura(color, scale); + } + + #endregion Constructors + + #region Private Methods + + private float Average(Vector3 values) + { + return (values.X + values.Y + values.Z)/3f; + } + + #endregion Private Methods + + #region Public Methods + + public void SetAura(Vector3 color, Vector3 scale) + { + SetAura(color, Average(scale) * 2.0f); + } + + public void SetAura(Vector3 color, float radius) + { + SceneObjectPart From = m_Entity.RootPart; + + //m_log.Debug("[META ENTITY] BEFORE: radius = " + radius); + float burstRadius = 0.1f; + Primitive.ParticleSystem.SourcePattern patternFlags = Primitive.ParticleSystem.SourcePattern.None; + float age = 1.5f; + float burstRate = 0.4f; + if (radius >= 8.0f) + { + //float sizeOfObject = radius / 2.0f; + burstRadius = (radius - 8.0f)/3f; + burstRate = 1.5f; + radius = 7.99f; + patternFlags = Primitive.ParticleSystem.SourcePattern.Explode; + age = 4.0f; + } + SetAura(From, color, radius, burstRadius, age, burstRate, patternFlags); + } + + public void SetAura(SceneObjectPart From, Vector3 color, float radius, float burstRadius, float age, float burstRate, Primitive.ParticleSystem.SourcePattern patternFlags) + { + Primitive.ParticleSystem prules = new Primitive.ParticleSystem(); + //prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Emissive | + // Primitive.ParticleSystem.ParticleDataFlags.FollowSrc; //PSYS_PART_FLAGS + //prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Beam | + // Primitive.ParticleSystem.ParticleDataFlags.TargetPos; + prules.PartStartColor.R = color.X; //PSYS_PART_START_COLOR + prules.PartStartColor.G = color.Y; + prules.PartStartColor.B = color.Z; + prules.PartStartColor.A = 0.5f; //PSYS_PART_START_ALPHA, transparency + prules.PartEndColor.R = color.X; //PSYS_PART_END_COLOR + prules.PartEndColor.G = color.Y; + prules.PartEndColor.B = color.Z; + prules.PartEndColor.A = 0.5f; //PSYS_PART_END_ALPHA, transparency + /*prules.PartStartScaleX = 0.5f; //PSYS_PART_START_SCALE + prules.PartStartScaleY = 0.5f; + prules.PartEndScaleX = 0.5f; //PSYS_PART_END_SCALE + prules.PartEndScaleY = 0.5f; + */ + prules.PartStartScaleX = radius; //PSYS_PART_START_SCALE + prules.PartStartScaleY = radius; + prules.PartEndScaleX = radius; //PSYS_PART_END_SCALE + prules.PartEndScaleY = radius; + prules.PartMaxAge = age; //PSYS_PART_MAX_AGE + prules.PartAcceleration.X = 0.0f; //PSYS_SRC_ACCEL + prules.PartAcceleration.Y = 0.0f; + prules.PartAcceleration.Z = 0.0f; + prules.Pattern = patternFlags; //PSYS_SRC_PATTERN + //prules.Texture = UUID.Zero;//= UUID //PSYS_SRC_TEXTURE, default used if blank + prules.BurstRate = burstRate; //PSYS_SRC_BURST_RATE + prules.BurstPartCount = 2; //PSYS_SRC_BURST_PART_COUNT + //prules.BurstRadius = radius; //PSYS_SRC_BURST_RADIUS + prules.BurstRadius = burstRadius; //PSYS_SRC_BURST_RADIUS + prules.BurstSpeedMin = 0.001f; //PSYS_SRC_BURST_SPEED_MIN + prules.BurstSpeedMax = 0.001f; //PSYS_SRC_BURST_SPEED_MAX + prules.MaxAge = 0.0f; //PSYS_SRC_MAX_AGE + //prules.Target = To; //PSYS_SRC_TARGET_KEY + prules.AngularVelocity.X = 0.0f; //PSYS_SRC_OMEGA + prules.AngularVelocity.Y = 0.0f; + prules.AngularVelocity.Z = 0.0f; + prules.InnerAngle = 0.0f; //PSYS_SRC_ANGLE_BEGIN + prules.OuterAngle = 0.0f; //PSYS_SRC_ANGLE_END + + prules.CRC = 1; //activates the particle system?? + From.AddNewParticleSystem(prules); + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/BeamMetaEntity.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/BeamMetaEntity.cs new file mode 100644 index 0000000..c0a0603 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/BeamMetaEntity.cs @@ -0,0 +1,139 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// BeamMetaEntity.cs created with MonoDevelop +// User: bongiojp at 3:03 PM 8/6/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Drawing; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class BeamMetaEntity : PointMetaEntity + { + #region Constructors + + public BeamMetaEntity(Scene scene, Vector3 groupPos, float transparency, SceneObjectPart To, Vector3 color) + : base(scene, groupPos, transparency) + { + SetBeamToUUID(To, color); + } + + public BeamMetaEntity(Scene scene, UUID uuid, Vector3 groupPos, float transparency, SceneObjectPart To, Vector3 color) + : base(scene, uuid, groupPos, transparency) + { + SetBeamToUUID(To, color); + } + + #endregion Constructors + + #region Public Methods + + public void SetBeamToUUID(SceneObjectPart To, Vector3 color) + { + SceneObjectPart From = m_Entity.RootPart; + //Scale size of particles to distance objects are apart (for better visibility) + Vector3 FromPos = From.GetWorldPosition(); + Vector3 ToPos = From.GetWorldPosition(); + // UUID toUUID = To.UUID; + float distance = (float) (Math.Sqrt(Math.Pow(FromPos.X-ToPos.X, 2) + + Math.Pow(FromPos.X-ToPos.Y, 2) + + Math.Pow(FromPos.X-ToPos.Z, 2) + ) + ); + //float rate = (float) (distance/4f); + float rate = 0.5f; + float scale = (float) (distance/128f); + float speed = (float) (2.0f - distance/128f); + + SetBeamToUUID(From, To, color, rate, scale, speed); + } + + public void SetBeamToUUID(SceneObjectPart From, SceneObjectPart To, Vector3 color, float rate, float scale, float speed) + { + Primitive.ParticleSystem prules = new Primitive.ParticleSystem(); + //prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Emissive | + // Primitive.ParticleSystem.ParticleDataFlags.FollowSrc; //PSYS_PART_FLAGS + prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Beam | + Primitive.ParticleSystem.ParticleDataFlags.TargetPos; + prules.PartStartColor.R = color.X; //PSYS_PART_START_COLOR + prules.PartStartColor.G = color.Y; + prules.PartStartColor.B = color.Z; + prules.PartStartColor.A = 1.0f; //PSYS_PART_START_ALPHA, transparency + prules.PartEndColor.R = color.X; //PSYS_PART_END_COLOR + prules.PartEndColor.G = color.Y; + prules.PartEndColor.B = color.Z; + prules.PartEndColor.A = 1.0f; //PSYS_PART_END_ALPHA, transparency + prules.PartStartScaleX = scale; //PSYS_PART_START_SCALE + prules.PartStartScaleY = scale; + prules.PartEndScaleX = scale; //PSYS_PART_END_SCALE + prules.PartEndScaleY = scale; + prules.PartMaxAge = 1.0f; //PSYS_PART_MAX_AGE + prules.PartAcceleration.X = 0.0f; //PSYS_SRC_ACCEL + prules.PartAcceleration.Y = 0.0f; + prules.PartAcceleration.Z = 0.0f; + //prules.Pattern = Primitive.ParticleSystem.SourcePattern.Explode; //PSYS_SRC_PATTERN + //prules.Texture = UUID.Zero;//= UUID //PSYS_SRC_TEXTURE, default used if blank + prules.BurstRate = rate; //PSYS_SRC_BURST_RATE + prules.BurstPartCount = 1; //PSYS_SRC_BURST_PART_COUNT + prules.BurstRadius = 0.5f; //PSYS_SRC_BURST_RADIUS + prules.BurstSpeedMin = speed; //PSYS_SRC_BURST_SPEED_MIN + prules.BurstSpeedMax = speed; //PSYS_SRC_BURST_SPEED_MAX + prules.MaxAge = 0.0f; //PSYS_SRC_MAX_AGE + prules.Target = To.UUID; //PSYS_SRC_TARGET_KEY + prules.AngularVelocity.X = 0.0f; //PSYS_SRC_OMEGA + prules.AngularVelocity.Y = 0.0f; + prules.AngularVelocity.Z = 0.0f; + prules.InnerAngle = 0.0f; //PSYS_SRC_ANGLE_BEGIN + prules.OuterAngle = 0.0f; //PSYS_SRC_ANGLE_END + + prules.CRC = 1; //activates the particle system?? + From.AddNewParticleSystem(prules); + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs new file mode 100644 index 0000000..80989a7 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs @@ -0,0 +1,757 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// CMController.cs +// User: bongiojp +// + +#endregion Header + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +using OpenMetaverse; + +using OpenSim; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + /// + /// The controller in a Model-View-Controller framework. This controller catches actions by the avatars, creates work packets, loops through these work packets in a separate thread, + /// then dictates to the model how the data should change and dictates to the view which data should be displayed. The main mechanism for interaction is through the simchat system. + /// + public class CMController + { + #region Static Fields + + private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The queue that keeps track of which actions have happened. The MainLoop thread eats through this queue. + /// + private static OpenSim.Framework.BlockingQueue m_WorkQueue = new OpenSim.Framework.BlockingQueue(); + + #endregion Static Fields + + #region Fields + + //bool init = false; + int m_channel = -1; + + /// + /// The estate module is used to identify which clients are estateManagers. Presently, the controller only pays attention to estate managers. + /// + IEstateModule m_estateModule = null; + + //These have to be global variables, threading doesn't allow for passing parameters. (Used in MainLoop) + CMModel m_model = null; + + /// + /// A list of all the scenes that should be revisioned. Controller is the only class that keeps track of all scenes in the region. + /// + Hashtable m_sceneList = Hashtable.Synchronized(new Hashtable()); + State m_state = State.NONE; + Thread m_thread = null; + CMView m_view = null; + + #endregion Fields + + #region Constructors + + /// + /// Initializes a work thread with an initial scene. Additional scenes should be added through the RegisterNewRegion method. + /// + /// + /// + /// + /// + /// + /// + /// + /// The first scene to keep track of. + /// + /// + /// The simchat channel number to listen to for instructions + /// + public CMController(CMModel model, CMView view, Scene scene, int channel) + { + m_model = model; m_view = view; m_channel = channel; + RegisterNewRegion(scene); + Initialize(model, view, scene, channel); + } + + #endregion Constructors + + #region Private Methods + + //------------------------------------------------ EVENTS ----------------------------------------------------// +// private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, LLUUID regionID) +// { +// } + + /// + /// Searches in all scenes for a SceneObjectGroup that contains a part with a specific localID. If found, the object is returned. Else null is returned. + /// + private SceneObjectGroup GetGroupByPrim(uint localID) + { + foreach (Object currScene in m_sceneList.Values) + { + foreach (EntityBase ent in ((Scene)currScene).GetEntities()) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).HasChildPrim(localID)) + return (SceneObjectGroup)ent; + } + } + } + return null; + } + + private void Initialize(CMModel model, CMView view, Scene scene, int channel) + { + lock (this) + { + m_estateModule = scene.RequestModuleInterface(); + m_thread = new Thread(MainLoop); + m_thread.Name = "Content Management"; + m_thread.IsBackground = true; + m_thread.Start(); + ThreadTracker.Add(m_thread); + m_state = State.NONE; + } + } + + /// + /// Run in a thread of its own. A endless loop that consumes (or blocks on) and work queue. Thw work queue is filled through client actions. + /// + private void MainLoop() + { + try + { + CMModel model = m_model; CMView view = m_view; int channel = m_channel; + Work currentJob = new Work(); + while (true) + { + currentJob = m_WorkQueue.Dequeue(); + m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- DeQueued a request"); + m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Work type: " + currentJob.Type); + switch (currentJob.Type) + { + case WorkType.NONE: + break; + case WorkType.OBJECTATTRIBUTECHANGE: + ObjectAttributeChanged(model, view, currentJob.LocalId); + break; + case WorkType.PRIMITIVEADDED: + PrimitiveAdded(model, view, currentJob); + break; + case WorkType.OBJECTDUPLICATED: + ObjectDuplicated(model, view, currentJob.LocalId); + break; + case WorkType.OBJECTKILLED: + ObjectKilled(model, view, (SceneObjectGroup) currentJob.Data1); + break; + case WorkType.UNDODID: + UndoDid(model, view, currentJob.UUID); + break; + case WorkType.NEWCLIENT: + NewClient(view, (IClientAPI) currentJob.Data1); + break; + case WorkType.SIMCHAT: + m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Message received: " + ((OSChatMessage) currentJob.Data1).Message); + SimChat(model, view, (OSChatMessage) currentJob.Data1, channel); + break; + default: + m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- uuuuuuuuuh, what?"); + break; + } + } + } + catch (Exception e) + { + // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened + m_log.ErrorFormat( + "[CONTENT MANAGEMENT]: Content management thread terminating with exception. PLEASE REBOOT YOUR SIM - CONTENT MANAGEMENT WILL NOT BE AVAILABLE UNTIL YOU DO. Exception is {0}", + e); + } + } + + /// + /// Only called by the MainLoop. Updates the view of a new client with metaentities if diff-mode is currently enabled. + /// + private void NewClient(CMView view, IClientAPI client) + { + if ((m_state & State.SHOWING_CHANGES) > 0) + view.SendMetaEntitiesToNewClient(client); + } + + /// + /// Only called by the MainLoop. + /// + private void ObjectAttributeChanged(CMModel model, CMView view, uint LocalId) + { + SceneObjectGroup group = null; + if ((m_state & State.SHOWING_CHANGES) > 0) + { + group = GetGroupByPrim(LocalId); + if (group != null) + { + view.DisplayAuras(model.UpdateNormalEntityEffects(group)); //Might be a normal entity (green aura) + m_view.DisplayMetaEntity(group.UUID); //Might be a meta entity (blue aura) + } + } + } + + /// + /// Only called by the MainLoop. Displays new green auras over the newly created part when a part is shift copied. + /// + private void ObjectDuplicated(CMModel model, CMView view, uint localId) + { + if ((m_state & State.SHOWING_CHANGES) > 0) + view.DisplayAuras(model.CheckForNewEntitiesMissingAuras(GetGroupByPrim(localId).Scene)); + } + + /// + /// Only called by the MainLoop. + /// + private void ObjectKilled(CMModel model, CMView view, SceneObjectGroup group) + { + if ((m_state & State.SHOWING_CHANGES) > 0) + { + view.RemoveOrUpdateDeletedEntity(group); + model.RemoveOrUpdateDeletedEntity(group); + } + } + + /// + /// Only called by the MainLoop. + /// + private void PrimitiveAdded(CMModel model, CMView view, Work currentJob) + { + if ((m_state & State.SHOWING_CHANGES) > 0) + { + foreach (Object scene in m_sceneList.Values) + m_view.DisplayAuras(model.CheckForNewEntitiesMissingAuras((Scene) scene)); + } + } + + /// + /// Only called by the MainLoop. + /// + private void UndoDid(CMModel model, CMView view, UUID uuid) + { + if ((m_state & State.SHOWING_CHANGES) > 0) + { + ContentManagementEntity ent = model.FindMetaEntityAffectedByUndo(uuid); + if (ent != null) + view.DisplayEntity(ent); + } + } + + #endregion Private Methods + + #region Protected Methods + + protected void GroupBeingDeleted(SceneObjectGroup group) + { + m_log.Debug("[CONTENT MANAGEMENT] Something was deleted!!!"); + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTKILLED; + moreWork.Data1 = group.Copy(); + m_WorkQueue.Enqueue(moreWork); + } + + protected void ObjectDuplicated(uint localID, Vector3 offset, uint dupeFlags, UUID AgentID, UUID GroupID) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTDUPLICATED; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] dup queue"); + } + + protected void ObjectDuplicatedOnRay(uint localID, uint dupeFlags, UUID AgentID, UUID GroupID, + UUID RayTargetObj, Vector3 RayEnd, Vector3 RayStart, + bool BypassRaycast, bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTDUPLICATED; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] dup queue"); + } + + protected void OnNewClient(IClientAPI client) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.NEWCLIENT; + moreWork.Data1 = client; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] new client"); + } + + protected void OnUnDid(IClientAPI remoteClient, UUID primId) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.UNDODID; + moreWork.UUID = primId; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] undid"); + } + + /// + /// Takes a list of scenes and forms a new orderd list according to the proximity of scenes to the second argument. + /// + protected static System.Collections.Generic.List ScenesInOrderOfProximity(Hashtable sceneList, Scene scene) + { + int somethingAddedToList = 1; + System.Collections.Generic.List newList = new List(); + newList.Add(scene); + + if (!sceneList.ContainsValue(scene)) + { + foreach (Object sceneObj in sceneList) + newList.Add((Scene) sceneObj); + return newList; + } + + while (somethingAddedToList > 0) + { + somethingAddedToList = 0; + for (int i = 0; i < newList.Count; i++) + { + foreach (Object sceneObj in sceneList.Values) + { + if (newList[i].CheckNeighborRegion(((Scene)sceneObj).RegionInfo) && (!newList.Contains((Scene)sceneObj))) + { + newList.Add((Scene)sceneObj); + somethingAddedToList++; + } + } + } + } + + foreach (Object sceneObj in sceneList.Values) + if (!newList.Contains((Scene)sceneObj)) + newList.Add((Scene)sceneObj); + + return newList; + } + + //This is stupid, the same information is contained in the first and second argument + protected void SimChatSent(Object x, OSChatMessage e) + { + m_log.Debug("[CONTENT MANAGEMENT] SIMCHAT SENT !!!!!!!"); + m_log.Debug("[CONTENT MANAGEMENT] message was: " + e.Message); + Work moreWork = new Work(); + moreWork.Type = WorkType.SIMCHAT; + moreWork.Data1 = e; + m_WorkQueue.Enqueue(moreWork); + } + + /// + /// Adds extra handlers to a number of events so that the controller can produce work based on the client's actions. + /// + protected void StartManaging(IClientAPI client) + { + m_log.Debug("[CONTENT MANAGEMENT] Registering channel with chat services."); + // client.OnChatFromClient += SimChatSent; + //init = true; + + OnNewClient(client); + + m_log.Debug("[CONTENT MANAGEMENT] Adding handlers to client."); + client.OnUpdatePrimScale += UpdateSingleScale; + client.OnUpdatePrimGroupScale += UpdateMultipleScale; + client.OnUpdatePrimGroupPosition += UpdateMultiplePosition; + client.OnUpdatePrimSinglePosition += UpdateSinglePosition; + client.OnUpdatePrimGroupRotation += UpdateMultipleRotation; + client.OnUpdatePrimSingleRotation += UpdateSingleRotation; + client.OnAddPrim += UpdateNewParts; + client.OnObjectDuplicate += ObjectDuplicated; + client.OnObjectDuplicateOnRay += ObjectDuplicatedOnRay; + client.OnUndo += OnUnDid; + //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; + } + + /// + /// + /// + protected void StopManaging(UUID clientUUID) + { + foreach (Object sceneobj in m_sceneList.Values) + { + ScenePresence presence = ((Scene)sceneobj).GetScenePresence(clientUUID); + if (presence != null) + { + IClientAPI client = presence.ControllingClient; + m_log.Debug("[CONTENT MANAGEMENT] Unregistering channel with chat services."); + // client.OnChatFromViewer -= SimChatSent; + + m_log.Debug("[CONTENT MANAGEMENT] Removing handlers to client"); + client.OnUpdatePrimScale -= UpdateSingleScale; + client.OnUpdatePrimGroupScale -= UpdateMultipleScale; + client.OnUpdatePrimGroupPosition -= UpdateMultiplePosition; + client.OnUpdatePrimSinglePosition -= UpdateSinglePosition; + client.OnUpdatePrimGroupRotation -= UpdateMultipleRotation; + client.OnUpdatePrimSingleRotation -= UpdateSingleRotation; + client.OnAddPrim -= UpdateNewParts; + client.OnObjectDuplicate -= ObjectDuplicated; + client.OnObjectDuplicateOnRay -= ObjectDuplicatedOnRay; + client.OnUndo -= OnUnDid; + //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; + return; + } + } + } + + protected void UpdateMultiplePosition(uint localID, Vector3 pos, IClientAPI remoteClient) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] pos"); + } + + protected void UpdateMultipleRotation(uint localID, Quaternion rot, IClientAPI remoteClient) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] rot"); + } + + protected void UpdateMultipleScale(uint localID, Vector3 scale, IClientAPI remoteClient) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT]scale"); + } + + protected void UpdateNewParts(UUID ownerID, UUID groupID, Vector3 RayEnd, Quaternion rot, PrimitiveBaseShape shape, + byte bypassRaycast, Vector3 RayStart, UUID RayTargetID, + byte RayEndIsIntersection) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.PRIMITIVEADDED; + moreWork.UUID = ownerID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] new parts"); + } + + protected void UpdateSinglePosition(uint localID, Vector3 pos, IClientAPI remoteClient) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] move"); + } + + /// + /// + /// + protected void UpdateSingleRotation(uint localID, Quaternion rot, IClientAPI remoteClient) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] rot"); + } + + protected void UpdateSingleScale(uint localID, Vector3 scale, IClientAPI remoteClient) + { + Work moreWork = new Work(); + moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; + moreWork.LocalId = localID; + m_WorkQueue.Enqueue(moreWork); + m_log.Debug("[CONTENT MANAGEMENT] scale"); + } + + /// + /// Only called from within the SimChat method. + /// + protected void commit(string message, Scene scene, CMModel model, CMView view) + { + System.Collections.Generic.List proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene); + + string[] args = message.Split(new char[] {' '}); + + char[] logMessage = {' '}; + if (args.Length > 1) + { + logMessage = new char[message.Length - (args[0].Length)]; + message.CopyTo(args[0].Length, logMessage, 0, message.Length - (args[0].Length)); + } + + m_log.Debug("[CONTENT MANAGEMENT] Saving terrain and objects of region."); + foreach (Scene currScene in proximitySceneList) + { + model.CommitRegion(currScene, new String(logMessage)); + view.SendSimChatMessage(scene, "Region Saved Successfully: " + currScene.RegionInfo.RegionName); + } + + view.SendSimChatMessage(scene, "Successfully saved all regions."); + m_state |= State.DIRTY; + + if ((m_state & State.SHOWING_CHANGES) > 0) //DISPLAY NEW CHANGES INSTEAD OF OLD CHANGES + { + view.SendSimChatMessage(scene, "Updating differences between new revision and current environment."); + //Hide objects from users and Forget about them + view.HideAllMetaEntities(); + view.HideAllAuras(); + model.DeleteAllMetaObjects(); + + //Recreate them from backend files + foreach (Scene currScene in proximitySceneList) + { + model.UpdateCMEntities(currScene); + view.SendSimChatMessage(scene, "Finished updating differences between current scene and last revision: " + currScene.RegionInfo.RegionName); + } + + //Display new objects to users1 + view.DisplayRecentChanges(); + view.SendSimChatMessage(scene, "Finished updating for DIFF-MODE."); + m_state &= ~(State.DIRTY); + m_state |= State.SHOWING_CHANGES; + } + } + + /// + /// Only called from within the SimChat method. + /// + protected void diffmode(Scene scene, CMModel model, CMView view) + { + System.Collections.Generic.List proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene); + + if ((m_state & State.SHOWING_CHANGES) > 0) // TURN OFF + { + view.SendSimChatMessage(scene, "Hiding all meta objects."); + view.HideAllMetaEntities(); + view.HideAllAuras(); + view.SendSimChatMessage(scene, "Diff-mode = OFF"); + + m_state &= ~State.SHOWING_CHANGES; + return; + } + else // TURN ON + { + if ((m_state & State.DIRTY) != 0 || m_state == State.NONE) + { + view.SendSimChatMessage(scene, "Hiding meta objects and replacing with latest revision"); + //Hide objects from users and Forget about them + view.HideAllMetaEntities(); + view.HideAllAuras(); + model.DeleteAllMetaObjects(); + //Recreate them from backend files + foreach (Object currScene in m_sceneList.Values) + model.UpdateCMEntities((Scene) currScene); + } + else if ((m_state & State.DIRTY) != 0) { + view.SendSimChatMessage(scene, "Forming list of meta entities with latest revision"); + foreach (Scene currScene in proximitySceneList) + model.UpdateCMEntities(currScene); + } + + view.SendSimChatMessage(scene, "Displaying differences between last revision and current environment"); + foreach (Scene currScene in proximitySceneList) + model.CheckForNewEntitiesMissingAuras(currScene); + view.DisplayRecentChanges(); + + view.SendSimChatMessage(scene, "Diff-mode = ON"); + m_state |= State.SHOWING_CHANGES; + m_state &= ~State.DIRTY; + } + } + + /// + /// Only called from within the SimChat method. Hides all auras and meta entities, + /// retrieves the current scene object list with the most recent revision retrieved from the model for each scene, + /// then lets the view update the clients of the new objects. + /// + protected void rollback(Scene scene, CMModel model, CMView view) + { + if ((m_state & State.SHOWING_CHANGES) > 0) + { + view.HideAllAuras(); + view.HideAllMetaEntities(); + } + + System.Collections.Generic.List proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene); + foreach (Scene currScene in proximitySceneList) + model.RollbackRegion(currScene); + + if ((m_state & State.DIRTY) != 0) + { + model.DeleteAllMetaObjects(); + foreach (Scene currScene in proximitySceneList) + model.UpdateCMEntities(currScene); + } + + if ((m_state & State.SHOWING_CHANGES) > 0) + view.DisplayRecentChanges(); + } + + #endregion Protected Methods + + #region Public Methods + + /// + /// Register a new scene object to keep track of for revisioning. Starts the controller monitoring actions of clients within the given scene. + /// + /// + /// A + /// + public void RegisterNewRegion(Scene scene) + { + m_sceneList.Add(scene.RegionInfo.RegionID, scene); + + m_log.Debug("[CONTENT MANAGEMENT] Registering new region: " + scene.RegionInfo.RegionID); + m_log.Debug("[CONTENT MANAGEMENT] Initializing Content Management System."); + + scene.EventManager.OnNewClient += StartManaging; + scene.EventManager.OnChatFromClient += SimChatSent; + scene.EventManager.OnRemovePresence += StopManaging; + // scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; + scene.EventManager.OnObjectBeingRemovedFromScene += GroupBeingDeleted; + } + + /// + /// Only called by the MainLoop. Takes the message from a user sent to the channel and executes the proper command. + /// + public void SimChat(CMModel model, CMView view, OSChatMessage e, int channel) + { + if (e.Channel != channel) + return; + if (e.Sender == null) + return; + + m_log.Debug("[CONTENT MANAGEMENT] Message received: " + e.Message); + + IClientAPI client = e.Sender; + Scene scene = (Scene) e.Scene; + string message = e.Message; + string[] args = e.Message.Split(new char[] {' '}); + + ScenePresence avatar = scene.GetScenePresence(client.AgentId); + + if (!(m_estateModule.IsManager(avatar.UUID))) + { + m_log.Debug("[CONTENT MANAGEMENT] Message sent from non Estate Manager ... ignoring."); + view.SendSimChatMessage(scene, "You must be an estate manager to perform that action."); + return; + } + + switch (args[0]) + { + case "ci": + case "commit": + commit(message, scene, model, view); + break; + case "dm": + case "diff-mode": + diffmode(scene, model, view); + break; + case "rb": + case "rollback": + rollback(scene, model, view); + break; + case "help": + m_view.DisplayHelpMenu(scene); + break; + default: + view.SendSimChatMessage(scene, "Command not found: " + args[0]); + break; + } + } + + #endregion Public Methods + + #region Other + + /// + /// Used to keep track of whether a list has been produced yet and whether that list is up-to-date compard to latest revision on disk. + /// + [Flags] + private enum State + { + NONE = 0, + DIRTY = 1, // The meta entities may not correctly represent the last revision. + SHOWING_CHANGES = 1<<1 // The meta entities are being shown to user. + } + + /// + /// The structure that defines the basic unit of work which is produced when a user sends commands to the ContentMangaementSystem. + /// + private struct Work + { + #region Fields + + public Object Data1; //Just space for holding data. + public Object Data2; //Just more space for holding data. + public uint LocalId; //Convenient + public WorkType Type; + public UUID UUID; //Convenient + + #endregion Fields + } + + /// + /// Identifies what the data in struct Work should be used for. + /// + private enum WorkType + { + NONE, + OBJECTATTRIBUTECHANGE, + PRIMITIVEADDED, + OBJECTDUPLICATED, + OBJECTKILLED, + UNDODID, + NEWCLIENT, + SIMCHAT + } + + #endregion Other + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/CMEntityCollection.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMEntityCollection.cs new file mode 100644 index 0000000..d5cf596 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMEntityCollection.cs @@ -0,0 +1,193 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// CMEntityCollection.cs created with MonoDevelop +// User: bongiojp at 10:09 AM 7/7/2008 +// +// Creates, Deletes, Stores ContentManagementEntities +// + +#endregion Header + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class CMEntityCollection + { + #region Fields + + // private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + // Any ContentManagementEntities that represent old versions of current SceneObjectGroups or + // old versions of deleted SceneObjectGroups will be stored in this hash table. + // The UUID keys are from the SceneObjectGroup RootPart UUIDs + protected Hashtable m_CMEntityHash = Hashtable.Synchronized(new Hashtable()); //UUID to ContentManagementEntity + + // SceneObjectParts that have not been revisioned will be given green auras stored in this hashtable + // The UUID keys are from the SceneObjectPart that they are supposed to be on. + protected Hashtable m_NewlyCreatedEntityAura = Hashtable.Synchronized(new Hashtable()); //UUID to AuraMetaEntity + + #endregion Fields + + #region Constructors + + public CMEntityCollection() + { + } + + #endregion Constructors + + #region Public Properties + + public Hashtable Auras + { + get {return m_NewlyCreatedEntityAura; } + } + + public Hashtable Entities + { + get { return m_CMEntityHash; } + } + + #endregion Public Properties + + #region Public Methods + + public bool AddAura(ContentManagementEntity aura) + { + if (m_NewlyCreatedEntityAura.ContainsKey(aura.UUID)) + return false; + m_NewlyCreatedEntityAura.Add(aura.UUID, aura); + return true; + } + + public bool AddEntity(ContentManagementEntity ent) + { + if (m_CMEntityHash.ContainsKey(ent.UUID)) + return false; + m_CMEntityHash.Add(ent.UUID, ent); + return true; + } + + // Check if there are SceneObjectGroups in the list that do not have corresponding ContentManagementGroups in the CMEntityHash + public System.Collections.ArrayList CheckForMissingEntities(System.Collections.Generic.List currList) + { + System.Collections.ArrayList missingList = new System.Collections.ArrayList(); + SceneObjectGroup temp = null; + foreach (EntityBase currObj in currList) + { + if (!(currObj is SceneObjectGroup)) + continue; + temp = (SceneObjectGroup) currObj; + + if (m_CMEntityHash.ContainsKey(temp.UUID)) + { + foreach (SceneObjectPart part in temp.Children.Values) + if (!((ContentManagementEntity)m_CMEntityHash[temp.UUID]).HasChildPrim(part.UUID)) + missingList.Add(part); + } + else //Entire group is missing from revision. (and is a new part in region) + { + foreach (SceneObjectPart part in temp.Children.Values) + missingList.Add(part); + } + } + return missingList; + } + + public void ClearAll() + { + m_CMEntityHash.Clear(); + m_NewlyCreatedEntityAura.Clear(); + } + + // Old uuid and new sceneobjectgroup + public AuraMetaEntity CreateAuraForNewlyCreatedEntity(SceneObjectPart part) + { + AuraMetaEntity ent = new AuraMetaEntity(part.ParentGroup.Scene, + part.GetWorldPosition(), + MetaEntity.TRANSLUCENT, + new Vector3(0,254,0), + part.Scale + ); + m_NewlyCreatedEntityAura.Add(part.UUID, ent); + return ent; + } + + // Old uuid and new sceneobjectgroup + public ContentManagementEntity CreateNewEntity(SceneObjectGroup group) + { + ContentManagementEntity ent = new ContentManagementEntity(group, false); + m_CMEntityHash.Add(group.UUID, ent); + return ent; + } + + public ContentManagementEntity CreateNewEntity(String xml, Scene scene) + { + ContentManagementEntity ent = new ContentManagementEntity(xml, scene, false); + if (ent == null) + return null; + m_CMEntityHash.Add(ent.UnchangedEntity.UUID, ent); + return ent; + } + + public bool RemoveEntity(UUID uuid) + { + if (!m_CMEntityHash.ContainsKey(uuid)) + return false; + m_CMEntityHash.Remove(uuid); + return true; + } + + public bool RemoveNewlyCreatedEntityAura(UUID uuid) + { + if (!m_NewlyCreatedEntityAura.ContainsKey(uuid)) + return false; + m_NewlyCreatedEntityAura.Remove(uuid); + return true; + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/CMModel.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMModel.cs new file mode 100644 index 0000000..761dca9 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMModel.cs @@ -0,0 +1,362 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// CMModel.cs +// User: bongiojp +// +// + +#endregion Header + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +using OpenMetaverse; + +using OpenSim; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class CMModel + { + #region Static Fields + + static float TimeToUpdate = 0; + static float TimeToConvertXml = 0; + private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #endregion Static Fields + + #region Fields + + /// + /// The class that contains all auras and metaentities used in the CMS. + /// + CMEntityCollection m_MetaEntityCollection = new CMEntityCollection(); + IContentDatabase m_database = null; + + #endregion Fields + + #region Constructors + + public CMModel() + { + } + + #endregion Constructors + + #region Public Properties + + public CMEntityCollection MetaEntityCollection + { + get { return m_MetaEntityCollection; } + } + + #endregion Public Properties + + #region Public Methods + + /// + /// Compares the scene's object group list to the list of meta entities. If there is an object group that does not have a corresponding meta entity + /// it is a new part that must have a green aura (for diff mode). + /// Returns list of ContentManagementEntities + /// + public ArrayList CheckForNewEntitiesMissingAuras(Scene scene) + { + ArrayList missingList = null; + ArrayList newList = new ArrayList(); + + m_log.Debug("[CONTENT MANAGEMENT] Checking for new scene object parts in scene: " + scene.RegionInfo.RegionName); + + //Check if the current scene has groups not included in the current list of MetaEntities + //If so, then the current scene's parts that are new should be marked green. + missingList = m_MetaEntityCollection.CheckForMissingEntities(scene.GetEntities()); + + foreach (Object missingPart in missingList) + { + if (m_MetaEntityCollection.Auras.ContainsKey(((SceneObjectPart)missingPart).UUID)) + continue; + newList.Add(m_MetaEntityCollection.CreateAuraForNewlyCreatedEntity((SceneObjectPart)missingPart)); + } + m_log.Info("Number of missing objects found: " + newList.Count); + return newList; + } + + /// + /// Uses the database to serialize all current scene objects into xml and save into a database with an accompanying log message. + /// + public void CommitRegion(Scene scene, String logMessage) + { + m_log.Debug("[CONTENT MANAG] saving " + scene.RegionInfo.RegionName + " with log message: " + logMessage + " length of message: " + logMessage.Length); + m_database.SaveRegion(scene.RegionInfo.RegionID, scene.RegionInfo.RegionName, logMessage); + m_log.Debug("[CONTENT MANAG] the region name we are dealing with heeeeeeeere: " + scene.RegionInfo.RegionName ); + } + + public void DeleteAllMetaObjects() + { + m_MetaEntityCollection.ClearAll(); + } + + public ContentManagementEntity FindMetaEntityAffectedByUndo(UUID uuid) + { + ContentManagementEntity ent = GetMetaGroupByPrim(uuid); + return ent; + } + + //-------------------------------- HELPERS --------------------------------------------------------------------// + public ContentManagementEntity GetMetaGroupByPrim(UUID uuid) + { + foreach (Object ent in m_MetaEntityCollection.Entities.Values) + { + if (((ContentManagementEntity)ent).HasChildPrim(uuid)) + return (ContentManagementEntity)ent; + } + return null; + } + + public void Initialise(string database) + { + if (database == "FileSystemDatabase") + m_database = new FileSystemDatabase(); + else if (database == "GitDatabase") + m_database = new GitDatabase(); + } + + public void InitialiseDatabase(Scene scene, string dir) + { + m_database.Initialise(scene, dir); + } + + /// + /// Should be called just once to finish initializing the database. + /// + public void PostInitialise() + { + m_database.PostInitialise(); + } + + /// + /// Removes the green aura when an a new scene object group is deleted. + /// + public void RemoveOrUpdateDeletedEntity(SceneObjectGroup group) + { + // Deal with new parts not revisioned that have been deleted. + foreach (SceneObjectPart part in group.Children.Values) + if (m_MetaEntityCollection.Auras.ContainsKey(part.UUID)) + m_MetaEntityCollection.RemoveNewlyCreatedEntityAura(part.UUID); + } + + /// + /// Retrieves the latest revision of a region in xml form, + /// converts it to scene object groups and scene presences, + /// swaps the current scene's entity list with the revision's list. + /// Note: Since deleted objects while + /// + public void RollbackRegion(Scene scene) + { + System.Collections.ArrayList xmllist = null; + SceneObjectGroup temp = null; + System.Collections.Hashtable deleteListUUIDs = new Hashtable(); +// Dictionary SearchList = new Dictionary(); + Dictionary ReplacementList = new Dictionary(); + int revision = m_database.GetMostRecentRevision(scene.RegionInfo.RegionID); +// EntityBase[] searchArray; + + xmllist = m_database.GetRegionObjectXMLList(scene.RegionInfo.RegionID, revision); + if (xmllist == null) + { + m_log.Info("[CMMODEL]: Region (" + scene.RegionInfo.RegionID + ") does not have given revision number (" + revision + ")."); + return; + } + + m_log.Info("[CMMODEL]: Region (" + scene.RegionInfo.RegionID + ") revision number (" + revision + ")."); + m_log.Info("[CMMODEL]: Scene Objects = " + xmllist.Count); + m_log.Info("[CMMODEL]: Converting scene entities list to specified revision."); + + m_log.ErrorFormat("[CMMODEL]: 1"); + + foreach (string xml in xmllist) + { + try{ + temp = new SceneObjectGroup(xml); + temp.SetScene(scene); + foreach (SceneObjectPart part in temp.Children.Values) + part.RegionHandle = scene.RegionInfo.RegionHandle; + ReplacementList.Add(temp.UUID, (EntityBase)temp); + } + catch(Exception e) + { + m_log.Info("[CMMODEL]: Error while creating replacement list for rollback: " + e); + } + } + + //If in scene but not in revision and not a client, remove them + while (true) + { + try + { + foreach (EntityBase entity in scene.GetEntities()) + { + if (entity == null) + continue; + + if (entity is ScenePresence) + { + ReplacementList.Add(entity.UUID, entity); + continue; + } + else //if (!ReplacementList.ContainsKey(entity.UUID)) + deleteListUUIDs.Add(entity.UUID, 0); + } + } + catch(Exception e) + { + m_log.ErrorFormat("[CMMODEL]: " + e); + deleteListUUIDs.Clear(); + ReplacementList.Clear(); + continue; + } + break; + } + + foreach (UUID uuid in deleteListUUIDs.Keys) + { + try + { + // I thought that the DeleteGroup() function would handle all of this, but it doesn't. I'm not sure WHAT it handles. + ((SceneObjectGroup)scene.Entities[uuid]).DetachFromBackup(); + scene.PhysicsScene.RemovePrim(((SceneObjectGroup)scene.Entities[uuid]).RootPart.PhysActor); + scene.SendKillObject(scene.Entities[uuid].LocalId); + scene.m_sceneGraph.DeleteSceneObject(uuid, false); + ((SceneObjectGroup)scene.Entities[uuid]).DeleteGroup(false); + } + catch(Exception e) + { + m_log.ErrorFormat("[CMMODEL]: Error while removing objects from scene: " + e); + } + } + + lock (scene) + { + scene.Entities.Clear(); + + foreach (KeyValuePair kvp in ReplacementList) + { + scene.Entities.Add(kvp.Value); + } + } + + foreach (EntityBase ent in ReplacementList.Values) + { + try + { + if (!(ent is SceneObjectGroup)) + continue; + + if ((((SceneObjectGroup)ent).RootPart.GetEffectiveObjectFlags() & (uint) PrimFlags.Phantom) == 0) + ((SceneObjectGroup)ent).ApplyPhysics(true); + ((SceneObjectGroup)ent).AttachToBackup(); + ((SceneObjectGroup)ent).HasGroupChanged = true; // If not true, then attaching to backup does nothing because no change is detected. + ((SceneObjectGroup)ent).ScheduleGroupForFullUpdate(); + } + catch(Exception e) + { + m_log.ErrorFormat("[CMMODEL]: Error while attaching new scene entities to backup and scheduling for a full update: " + e); + } + } + m_log.Info("[CMMODEL]: Scheduling a backup of new scene object groups to backup."); + scene.Backup(); + } + + /// + /// Downloads the latest revision of the given scene and converts the xml file to CMEntities. After this method, the view can find the differences + /// and display the differences to clients. + /// + public void UpdateCMEntities(Scene scene) + { + Stopwatch x = new Stopwatch(); + x.Start(); + + System.Collections.ArrayList xmllist = null; + m_log.Debug("[CONTENT MANAGEMENT] Retrieving object xml files for region: " + scene.RegionInfo.RegionID); + xmllist = m_database.GetRegionObjectXMLList(scene.RegionInfo.RegionID); + m_log.Info("[FSDB]: got list"); + if (xmllist == null) + return; + + Stopwatch y = new Stopwatch(); + y.Start(); + foreach (string xml in xmllist) + m_MetaEntityCollection.CreateNewEntity(xml, scene); + y.Stop(); + TimeToConvertXml += y.ElapsedMilliseconds; + m_log.Info("[FileSystemDatabase] Time spent converting xml to metaentities for " + scene.RegionInfo.RegionName + ": " + y.ElapsedMilliseconds); + m_log.Info("[FileSystemDatabase] Time spent converting xml to metaentities so far: " + TimeToConvertXml); + + m_log.Info("[FSDB]: checking for new scene object parts missing green auras and create the auras"); + CheckForNewEntitiesMissingAuras(scene); + + x.Stop(); + TimeToUpdate += x.ElapsedMilliseconds; + m_log.Info("[FileSystemDatabase] Time spent Updating entity list for " + scene.RegionInfo.RegionName + ": " + x.ElapsedMilliseconds); + m_log.Info("[FileSystemDatabase] Time spent Updating so far: " + TimeToUpdate); + } + + /// + /// Detects if a scene object group from the scene list has moved or changed scale. The green aura + /// that surrounds the object is then moved or scaled with the group. + /// + public System.Collections.ArrayList UpdateNormalEntityEffects(SceneObjectGroup group) + { + System.Collections.ArrayList auraList = new System.Collections.ArrayList(); + if (group == null) + return null; + foreach (SceneObjectPart part in group.Children.Values) + { + if (m_MetaEntityCollection.Auras.ContainsKey(part.UUID)) + { + ((AuraMetaEntity)m_MetaEntityCollection.Auras[part.UUID]).SetAura(new Vector3(0,254,0), part.Scale); + ((AuraMetaEntity)m_MetaEntityCollection.Auras[part.UUID]).RootPart.GroupPosition = part.GetWorldPosition(); + auraList.Add((AuraMetaEntity)m_MetaEntityCollection.Auras[part.UUID]); + } + } + return auraList; + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/CMView.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMView.cs new file mode 100644 index 0000000..a1a4d94 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMView.cs @@ -0,0 +1,206 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// CMView.cs created with MonoDevelop +// User: bongiojp at 11:57 AM 7/3/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +#endregion Header + +using System; +using System.Collections; +using System.Collections.Generic; + +using OpenMetaverse; + +using OpenSim; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class CMView + { + #region Static Fields + + private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #endregion Static Fields + + #region Fields + + CMModel m_model = null; + + #endregion Fields + + #region Constructors + + public CMView() + { + } + + #endregion Constructors + + #region Public Methods + + // Auras To + public void DisplayAuras(CMEntityCollection auraCollection) + { + foreach (Object ent in auraCollection.Auras.Values) + ((AuraMetaEntity)ent).SendFullUpdateToAll(); + } + + // Auras To Client + public void DisplayAuras(CMEntityCollection auraCollection, IClientAPI client) + { + foreach (Object ent in auraCollection.Auras.Values) + ((AuraMetaEntity)ent).SendFullUpdate(client); + } + + // Auras from List To ALL + public void DisplayAuras(ArrayList list) + { + foreach (Object ent in list) + { + m_log.Debug("[CONTENT MANAGEMENT] displaying new aura riiiiiiiiiiiight NOW"); + ((AuraMetaEntity)ent).SendFullUpdateToAll(); + } + } + + // Entities to ALL + public void DisplayEntities(CMEntityCollection entityCollection) + { + foreach (Object ent in entityCollection.Entities.Values) + ((ContentManagementEntity)ent).SendFullDiffUpdateToAll(); + } + + // Entities to Client + public void DisplayEntities(CMEntityCollection entityCollection, IClientAPI client) + { + foreach (Object ent in entityCollection.Entities.Values) + ((ContentManagementEntity)ent).SendFullDiffUpdate(client); + } + + // Entities from List to ALL + public void DisplayEntities(ArrayList list) + { + foreach (Object ent in list) + ((ContentManagementEntity)ent).SendFullDiffUpdateToAll(); + } + + // Entity to ALL + public void DisplayEntity(ContentManagementEntity ent) + { + ent.SendFullDiffUpdateToAll(); + } + + public void DisplayHelpMenu(Scene scene) + { + string menu = "Menu:\n"; + menu += "commit (ci) - saves current state of the region to a database on the server\n"; + menu += "diff-mode (dm) - displays those aspects of region that have not been saved but changed since the very last revision. Will dynamically update as you change environment.\n"; + SendSimChatMessage(scene, menu); + } + + public void DisplayMetaEntity(UUID uuid) + { + ContentManagementEntity group = m_model.GetMetaGroupByPrim(uuid); + if (group != null) + group.SendFullDiffUpdateToAll(); + } + + /// + /// update all clients of red/green/blue auras and meta entities that the model knows about. + /// + public void DisplayRecentChanges() + { + m_log.Debug("[CONTENT MANAGEMENT] Sending update to clients for " + m_model.MetaEntityCollection.Entities.Count + " objects."); + DisplayEntities(m_model.MetaEntityCollection); + DisplayAuras(m_model.MetaEntityCollection); + } + + public void Hide(ContentManagementEntity ent) + { + ent.HideFromAll(); + } + + public void HideAllAuras() + { + foreach (Object obj in m_model.MetaEntityCollection.Auras.Values) + ((MetaEntity)obj).HideFromAll(); + } + + public void HideAllMetaEntities() + { + foreach (Object obj in m_model.MetaEntityCollection.Entities.Values) + ((ContentManagementEntity)obj).HideFromAll(); + } + + public void Initialise(CMModel model) + { + m_model = model; + } + + /// + /// Figures out if the part deleted was a new scene object part or a revisioned part that's been deleted. + /// If it's a new scene object, any green aura attached to it is deleted. + /// If a revisioned part is deleted, a new full update is sent to the environment of the meta entity, which will + /// figure out that there should be a red aura and not a blue aura/beam. + /// + public void RemoveOrUpdateDeletedEntity(SceneObjectGroup group) + { + // Deal with revisioned parts that have been deleted. + if (m_model.MetaEntityCollection.Entities.ContainsKey(group.UUID)) + ((ContentManagementEntity)m_model.MetaEntityCollection.Entities[group.UUID]).SendFullDiffUpdateToAll(); + + // Deal with new parts not revisioned that have been deleted. + foreach (SceneObjectPart part in group.Children.Values) + if (m_model.MetaEntityCollection.Auras.ContainsKey(part.UUID)) + ((AuraMetaEntity)m_model.MetaEntityCollection.Auras[part.UUID]).HideFromAll(); + } + + public void SendMetaEntitiesToNewClient(IClientAPI client) + { + } + + public void SendSimChatMessage(Scene scene, string message) + { + scene.SimChat(Utils.StringToBytes(message), + ChatTypeEnum.Broadcast, 0, new Vector3(0,0,0), "Content Manager", UUID.Zero, false); + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementEntity.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementEntity.cs new file mode 100644 index 0000000..4d65038 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementEntity.cs @@ -0,0 +1,383 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// ContentManagementEntity.cs +// User: bongiojp +// +// + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Drawing; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class ContentManagementEntity : MetaEntity + { + #region Static Fields + +// static float TimeToDiff = 0; +// static float TimeToCreateEntities = 0; + + #endregion Static Fields + + #region Fields + + protected Dictionary m_AuraEntities = new Dictionary(); + protected Dictionary m_BeamEntities = new Dictionary(); + + // The LinkNum of parts in m_Entity and m_UnchangedEntity are the same though UUID and LocalId are different. + // This can come in handy. + protected SceneObjectGroup m_UnchangedEntity = null; + + /// + /// Should be set to true when there is a difference between m_UnchangedEntity and the corresponding scene object group in the scene entity list. + /// + bool DiffersFromSceneGroup = false; + + #endregion Fields + + #region Constructors + + public ContentManagementEntity(SceneObjectGroup Unchanged, bool physics) + : base(Unchanged, false) + { + m_UnchangedEntity = Unchanged.Copy(Unchanged.RootPart.OwnerID, Unchanged.RootPart.GroupID, false); + } + + public ContentManagementEntity(string objectXML, Scene scene, bool physics) + : base(objectXML, scene, false) + { + m_UnchangedEntity = new SceneObjectGroup(objectXML); + } + + #endregion Constructors + + #region Public Properties + + public SceneObjectGroup UnchangedEntity + { + get { return m_UnchangedEntity; } + } + + #endregion Public Properties + + #region Private Methods + + /// + /// Check if an entitybase list (like that returned by scene.GetEntities()) contains a group with the rootpart uuid that matches the current uuid. + /// + private bool ContainsKey(List list, UUID uuid) + { + foreach (EntityBase part in list) + if (part.UUID == uuid) + return true; + return false; + } + + private SceneObjectGroup GetGroupByUUID(System.Collections.Generic.List list, UUID uuid) + { + foreach (EntityBase ent in list) + { + if (ent is SceneObjectGroup) + if (ent.UUID == uuid) + return (SceneObjectGroup)ent; + } + return null; + } + + #endregion Private Methods + + #region Public Methods + + /// + /// Search for a corresponding group UUID in the scene. If not found, then the revisioned group this CMEntity represents has been deleted. Mark the metaentity appropriately. + /// If a matching UUID is found in a scene object group, compare the two for differences. If differences exist, Mark the metaentity appropriately. + /// + public void FindDifferences() + { + System.Collections.Generic.List sceneEntityList = m_Entity.Scene.GetEntities(); + DiffersFromSceneGroup = false; + // if group is not contained in scene's list + if (!ContainsKey(sceneEntityList, m_UnchangedEntity.UUID)) + { + foreach (SceneObjectPart part in m_UnchangedEntity.Children.Values) + { + // if scene list no longer contains this part, display translucent part and mark with red aura + if (!ContainsKey(sceneEntityList, part.UUID)) + { + // if already displaying a red aura over part, make sure its red + if (m_AuraEntities.ContainsKey(part.UUID)) + { + m_AuraEntities[part.UUID].SetAura(new Vector3(254,0,0), part.Scale); + } + else + { + AuraMetaEntity auraGroup = new AuraMetaEntity(m_Entity.Scene, + part.GetWorldPosition(), + MetaEntity.TRANSLUCENT, + new Vector3(254,0,0), + part.Scale + ); + m_AuraEntities.Add(part.UUID, auraGroup); + } + SceneObjectPart metaPart = m_Entity.GetLinkNumPart(part.LinkNum); + SetPartTransparency(metaPart, MetaEntity.TRANSLUCENT); + } + // otherwise, scene will not contain the part. note: a group can not remove a part without changing group id + } + + // a deleted part has no where to point a beam particle system, + // if a metapart had a particle system (maybe it represented a moved part) remove it + if (m_BeamEntities.ContainsKey(m_UnchangedEntity.RootPart.UUID)) + { + m_BeamEntities[m_UnchangedEntity.RootPart.UUID].HideFromAll(); + m_BeamEntities.Remove(m_UnchangedEntity.RootPart.UUID); + } + + DiffersFromSceneGroup = true; + } + // if scene list does contain group, compare each part in group for differences and display beams and auras appropriately + else + { + MarkWithDifferences((SceneObjectGroup)GetGroupByUUID(sceneEntityList, m_UnchangedEntity.UUID)); + } + } + + /// + /// Check if the revisioned scene object group that this CMEntity is based off of contains a child with the given UUID. + /// + public bool HasChildPrim(UUID uuid) + { + if (m_UnchangedEntity.Children.ContainsKey(uuid)) + return true; + return false; + } + + /// + /// Check if the revisioned scene object group that this CMEntity is based off of contains a child with the given LocalId. + /// + public bool HasChildPrim(uint localID) + { + foreach (SceneObjectPart part in m_UnchangedEntity.Children.Values) + if (part.LocalId == localID) + return true; + return false; + } + + public override void Hide(IClientAPI client) + { + base.Hide(client); + foreach (MetaEntity group in m_AuraEntities.Values) + group.Hide(client); + foreach (MetaEntity group in m_BeamEntities.Values) + group.Hide(client); + } + + public override void HideFromAll() + { + base.HideFromAll(); + foreach (MetaEntity group in m_AuraEntities.Values) + group.HideFromAll(); + foreach (MetaEntity group in m_BeamEntities.Values) + group.HideFromAll(); + } + + /// + /// Returns true if there was a change between meta entity and the entity group, false otherwise. + /// If true is returned, it is assumed the metaentity's appearance has changed to reflect the difference (though clients haven't been updated). + /// + public bool MarkWithDifferences(SceneObjectGroup sceneEntityGroup) + { + SceneObjectPart sceneEntityPart; + SceneObjectPart metaEntityPart; + Diff differences; + bool changed = false; + + // Use "UnchangedEntity" to do comparisons because its text, transparency, and other attributes will be just as the user + // had originally saved. + // m_Entity will NOT necessarily be the same entity as the user had saved. + foreach (SceneObjectPart UnchangedPart in m_UnchangedEntity.Children.Values) + { + //This is the part that we use to show changes. + metaEntityPart = m_Entity.GetLinkNumPart(UnchangedPart.LinkNum); + if (sceneEntityGroup.Children.ContainsKey(UnchangedPart.UUID)) + { + sceneEntityPart = sceneEntityGroup.Children[UnchangedPart.UUID]; + differences = Difference.FindDifferences(UnchangedPart, sceneEntityPart); + if (differences != Diff.NONE) + metaEntityPart.Text = "CHANGE: " + differences.ToString(); + if (differences != 0) + { + // Root Part that has been modified + if ((differences&Diff.POSITION) > 0) + { + // If the position of any part has changed, make sure the RootPart of the + // meta entity is pointing with a beam particle system + if (m_BeamEntities.ContainsKey(m_UnchangedEntity.RootPart.UUID)) + { + m_BeamEntities[m_UnchangedEntity.RootPart.UUID].HideFromAll(); + m_BeamEntities.Remove(m_UnchangedEntity.RootPart.UUID); + } + BeamMetaEntity beamGroup = new BeamMetaEntity(m_Entity.Scene, + m_UnchangedEntity.RootPart.GetWorldPosition(), + MetaEntity.TRANSLUCENT, + sceneEntityPart, + new Vector3(0,0,254) + ); + m_BeamEntities.Add(m_UnchangedEntity.RootPart.UUID, beamGroup); + } + + if (m_AuraEntities.ContainsKey(UnchangedPart.UUID)) + { + m_AuraEntities[UnchangedPart.UUID].HideFromAll(); + m_AuraEntities.Remove(UnchangedPart.UUID); + } + AuraMetaEntity auraGroup = new AuraMetaEntity(m_Entity.Scene, + UnchangedPart.GetWorldPosition(), + MetaEntity.TRANSLUCENT, + new Vector3(0,0,254), + UnchangedPart.Scale + ); + m_AuraEntities.Add(UnchangedPart.UUID, auraGroup); + SetPartTransparency(metaEntityPart, MetaEntity.TRANSLUCENT); + + DiffersFromSceneGroup = true; + } + else // no differences between scene part and meta part + { + if (m_BeamEntities.ContainsKey(m_UnchangedEntity.RootPart.UUID)) + { + m_BeamEntities[m_UnchangedEntity.RootPart.UUID].HideFromAll(); + m_BeamEntities.Remove(m_UnchangedEntity.RootPart.UUID); + } + if (m_AuraEntities.ContainsKey(UnchangedPart.UUID)) + { + m_AuraEntities[UnchangedPart.UUID].HideFromAll(); + m_AuraEntities.Remove(UnchangedPart.UUID); + } + SetPartTransparency(metaEntityPart, MetaEntity.NONE); + } + } + else //The entity currently in the scene is missing parts from the metaentity saved, so mark parts red as deleted. + { + if (m_AuraEntities.ContainsKey(UnchangedPart.UUID)) + { + m_AuraEntities[UnchangedPart.UUID].HideFromAll(); + m_AuraEntities.Remove(UnchangedPart.UUID); + } + AuraMetaEntity auraGroup = new AuraMetaEntity(m_Entity.Scene, + UnchangedPart.GetWorldPosition(), + MetaEntity.TRANSLUCENT, + new Vector3(254,0,0), + UnchangedPart.Scale + ); + m_AuraEntities.Add(UnchangedPart.UUID, auraGroup); + SetPartTransparency(metaEntityPart, MetaEntity.TRANSLUCENT); + + DiffersFromSceneGroup = true; + } + } + return changed; + } + + public void SendFullAuraUpdate(IClientAPI client) + { + if (DiffersFromSceneGroup) + { + foreach (AuraMetaEntity group in m_AuraEntities.Values) + group.SendFullUpdate(client); + } + } + + public void SendFullAuraUpdateToAll() + { + if (DiffersFromSceneGroup) + { + foreach (AuraMetaEntity group in m_AuraEntities.Values) + group.SendFullUpdateToAll(); + } + } + + public void SendFullBeamUpdate(IClientAPI client) + { + if (DiffersFromSceneGroup) + { + foreach (BeamMetaEntity group in m_BeamEntities.Values) + group.SendFullUpdate(client); + } + } + + public void SendFullBeamUpdateToAll() + { + if (DiffersFromSceneGroup) + { + foreach (BeamMetaEntity group in m_BeamEntities.Values) + group.SendFullUpdateToAll(); + } + } + + public void SendFullDiffUpdate(IClientAPI client) + { + FindDifferences(); + if (DiffersFromSceneGroup) + { + SendFullUpdate(client); + SendFullAuraUpdate(client); + SendFullBeamUpdate(client); + } + } + + public void SendFullDiffUpdateToAll() + { + FindDifferences(); + if (DiffersFromSceneGroup) + { + SendFullUpdateToAll(); + SendFullAuraUpdateToAll(); + SendFullBeamUpdateToAll(); + } + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementModule.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementModule.cs new file mode 100644 index 0000000..21bc8b2 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/ContentManagementModule.cs @@ -0,0 +1,163 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// ContentManagementModule.cs +// User: bongiojp + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Threading; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class ContentManagementModule : IRegionModule + { + #region Static Fields + + private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #endregion Static Fields + + #region Fields + + bool initialised = false; + CMController m_control = null; + bool m_enabled = false; + CMModel m_model = null; + bool m_posted = false; + CMView m_view = null; + + #endregion Fields + + #region Public Properties + + public bool IsSharedModule + { + get { return true; } + } + + public string Name + { + get { return "ContentManagementModule"; } + } + + #endregion Public Properties + + #region Public Methods + + public void Close() + { + } + + public void Initialise(Scene scene, IConfigSource source) + { + string databaseDir = "./"; + string database = "FileSystemDatabase"; + int channel = 345; + try + { + if (source.Configs["CMS"] == null) + return; + + m_enabled = source.Configs["CMS"].GetBoolean("enabled", false); + databaseDir = source.Configs["CMS"].GetString("directory", databaseDir); + database = source.Configs["CMS"].GetString("database", database); + channel = source.Configs["CMS"].GetInt("channel", channel); + + if (database != "FileSystemDatabase" && database != "GitDatabase") + { + m_log.ErrorFormat("[Content Management]: The Database attribute must be defined as either FileSystemDatabase or GitDatabase"); + m_enabled = false; + } + } + catch (Exception e) + { + m_log.ErrorFormat("[Content Management]: Exception thrown while reading parameters from configuration file. Message: " + e); + m_enabled = false; + } + + if (!m_enabled) + { + m_log.Info("[Content Management]: Content Management System is not Enabled."); + return; + } + + lock (this) + { + if (!initialised) //only init once + { + m_view = new CMView(); + m_model = new CMModel(); + m_control = new CMController(m_model, m_view, scene, channel); + m_model.Initialise(database); + m_view.Initialise(m_model); + + initialised = true; + m_model.InitialiseDatabase(scene, databaseDir); + } + else + { + m_model.InitialiseDatabase(scene, databaseDir); + m_control.RegisterNewRegion(scene); + } + } + } + + public void PostInitialise() + { + if (! m_enabled) + return; + + lock (this) + { + if (!m_posted) //only post once + { + m_model.PostInitialise(); + m_posted = true; + } + } + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/FileSystemDatabase.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/FileSystemDatabase.cs new file mode 100644 index 0000000..6a50906 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/FileSystemDatabase.cs @@ -0,0 +1,317 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// FileSystemDatabase.cs +// User: bongiojp + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Slash = System.IO.Path; +using System.Reflection; +using System.Xml; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.CoreModules.World.Serialiser; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class FileSystemDatabase : IContentDatabase + { + #region Static Fields + + public static float TimeToDownload = 0; + public static float TimeToSave = 0; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #endregion Static Fields + + #region Fields + + private string m_repodir = null; + private Dictionary m_scenes = new Dictionary(); + private Dictionary m_serialiser = new Dictionary(); + + #endregion Fields + + #region Constructors + + public FileSystemDatabase() + { + } + + #endregion Constructors + + #region Private Methods + + // called by postinitialise + private void CreateDirectory() + { + string scenedir; + if (!Directory.Exists(m_repodir)) + Directory.CreateDirectory(m_repodir); + + foreach (UUID region in m_scenes.Keys) + { + scenedir = m_repodir + Slash.DirectorySeparatorChar + region + Slash.DirectorySeparatorChar; + if (!Directory.Exists(scenedir)) + Directory.CreateDirectory(scenedir); + } + } + + // called by postinitialise + private void SetupSerialiser() + { + if (m_serialiser.Count == 0) + { + foreach (UUID region in m_scenes.Keys) + { + m_serialiser.Add(region, m_scenes[region].RequestModuleInterface()); + } + } + } + + #endregion Private Methods + + #region Public Methods + + public int GetMostRecentRevision(UUID regionid) + { + return NumOfRegionRev(regionid); + } + + public string GetRegionObjectHeightMap(UUID regionid) + { + String filename = m_repodir + Slash.DirectorySeparatorChar + regionid + + Slash.DirectorySeparatorChar + "heightmap.r32"; + FileStream fs = new FileStream( filename, FileMode.Open); + StreamReader sr = new StreamReader(fs); + String result = sr.ReadToEnd(); + sr.Close(); + fs.Close(); + return result; + } + + public string GetRegionObjectHeightMap(UUID regionid, int revision) + { + String filename = m_repodir + Slash.DirectorySeparatorChar + regionid + + Slash.DirectorySeparatorChar + "heightmap.r32"; + FileStream fs = new FileStream( filename, FileMode.Open); + StreamReader sr = new StreamReader(fs); + String result = sr.ReadToEnd(); + sr.Close(); + fs.Close(); + return result; + } + + public System.Collections.ArrayList GetRegionObjectXMLList(UUID regionid, int revision) + { + System.Collections.ArrayList objectList = new System.Collections.ArrayList(); + string filename = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar + + + revision + Slash.DirectorySeparatorChar + "objects.xml"; + XmlDocument doc = new XmlDocument(); + XmlNode rootNode; + //int primCount = 0; + //SceneObjectGroup obj = null; + + if (File.Exists(filename)) + { + XmlTextReader reader = new XmlTextReader(filename); + reader.WhitespaceHandling = WhitespaceHandling.None; + doc.Load(reader); + reader.Close(); + rootNode = doc.FirstChild; + foreach (XmlNode aPrimNode in rootNode.ChildNodes) + { + objectList.Add(aPrimNode.OuterXml); + } + return objectList; + } + return null; + } + + public System.Collections.ArrayList GetRegionObjectXMLList(UUID regionid) + { + int revision = NumOfRegionRev(regionid); + m_log.Info("[FSDB]: found revisions:" + revision); + System.Collections.ArrayList xmlList = new System.Collections.ArrayList(); + string filename = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar + + + revision + Slash.DirectorySeparatorChar + "objects.xml"; + XmlDocument doc = new XmlDocument(); + XmlNode rootNode; + + m_log.Info("[FSDB]: Checking if " + filename + " exists."); + if (File.Exists(filename)) + { + Stopwatch x = new Stopwatch(); + x.Start(); + + XmlTextReader reader = new XmlTextReader(filename); + reader.WhitespaceHandling = WhitespaceHandling.None; + doc.Load(reader); + reader.Close(); + rootNode = doc.FirstChild; + + foreach (XmlNode aPrimNode in rootNode.ChildNodes) + { + xmlList.Add(aPrimNode.OuterXml); + } + + x.Stop(); + TimeToDownload += x.ElapsedMilliseconds; + m_log.Info("[FileSystemDatabase] Time spent retrieving xml files so far: " + TimeToDownload); + + return xmlList; + } + return null; + } + + public void Initialise(Scene scene, string dir) + { + lock (this) + { + if (m_repodir == null) + m_repodir = dir; + } + lock (m_scenes) + m_scenes.Add(scene.RegionInfo.RegionID, scene); + } + + public System.Collections.Generic.SortedDictionary ListOfRegionRevisions(UUID regionid) + { + SortedDictionary revisionDict = new SortedDictionary(); + + string scenedir = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar; + string[] directories = Directory.GetDirectories(scenedir); + + FileStream fs = null; + StreamReader sr = null; + String logMessage = ""; + String logLocation = ""; + foreach (string revisionDir in directories) + { + try + { + logLocation = revisionDir + Slash.DirectorySeparatorChar + "log"; + fs = new FileStream( logLocation, FileMode.Open); + sr = new StreamReader(fs); + logMessage = sr.ReadToEnd(); + sr.Close(); + fs.Close(); + revisionDict.Add(revisionDir, logMessage); + } + catch (Exception) + { + } + } + + return revisionDict; + } + + public int NumOfRegionRev(UUID regionid) + { + string scenedir = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar; + m_log.Info("[FSDB]: Reading scene dir: " + scenedir); + string[] directories = Directory.GetDirectories(scenedir); + return directories.Length; + } + + // Run once and only once. + public void PostInitialise() + { + SetupSerialiser(); + + m_log.Info("[FSDB]: Creating repository in " + m_repodir + "."); + CreateDirectory(); + } + + public void SaveRegion(UUID regionid, string regionName, string logMessage) + { + m_log.Info("[FSDB]: ..............................."); + string scenedir = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar; + + m_log.Info("[FSDB]: checking if scene directory exists: " + scenedir); + if (!Directory.Exists(scenedir)) + Directory.CreateDirectory(scenedir); + + int newRevisionNum = GetMostRecentRevision(regionid)+1; + string revisiondir = scenedir + newRevisionNum + Slash.DirectorySeparatorChar; + + m_log.Info("[FSDB]: checking if revision directory exists: " + revisiondir); + if (!Directory.Exists(revisiondir)) + Directory.CreateDirectory(revisiondir); + + try { + Stopwatch x = new Stopwatch(); + x.Start(); + if (m_scenes.ContainsKey(regionid)) + { + m_serialiser[regionid].SerialiseRegion(m_scenes[regionid], revisiondir); + } + x.Stop(); + TimeToSave += x.ElapsedMilliseconds; + m_log.Info("[FileSystemDatabase] Time spent serialising regions to files on disk for " + regionName + ": " + x.ElapsedMilliseconds); + m_log.Info("[FileSystemDatabase] Time spent serialising regions to files on disk so far: " + TimeToSave); + } + catch (Exception e) + { + m_log.ErrorFormat("[FSDB]: Serialisation of region failed: " + e); + return; + } + + try { + // Finish by writing log message. + FileStream file = new FileStream(revisiondir + "log", FileMode.Create, FileAccess.ReadWrite); + StreamWriter sw = new StreamWriter(file); + sw.Write(logMessage); + sw.Close(); + } + catch (Exception e) + { + m_log.ErrorFormat("[FSDB]: Failed trying to save log file " + e); + return; + } + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/GitDatabase.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/GitDatabase.cs new file mode 100644 index 0000000..d24747c --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/GitDatabase.cs @@ -0,0 +1,167 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// GitDatabase.cs +// +// +// + +#endregion Header + +using System; +using System.Collections.Generic; +using System.IO; +using Slash = System.IO.Path; +using System.Reflection; +using System.Xml; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.CoreModules.World.Serialiser; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + /// + /// Just a stub :-( + /// + public class GitDatabase : IContentDatabase + { + #region Constructors + + public GitDatabase() + { + } + + #endregion Constructors + + #region Public Methods + + public SceneObjectGroup GetMostRecentObjectRevision(UUID id) + { + return null; + } + + public int GetMostRecentRevision(UUID regionid) + { + return 0; + } + + public SceneObjectGroup GetObjectRevision(UUID id, int revision) + { + return null; + } + + public System.Collections.ArrayList GetObjectsFromRegion(UUID regionid, int revision) + { + return null; + } + + public string GetRegionObjectHeightMap(UUID regionid) + { + return null; + } + + public string GetRegionObjectHeightMap(UUID regionid, int revision) + { + return null; + } + + public string GetRegionObjectXML(UUID regionid) + { + return null; + } + + public string GetRegionObjectXML(UUID regionid, int revision) + { + return null; + } + + public System.Collections.ArrayList GetRegionObjectXMLList(UUID regionid) + { + return null; + } + + public System.Collections.ArrayList GetRegionObjectXMLList(UUID regionid, int revision) + { + return null; + } + + public bool InRepository(UUID id) + { + return false; + } + + public void Initialise(Scene scene, String dir) + { + } + + public System.Collections.Generic.SortedDictionary ListOfObjectRevisions(UUID id) + { + return null; + } + + public System.Collections.Generic.SortedDictionary ListOfRegionRevisions(UUID id) + { + return null; + } + + public int NumOfObjectRev(UUID id) + { + return 0; + } + + public int NumOfRegionRev(UUID regionid) + { + return 0; + } + + public void PostInitialise() + { + } + + public void SaveObject(SceneObjectGroup entity) + { + } + + public void SaveRegion(UUID regionid, string regionName, string logMessage) + { + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/IContentDatabase.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/IContentDatabase.cs new file mode 100644 index 0000000..7a25fa5 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/IContentDatabase.cs @@ -0,0 +1,94 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// IContentDatabase.cs +// User: bongiojp +// +// +// + +#endregion Header + +using System; +using OpenMetaverse; +using OpenSim.Region.Framework.Scenes; +using Nini.Config; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public interface IContentDatabase + { + #region Methods + + /// + /// Returns the most recent revision number of a region. + /// + int GetMostRecentRevision(UUID regionid); + + string GetRegionObjectHeightMap(UUID regionid); + + string GetRegionObjectHeightMap(UUID regionid, int revision); + + /// + /// Retrieves the xml that describes each individual object from the last revision or specific revision of the given region. + /// + System.Collections.ArrayList GetRegionObjectXMLList(UUID regionid); + + System.Collections.ArrayList GetRegionObjectXMLList(UUID regionid, int revision); + + /// + /// Similar to the IRegionModule function. This is the function to be called before attempting to interface with the database. + /// Initialise should be called one for each region to be contained in the database. The directory should be the full path + /// to the repository and will only be defined once, regardless of how many times the method is called. + /// + void Initialise(Scene scene, String dir); + + /// + /// Returns a list of the revision numbers and corresponding log messages for a given region. + /// + System.Collections.Generic.SortedDictionary ListOfRegionRevisions(UUID id); + + /// + /// Returns the total number of revisions saved for a specific region. + /// + int NumOfRegionRev(UUID regionid); + + /// + /// Should be called once after Initialise has been called. + /// + void PostInitialise(); + + /// + /// Saves the Region terrain map and objects within the region as xml to the database. + /// + void SaveRegion(UUID regionid, string regionName, string logMessage); + + #endregion Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs new file mode 100644 index 0000000..63c74e1 --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs @@ -0,0 +1,274 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// MetaEntity.cs +// User: bongiojp +// +// TODO: +// Create a physics manager to the meta object if there isn't one or the object knows of no scene but the user wants physics enabled. + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Drawing; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class MetaEntity + { + #region Constants + + public const float INVISIBLE = .95f; + + // Settings for transparency of metaentity + public const float NONE = 0f; + public const float TRANSLUCENT = .5f; + + #endregion Constants + + #region Static Fields + + protected static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #endregion Static Fields + + #region Fields + + protected SceneObjectGroup m_Entity = null; // The scene object group that represents this meta entity. + protected uint m_metaLocalid; + + #endregion Fields + + #region Constructors + + public MetaEntity() + { + } + + /// + /// Makes a new meta entity by copying the given scene object group. + /// The physics boolean is just a stub right now. + /// + public MetaEntity(SceneObjectGroup orig, bool physics) + { + m_Entity = orig.Copy(orig.RootPart.OwnerID, orig.RootPart.GroupID, false); + Initialize(physics); + } + + /// + /// Takes an XML description of a scene object group and converts it to a meta entity. + /// + public MetaEntity(string objectXML, Scene scene, bool physics) + { + m_Entity = new SceneObjectGroup(objectXML); + m_Entity.SetScene(scene); + Initialize(physics); + } + + #endregion Constructors + + #region Public Properties + + public Dictionary Children + { + get { return m_Entity.Children; } + set { m_Entity.Children = value; } + } + + public uint LocalId + { + get { return m_Entity.LocalId; } + set { m_Entity.LocalId = value; } + } + + public SceneObjectGroup ObjectGroup + { + get { return m_Entity; } + } + + public int PrimCount + { + get { return m_Entity.PrimCount; } + } + + public SceneObjectPart RootPart + { + get { return m_Entity.RootPart; } + } + + public Scene Scene + { + get { return m_Entity.Scene; } + } + + public UUID UUID + { + get { return m_Entity.UUID; } + set { m_Entity.UUID = value; } + } + + #endregion Public Properties + + #region Protected Methods + + // The metaentity objectgroup must have unique localids as well as unique uuids. + // localids are used by the client to refer to parts. + // uuids are sent to the client and back to the server to identify parts on the server side. + /// + /// Changes localids and uuids of m_Entity. + /// + protected void Initialize(bool physics) + { + //make new uuids + Dictionary parts = new Dictionary(); + foreach (SceneObjectPart part in m_Entity.Children.Values) + { + part.ResetIDs(part.LinkNum); + parts.Add(part.UUID, part); + } + + //finalize + m_Entity.RootPart.PhysActor = null; + m_Entity.Children = parts; + } + + #endregion Protected Methods + + #region Public Methods + + /// + /// Hides the metaentity from a single client. + /// + public virtual void Hide(IClientAPI client) + { + //This deletes the group without removing from any databases. + //This is important because we are not IN any database. + //m_Entity.FakeDeleteGroup(); + foreach (SceneObjectPart part in m_Entity.Children.Values) + client.SendKillObject(m_Entity.RegionHandle, part.LocalId); + } + + /// + /// Sends a kill object message to all clients, effectively "hiding" the metaentity even though it's still on the server. + /// + public virtual void HideFromAll() + { + foreach (SceneObjectPart part in m_Entity.Children.Values) + m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller) + { controller.SendKillObject(m_Entity.RegionHandle, part.LocalId); } + ); + } + + public void SendFullUpdate(IClientAPI client) + { + // Not sure what clientFlags should be but 0 seems to work + SendFullUpdate(client, 0); + } + + public void SendFullUpdate(IClientAPI client, uint clientFlags) + { + m_Entity.SendFullUpdateToClient(client); + } + + public void SendFullUpdateToAll() + { + m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller) + { m_Entity.SendFullUpdateToClient(controller); } + ); + } + + /// + /// Makes a single SceneObjectPart see through. + /// + /// + /// A + /// The part to make see through + /// + /// + /// A + /// The degree of transparency to imbue the part with, 0f being solid and .95f being invisible. + /// + public static void SetPartTransparency(SceneObjectPart part, float transparencyAmount) + { + Primitive.TextureEntry tex = null; + Color4 texcolor; + try + { + tex = part.Shape.Textures; + texcolor = new Color4(); + } + catch(Exception) + { + //m_log.ErrorFormat("[Content Management]: Exception thrown while accessing textures of scene object: " + e); + return; + } + + for (uint i = 0; i < tex.FaceTextures.Length; i++) + { + try { + if (tex.FaceTextures[i] != null) + { + texcolor = tex.FaceTextures[i].RGBA; + texcolor.A = transparencyAmount; + tex.FaceTextures[i].RGBA = texcolor; + } + } + catch (Exception) + { + //m_log.ErrorFormat("[Content Management]: Exception thrown while accessing different face textures of object: " + e); + continue; + } + } + try { + texcolor = tex.DefaultTexture.RGBA; + texcolor.A = transparencyAmount; + tex.DefaultTexture.RGBA = texcolor; + part.Shape.TextureEntry = tex.ToBytes(); + } + catch (Exception) + { + //m_log.Info("[Content Management]: Exception thrown while accessing default face texture of object: " + e); + } + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/PointMetaEntity.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/PointMetaEntity.cs new file mode 100644 index 0000000..22f09fd --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/PointMetaEntity.cs @@ -0,0 +1,116 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// PointMetaEntity.cs created with MonoDevelop +// User: bongiojp at 3:03 PM 8/6/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Drawing; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + public class PointMetaEntity : MetaEntity + { + #region Constructors + + public PointMetaEntity(Scene scene, Vector3 groupPos, float transparency) + : base() + { + CreatePointEntity(scene, UUID.Random(), groupPos); + SetPartTransparency(m_Entity.RootPart, transparency); + } + + public PointMetaEntity(Scene scene, UUID uuid, Vector3 groupPos, float transparency) + : base() + { + CreatePointEntity(scene, uuid, groupPos); + SetPartTransparency(m_Entity.RootPart, transparency); + } + + #endregion Constructors + + #region Private Methods + + private void CreatePointEntity(Scene scene, UUID uuid, Vector3 groupPos) + { + SceneObjectGroup x = new SceneObjectGroup(); + SceneObjectPart y = new SceneObjectPart(); + + //Initialize part + y.Name = "Very Small Point"; + y.RegionHandle = scene.RegionInfo.RegionHandle; + y.CreationDate = (Int32) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + y.OwnerID = UUID.Zero; + y.CreatorID = UUID.Zero; + y.LastOwnerID = UUID.Zero; + y.UUID = uuid; + + y.Shape = PrimitiveBaseShape.CreateBox(); + y.Scale = new Vector3(0.01f,0.01f,0.01f); + y.LastOwnerID = UUID.Zero; + y.GroupPosition = groupPos; + y.OffsetPosition = new Vector3(0, 0, 0); + y.RotationOffset = new Quaternion(0,0,0,0); + y.Velocity = new Vector3(0, 0, 0); + y.RotationalVelocity = new Vector3(0, 0, 0); + y.AngularVelocity = new Vector3(0, 0, 0); + y.Acceleration = new Vector3(0, 0, 0); + + y.Flags = 0; + y.TrimPermissions(); + + //Initialize group and add part as root part + x.SetScene(scene); + x.SetRootPart(y); + x.RegionHandle = scene.RegionInfo.RegionHandle; + x.SetScene(scene); + + m_Entity = x; + } + + #endregion Private Methods + } +} diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/README b/OpenSim/Region/OptionalModules/ContentManagementSystem/README new file mode 100644 index 0000000..1a69fef --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/README @@ -0,0 +1,52 @@ +This module is meant to be built alone and not added to the Opensim code base. References are made to required dlls through a +reference file, ContentManagement.mdp. Originally, for development, this project was contained in the Opensim/Region/Modules/ +directory. + +To compile: nant +To use: Copy ContentManagement.dll into the bin directory of your Opensim build. You should find many other dlls in the same directory. + + +-------------------------------------------------------------------------------------------------------------------- +To build the libgit.so file: + +#Download GIT git repository +$ git clone git://git2.kernel.org/pub/OpenSim/Region/Environment/Modules/ContentManagementSystem/scm/git/git.git +$ cd git + +#Compile GIT +#Note that we are adding two extra flags to pass to gcc while compiling (-c and -fPIC) +$ autoconf +$ ./configure +$ CFLAGS="-g -O2 -Wall -c -fPIC" make + +#Copy necessary object files (and some not so necessary) to their own directory for shared object file creation +$ mkdir ../libgit-objects +$ cp builtin*.o ../libgit-objects +$ cp xdiff/*.o ../libgit-objects +$ cp libgit.a ../libgit-objects + +#Remove the main symbol from any object files (like git.o) +$ cd ../libgit-objects +$ strip -N main *.o + +#Uncompress the plumbing objects from archive created by git +$ ar x libgit.a + +#Create shared object file from all objects (including the zlib library) +$ ld -shared -soname libgit.so.1 -o libgit.so.1.5.6.3 -lc -lz *.o + + +#You can also just copy the following commands into a file and run as a script inside the git directory + +make clean +autoconf +./configure +CFLAGS="-g -O2 -Wall -c -fPIC" make +mkdir libgit-objects +cp builtin*.o libgit-objects +cp xdiff/*.o libgit-objects +cp libgit.a libgit-objects +cd libgit-objects +strip -N main *.o +ar x libgit.a +ld -shared -soname libgit.so.1 -o libgit.so.1.5.6.3 -lc -lz *.o \ No newline at end of file diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs new file mode 100644 index 0000000..8957e8f --- /dev/null +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/SceneObjectGroupDiff.cs @@ -0,0 +1,218 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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. + */ + +#region Header + +// SceneObjectGroupDiff.cs +// User: bongiojp + +#endregion Header + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; + +using OpenMetaverse; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; + +using log4net; + +namespace OpenSim.Region.OptionalModules.ContentManagement +{ + #region Enumerations + + [Flags] + public enum Diff + { + NONE = 0, + FACECOLOR = 1, + SHAPE = 1<<1, + MATERIAL = 1<<2, + TEXTURE = 1<<3, + SCALE = 1<<4, + POSITION = 1<<5, + OFFSETPOSITION = 1<<6, + ROTATIONOFFSET = 1<<7, + ROTATIONALVELOCITY = 1<<8, + ACCELERATION = 1<<9, + ANGULARVELOCITY = 1<<10, + VELOCITY = 1<<11, + OBJECTOWNER = 1<<12, + PERMISSIONS = 1<<13, + DESCRIPTION = 1<<14, + NAME = 1<<15, + SCRIPT = 1<<16, + CLICKACTION = 1<<17, + PARTICLESYSTEM = 1<<18, + GLOW = 1<<19, + SALEPRICE = 1<<20, + SITNAME = 1<<21, + SITTARGETORIENTATION = 1<<22, + SITTARGETPOSITION = 1<<23, + TEXT = 1<<24, + TOUCHNAME = 1<<25 + } + + #endregion Enumerations + + public static class Difference + { + #region Static Fields + + static float TimeToDiff = 0; +// private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + #endregion Static Fields + + #region Private Methods + + private static bool AreQuaternionsEquivalent(Quaternion first, Quaternion second) + { + Vector3 firstVector = llRot2Euler(first); + Vector3 secondVector = llRot2Euler(second); + return AreVectorsEquivalent(firstVector, secondVector); + } + + private static bool AreVectorsEquivalent(Vector3 first, Vector3 second) + { + if (TruncateSignificant(first.X, 2) == TruncateSignificant(second.X, 2) + && TruncateSignificant(first.Y, 2) == TruncateSignificant(second.Y, 2) + && TruncateSignificant(first.Z, 2) == TruncateSignificant(second.Z, 2) + ) + return true; + else + return false; + } + + // Taken from Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs + private static double NormalizeAngle(double angle) + { + angle = angle % (Math.PI * 2); + if (angle < 0) angle = angle + Math.PI * 2; + return angle; + } + + private static int TruncateSignificant(float num, int digits) + { + return (int) Math.Ceiling((Math.Truncate(num * 10 * digits)/10*digits)); + // return (int) ((num * (10*digits))/10*digits); + } + + // Taken from Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs + // Also changed the original function from LSL_Types to LL types + private static Vector3 llRot2Euler(Quaternion r) + { + Quaternion t = new Quaternion(r.X * r.X, r.Y * r.Y, r.Z * r.Z, r.W * r.W); + double m = (t.X + t.Y + t.Z + t.W); + if (m == 0) return new Vector3(); + double n = 2 * (r.Y * r.W + r.X * r.Z); + double p = m * m - n * n; + if (p > 0) + return new Vector3((float)NormalizeAngle(Math.Atan2(2.0 * (r.X * r.W - r.Y * r.Z), (-t.X - t.Y + t.Z + t.W))), + (float)NormalizeAngle(Math.Atan2(n, Math.Sqrt(p))), + (float)NormalizeAngle(Math.Atan2(2.0 * (r.Z * r.W - r.X * r.Y), (t.X - t.Y - t.Z + t.W)))); + else if (n > 0) + return new Vector3(0.0f, (float)(Math.PI / 2), (float)NormalizeAngle(Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z))); + else + return new Vector3(0.0f, (float)(-Math.PI / 2), (float)NormalizeAngle(Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z))); + } + + #endregion Private Methods + + #region Public Methods + + /// + /// Compares the attributes (Vectors, Quaternions, Strings, etc.) between two scene object parts + /// and returns a Diff bitmask which details what the differences are. + /// + public static Diff FindDifferences(SceneObjectPart first, SceneObjectPart second) + { + Stopwatch x = new Stopwatch(); + x.Start(); + + Diff result = 0; + + // VECTOR COMPARISONS + if (!AreVectorsEquivalent(first.Acceleration, second.Acceleration)) + result |= Diff.ACCELERATION; + if (!AreVectorsEquivalent(first.AbsolutePosition, second.AbsolutePosition)) + result |= Diff.POSITION; + if (!AreVectorsEquivalent(first.AngularVelocity, second.AngularVelocity)) + result |= Diff.ANGULARVELOCITY; + if (!AreVectorsEquivalent(first.OffsetPosition, second.OffsetPosition)) + result |= Diff.OFFSETPOSITION; + if (!AreVectorsEquivalent(first.RotationalVelocity, second.RotationalVelocity)) + result |= Diff.ROTATIONALVELOCITY; + if (!AreVectorsEquivalent(first.Scale, second.Scale)) + result |= Diff.SCALE; + if (!AreVectorsEquivalent(first.Velocity, second.Velocity)) + result |= Diff.VELOCITY; + + + // QUATERNION COMPARISONS + if (!AreQuaternionsEquivalent(first.RotationOffset, second.RotationOffset)) + result |= Diff.ROTATIONOFFSET; + + + // MISC COMPARISONS (UUID, Byte) + if (first.ClickAction != second.ClickAction) + result |= Diff.CLICKACTION; + if (first.ObjectOwner != second.ObjectOwner) + result |= Diff.OBJECTOWNER; + + + // STRING COMPARISONS + if (first.Description != second.Description) + result |= Diff.DESCRIPTION; + if (first.Material != second.Material) + result |= Diff.MATERIAL; + if (first.Name != second.Name) + result |= Diff.NAME; + if (first.SitName != second.SitName) + result |= Diff.SITNAME; + if (first.Text != second.Text) + result |= Diff.TEXT; + if (first.TouchName != second.TouchName) + result |= Diff.TOUCHNAME; + + x.Stop(); + TimeToDiff += x.ElapsedMilliseconds; + //m_log.Info("[DIFFERENCES] Time spent diffing objects so far" + TimeToDiff); + + return result; + } + + #endregion Public Methods + } +} diff --git a/OpenSim/Region/OptionalModules/Grid/Interregion/IInterregionModule.cs b/OpenSim/Region/OptionalModules/Grid/Interregion/IInterregionModule.cs new file mode 100644 index 0000000..a2c98a4 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Grid/Interregion/IInterregionModule.cs @@ -0,0 +1,43 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.Grid.Interregion +{ + public interface IInterregionModule + { + void RegisterMethod(T e); + bool HasInterface(Location loc); + T RequestInterface(Location loc); + T[] RequestInterface(); + Location GetLocationByDirection(Scene scene, InterregionModule.Direction dir); + void internal_CreateRemotingObjects(); + void RegisterRemoteRegion(string uri); + } +} \ No newline at end of file diff --git a/OpenSim/Region/OptionalModules/Grid/Interregion/InterregionModule.cs b/OpenSim/Region/OptionalModules/Grid/Interregion/InterregionModule.cs new file mode 100644 index 0000000..21df83c --- /dev/null +++ b/OpenSim/Region/OptionalModules/Grid/Interregion/InterregionModule.cs @@ -0,0 +1,199 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Tcp; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.Grid.Interregion +{ + public class InterregionModule : IInterregionModule, IRegionModule + { + #region Direction enum + + public enum Direction + { + North, + NorthEast, + East, + SouthEast, + South, + SouthWest, + West, + NorthWest + } + + #endregion + + private readonly Dictionary m_interfaces = new Dictionary(); + private readonly Object m_lockObject = new object(); + private readonly List m_myLocations = new List(); + + private readonly Dictionary m_neighbourInterfaces = new Dictionary(); + private readonly Dictionary m_neighbourRemote = new Dictionary(); + // private IConfigSource m_config; + private const bool m_enabled = false; + + private RemotingObject m_myRemote; + private TcpChannel m_tcpChannel; + private int m_tcpPort = 10101; + + #region IInterregionModule Members + + public void internal_CreateRemotingObjects() + { + lock (m_lockObject) + { + if (m_tcpChannel == null) + { + m_myRemote = new RemotingObject(m_interfaces, m_myLocations.ToArray()); + m_tcpChannel = new TcpChannel(m_tcpPort); + + ChannelServices.RegisterChannel(m_tcpChannel, false); + RemotingServices.Marshal(m_myRemote, "OpenSimRemote2", typeof (RemotingObject)); + } + } + } + + public void RegisterMethod(T e) + { + m_interfaces[typeof (T)] = e; + } + + public bool HasInterface(Location loc) + { + foreach (string val in m_neighbourInterfaces[loc]) + { + if (val == typeof (T).FullName) + { + return true; + } + } + return false; + } + + public T RequestInterface(Location loc) + { + if (m_neighbourRemote.ContainsKey(loc)) + { + return m_neighbourRemote[loc].RequestInterface(); + } + throw new IndexOutOfRangeException("No neighbour availible at that location"); + } + + public T[] RequestInterface() + { + List m_t = new List(); + foreach (RemotingObject remote in m_neighbourRemote.Values) + { + try + { + m_t.Add(remote.RequestInterface()); + } + catch (NotSupportedException) + { + } + } + return m_t.ToArray(); + } + + public Location GetLocationByDirection(Scene scene, Direction dir) + { + return new Location(0, 0); + } + + public void RegisterRemoteRegion(string uri) + { + RegisterRemotingInterface((RemotingObject) Activator.GetObject(typeof (RemotingObject), uri)); + } + + #endregion + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource source) + { + m_myLocations.Add(new Location((int) scene.RegionInfo.RegionLocX, + (int) scene.RegionInfo.RegionLocY)); + // m_config = source; + + scene.RegisterModuleInterface(this); + } + + public void PostInitialise() + { + // Commenting out to remove 'unreachable code' warning since m_enabled is never true +// if (m_enabled) +// { +// try +// { +// m_tcpPort = m_config.Configs["Comms"].GetInt("remoting_port", m_tcpPort); +// } +// catch +// { +// } +// +// internal_CreateRemotingObjects(); +// } + } + + public void Close() + { + if (null != m_tcpChannel) + ChannelServices.UnregisterChannel(m_tcpChannel); + } + + public string Name + { + get { return "InterregionModule"; } + } + + public bool IsSharedModule + { + get { return true; } + } + + #endregion + + private void RegisterRemotingInterface(RemotingObject remote) + { + Location[] locs = remote.GetLocations(); + string[] interfaces = remote.GetInterfaces(); + foreach (Location loc in locs) + { + m_neighbourInterfaces[loc] = interfaces; + m_neighbourRemote[loc] = remote; + } + } + } +} diff --git a/OpenSim/Region/OptionalModules/Grid/Interregion/RemotingObject.cs b/OpenSim/Region/OptionalModules/Grid/Interregion/RemotingObject.cs new file mode 100644 index 0000000..b09c848 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Grid/Interregion/RemotingObject.cs @@ -0,0 +1,77 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using OpenSim.Framework; + +namespace OpenSim.Region.CoreModules.Grid.Interregion +{ + public class RemotingObject : MarshalByRefObject + { + private readonly Location[] m_coords; + private readonly Dictionary m_interfaces = new Dictionary(); + + public RemotingObject(Dictionary myInterfaces, Location[] coords) + { + m_interfaces = myInterfaces; + m_coords = coords; + } + + public Location[] GetLocations() + { + return (Location[]) m_coords.Clone(); + } + + public string[] GetInterfaces() + { + string[] interfaces = new string[m_interfaces.Count]; + int i = 0; + + foreach (KeyValuePair pair in m_interfaces) + { + interfaces[i++] = pair.Key.FullName; + } + + return interfaces; + } + + /// + /// Returns a registered interface availible to neighbouring regions. + /// + /// The type of interface you wish to request + /// A MarshalByRefObject inherited from this region inheriting the interface requested. + /// All registered interfaces MUST inherit from MarshalByRefObject and use only serialisable types. + public T RequestInterface() + { + if (m_interfaces.ContainsKey(typeof (T))) + return (T) m_interfaces[typeof (T)]; + + throw new NotSupportedException("No such interface registered."); + } + } +} diff --git a/OpenSim/Region/OptionalModules/Python/PythonAPI/Console.cs b/OpenSim/Region/OptionalModules/Python/PythonAPI/Console.cs new file mode 100644 index 0000000..215a9ad --- /dev/null +++ b/OpenSim/Region/OptionalModules/Python/PythonAPI/Console.cs @@ -0,0 +1,48 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Reflection; +using log4net; + +namespace OpenSim.Region.Modules.Python.PythonAPI +{ + class Console + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public void WriteLine(string txt) + { + m_log.Info(txt); + } + + public void WriteLine(string txt, params Object[] e) + { + m_log.Info(String.Format(txt, e)); + } + } +} diff --git a/OpenSim/Region/OptionalModules/Python/PythonModule.cs b/OpenSim/Region/OptionalModules/Python/PythonModule.cs new file mode 100644 index 0000000..5dedac2 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Python/PythonModule.cs @@ -0,0 +1,71 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.Reflection; +using System.Security.Policy; +using System.Text; +using IronPython.Hosting; +using log4net; +using Nini.Config; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Modules.Python +{ + class PythonModule : IRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private PythonEngine m_python; + + public void Initialise(Scene scene, IConfigSource source) + { + } + + public void PostInitialise() + { + m_log.Info("[PYTHON] Initialising IronPython engine."); + m_python = new PythonEngine(); + m_python.AddToPath(System.Environment.CurrentDirectory + System.IO.Path.DirectorySeparatorChar + "Python"); + } + + public void Close() + { + } + + public string Name + { + get { return "PythonModule"; } + } + + public bool IsSharedModule + { + get { return true; } + } + } +} diff --git a/OpenSim/Region/OptionalModules/README b/OpenSim/Region/OptionalModules/README new file mode 100644 index 0000000..0fe7b87 --- /dev/null +++ b/OpenSim/Region/OptionalModules/README @@ -0,0 +1,9 @@ += OpenSim.Region.OptionalModules = + +The modules in this part of the OpenSim source tree are candidates for forge. For the time being, until we have reached consensus on + + (a) what should be core or optional + (b) that we have reached a stable API + (c) how to maintain optional modules + +we will keep these modules in-tree. \ No newline at end of file diff --git a/OpenSim/Region/OptionalModules/SvnSerialiser/Properties/AssemblyInfo.cs b/OpenSim/Region/OptionalModules/SvnSerialiser/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..165fcea --- /dev/null +++ b/OpenSim/Region/OptionalModules/SvnSerialiser/Properties/AssemblyInfo.cs @@ -0,0 +1,62 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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.Reflection; +using System.Runtime.InteropServices; + +// General information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SvnSerialiser")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SvnSerialiser")] +[assembly: AssemblyCopyright("Copyright (c) 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c0893655-0c18-4dd7-8b5b-5f58ab1ec6c7")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Region/OptionalModules/SvnSerialiser/SvnBackupModule.cs b/OpenSim/Region/OptionalModules/SvnSerialiser/SvnBackupModule.cs new file mode 100644 index 0000000..c4fc584 --- /dev/null +++ b/OpenSim/Region/OptionalModules/SvnSerialiser/SvnBackupModule.cs @@ -0,0 +1,396 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Timers; +using log4net; +using Nini.Config; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.CoreModules.World.Serialiser; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Scenes; +using PumaCode.SvnDotNet.AprSharp; +using PumaCode.SvnDotNet.SubversionSharp; +using Slash = System.IO.Path; + +namespace OpenSim.Region.Modules.SvnSerialiser +{ + public class SvnBackupModule : IRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly List m_scenes = new List(); + private readonly Timer m_timer = new Timer(); + + private bool m_enabled = false; + private bool m_installBackupOnLoad = false; + private IRegionSerialiserModule m_serialiser; + private bool m_svnAutoSave = false; + private SvnClient m_svnClient; + private string m_svndir = "SVNmodule" + Slash.DirectorySeparatorChar + "repo"; + private string m_svnpass = "password"; + + private TimeSpan m_svnperiod = new TimeSpan(0, 0, 15, 0, 0); + private string m_svnurl = "svn://insert.Your.svn/here/"; + private string m_svnuser = "username"; + + #region SvnModule Core + + /// + /// Exports a specified scene to the SVN repo directory, then commits. + /// + /// The scene to export + public void SaveRegion(Scene scene) + { + List svnfilenames = CreateAndAddExport(scene); + + m_svnClient.Commit3(svnfilenames, true, false); + m_log.Info("[SVNBACKUP]: Region backup successful (" + scene.RegionInfo.RegionName + ")."); + } + + /// + /// Saves all registered scenes to the SVN repo, then commits. + /// + public void SaveAllRegions() + { + List svnfilenames = new List(); + List regions = new List(); + + foreach (Scene scene in m_scenes) + { + svnfilenames.AddRange(CreateAndAddExport(scene)); + regions.Add("'" + scene.RegionInfo.RegionName + "' "); + } + + m_svnClient.Commit3(svnfilenames, true, false); + m_log.Info("[SVNBACKUP]: Server backup successful (" + String.Concat(regions.ToArray()) + ")."); + } + + private List CreateAndAddExport(Scene scene) + { + m_log.Info("[SVNBACKUP]: Saving a region to SVN with name " + scene.RegionInfo.RegionName); + + List filenames = m_serialiser.SerialiseRegion(scene, m_svndir + Slash.DirectorySeparatorChar + scene.RegionInfo.RegionID + Slash.DirectorySeparatorChar); + + try + { + m_svnClient.Add3(m_svndir + Slash.DirectorySeparatorChar + scene.RegionInfo.RegionID, true, false, false); + } + catch (SvnException) + { + } + + List svnfilenames = new List(); + foreach (string filename in filenames) + svnfilenames.Add(m_svndir + Slash.DirectorySeparatorChar + scene.RegionInfo.RegionID + Slash.DirectorySeparatorChar + filename); + svnfilenames.Add(m_svndir + Slash.DirectorySeparatorChar + scene.RegionInfo.RegionID); + + return svnfilenames; + } + + public void LoadRegion(Scene scene) + { + IRegionSerialiserModule serialiser = scene.RequestModuleInterface(); + if (serialiser != null) + { + serialiser.LoadPrimsFromXml2( + scene, + m_svndir + Slash.DirectorySeparatorChar + scene.RegionInfo.RegionID + + Slash.DirectorySeparatorChar + "objects.xml"); + + scene.RequestModuleInterface().LoadFromFile( + m_svndir + Slash.DirectorySeparatorChar + scene.RegionInfo.RegionID + + Slash.DirectorySeparatorChar + "heightmap.r32"); + + m_log.Info("[SVNBACKUP]: Region load successful (" + scene.RegionInfo.RegionName + ")."); + } + else + { + m_log.ErrorFormat( + "[SVNBACKUP]: Region load of {0} failed - no serialisation module available", + scene.RegionInfo.RegionName); + } + } + + private void CheckoutSvn() + { + m_svnClient.Checkout2(m_svnurl, m_svndir, Svn.Revision.Head, Svn.Revision.Head, true, false); + } + + private void CheckoutSvn(SvnRevision revision) + { + m_svnClient.Checkout2(m_svnurl, m_svndir, revision, revision, true, false); + } + + // private void CheckoutSvnPartial(string subdir) + // { + // if (!Directory.Exists(m_svndir + Slash.DirectorySeparatorChar + subdir)) + // Directory.CreateDirectory(m_svndir + Slash.DirectorySeparatorChar + subdir); + + // m_svnClient.Checkout2(m_svnurl + "/" + subdir, m_svndir, Svn.Revision.Head, Svn.Revision.Head, true, false); + // } + + // private void CheckoutSvnPartial(string subdir, SvnRevision revision) + // { + // if (!Directory.Exists(m_svndir + Slash.DirectorySeparatorChar + subdir)) + // Directory.CreateDirectory(m_svndir + Slash.DirectorySeparatorChar + subdir); + + // m_svnClient.Checkout2(m_svnurl + "/" + subdir, m_svndir, revision, revision, true, false); + // } + + #endregion + + #region SvnDotNet Callbacks + + private SvnError SimpleAuth(out SvnAuthCredSimple svnCredentials, IntPtr baton, + AprString realm, AprString username, bool maySave, AprPool pool) + { + svnCredentials = SvnAuthCredSimple.Alloc(pool); + svnCredentials.Username = new AprString(m_svnuser, pool); + svnCredentials.Password = new AprString(m_svnpass, pool); + svnCredentials.MaySave = false; + return SvnError.NoError; + } + + private SvnError GetCommitLogCallback(out AprString logMessage, out SvnPath tmpFile, AprArray commitItems, IntPtr baton, AprPool pool) + { + if (!commitItems.IsNull) + { + foreach (SvnClientCommitItem2 item in commitItems) + { + m_log.Debug("[SVNBACKUP]: ... " + Path.GetFileName(item.Path.ToString()) + " (" + item.Kind.ToString() + ") r" + item.Revision.ToString()); + } + } + + string msg = "Region Backup (" + System.Environment.MachineName + " at " + DateTime.UtcNow + " UTC)"; + + m_log.Debug("[SVNBACKUP]: Saved with message: " + msg); + + logMessage = new AprString(msg, pool); + tmpFile = new SvnPath(pool); + + return (SvnError.NoError); + } + + #endregion + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource source) + { + try + { + if (!source.Configs["SVN"].GetBoolean("Enabled", false)) + return; + + m_enabled = true; + + m_svndir = source.Configs["SVN"].GetString("Directory", m_svndir); + m_svnurl = source.Configs["SVN"].GetString("URL", m_svnurl); + m_svnuser = source.Configs["SVN"].GetString("Username", m_svnuser); + m_svnpass = source.Configs["SVN"].GetString("Password", m_svnpass); + m_installBackupOnLoad = source.Configs["SVN"].GetBoolean("ImportOnStartup", m_installBackupOnLoad); + m_svnAutoSave = source.Configs["SVN"].GetBoolean("Autosave", m_svnAutoSave); + m_svnperiod = new TimeSpan(0, source.Configs["SVN"].GetInt("AutosavePeriod", (int) m_svnperiod.TotalMinutes), 0); + } + catch (Exception) + { + } + + lock (m_scenes) + { + m_scenes.Add(scene); + } + //Only register it once, to prevent command being executed x*region times + if (m_scenes.Count == 1) + { + scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; + } + } + + public void PostInitialise() + { + if (m_enabled == false) + return; + + if (m_svnAutoSave) + { + m_timer.Interval = m_svnperiod.TotalMilliseconds; + m_timer.Elapsed += m_timer_Elapsed; + m_timer.AutoReset = true; + m_timer.Start(); + } + + m_log.Info("[SVNBACKUP]: Connecting to SVN server " + m_svnurl + " ..."); + SetupSvnProvider(); + + m_log.Info("[SVNBACKUP]: Creating repository in " + m_svndir + "."); + CreateSvnDirectory(); + CheckoutSvn(); + SetupSerialiser(); + + if (m_installBackupOnLoad) + { + m_log.Info("[SVNBACKUP]: Importing latest SVN revision to scenes..."); + foreach (Scene scene in m_scenes) + { + LoadRegion(scene); + } + } + } + + public void Close() + { + } + + public string Name + { + get { return "SvnBackupModule"; } + } + + public bool IsSharedModule + { + get { return true; } + } + + #endregion + + private void EventManager_OnPluginConsole(string[] args) + { + if (args[0] == "svn" && args[1] == "save") + { + SaveAllRegions(); + } + if (args.Length == 2) + { + if (args[0] == "svn" && args[1] == "load") + { + LoadAllScenes(); + } + } + if (args.Length == 3) + { + if (args[0] == "svn" && args[1] == "load") + { + LoadAllScenes(Int32.Parse(args[2])); + } + } + if (args.Length == 3) + { + if (args[0] == "svn" && args[1] == "load-region") + { + LoadScene(args[2]); + } + } + if (args.Length == 4) + { + if (args[0] == "svn" && args[1] == "load-region") + { + LoadScene(args[2], Int32.Parse(args[3])); + } + } + } + + public void LoadScene(string name) + { + CheckoutSvn(); + + foreach (Scene scene in m_scenes) + { + if (scene.RegionInfo.RegionName.ToLower().Equals(name.ToLower())) + { + LoadRegion(scene); + return; + } + } + m_log.Warn("[SVNBACKUP]: No region loaded - unable to find matching name."); + } + + public void LoadScene(string name, int revision) + { + CheckoutSvn(new SvnRevision(revision)); + + foreach (Scene scene in m_scenes) + { + if (scene.RegionInfo.RegionName.ToLower().Equals(name.ToLower())) + { + LoadRegion(scene); + return; + } + } + m_log.Warn("[SVNBACKUP]: No region loaded - unable to find matching name."); + } + + public void LoadAllScenes() + { + CheckoutSvn(); + + foreach (Scene scene in m_scenes) + { + LoadRegion(scene); + } + } + + public void LoadAllScenes(int revision) + { + CheckoutSvn(new SvnRevision(revision)); + + foreach (Scene scene in m_scenes) + { + LoadRegion(scene); + } + } + + private void m_timer_Elapsed(object sender, ElapsedEventArgs e) + { + SaveAllRegions(); + } + + private void SetupSerialiser() + { + if (m_scenes.Count > 0) + m_serialiser = m_scenes[0].RequestModuleInterface(); + } + + private void SetupSvnProvider() + { + m_svnClient = new SvnClient(); + m_svnClient.AddUsernameProvider(); + m_svnClient.AddPromptProvider(new SvnAuthProviderObject.SimplePrompt(SimpleAuth), IntPtr.Zero, 2); + m_svnClient.OpenAuth(); + m_svnClient.Context.LogMsgFunc2 = new SvnDelegate(new SvnClient.GetCommitLog2(GetCommitLogCallback)); + } + + private void CreateSvnDirectory() + { + if (!Directory.Exists(m_svndir)) + Directory.CreateDirectory(m_svndir); + } + } +} diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs new file mode 100644 index 0000000..3ba8a97 --- /dev/null +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -0,0 +1,1060 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.Net; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.World.NPC +{ + public class NPCAvatar : IClientAPI + { + private readonly string m_firstname; + private readonly string m_lastname; + private readonly Vector3 m_startPos; + private readonly UUID m_uuid = UUID.Random(); + private readonly Scene m_scene; + + + public NPCAvatar(string firstname, string lastname, Vector3 position, Scene scene) + { + m_firstname = firstname; + m_lastname = lastname; + m_startPos = position; + m_scene = scene; + } + + public IScene Scene + { + get { return m_scene; } + } + + public void Say(string message) + { + SendOnChatFromClient(message, ChatTypeEnum.Say); + } + + public void Shout(string message) + { + SendOnChatFromClient(message, ChatTypeEnum.Shout); + } + + public void Whisper(string message) + { + SendOnChatFromClient(message, ChatTypeEnum.Whisper); + } + + public void Broadcast(string message) + { + SendOnChatFromClient(message, ChatTypeEnum.Broadcast); + } + + public void GiveMoney(UUID target, int amount) + { + OnMoneyTransferRequest(m_uuid, target, amount, 1, "Payment"); + } + + public void InstantMessage(UUID target, string message) + { + OnInstantMessage(this, new GridInstantMessage(m_scene, + m_uuid, m_firstname + " " + m_lastname, + target, 0, false, message, + UUID.Zero, false, Position, new byte[0])); + } + + public void SendAgentOffline(UUID[] agentIDs) + { + + } + + public void SendAgentOnline(UUID[] agentIDs) + { + + } + public void SendSitResponse(UUID TargetID, Vector3 OffsetPos, Quaternion SitOrientation, bool autopilot, + Vector3 CameraAtOffset, Vector3 CameraEyeOffset, bool ForceMouseLook) + { + + } + + public void SendAdminResponse(UUID Token, uint AdminLevel) + { + + } + + public void SendGroupMembership(GroupMembershipData[] GroupMembership) + { + + } + + public UUID GetDefaultAnimation(string name) + { + return UUID.Zero; + } + + public Vector3 Position + { + get { return m_scene.Entities[m_uuid].AbsolutePosition; } + set { m_scene.Entities[m_uuid].AbsolutePosition = value; } + } + + public bool SendLogoutPacketWhenClosing + { + set { } + } + + #region Internal Functions + + private void SendOnChatFromClient(string message, ChatTypeEnum chatType) + { + OSChatMessage chatFromClient = new OSChatMessage(); + chatFromClient.Channel = 0; + chatFromClient.From = Name; + chatFromClient.Message = message; + chatFromClient.Position = StartPos; + chatFromClient.Scene = m_scene; + chatFromClient.Sender = this; + chatFromClient.SenderUUID = AgentId; + chatFromClient.Type = chatType; + + OnChatFromClient(this, chatFromClient); + } + + #endregion + + #region Event Definitions IGNORE + +// disable warning: public events constituting public API +#pragma warning disable 67 + public event Action OnLogout; + public event ObjectPermissions OnObjectPermissions; + + public event MoneyTransferRequest OnMoneyTransferRequest; + public event ParcelBuy OnParcelBuy; + public event Action OnConnectionClosed; + public event GenericMessage OnGenericMessage; + public event ImprovedInstantMessage OnInstantMessage; + public event ChatMessage OnChatFromClient; + public event TextureRequest OnRequestTexture; + public event RezObject OnRezObject; + public event ModifyTerrain OnModifyTerrain; + public event SetAppearance OnSetAppearance; + public event AvatarNowWearing OnAvatarNowWearing; + public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; + public event UUIDNameRequest OnDetachAttachmentIntoInv; + public event ObjectAttach OnObjectAttach; + public event ObjectDeselect OnObjectDetach; + public event ObjectDrop OnObjectDrop; + public event StartAnim OnStartAnim; + public event StopAnim OnStopAnim; + public event LinkObjects OnLinkObjects; + public event DelinkObjects OnDelinkObjects; + public event RequestMapBlocks OnRequestMapBlocks; + public event RequestMapName OnMapNameRequest; + public event TeleportLocationRequest OnTeleportLocationRequest; + public event TeleportLandmarkRequest OnTeleportLandmarkRequest; + public event DisconnectUser OnDisconnectUser; + public event RequestAvatarProperties OnRequestAvatarProperties; + public event SetAlwaysRun OnSetAlwaysRun; + + public event DeRezObject OnDeRezObject; + public event Action OnRegionHandShakeReply; + public event GenericCall2 OnRequestWearables; + public event GenericCall2 OnCompleteMovementToRegion; + public event UpdateAgent OnAgentUpdate; + public event AgentRequestSit OnAgentRequestSit; + public event AgentSit OnAgentSit; + public event AvatarPickerRequest OnAvatarPickerRequest; + public event Action OnRequestAvatarsData; + public event AddNewPrim OnAddPrim; + public event RequestGodlikePowers OnRequestGodlikePowers; + public event GodKickUser OnGodKickUser; + public event ObjectDuplicate OnObjectDuplicate; + public event GrabObject OnGrabObject; + public event ObjectSelect OnDeGrabObject; + public event MoveObject OnGrabUpdate; + public event ViewerEffectEventHandler OnViewerEffect; + + public event FetchInventory OnAgentDataUpdateRequest; + public event TeleportLocationRequest OnSetStartLocationRequest; + + public event UpdateShape OnUpdatePrimShape; + public event ObjectExtraParams OnUpdateExtraParams; + public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; + public event ObjectSelect OnObjectSelect; + public event GenericCall7 OnObjectDescription; + public event GenericCall7 OnObjectName; + public event GenericCall7 OnObjectClickAction; + public event GenericCall7 OnObjectMaterial; + public event UpdatePrimFlags OnUpdatePrimFlags; + public event UpdatePrimTexture OnUpdatePrimTexture; + public event UpdateVector OnUpdatePrimGroupPosition; + public event UpdateVector OnUpdatePrimSinglePosition; + public event UpdatePrimRotation OnUpdatePrimGroupRotation; + public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; + public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; + public event UpdateVector OnUpdatePrimScale; + public event UpdateVector OnUpdatePrimGroupScale; + public event StatusChange OnChildAgentStatus; + public event GenericCall2 OnStopMovement; + public event Action OnRemoveAvatar; + + public event CreateNewInventoryItem OnCreateNewInventoryItem; + public event CreateInventoryFolder OnCreateNewInventoryFolder; + public event UpdateInventoryFolder OnUpdateInventoryFolder; + public event MoveInventoryFolder OnMoveInventoryFolder; + public event RemoveInventoryFolder OnRemoveInventoryFolder; + public event RemoveInventoryItem OnRemoveInventoryItem; + public event FetchInventoryDescendents OnFetchInventoryDescendents; + public event PurgeInventoryDescendents OnPurgeInventoryDescendents; + public event FetchInventory OnFetchInventory; + public event RequestTaskInventory OnRequestTaskInventory; + public event UpdateInventoryItem OnUpdateInventoryItem; + public event CopyInventoryItem OnCopyInventoryItem; + public event MoveInventoryItem OnMoveInventoryItem; + public event UDPAssetUploadRequest OnAssetUploadRequest; + public event XferReceive OnXferReceive; + public event RequestXfer OnRequestXfer; + public event AbortXfer OnAbortXfer; + public event ConfirmXfer OnConfirmXfer; + public event RezScript OnRezScript; + public event UpdateTaskInventory OnUpdateTaskInventory; + public event MoveTaskInventory OnMoveTaskItem; + public event RemoveTaskInventory OnRemoveTaskItem; + public event RequestAsset OnRequestAsset; + + public event UUIDNameRequest OnNameFromUUIDRequest; + public event UUIDNameRequest OnUUIDGroupNameRequest; + + public event ParcelPropertiesRequest OnParcelPropertiesRequest; + public event ParcelDivideRequest OnParcelDivideRequest; + public event ParcelJoinRequest OnParcelJoinRequest; + public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; + public event ParcelAbandonRequest OnParcelAbandonRequest; + public event ParcelGodForceOwner OnParcelGodForceOwner; + public event ParcelReclaim OnParcelReclaim; + public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; + public event ParcelAccessListRequest OnParcelAccessListRequest; + public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; + public event ParcelSelectObjects OnParcelSelectObjects; + public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; + public event ObjectDeselect OnObjectDeselect; + public event RegionInfoRequest OnRegionInfoRequest; + public event EstateCovenantRequest OnEstateCovenantRequest; + public event RequestTerrain OnRequestTerrain; + public event RequestTerrain OnUploadTerrain; + public event ObjectDuplicateOnRay OnObjectDuplicateOnRay; + + public event FriendActionDelegate OnApproveFriendRequest; + public event FriendActionDelegate OnDenyFriendRequest; + public event FriendshipTermination OnTerminateFriendship; + + public event EconomyDataRequest OnEconomyDataRequest; + public event MoneyBalanceRequest OnMoneyBalanceRequest; + public event UpdateAvatarProperties OnUpdateAvatarProperties; + + public event ObjectIncludeInSearch OnObjectIncludeInSearch; + public event UUIDNameRequest OnTeleportHomeRequest; + + public event ScriptAnswer OnScriptAnswer; + public event RequestPayPrice OnRequestPayPrice; + public event ObjectSaleInfo OnObjectSaleInfo; + public event ObjectBuy OnObjectBuy; + public event BuyObjectInventory OnBuyObjectInventory; + public event AgentSit OnUndo; + + public event ForceReleaseControls OnForceReleaseControls; + public event GodLandStatRequest OnLandStatRequest; + public event RequestObjectPropertiesFamily OnObjectGroupRequest; + + public event DetailedEstateDataRequest OnDetailedEstateDataRequest; + public event SetEstateFlagsRequest OnSetEstateFlagsRequest; + public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; + public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; + public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; + public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; + public event SetRegionTerrainSettings OnSetRegionTerrainSettings; + public event BakeTerrain OnBakeTerrain; + public event EstateRestartSimRequest OnEstateRestartSimRequest; + public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; + public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; + public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; + public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; + public event EstateDebugRegionRequest OnEstateDebugRegionRequest; + public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; + public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; + public event EstateChangeInfo OnEstateChangeInfo; + public event ScriptReset OnScriptReset; + public event GetScriptRunning OnGetScriptRunning; + public event SetScriptRunning OnSetScriptRunning; + public event UpdateVector OnAutoPilotGo; + + public event TerrainUnacked OnUnackedTerrain; + + public event RegionHandleRequest OnRegionHandleRequest; + public event ParcelInfoRequest OnParcelInfoRequest; + + public event ActivateGesture OnActivateGesture; + public event DeactivateGesture OnDeactivateGesture; + public event ObjectOwner OnObjectOwner; + + public event DirPlacesQuery OnDirPlacesQuery; + public event DirFindQuery OnDirFindQuery; + public event DirLandQuery OnDirLandQuery; + public event DirPopularQuery OnDirPopularQuery; + public event DirClassifiedQuery OnDirClassifiedQuery; + public event EventInfoRequest OnEventInfoRequest; + public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; + + public event MapItemRequest OnMapItemRequest; + + public event OfferCallingCard OnOfferCallingCard; + public event AcceptCallingCard OnAcceptCallingCard; + public event DeclineCallingCard OnDeclineCallingCard; + public event SoundTrigger OnSoundTrigger; + + public event StartLure OnStartLure; + public event TeleportLureRequest OnTeleportLureRequest; + public event NetworkStats OnNetworkStatsUpdate; + + public event ClassifiedInfoRequest OnClassifiedInfoRequest; + public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; + public event ClassifiedDelete OnClassifiedDelete; + public event ClassifiedDelete OnClassifiedGodDelete; + + public event EventNotificationAddRequest OnEventNotificationAddRequest; + public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; + public event EventGodDelete OnEventGodDelete; + + public event ParcelDwellRequest OnParcelDwellRequest; + + public event UserInfoRequest OnUserInfoRequest; + public event UpdateUserInfo OnUpdateUserInfo; + +#pragma warning restore 67 + + #endregion + + public void ActivateGesture(UUID assetId, UUID gestureId) + { + } + public void DeactivateGesture(UUID assetId, UUID gestureId) + { + } + + #region Overrriden Methods IGNORE + + public virtual Vector3 StartPos + { + get { return m_startPos; } + set { } + } + + public virtual UUID AgentId + { + get { return m_uuid; } + } + + public UUID SessionId + { + get { return UUID.Zero; } + } + + public UUID SecureSessionId + { + get { return UUID.Zero; } + } + + public virtual string FirstName + { + get { return m_firstname; } + } + + public virtual string LastName + { + get { return m_lastname; } + } + + public virtual String Name + { + get { return FirstName + " " + LastName; } + } + + public bool IsActive + { + get { return true; } + set { } + } + + public UUID ActiveGroupId + { + get { return UUID.Zero; } + } + + public string ActiveGroupName + { + get { return String.Empty; } + } + + public ulong ActiveGroupPowers + { + get { return 0; } + } + + public bool IsGroupMember(UUID groupID) + { + return false; + } + + public ulong GetGroupPowers(UUID groupID) + { + return 0; + } + + public virtual int NextAnimationSequenceNumber + { + get { return 1; } + } + + public virtual void SendWearables(AvatarWearable[] wearables, int serial) + { + } + + public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry) + { + } + + public virtual void Kick(string message) + { + } + + public virtual void SendStartPingCheck(byte seq) + { + } + + public virtual void SendAvatarPickerReply(AvatarPickerReplyAgentDataArgs AgentData, List Data) + { + } + + public virtual void SendAgentDataUpdate(UUID agentid, UUID activegroupid, string firstname, string lastname, ulong grouppowers, string groupname, string grouptitle) + { + + } + + public virtual void SendKillObject(ulong regionHandle, uint localID) + { + } + + public virtual void SetChildAgentThrottle(byte[] throttle) + { + } + public byte[] GetThrottlesPacked(float multiplier) + { + return new byte[0]; + } + + + public virtual void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId) + { + } + + public virtual void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, byte source, byte audible) + { + } + + public virtual void SendChatMessage(byte[] message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, byte source, byte audible) + { + } + + public void SendInstantMessage(UUID fromAgent, string message, UUID toAgent, string fromName, byte dialog, uint timeStamp) + { + + } + + public void SendInstantMessage(UUID fromAgent, string message, UUID toAgent, string fromName, byte dialog, uint timeStamp, UUID transationID, bool fromGroup, byte[] binaryBucket) + { + + } + + public void SendGenericMessage(string method, List message) + { + + } + + public virtual void SendLayerData(float[] map) + { + } + + public virtual void SendLayerData(int px, int py, float[] map) + { + } + public virtual void SendLayerData(int px, int py, float[] map, bool track) + { + } + + public virtual void SendWindData(Vector2[] windSpeeds) { } + + public virtual void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) + { + } + + public virtual void InformClientOfNeighbour(ulong neighbourHandle, IPEndPoint neighbourExternalEndPoint) + { + } + + public virtual AgentCircuitData RequestClientInfo() + { + return new AgentCircuitData(); + } + + public virtual void CrossRegion(ulong newRegionHandle, Vector3 pos, Vector3 lookAt, + IPEndPoint newRegionExternalEndPoint, string capsURL) + { + } + + public virtual void SendMapBlock(List mapBlocks, uint flag) + { + } + + public virtual void SendLocalTeleport(Vector3 position, Vector3 lookAt, uint flags) + { + } + + public virtual void SendRegionTeleport(ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, + uint locationID, uint flags, string capsURL) + { + } + + public virtual void SendTeleportFailed(string reason) + { + } + + public virtual void SendTeleportLocationStart() + { + } + + public virtual void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance) + { + } + + public virtual void SendPayPrice(UUID objectID, int[] payPrice) + { + } + + public virtual void SendAvatarData(ulong regionHandle, string firstName, string lastName, string grouptitle, UUID avatarID, + uint avatarLocalID, Vector3 Pos, byte[] textureEntry, uint parentID, Quaternion rotation) + { + } + + public virtual void SendAvatarTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, + Vector3 position, Vector3 velocity, Quaternion rotation) + { + } + + public virtual void SendCoarseLocationUpdate(List CoarseLocations) + { + } + + public virtual void AttachObject(uint localID, Quaternion rotation, byte attachPoint, UUID ownerID) + { + } + + public virtual void SendDialog(string objectname, UUID objectID, UUID ownerID, string msg, UUID textureID, int ch, string[] buttonlabels) + { + } + + public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, + PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, + Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, + UUID objectID, UUID ownerID, string text, byte[] color, + uint parentID, + byte[] particleSystem, byte clickAction, byte material) + { + } + public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, + PrimitiveBaseShape primShape, Vector3 pos, Vector3 vel, + Vector3 acc, Quaternion rotation, Vector3 rvel, uint flags, + UUID objectID, UUID ownerID, string text, byte[] color, + uint parentID, + byte[] particleSystem, byte clickAction, byte material, byte[] textureanimation, + bool attachment, uint AttachmentPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) + { + } + public virtual void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, + Vector3 position, Quaternion rotation, Vector3 velocity, + Vector3 rotationalvelocity, byte state, UUID AssetId, UUID ownerID, int attachPoint) + { + } + + public virtual void SendInventoryFolderDetails(UUID ownerID, UUID folderID, + List items, + List folders, + bool fetchFolders, + bool fetchItems) + { + } + + public virtual void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item) + { + } + + public virtual void SendInventoryItemCreateUpdate(InventoryItemBase Item) + { + } + + public virtual void SendRemoveInventoryItem(UUID itemID) + { + } + + /// IClientAPI.SendBulkUpdateInventory(InventoryItemBase) + public virtual void SendBulkUpdateInventory(InventoryItemBase item) + { + } + + public virtual void SendBulkUpdateInventory(InventoryFolderBase folderBase) + {} + + public void SendTakeControls(int controls, bool passToAgent, bool TakeControls) + { + } + + public virtual void SendTaskInventory(UUID taskID, short serial, byte[] fileName) + { + } + + public virtual void SendXferPacket(ulong xferID, uint packet, byte[] data) + { + } + + public virtual void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit, + int PriceGroupCreate, int PriceObjectClaim, float PriceObjectRent, float PriceObjectScaleFactor, + int PriceParcelClaim, float PriceParcelClaimFactor, int PriceParcelRent, int PricePublicObjectDecay, + int PricePublicObjectDelete, int PriceRentLight, int PriceUpload, int TeleportMinPrice, float TeleportPriceExponent) + { + + } + public virtual void SendNameReply(UUID profileId, string firstname, string lastname) + { + } + + public virtual void SendPreLoadSound(UUID objectID, UUID ownerID, UUID soundID) + { + } + + public virtual void SendPlayAttachedSound(UUID soundID, UUID objectID, UUID ownerID, float gain, + byte flags) + { + } + + public void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, float gain) + { + } + + public void SendAttachedSoundGainChange(UUID objectID, float gain) + { + + } + + public void SendAlertMessage(string message) + { + } + + public void SendAgentAlertMessage(string message, bool modal) + { + } + + public void SendSystemAlertMessage(string message) + { + } + + public void SendLoadURL(string objectname, UUID objectID, UUID ownerID, bool groupOwned, string message, + string url) + { + } + + public virtual void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args) + { + if (OnRegionHandShakeReply != null) + { + OnRegionHandShakeReply(this); + } + + if (OnCompleteMovementToRegion != null) + { + OnCompleteMovementToRegion(); + } + } + public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) + { + } + + public void SendConfirmXfer(ulong xferID, uint PacketID) + { + } + + public void SendXferRequest(ulong XferID, short AssetType, UUID vFileID, byte FilePath, byte[] FileName) + { + } + + public void SendInitiateDownload(string simFileName, string clientFileName) + { + } + + public void SendImageFirstPart(ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec) + { + } + + public void SendImageNotFound(UUID imageid) + { + } + + public void SendImageNextPart(ushort partNumber, UUID imageUuid, byte[] imageData) + { + } + + public void SendShutdownConnectionNotice() + { + } + + public void SendSimStats(SimStats stats) + { + } + + public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, + uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, + uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, + UUID LastOwnerID, string ObjectName, string Description) + { + } + + public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, + UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, + UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, + string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, + uint BaseMask, byte saleType, int salePrice) + { + } + + public bool AddMoney(int debit) + { + return false; + } + + public void SendSunPos(Vector3 sunPos, Vector3 sunVel, ulong time, uint dlen, uint ylen, float phase) + { + } + + public void SendViewerEffect(ViewerEffectPacket.EffectBlock[] effectBlocks) + { + } + + public void SendViewerTime(int phase) + { + } + + public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, + string flAbout, uint flags, UUID flImageID, UUID imageID, string profileURL, + UUID partnerID) + { + } + + public void SendAsset(AssetRequestToClient req) + { + } + + public void SendTexture(AssetBase TextureAsset) + { + } + + public void SetDebugPacketLevel(int newDebug) + { + } + + public void InPacket(object NewPack) + { + } + + public void ProcessInPacket(Packet NewPack) + { + } + + public void Close(bool ShutdownCircuit) + { + } + + public void Start() + { + } + + public void Stop() + { + } + + private uint m_circuitCode; + + public uint CircuitCode + { + get { return m_circuitCode; } + set { m_circuitCode = value; } + } + + public void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message) + { + + } + public void SendLogoutPacket() + { + } + + public void Terminate() + { + } + + public ClientInfo GetClientInfo() + { + return null; + } + + public void SetClientInfo(ClientInfo info) + { + } + + public void SendScriptQuestion(UUID objectID, string taskName, string ownerName, UUID itemID, int question) + { + } + public void SendHealth(float health) + { + } + + public void SendEstateManagersList(UUID invoice, UUID[] EstateManagers, uint estateID) + { + } + + public void SendBannedUserList(UUID invoice, EstateBan[] banlist, uint estateID) + { + } + + public void SendRegionInfoToEstateMenu(RegionInfoForEstateMenuArgs args) + { + } + public void SendEstateCovenantInformation(UUID covenant) + { + } + public void SendDetailedEstateData(UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, uint sunPosition, UUID covenant, string abuseEmail, UUID estateOwner) + { + } + + public void SendLandProperties(int sequence_id, bool snap_selection, int request_result, LandData landData, float simObjectBonusFactor,int parcelObjectCapacity, int simObjectCapacity, uint regionFlags) + { + } + public void SendLandAccessListData(List avatars, uint accessFlag, int localLandID) + { + } + public void SendForceClientSelectObjects(List objectIDs) + { + } + public void SendLandObjectOwners(Dictionary ownersAndCount) + { + } + public void SendLandParcelOverlay(byte[] data, int sequence_id) + { + } + + public void SendGroupNameReply(UUID groupLLUID, string GroupName) + { + } + + public void SendScriptRunningReply(UUID objectID, UUID itemID, bool running) + { + } + + public void SendLandStatReply(uint reportType, uint requestFlags, uint resultCount, LandStatReportItem[] lsrpia) + { + } + #endregion + + + public void SendParcelMediaCommand(uint flags, ParcelMediaCommandEnum command, float time) + { + } + + public void SendParcelMediaUpdate(string mediaUrl, UUID mediaTextureID, + byte autoScale, string mediaType, string mediaDesc, int mediaWidth, int mediaHeight, + byte mediaLoop) + { + } + + public void SendSetFollowCamProperties (UUID objectID, SortedDictionary parameters) + { + } + + public void SendClearFollowCamProperties (UUID objectID) + { + } + + public void SendRegionHandle (UUID regoinID, ulong handle) + { + } + + public void SendParcelInfo (RegionInfo info, LandData land, UUID parcelID, uint x, uint y) + { + } + + public void SetClientOption(string option, string value) + { + } + + public string GetClientOption(string option) + { + return string.Empty; + } + + public void SendScriptTeleportRequest (string objName, string simName, Vector3 pos, Vector3 lookAt) + { + } + + public void SendDirPlacesReply(UUID queryID, DirPlacesReplyData[] data) + { + } + + public void SendDirPeopleReply(UUID queryID, DirPeopleReplyData[] data) + { + } + + public void SendDirEventsReply(UUID queryID, DirEventsReplyData[] data) + { + } + + public void SendDirGroupsReply(UUID queryID, DirGroupsReplyData[] data) + { + } + + public void SendDirClassifiedReply(UUID queryID, DirClassifiedReplyData[] data) + { + } + + public void SendDirLandReply(UUID queryID, DirLandReplyData[] data) + { + } + + public void SendDirPopularReply(UUID queryID, DirPopularReplyData[] data) + { + } + + public void SendMapItemReply(mapItemReply[] replies, uint mapitemtype, uint flags) + { + } + + public void KillEndDone() + { + } + + public void SendEventInfoReply (EventData info) + { + } + + public void SendOfferCallingCard (UUID destID, UUID transactionID) + { + } + + public void SendAcceptCallingCard (UUID transactionID) + { + } + + public void SendDeclineCallingCard (UUID transactionID) + { + } + + public void SendJoinGroupReply(UUID groupID, bool success) + { + } + + public void SendEjectGroupMemberReply(UUID agentID, UUID groupID, bool success) + { + } + + public void SendLeaveGroupReply(UUID groupID, bool success) + { + } + + public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data) + { + } + + public void SendTerminateFriend(UUID exFriendID) + { + } + + #region IClientAPI Members + + + public bool AddGenericPacketHandler(string MethodName, GenericMessage handler) + { + throw new NotImplementedException(); + } + + public void SendAvatarClassifiedReply(UUID targetID, UUID[] classifiedID, string[] name) + { + } + + public void SendClassifiedInfoReply(UUID classifiedID, UUID creatorID, uint creationDate, uint expirationDate, uint category, string name, string description, UUID parcelID, uint parentEstate, UUID snapshotID, string simName, Vector3 globalPos, string parcelName, byte classifiedFlags, int price) + { + } + + public void SendAgentDropGroup(UUID groupID) + { + } + + public void SendAvatarNotesReply(UUID targetID, string text) + { + } + + public void SendAvatarPicksReply(UUID targetID, Dictionary picks) + { + } + + public void SendAvatarClassifiedReply(UUID targetID, Dictionary classifieds) + { + } + + public void SendParcelDwellReply(int localID, UUID parcelID, float dwell) + { + } + + public void SendUserInfoReply(bool imViaEmail, bool visible, string email) + { + } + + #endregion + } +} diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs new file mode 100644 index 0000000..9dace33 --- /dev/null +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -0,0 +1,68 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 OpenMetaverse; +using Nini.Config; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.World.NPC +{ + public class NPCModule : IRegionModule + { + // private const bool m_enabled = false; + + public void Initialise(Scene scene, IConfigSource source) + { + // if (m_enabled) + // { + // NPCAvatar testAvatar = new NPCAvatar("Jack", "NPC", new Vector3(128, 128, 40), scene); + // NPCAvatar testAvatar2 = new NPCAvatar("Jill", "NPC", new Vector3(136, 128, 40), scene); + // scene.AddNewClient(testAvatar, false); + // scene.AddNewClient(testAvatar2, false); + // } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "NPCModule"; } + } + + public bool IsSharedModule + { + get { return true; } + } + } +} diff --git a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs new file mode 100644 index 0000000..94ebcac --- /dev/null +++ b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs @@ -0,0 +1,304 @@ +/* + * Copyright (c) Contributors, http://opensimulator.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 System.Collections.Generic; +using System.Reflection; +using System.Timers; +using OpenMetaverse; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.OptionalModules.World.TreePopulator +{ + /// + /// Version 2.0 - Very hacky compared to the original. Will fix original and release as 0.3 later. + /// + public class TreePopulatorModule : IRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private Scene m_scene; + + public double m_tree_density = 50.0; // Aim for this many per region + public double m_tree_updates = 1000.0; // MS between updates + private bool m_active_trees = false; + private List m_trees; + Timer CalculateTrees; + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource config) + { + try + { + m_tree_density = config.Configs["Trees"].GetDouble("tree_density", m_tree_density); + m_active_trees = config.Configs["Trees"].GetBoolean("active_trees", m_active_trees); + } + catch (Exception) + { + } + + m_trees = new List(); + m_scene = scene; + + m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; + + if (m_active_trees) + activeizeTreeze(true); + + m_log.Debug("[TREES]: Initialised tree module"); + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "TreePopulatorModule"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + #endregion + + private void EventManager_OnPluginConsole(string[] args) + { + if (args.Length == 1) + { + if (args[0] == "tree") + { + UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; + if (uuid == UUID.Zero) + uuid = m_scene.RegionInfo.MasterAvatarAssignedUUID; + m_log.Debug("[TREES]: New tree planting"); + CreateTree(uuid, new Vector3(128.0f, 128.0f, 0.0f)); + + } + } + + if (args.Length == 2 || args.Length == 3) + { + if (args[1] == "active") + { + if (args.Length >= 3) + { + if (args[2] == "true" && !m_active_trees) + { + m_active_trees = true; + activeizeTreeze(m_active_trees); + m_log.Info("[TREES]: Activizing Trees"); + } + if (args[2] == "false" && m_active_trees) + { + m_active_trees = false; + activeizeTreeze(m_active_trees); + m_log.Info("[TREES]: Trees no longer Active, for now..."); + } + } + else + { + m_log.Info("[TREES]: When setting the tree module active via the console, you must specify true or false"); + } + } + } + + } + + private void activeizeTreeze(bool activeYN) + { + if (activeYN) + { + CalculateTrees = new Timer(m_tree_updates); + CalculateTrees.Elapsed += CalculateTrees_Elapsed; + CalculateTrees.Start(); + } + else + { + CalculateTrees.Stop(); + } + } + + private void growTrees() + { + foreach (UUID tree in m_trees) + { + if (m_scene.Entities.ContainsKey(tree)) + { + SceneObjectPart s_tree = ((SceneObjectGroup) m_scene.Entities[tree]).RootPart; + + // 100 seconds to grow 1m + s_tree.Scale += new Vector3(0.1f, 0.1f, 0.1f); + s_tree.SendFullUpdateToAllClients(); + //s_tree.ScheduleTerseUpdate(); + } + else + { + m_trees.Remove(tree); + } + } + } + + private void seedTrees() + { + foreach (UUID tree in m_trees) + { + if (m_scene.Entities.ContainsKey(tree)) + { + SceneObjectPart s_tree = ((SceneObjectGroup) m_scene.Entities[tree]).RootPart; + + if (s_tree.Scale.X > 0.5) + { + if (Util.RandomClass.NextDouble() > 0.75) + { + SpawnChild(s_tree); + } + } + } + else + { + m_trees.Remove(tree); + } + } + } + + private void killTrees() + { + foreach (UUID tree in m_trees) + { + double killLikelyhood = 0.0; + + if (m_scene.Entities.ContainsKey(tree)) + { + SceneObjectPart selectedTree = ((SceneObjectGroup) m_scene.Entities[tree]).RootPart; + double selectedTreeScale = Math.Sqrt(Math.Pow(selectedTree.Scale.X, 2) + + Math.Pow(selectedTree.Scale.Y, 2) + + Math.Pow(selectedTree.Scale.Z, 2)); + + foreach (UUID picktree in m_trees) + { + if (picktree != tree) + { + SceneObjectPart pickedTree = ((SceneObjectGroup) m_scene.Entities[picktree]).RootPart; + + double pickedTreeScale = Math.Sqrt(Math.Pow(pickedTree.Scale.X, 2) + + Math.Pow(pickedTree.Scale.Y, 2) + + Math.Pow(pickedTree.Scale.Z, 2)); + + double pickedTreeDistance = Math.Sqrt(Math.Pow(Math.Abs(pickedTree.AbsolutePosition.X - selectedTree.AbsolutePosition.X), 2) + + Math.Pow(Math.Abs(pickedTree.AbsolutePosition.Y - selectedTree.AbsolutePosition.Y), 2) + + Math.Pow(Math.Abs(pickedTree.AbsolutePosition.Z - selectedTree.AbsolutePosition.Z), 2)); + + killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1; + } + } + + if (Util.RandomClass.NextDouble() < killLikelyhood) + { + m_scene.DeleteSceneObject(selectedTree.ParentGroup, false); + m_trees.Remove(selectedTree.ParentGroup.UUID); + + m_scene.ForEachClient(delegate(IClientAPI controller) + { + controller.SendKillObject(m_scene.RegionInfo.RegionHandle, + selectedTree.LocalId); + }); + + break; + } + selectedTree.SetText(killLikelyhood.ToString(), new Vector3(1.0f, 1.0f, 1.0f), 1.0); + } + else + { + m_trees.Remove(tree); + } + } + } + + private void SpawnChild(SceneObjectPart s_tree) + { + Vector3 position = new Vector3(); + + position.X = s_tree.AbsolutePosition.X + (1 * (-1 * Util.RandomClass.Next(1))); + if (position.X > 255) + position.X = 255; + if (position.X < 0) + position.X = 0; + position.Y = s_tree.AbsolutePosition.Y + (1 * (-1 * Util.RandomClass.Next(1))); + if (position.Y > 255) + position.Y = 255; + if (position.Y < 0) + position.Y = 0; + + double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); + double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); + + position.X += (float) randX; + position.Y += (float) randY; + + UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; + if (uuid == UUID.Zero) + uuid = m_scene.RegionInfo.MasterAvatarAssignedUUID; + + CreateTree(uuid, position); + } + + private void CreateTree(UUID uuid, Vector3 position) + { + position.Z = (float) m_scene.Heightmap[(int) position.X, (int) position.Y]; + + IVegetationModule module = m_scene.RequestModuleInterface(); + + if (null == module) + return; + + SceneObjectGroup tree + = module.AddTree( + uuid, UUID.Zero, new Vector3(0.1f, 0.1f, 0.1f), Quaternion.Identity, position, Tree.Cypress1, false); + + m_trees.Add(tree.UUID); + tree.SendGroupFullUpdate(); + } + + private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e) + { + growTrees(); + seedTrees(); + killTrees(); + } + } +} -- cgit v1.1